From bfef5b742aff9eddb8b096fa949665d9d073ac64 Mon Sep 17 00:00:00 2001 From: linwumingshi Date: Thu, 4 Sep 2025 17:48:27 +0800 Subject: [PATCH] fix: :bug: support path attribute in `@FeignClient` and refactor annotation constants - Support the `path` attribute of `@FeignClient` for base URL configuration in API documentation. - Refactor `SpringBootDocBuildTemplate` to extract mapping annotations into `buildSpringMappingAnnotations()` method for better maintainability. - Add new constants in `DocAnnotationConstants`: `CONSUMES`, `PRODUCES`, `METHOD`, `PARAMS`, and `PATH_MAPPING_PROPS`. - Deprecate misnamed `REQUEST_HERDER` and introduce correct `REQUEST_HEADER` in `SpringMvcAnnotations`. - Update `SpringMVCRequestHeaderHandler` and `JavaClassValidateUtil` to use the new `REQUEST_HEADER` constant. - Improve code consistency by using constants instead of string literals across annotation processing. - Update copyright year to 2025 across relevant files. Fixes #1114 --- .../doc/constants/DocAnnotationConstants.java | 54 +++++- .../doc/constants/SpringMvcAnnotations.java | 8 +- .../SpringMVCRequestHeaderHandler.java | 2 +- .../template/SpringBootDocBuildTemplate.java | 176 ++++++++++-------- .../ly/doc/utils/JavaClassValidateUtil.java | 15 +- 5 files changed, 169 insertions(+), 86 deletions(-) diff --git a/src/main/java/com/ly/doc/constants/DocAnnotationConstants.java b/src/main/java/com/ly/doc/constants/DocAnnotationConstants.java index 15b37ba2e..a2c79a7a3 100644 --- a/src/main/java/com/ly/doc/constants/DocAnnotationConstants.java +++ b/src/main/java/com/ly/doc/constants/DocAnnotationConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2024 smart-doc + * Copyright (C) 2018-2025 smart-doc * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -215,4 +215,56 @@ public interface DocAnnotationConstants { */ String PATH_PARAM = "PathParam"; + /** + * The name of the 'consumes' attribute in Spring MVC request mapping annotations, + * used to specify the media types that the annotated handler method can consume. + *

+ * Corresponds to + * {@code org.springframework.web.bind.annotation.RequestMapping#consumes()} + *

+ */ + String CONSUMES = "consumes"; + + /** + * The name of the 'produces' attribute in Spring MVC request mapping annotations, + * used to specify the media types that the annotated handler method can produce. + *

+ * Corresponds to + * {@code org.springframework.web.bind.annotation.RequestMapping#produces()} + *

+ */ + String PRODUCES = "produces"; + + /** + * The name of the 'method' attribute in Spring MVC request mapping annotations, used + * to specify the HTTP request methods that the annotated handler method supports. + *

+ * Corresponds to + * {@code org.springframework.web.bind.annotation.RequestMapping#method()} + *

+ */ + String METHOD = "method"; + + /** + * The name of the 'params' attribute in Spring MVC request mapping annotations, used + * to specify HTTP request parameters that must be present for the mapping to be + * matched. + *

+ * Corresponds to + * {@code org.springframework.web.bind.annotation.RequestMapping#params()} + *

+ */ + String PARAMS = "params"; + + /** + * Common property names used to define request mapping paths. Typically used in + * annotations like {@code @RequestMapping}, {@code @GetMapping}, etc. Corresponds to + * 'value' and 'path' attributes which are synonyms in Spring MVC. + * + *

+ * Used to configure URL mapping paths for handler methods. + *

+ */ + String[] PATH_MAPPING_PROPS = { VALUE_PROP, PATH_PROP }; + } diff --git a/src/main/java/com/ly/doc/constants/SpringMvcAnnotations.java b/src/main/java/com/ly/doc/constants/SpringMvcAnnotations.java index 957297f3c..783aec135 100644 --- a/src/main/java/com/ly/doc/constants/SpringMvcAnnotations.java +++ b/src/main/java/com/ly/doc/constants/SpringMvcAnnotations.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2024 smart-doc + * Copyright (C) 2018-2025 smart-doc * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -40,8 +40,14 @@ public interface SpringMvcAnnotations { String DELETE_MAPPING = "DeleteMapping"; + @Deprecated String REQUEST_HERDER = "RequestHeader"; + /** + * RequestHeader + */ + String REQUEST_HEADER = "RequestHeader"; + String REQUEST_PARAM = "RequestParam"; String REQUEST_PART = "RequestPart"; diff --git a/src/main/java/com/ly/doc/handler/SpringMVCRequestHeaderHandler.java b/src/main/java/com/ly/doc/handler/SpringMVCRequestHeaderHandler.java index 258a8e1a1..3a86b8db3 100644 --- a/src/main/java/com/ly/doc/handler/SpringMVCRequestHeaderHandler.java +++ b/src/main/java/com/ly/doc/handler/SpringMVCRequestHeaderHandler.java @@ -49,7 +49,7 @@ public boolean isMapping(String annotationName) { @Override public HeaderAnnotation getHeaderAnnotation() { return HeaderAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.REQUEST_HERDER) + .setAnnotationName(SpringMvcAnnotations.REQUEST_HEADER) .setValueProp(DocAnnotationConstants.VALUE_PROP) .setDefaultValueProp(DocAnnotationConstants.DEFAULT_VALUE_PROP) .setRequiredProp(DocAnnotationConstants.REQUIRED_PROP); diff --git a/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java b/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java index 2bd5beb74..04cdd1f24 100644 --- a/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java +++ b/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2024 smart-doc + * Copyright (C) 2018-2025 smart-doc * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -108,15 +108,17 @@ public boolean ignoreReturnObject(String typeName, List ignoreParams) { @Override public FrameworkAnnotations registeredAnnotations() { FrameworkAnnotations annotations = FrameworkAnnotations.builder(); + + // Header annotation HeaderAnnotation headerAnnotation = HeaderAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.REQUEST_HERDER) + .setAnnotationName(SpringMvcAnnotations.REQUEST_HEADER) .setValueProp(DocAnnotationConstants.VALUE_PROP) .setDefaultValueProp(DocAnnotationConstants.DEFAULT_VALUE_PROP) .setRequiredProp(DocAnnotationConstants.REQUIRED_PROP); // add header annotation annotations.setHeaderAnnotation(headerAnnotation); - // add entry annotation + // Entry annotations (Controller, RestController) Map entryAnnotations = new HashMap<>(16); EntryAnnotation controllerAnnotation = EntryAnnotation.builder() .setAnnotationName(SpringMvcAnnotations.CONTROLLER) @@ -128,108 +130,43 @@ public FrameworkAnnotations registeredAnnotations() { entryAnnotations.put(restController.getAnnotationName(), restController); annotations.setEntryAnnotations(entryAnnotations); - // add request body annotation + // RequestBody annotation RequestBodyAnnotation bodyAnnotation = RequestBodyAnnotation.builder() .setAnnotationName(SpringMvcAnnotations.REQUEST_BODY) .setAnnotationFullyName(SpringMvcAnnotations.REQUEST_BODY_FULLY); annotations.setRequestBodyAnnotation(bodyAnnotation); - // request param annotation - RequestParamAnnotation requestAnnotation = RequestParamAnnotation.builder() + // RequestParam annotation + RequestParamAnnotation requestParamAnnotation = RequestParamAnnotation.builder() .setAnnotationName(SpringMvcAnnotations.REQUEST_PARAM) .setDefaultValueProp(DocAnnotationConstants.DEFAULT_VALUE_PROP) .setRequiredProp(DocAnnotationConstants.REQUIRED_PROP); - annotations.setRequestParamAnnotation(requestAnnotation); + annotations.setRequestParamAnnotation(requestParamAnnotation); - // request part annotation + // RequestPart annotation RequestPartAnnotation requestPartAnnotation = RequestPartAnnotation.builder() .setAnnotationName(SpringMvcAnnotations.REQUEST_PART) .setDefaultValueProp(DocAnnotationConstants.DEFAULT_VALUE_PROP) .setRequiredProp(DocAnnotationConstants.REQUIRED_PROP); annotations.setRequestPartAnnotation(requestPartAnnotation); - // add path variable annotation + // PathVariable annotation PathVariableAnnotation pathVariableAnnotation = PathVariableAnnotation.builder() .setAnnotationName(SpringMvcAnnotations.PATH_VARIABLE) .setDefaultValueProp(DocAnnotationConstants.DEFAULT_VALUE_PROP) .setRequiredProp(DocAnnotationConstants.REQUIRED_PROP); annotations.setPathVariableAnnotation(pathVariableAnnotation); - // add websocket server endpoint annotation + // ServerEndpoint annotation (WebSocket) ServerEndpointAnnotation serverEndpointAnnotation = ServerEndpointAnnotation.builder() .setAnnotationName(SpringMvcAnnotations.SERVER_ENDPOINT); annotations.setServerEndpointAnnotation(serverEndpointAnnotation); // add mapping annotations - Map mappingAnnotations = new HashMap<>(16); - - MappingAnnotation requestMappingAnnotation = MappingAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.REQUEST_MAPPING) - .setConsumesProp("consumes") - .setProducesProp("produces") - .setMethodProp("method") - .setParamsProp("params") - .setScope("class", "method") - .setPathProps(DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP); - mappingAnnotations.put(requestMappingAnnotation.getAnnotationName(), requestMappingAnnotation); - - MappingAnnotation postMappingAnnotation = MappingAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.POST_MAPPING) - .setConsumesProp("consumes") - .setProducesProp("produces") - .setMethodProp("method") - .setParamsProp("params") - .setMethodType(Methods.POST.getValue()) - .setPathProps(DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP); - mappingAnnotations.put(postMappingAnnotation.getAnnotationName(), postMappingAnnotation); - - MappingAnnotation getMappingAnnotation = MappingAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.GET_MAPPING) - .setConsumesProp("consumes") - .setProducesProp("produces") - .setMethodProp("method") - .setParamsProp("params") - .setMethodType(Methods.GET.getValue()) - .setPathProps(DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP); - mappingAnnotations.put(getMappingAnnotation.getAnnotationName(), getMappingAnnotation); - - MappingAnnotation putMappingAnnotation = MappingAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.PUT_MAPPING) - .setConsumesProp("consumes") - .setProducesProp("produces") - .setParamsProp("params") - .setMethodProp("method") - .setMethodType(Methods.PUT.getValue()) - .setPathProps(DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP); - mappingAnnotations.put(putMappingAnnotation.getAnnotationName(), putMappingAnnotation); - - MappingAnnotation patchMappingAnnotation = MappingAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.PATCH_MAPPING) - .setConsumesProp("consumes") - .setProducesProp("produces") - .setMethodProp("method") - .setParamsProp("params") - .setMethodType(Methods.PATCH.getValue()) - .setPathProps(DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP); - mappingAnnotations.put(patchMappingAnnotation.getAnnotationName(), patchMappingAnnotation); - - MappingAnnotation deleteMappingAnnotation = MappingAnnotation.builder() - .setAnnotationName(SpringMvcAnnotations.DELETE_MAPPING) - .setConsumesProp("consumes") - .setProducesProp("produces") - .setMethodProp("method") - .setParamsProp("params") - .setMethodType(Methods.DELETE.getValue()) - .setPathProps(DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP); - mappingAnnotations.put(deleteMappingAnnotation.getAnnotationName(), deleteMappingAnnotation); - - MappingAnnotation feignClientAnnotation = MappingAnnotation.builder() - .setAnnotationName(DocGlobalConstants.FEIGN_CLIENT) - .setAnnotationFullyName(DocGlobalConstants.FEIGN_CLIENT_FULLY); - mappingAnnotations.put(feignClientAnnotation.getAnnotationName(), feignClientAnnotation); + Map mappingAnnotations = buildSpringMappingAnnotations(); annotations.setMappingAnnotations(mappingAnnotations); - // Add Exception advice + // Exception advice annotations Map exceptionAdviceAnnotations = new HashMap<>(16); ExceptionAdviceAnnotation controllerAdviceAnnotation = ExceptionAdviceAnnotation.builder() @@ -432,4 +369,89 @@ public List defaultHttpErrorStatuses() { return exceptionStatusList; } + /** + * Builds and returns all Spring MVC request mapping annotations + * including @RequestMapping, @GetMapping, @PostMapping, etc., with consistent + * attribute configurations. + * @return a map of annotation name to {@link MappingAnnotation} + */ + private Map buildSpringMappingAnnotations() { + Map mappingAnnotations = new HashMap<>(16); + + // Common properties + String consumes = DocAnnotationConstants.CONSUMES; + String produces = DocAnnotationConstants.PRODUCES; + String method = DocAnnotationConstants.METHOD; + String params = DocAnnotationConstants.PARAMS; + String[] pathProps = DocAnnotationConstants.PATH_MAPPING_PROPS; + + // @RequestMapping + MappingAnnotation requestMapping = MappingAnnotation.builder() + .setAnnotationName(SpringMvcAnnotations.REQUEST_MAPPING) + .setConsumesProp(consumes) + .setProducesProp(produces) + .setMethodProp(method) + .setParamsProp(params) + .setScope("class", "method") + .setPathProps(pathProps); + mappingAnnotations.put(requestMapping.getAnnotationName(), requestMapping); + + // @PostMapping + MappingAnnotation postMapping = this.createMapping(SpringMvcAnnotations.POST_MAPPING, Methods.POST.getValue(), + pathProps, consumes, produces, method, params); + mappingAnnotations.put(postMapping.getAnnotationName(), postMapping); + + // @GetMapping + MappingAnnotation getMapping = this.createMapping(SpringMvcAnnotations.GET_MAPPING, Methods.GET.getValue(), + pathProps, consumes, produces, method, params); + mappingAnnotations.put(getMapping.getAnnotationName(), getMapping); + + // @PutMapping + MappingAnnotation putMapping = this.createMapping(SpringMvcAnnotations.PUT_MAPPING, Methods.PUT.getValue(), + pathProps, consumes, produces, method, params); + mappingAnnotations.put(putMapping.getAnnotationName(), putMapping); + + // @PatchMapping + MappingAnnotation patchMapping = this.createMapping(SpringMvcAnnotations.PATCH_MAPPING, + Methods.PATCH.getValue(), pathProps, consumes, produces, method, params); + mappingAnnotations.put(patchMapping.getAnnotationName(), patchMapping); + + // @DeleteMapping + MappingAnnotation deleteMapping = this.createMapping(SpringMvcAnnotations.DELETE_MAPPING, + Methods.DELETE.getValue(), pathProps, consumes, produces, method, params); + mappingAnnotations.put(deleteMapping.getAnnotationName(), deleteMapping); + + // @FeignClient + MappingAnnotation feignClient = MappingAnnotation.builder() + .setAnnotationName(DocGlobalConstants.FEIGN_CLIENT) + .setAnnotationFullyName(DocGlobalConstants.FEIGN_CLIENT_FULLY) + .setPathProps(DocAnnotationConstants.PATH_PROP); + mappingAnnotations.put(feignClient.getAnnotationName(), feignClient); + + return mappingAnnotations; + } + + /** + * Helper method to create common HTTP method-based mappings + * (e.g., @GetMapping, @PostMapping). + * @param annotationName the annotation name + * @param methodType the method type + * @param pathProps the path properties + * @param consumes the consumes property + * @param produces the produces property + * @param method the HTTP method + * @param params the params property + */ + private MappingAnnotation createMapping(String annotationName, String methodType, String[] pathProps, + String consumes, String produces, String method, String params) { + return MappingAnnotation.builder() + .setAnnotationName(annotationName) + .setConsumesProp(consumes) + .setProducesProp(produces) + .setMethodProp(method) + .setParamsProp(params) + .setMethodType(methodType) + .setPathProps(pathProps); + } + } diff --git a/src/main/java/com/ly/doc/utils/JavaClassValidateUtil.java b/src/main/java/com/ly/doc/utils/JavaClassValidateUtil.java index 70a4d8355..cfc67511a 100644 --- a/src/main/java/com/ly/doc/utils/JavaClassValidateUtil.java +++ b/src/main/java/com/ly/doc/utils/JavaClassValidateUtil.java @@ -1,7 +1,5 @@ /* - * smart-doc - * - * Copyright (C) 2018-2024 smart-doc + * Copyright (C) 2018-2025 smart-doc * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -20,9 +18,14 @@ * specific language governing permissions and limitations * under the License. */ + package com.ly.doc.utils; -import com.ly.doc.constants.*; +import com.ly.doc.constants.DocAnnotationConstants; +import com.ly.doc.constants.JSRAnnotationConstants; +import com.ly.doc.constants.JavaTypeConstants; +import com.ly.doc.constants.SolonAnnotations; +import com.ly.doc.constants.SpringMvcAnnotations; import com.power.common.util.CollectionUtil; import com.power.common.util.StringUtil; import com.power.common.util.ValidateUtil; @@ -227,7 +230,7 @@ public static boolean isJSR303Required(String annotationSimpleName) { * @return boolean */ public static boolean isIgnoreTag(String tagName) { - return tagName.equals("ignore"); + return "ignore".equals(tagName); } /** @@ -355,7 +358,7 @@ public static boolean ignoreSpringMvcParamWithAnnotation(String annotation) { switch (annotation) { case SpringMvcAnnotations.SESSION_ATTRIBUTE: case SpringMvcAnnotations.REQUEST_ATTRIBUTE: - case SpringMvcAnnotations.REQUEST_HERDER: + case SpringMvcAnnotations.REQUEST_HEADER: return true; default: return false;