This is an automated email from the ASF dual-hosted git repository.

jianbin pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 07f56b52a3 optimize: add support for parsing @RequestParam annotation 
in netty-http-server (#7430)
07f56b52a3 is described below

commit 07f56b52a327cab72f020a5e773aab2a8eb110bc
Author: xiaoyu <93440108+yvce...@users.noreply.github.com>
AuthorDate: Sat Jun 14 16:00:42 2025 +0800

    optimize: add support for parsing @RequestParam annotation in 
netty-http-server (#7430)
---
 changes/en-us/2.x.md                               |   3 +
 changes/zh-cn/2.x.md                               |   4 +-
 .../core/rpc/netty/http/HttpDispatchHandler.java   |   3 +
 .../seata/core/rpc/netty/http/ParamMetaData.java   |  30 ++++
 .../seata/core/rpc/netty/http/ParameterParser.java |  33 +++-
 .../rpc/netty/http/HttpDispatchHandlerTest.java    |   1 +
 .../core/rpc/netty/http/ParameterParserTest.java   | 152 +++++++++++++++++-
 .../http/RestControllerBeanPostProcessor.java      | 114 ++++++++++++--
 .../http/RestControllerBeanPostProcessorTest.java  | 172 +++++++++++++++++++--
 .../seata/server/controller/ClusterController.java |   6 +-
 10 files changed, 487 insertions(+), 31 deletions(-)

diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index d2749f0ab3..f32621d09f 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -56,6 +56,7 @@ Add changes here for all PR submitted to the 2.x branch.
 - [[#7418](https://github.com/apache/incubator-seata/pull/7418)] add jackson 
notice
 - [[#7419](https://github.com/apache/incubator-seata/pull/7419)] Add maven 
profile to support packaging source code
 - [[#7428](https://github.com/apache/incubator-seata/pull/7428)] pmd-check log 
as ERROR level
+- [[#7430](https://github.com/apache/incubator-seata/pull/7430)] Add support 
for parsing @RequestParam annotation in netty-http-server
 - [[#7432](https://github.com/apache/incubator-seata/pull/7432)] conditionally 
include test modules using Maven profiles
 
 
@@ -120,6 +121,8 @@ Thanks to these contributors for their code commits. Please 
report an unintended
 - [PengningYang](https://github.com/PengningYang)
 - [WangzJi](https://github.com/WangzJi)
 - [maple525866](https://github.com/maple525866)
+- [YvCeung](https://github.com/YvCeung)
+
 
 
 Also, we receive many valuable issues, questions and advices from our 
community. Thanks for you all.
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index 599cfcf1de..0e5350ad4d 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -56,6 +56,7 @@
 - [[#7418](https://github.com/apache/incubator-seata/pull/7418)] 添加 jackson 
notice
 - [[#7419](https://github.com/apache/incubator-seata/pull/7419)] 添加 Maven 
配置文件以支持源码打包
 - [[#7428](https://github.com/apache/incubator-seata/pull/7428)] 修改 pmd-check 
输出日志为 ERROR 级别
+- [[#7430](https://github.com/apache/incubator-seata/pull/7430)] 
在netty-http-server中增加了对解析@RequestParam注释的支持
 
 
 ### security:
@@ -122,8 +123,7 @@
 - [PengningYang](https://github.com/PengningYang)
 - [WangzJi](https://github.com/WangzJi)
 - [maple525866](https://github.com/maple525866)
-
-
+- [YvCeung](https://github.com/YvCeung)
 
 
 同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。
diff --git 
a/core/src/main/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandler.java
 
b/core/src/main/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandler.java
index 40dd67d2a5..782c7ebf6a 100644
--- 
a/core/src/main/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandler.java
+++ 
b/core/src/main/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandler.java
@@ -48,6 +48,9 @@ import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+/**
+ * A Netty HTTP request handler that dispatches incoming requests to 
corresponding controller methods
+ */
 public class HttpDispatchHandler extends 
SimpleChannelInboundHandler<HttpRequest> {
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(HttpDispatchHandler.class);
diff --git 
a/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParamMetaData.java 
b/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParamMetaData.java
index 4828e38cbf..41625ec3ff 100644
--- a/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParamMetaData.java
+++ b/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParamMetaData.java
@@ -20,6 +20,12 @@ public class ParamMetaData {
 
     private ParamConvertType paramConvertType;
 
+    private String paramName;
+
+    private boolean required;
+
+    private String defaultValue;
+
     public ParamConvertType getParamConvertType() {
         return paramConvertType;
     }
@@ -28,6 +34,30 @@ public class ParamMetaData {
         this.paramConvertType = paramConvertType;
     }
 
+    public String getParamName() {
+        return paramName;
+    }
+
+    public void setParamName(String paramName) {
+        this.paramName = paramName;
+    }
+
+    public boolean isRequired() {
+        return required;
+    }
+
+    public void setRequired(boolean required) {
+        this.required = required;
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
     public enum ParamConvertType {
 
         /**
diff --git 
a/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParameterParser.java 
b/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParameterParser.java
index 149681a4ec..5ec665ea80 100644
--- 
a/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParameterParser.java
+++ 
b/core/src/main/java/org/apache/seata/core/rpc/netty/http/ParameterParser.java
@@ -26,12 +26,18 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Parameter;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+
 import org.apache.seata.common.rpc.http.HttpContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static 
com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS;
 
+/**
+ * A utility class for parsing HTTP request parameters and converting them 
into Java objects.
+ * Supports various parameter types including request params, request body, 
model attributes, etc.
+ */
 public class ParameterParser {
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ParameterParser.class);
@@ -39,6 +45,9 @@ public class ParameterParser {
     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
         .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false).configure(FAIL_ON_EMPTY_BEANS, false);
 
+    private static final String DEFAULT_NONE = 
"\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
+
+
     public static ObjectNode convertParamMap(Map<String, List<String>> 
paramMap) {
         ObjectNode paramNode = OBJECT_MAPPER.createObjectNode();
         for (Map.Entry<String, List<String>> entry : paramMap.entrySet()) {
@@ -89,7 +98,7 @@ public class ParameterParser {
     private static Object getArgValue(Class<?> parameterType, String 
parameterName, ParamMetaData paramMetaData,
         ObjectNode paramMap, HttpContext httpContext) {
         ParamMetaData.ParamConvertType paramConvertType = 
paramMetaData.getParamConvertType();
-        if (parameterType.equals(HttpContext.class)) {
+        if (HttpContext.class.equals(parameterType)) {
             return httpContext;
         } else if 
(ParamMetaData.ParamConvertType.MODEL_ATTRIBUTE.equals(paramConvertType)) {
             JsonNode param = paramMap.get("param");
@@ -97,6 +106,28 @@ public class ParameterParser {
         } else if 
(ParamMetaData.ParamConvertType.REQUEST_BODY.equals(paramConvertType)) {
             JsonNode body = paramMap.get("body");
             return OBJECT_MAPPER.convertValue(body, parameterType);
+        } else if 
(ParamMetaData.ParamConvertType.REQUEST_PARAM.equals(paramConvertType)) {
+            String paramName = paramMetaData.getParamName();
+            JsonNode jsonNode = Optional.ofNullable(paramMap.get("param"))
+                    .map(body -> body.get(paramName))
+                    .orElse(null);
+
+            // Step 1: If body exists and contains paramName, use its value 
first
+            if (jsonNode != null && !jsonNode.isNull()) {
+                return OBJECT_MAPPER.convertValue(jsonNode, parameterType);
+            }
+
+            // Step 2: If the parameter is missing but a defaultValue is set, 
use the defaultValue
+            String defaultValue = paramMetaData.getDefaultValue();
+            if (defaultValue != null && !defaultValue.equals(DEFAULT_NONE)) {
+                return OBJECT_MAPPER.convertValue(defaultValue, parameterType);
+            }
+
+            // Step 3: If the parameter is required but no value or 
defaultValue is provided, throw an exception
+            if (paramMetaData.isRequired()) {
+                throw new IllegalArgumentException("Required request parameter 
'" + paramName + "' is missing");
+            }
+            return null;
         } else {
             JsonNode paramNode = paramMap.get("param");
             if (paramNode != null) {
diff --git 
a/core/src/test/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandlerTest.java
 
b/core/src/test/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandlerTest.java
index 4b55cc2af7..9763f9c18b 100644
--- 
a/core/src/test/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandlerTest.java
+++ 
b/core/src/test/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandlerTest.java
@@ -57,6 +57,7 @@ class HttpDispatchHandlerTest {
         Method method = TestController.class.getMethod("handleRequest", 
String.class);
         ParamMetaData paramMetaData = new ParamMetaData();
         
paramMetaData.setParamConvertType(ParamMetaData.ParamConvertType.REQUEST_PARAM);
+        paramMetaData.setParamName("param");
         ParamMetaData[] paramMetaDatas = new ParamMetaData[]{paramMetaData};
 
         HttpInvocation invocation = new HttpInvocation();
diff --git 
a/core/src/test/java/org/apache/seata/core/rpc/netty/http/ParameterParserTest.java
 
b/core/src/test/java/org/apache/seata/core/rpc/netty/http/ParameterParserTest.java
index f9bfcf071c..1e4ee9b0e4 100644
--- 
a/core/src/test/java/org/apache/seata/core/rpc/netty/http/ParameterParserTest.java
+++ 
b/core/src/test/java/org/apache/seata/core/rpc/netty/http/ParameterParserTest.java
@@ -30,11 +30,16 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class ParameterParserTest {
 
     private final ObjectMapper objectMapper = new ObjectMapper();
+    private static final String DEFAULT_NONE = 
"\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
 
     @Test
     void testConvertParamMapWithSingleValue() throws JsonProcessingException {
@@ -95,9 +100,152 @@ class ParameterParserTest {
         assertNotNull(args[0]);
     }
 
-    // 测试辅助类
+    @Test
+    void testGetArgValuesWithRequestParam() throws Exception {
+        Method method = TestClassA.class.getMethod("objectMethod", 
String.class);
+
+        ParamMetaData paramMetaData = new ParamMetaData();
+        
paramMetaData.setParamConvertType(ParamMetaData.ParamConvertType.REQUEST_PARAM);
+        paramMetaData.setParamName("userName");
+        paramMetaData.setDefaultValue("a");
+        paramMetaData.setRequired(false);
+
+        ObjectNode paramMap = objectMapper.createObjectNode();
+        ObjectNode bodyNode = paramMap.putObject("param");
+        bodyNode.put("userName", "LiHua");
+        HttpContext httpContext = new HttpContext(null,null,false);
+        Object[] args = ParameterParser.getArgValues(
+                new ParamMetaData[]{paramMetaData},
+                method,
+                paramMap, httpContext
+        );
+
+        assertEquals(1, args.length);
+        assertNotNull(args[0]);
+        assertEquals("LiHua", args[0]);
+    }
+
+    @Test
+    void testGetArgValuesWithRequestParamAndDefaultValue() throws Exception {
+        Method method = TestClassA.class.getMethod("objectMethod", 
String.class);
+
+        ParamMetaData paramMetaData = new ParamMetaData();
+        
paramMetaData.setParamConvertType(ParamMetaData.ParamConvertType.REQUEST_PARAM);
+        paramMetaData.setParamName("userName");
+        paramMetaData.setDefaultValue("XiaMing");
+        paramMetaData.setRequired(false);
+
+        ObjectNode paramMap = objectMapper.createObjectNode();
+        HttpContext httpContext = new HttpContext(null,null,false);
+        Object[] args = ParameterParser.getArgValues(
+                new ParamMetaData[]{paramMetaData},
+                method,
+                paramMap, httpContext
+        );
+
+        assertEquals(1, args.length);
+        assertNotNull(args[0]);
+        assertEquals("XiaMing", args[0]);
+    }
+
+    @Test
+    void testGetArgValuesWithRequestParamThrowException() throws Exception {
+        Method method = TestClassA.class.getMethod("objectMethod", 
String.class);
+
+        ParamMetaData paramMetaData = new ParamMetaData();
+        
paramMetaData.setParamConvertType(ParamMetaData.ParamConvertType.REQUEST_PARAM);
+        paramMetaData.setParamName("userName");
+        paramMetaData.setDefaultValue(DEFAULT_NONE);
+        paramMetaData.setRequired(true);
+        assertThrows(IllegalArgumentException.class, () ->{
+            ObjectNode paramMap = objectMapper.createObjectNode();
+            HttpContext httpContext = new HttpContext(null,null,false);
+            ParameterParser.getArgValues(
+                    new ParamMetaData[]{paramMetaData},
+                    method,
+                    paramMap, httpContext
+            );
+        });
+    }
+
+    @Test
+    void testGetArgValuesWithRequestParamAndReturnNull() throws Exception {
+        Method method = TestClassA.class.getMethod("objectMethod", 
String.class);
+
+        ParamMetaData paramMetaData = new ParamMetaData();
+        
paramMetaData.setParamConvertType(ParamMetaData.ParamConvertType.REQUEST_PARAM);
+        paramMetaData.setParamName("userName");
+        paramMetaData.setRequired(false);
+
+        ObjectNode paramMap = objectMapper.createObjectNode();
+        HttpContext httpContext = new HttpContext(null,null,false);
+        Object[] args = ParameterParser.getArgValues(
+                new ParamMetaData[]{paramMetaData},
+                method,
+                paramMap, httpContext
+        );
+
+        assertEquals(1, args.length);
+        assertNull(args[0]);
+    }
+
+    @Test
+    void testGetArgValuesWithJavaBeanParam() throws Exception {
+        Method method = TestClassB.class.getMethod("objectMethod", User.class);
+
+        ParamMetaData paramMetaData = new ParamMetaData();
+        
paramMetaData.setParamConvertType(ParamMetaData.ParamConvertType.MODEL_ATTRIBUTE);
+        ObjectNode paramMap = objectMapper.createObjectNode();
+        ObjectNode bodyNode = paramMap.putObject("param");
+        bodyNode.put("name", "LiHua");
+        bodyNode.put("age", 10);
+        HttpContext httpContext = new HttpContext(null, null, false);
+        Object[] args = ParameterParser.getArgValues(
+                new ParamMetaData[]{paramMetaData},
+                method,
+                paramMap, httpContext
+        );
+
+        assertEquals(1, args.length);
+        assertTrue(args[0] instanceof User);
+        assertEquals("LiHua", ((User) args[0]).name);
+        assertEquals(10, ((User) args[0]).age);
+    }
+
+
+    // Test support class
     class TestClass {
         public void objectMethod(Object obj) {
         }
     }
+
+    // Test support classA
+    class TestClassA{
+        public void objectMethod(String userName){
+
+        }
+    }
+
+    // Test support classB
+    class TestClassB{
+        public void objectMethod(User user){
+
+        }
+    }
+
+    static class User{
+        String name;
+        Integer age;
+
+        public User(){
+        }
+
+        public void setName(String name){
+            this.name = name;
+        }
+
+        public void setAge(Integer age){
+            this.age = age;
+        }
+    }
 }
\ No newline at end of file
diff --git 
a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessor.java
 
b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessor.java
index ec96beff70..64fad3e381 100644
--- 
a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessor.java
+++ 
b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessor.java
@@ -34,11 +34,17 @@ import 
org.springframework.web.bind.annotation.RestController;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static 
org.springframework.web.bind.annotation.ValueConstants.DEFAULT_NONE;
 
 /**
  * Handles classes annotated with @RestController to establish a request path 
-> controller mapping relationship
@@ -50,6 +56,8 @@ public class RestControllerBeanPostProcessor implements 
BeanPostProcessor {
 
     private static final List<Class<? extends Annotation>> MAPPING_CLASS = new 
ArrayList<>();
     private static final Map<Class<? extends Annotation>, 
ParamMetaData.ParamConvertType> MAPPING_PARAM_TYPE = new HashMap<>();
+    private static final Set<Class<?>> SIMPLE_TYPE = new HashSet<>();
+    private static final Set<Class<?>> SPECIAL_INJECTED_TYPE = new HashSet<>();
 
     static {
         MAPPING_CLASS.add(GetMapping.class);
@@ -61,6 +69,31 @@ public class RestControllerBeanPostProcessor implements 
BeanPostProcessor {
         MAPPING_PARAM_TYPE.put(RequestParam.class, 
ParamMetaData.ParamConvertType.REQUEST_PARAM);
         MAPPING_PARAM_TYPE.put(RequestBody.class, 
ParamMetaData.ParamConvertType.REQUEST_BODY);
         MAPPING_PARAM_TYPE.put(ModelAttribute.class, 
ParamMetaData.ParamConvertType.MODEL_ATTRIBUTE);
+
+        SIMPLE_TYPE.add(String.class);
+        SIMPLE_TYPE.add(Integer.class);
+        SIMPLE_TYPE.add(int.class);
+        SIMPLE_TYPE.add(Long.class);
+        SIMPLE_TYPE.add(long.class);
+        SIMPLE_TYPE.add(Boolean.class);
+        SIMPLE_TYPE.add(boolean.class);
+        SIMPLE_TYPE.add(Double.class);
+        SIMPLE_TYPE.add(double.class);
+        SIMPLE_TYPE.add(Float.class);
+        SIMPLE_TYPE.add(float.class);
+        SIMPLE_TYPE.add(Short.class);
+        SIMPLE_TYPE.add(short.class);
+        SIMPLE_TYPE.add(Byte.class);
+        SIMPLE_TYPE.add(byte.class);
+        SIMPLE_TYPE.add(Character.class);
+        SIMPLE_TYPE.add(char.class);
+        SIMPLE_TYPE.add(java.math.BigDecimal.class);
+        SIMPLE_TYPE.add(java.math.BigInteger.class);
+        SIMPLE_TYPE.add(java.util.Date.class);
+        SIMPLE_TYPE.add(java.time.LocalDate.class);
+        SIMPLE_TYPE.add(java.time.LocalDateTime.class);
+
+        
SPECIAL_INJECTED_TYPE.add(org.apache.seata.common.rpc.http.HttpContext.class);
     }
 
     @Override
@@ -106,19 +139,20 @@ public class RestControllerBeanPostProcessor implements 
BeanPostProcessor {
         Class<?>[] parameterTypes = method.getParameterTypes();
         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
         ParamMetaData[] paramMetaDatas = new 
ParamMetaData[parameterTypes.length];
+        Parameter[] parameters = method.getParameters();
         for (int i = 0; i < parameterTypes.length; i++) {
+            Annotation matchedAnnotation = null;
             Class<? extends Annotation> parameterAnnotationType = null;
             if (parameterAnnotations[i] != null && 
parameterAnnotations[i].length > 0) {
-                parameterAnnotationType = 
parameterAnnotations[i][0].annotationType();
-            }
-
-            if (parameterAnnotationType == null) {
-                parameterAnnotationType = RequestParam.class;
+                for (Annotation annotation : parameterAnnotations[i]) {
+                    if 
(MAPPING_PARAM_TYPE.containsKey(annotation.annotationType())) {
+                        parameterAnnotationType = annotation.annotationType();
+                        matchedAnnotation = annotation;
+                        break;
+                    }
+                }
             }
-
-            ParamMetaData paramMetaData = new ParamMetaData();
-            ParamMetaData.ParamConvertType paramConvertType = 
MAPPING_PARAM_TYPE.get(parameterAnnotationType);
-            paramMetaData.setParamConvertType(paramConvertType);
+            ParamMetaData paramMetaData = 
buildParamMetaData(matchedAnnotation, parameterTypes[i], 
parameterAnnotationType, parameters[i]);
             paramMetaDatas[i] = paramMetaData;
         }
         int maxSize = Math.max(prePaths.size(), postPaths.size());
@@ -143,5 +177,67 @@ public class RestControllerBeanPostProcessor implements 
BeanPostProcessor {
         }
     }
 
+    private static ParamMetaData buildParamMetaData(Annotation 
matchedAnnotation, Class<?> parameterType,
+                                                    Class<? extends 
Annotation> parameterAnnotationType, Parameter parameter) {
+        ParamMetaData paramMetaData = new ParamMetaData();
+
+        // No annotation on the parameter: resolve the default annotation type 
based on the parameter type
+        if (parameterAnnotationType == null) {
+            parameterAnnotationType = 
resolveDefaultAnnotationType(parameterType);
+            ParamMetaData.ParamConvertType paramConvertType = 
MAPPING_PARAM_TYPE.get(parameterAnnotationType);
+            paramMetaData.setParamConvertType(paramConvertType);
+            if (parameterAnnotationType == RequestParam.class) {
+                paramMetaData.setParamName(parameter.getName());
+                paramMetaData.setRequired(true);
+                paramMetaData.setDefaultValue(DEFAULT_NONE);
+            }
+        // Annotation is present on the parameter; proceed with standard 
parsing logic
+        } else {
+            ParamMetaData.ParamConvertType paramConvertType = 
MAPPING_PARAM_TYPE.get(parameterAnnotationType);
+            paramMetaData.setParamConvertType(paramConvertType);
+            if (parameterAnnotationType == RequestParam.class) {
+                RequestParam requestParam = (RequestParam) matchedAnnotation;
+                boolean required = true;
+                String defaultValue = null;
+                String paramName = Optional.ofNullable(requestParam.name())
+                        .filter(name -> !name.isEmpty())
+                        .orElseGet(() -> {
+                            String value = requestParam.value();
+                            return !value.isEmpty() ? value : 
parameter.getName();
+                        });
+
+                required = requestParam.required();
+                defaultValue = requestParam.defaultValue();
+
+                if (!DEFAULT_NONE.equals(defaultValue)) {
+                    required = false;
+                }
+
+                paramMetaData.setParamName(paramName);
+                paramMetaData.setRequired(required);
+                paramMetaData.setDefaultValue(defaultValue);
+            }
+        }
+
+        return paramMetaData;
+    }
+
+    /**
+     * Determines the default annotation type for a parameter based on its 
class.
+     * Returns:
+     * - null for special injected types (e.g., HttpContext),
+     * - RequestParam for primitives, simple types, or MultipartFile,
+     * - ModelAttribute for all others.
+     */
+    private static Class<? extends Annotation> 
resolveDefaultAnnotationType(Class<?> paramType) {
+        if (SPECIAL_INJECTED_TYPE.stream().anyMatch(t -> 
t.isAssignableFrom(paramType))) {
+            return null;
+        } else if (paramType.isPrimitive() || SIMPLE_TYPE.contains(paramType)
+                || 
org.springframework.web.multipart.MultipartFile.class.isAssignableFrom(paramType))
 {
+            return RequestParam.class;
+        } else {
+            return ModelAttribute.class;
+        }
+    }
 
 }
\ No newline at end of file
diff --git 
a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessorTest.java
 
b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessorTest.java
index 33805817a2..6a5c984e75 100644
--- 
a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessorTest.java
+++ 
b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/http/RestControllerBeanPostProcessorTest.java
@@ -16,20 +16,34 @@
  */
 package org.apache.seata.spring.boot.autoconfigure.http;
 
+import org.apache.seata.common.rpc.http.HttpContext;
 import org.apache.seata.core.rpc.netty.http.ControllerManager;
 import org.apache.seata.core.rpc.netty.http.HttpInvocation;
+import org.apache.seata.core.rpc.netty.http.ParamMetaData;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.MockitoAnnotations;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
 
-import static org.mockito.Mockito.verify;
+import javax.annotation.Nonnull;
 
-public class RestControllerBeanPostProcessorTest {
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
 
-    @Mock
-    private ControllerManager controllerManager;
+public class RestControllerBeanPostProcessorTest {
 
     private RestControllerBeanPostProcessor processor;
 
@@ -44,13 +58,104 @@ public class RestControllerBeanPostProcessorTest {
         // Mock the bean and its annotations
         TestController mockBean = new TestController();
 
+        try (MockedStatic<ControllerManager> mocked = 
mockStatic(ControllerManager.class)) {
+            // Call the method under test
+            processor.postProcessAfterInitialization(mockBean, 
"testController");
+
+            // Verify that the paths were added correctly
+            mocked.verify(() -> 
ControllerManager.addHttpInvocation(any(HttpInvocation.class)), times(2));
+        }
+    }
+
+    @Test
+    public void testRegisterHttpInvocationWithCorrectMetadata() {
+        // Mock the bean and its annotations
+        TestApiController controller = new TestApiController();
+
         // Call the method under test
-        processor.postProcessAfterInitialization(mockBean, "testController");
+        processor.postProcessAfterInitialization(controller, 
"testApiController");
+
+        // Verify whether the parsed data is correct
+        HttpInvocation getInvocation = 
ControllerManager.getHttpInvocation("/api/get");
+        assertNotNull(getInvocation, "getMethod should be registered");
+        assertEquals("getMethod", getInvocation.getMethod().getName());
+        assertSame(controller, getInvocation.getController());
+
+        // Verify whether the defaultValue attribute "value" of @RequestParam 
is correct
+        ParamMetaData[] getParams = getInvocation.getParamMetaData();
+        assertEquals(1, getParams.length);
+        assertEquals("param", getParams[0].getParamName());
+        assertEquals("defaultValue", getParams[0].getDefaultValue());
+        assertEquals(ParamMetaData.ParamConvertType.REQUEST_PARAM, 
getParams[0].getParamConvertType());
+        assertFalse(getParams[0].isRequired());
+
+        // Verify whether the default value of the "required" attribute of 
@RequestParam is correct
+        HttpInvocation postInvocation = 
ControllerManager.getHttpInvocation("/api/post");
+        assertNotNull(postInvocation, "postMethod should be registered");
+        assertEquals("postMethod", postInvocation.getMethod().getName());
+        assertSame(controller, postInvocation.getController());
+
+        // Verify whether the value of the "name" attribute of @RequestParam 
is correct
+        ParamMetaData[] postParams = postInvocation.getParamMetaData();
+        assertEquals(1, postParams.length);
+        assertEquals("requestBody", postParams[0].getParamName());
+        assertEquals(ParamMetaData.ParamConvertType.REQUEST_PARAM, 
postParams[0].getParamConvertType());
+
+        HttpInvocation updateInvocation = 
ControllerManager.getHttpInvocation("/api/update");
+        assertNotNull(updateInvocation, "updateMethod should be registered");
+        assertEquals("updateMethod", updateInvocation.getMethod().getName());
+        assertSame(controller, updateInvocation.getController());
 
-        // Verify that the paths were added correctly
-        HttpInvocation httpInvocation = new HttpInvocation();
-        httpInvocation.setPath("/path");
-        verify(controllerManager).addHttpInvocation(httpInvocation);
+        ParamMetaData[] updateParams = updateInvocation.getParamMetaData();
+        assertEquals(2, updateParams.length);
+
+        // Verify whether the value attribute of @RequestParam is correct
+        assertEquals("userName", updateParams[0].getParamName());
+        assertEquals(ParamMetaData.ParamConvertType.REQUEST_PARAM, 
updateParams[0].getParamConvertType());
+        assertEquals("age", updateParams[1].getParamName());
+
+        // Verify whether @RequestParam can be correctly parsed when there are 
multiple annotations before a parameter
+        assertEquals(ParamMetaData.ParamConvertType.REQUEST_PARAM, 
updateParams[1].getParamConvertType());
+        assertFalse(updateParams[1].isRequired());
+    }
+
+    @Test
+    public void testRegisterHttpInvocationWithNoAnnotation() {
+        // Mock the bean and its annotations
+        TestNonController controller = new TestNonController();
+
+        // Call the method under test
+        processor.postProcessAfterInitialization(controller, 
"testNonController");
+
+        // Verify whether the parsed data is correct
+        HttpInvocation getInvocation = 
ControllerManager.getHttpInvocation("/non/get");
+        assertNotNull(getInvocation, "getMethod should be registered");
+        assertEquals("getMethod", getInvocation.getMethod().getName());
+        assertSame(controller, getInvocation.getController());
+
+        ParamMetaData[] getParams = getInvocation.getParamMetaData();
+        assertEquals(1, getParams.length);
+        assertEquals("param", getParams[0].getParamName());
+        assertEquals(ParamMetaData.ParamConvertType.REQUEST_PARAM, 
getParams[0].getParamConvertType());
+        assertTrue(getParams[0].isRequired());
+
+        HttpInvocation postInvocation = 
ControllerManager.getHttpInvocation("/non/post");
+        assertNotNull(postInvocation, "postMethod should be registered");
+        assertEquals("postMethod", postInvocation.getMethod().getName());
+        assertSame(controller, postInvocation.getController());
+
+        ParamMetaData[] postParams = postInvocation.getParamMetaData();
+        assertEquals(1, postParams.length);
+        assertEquals(ParamMetaData.ParamConvertType.MODEL_ATTRIBUTE, 
postParams[0].getParamConvertType());
+
+        HttpInvocation updateInvocation = 
ControllerManager.getHttpInvocation("/non/update");
+        assertNotNull(updateInvocation, "updateMethod should be registered");
+        assertEquals("updateMethod", updateInvocation.getMethod().getName());
+        assertSame(controller, updateInvocation.getController());
+
+        ParamMetaData[] updateParams = updateInvocation.getParamMetaData();
+        assertEquals(1, updateParams.length);
+        assertNull(updateParams[0].getParamConvertType());
     }
 
     @RestController
@@ -67,7 +172,50 @@ public class RestControllerBeanPostProcessorTest {
             return "POST";
         }
     }
-}
 
+    @RestController
+    @RequestMapping("/api")
+    static class TestApiController {
+
+        @GetMapping("/get")
+        public String getMethod(@RequestParam(defaultValue = "defaultValue") 
String param) {
+            return "GET";
+        }
+
+        @PostMapping("/post")
+        public String postMethod(@RequestParam(name = "requestBody") String 
body) {
+            return "POST";
+        }
+
+        @GetMapping("/update")
+        public String updateMethod(@RequestParam(value = "userName") String 
name,
+                                   @Nonnull @RequestParam(required = false) 
Integer age) {
+            return "update";
+        }
+    }
+
+    @RestController
+    @RequestMapping("/non")
+    static class TestNonController {
 
+        @GetMapping("/get")
+        public String getMethod(String param) {
+            return "GET";
+        }
 
+        @PostMapping("/post")
+        public String postMethod(User user) {
+            return "POST";
+        }
+
+        @GetMapping("/update")
+        public String updateMethod(HttpContext httpContext) {
+            return "update";
+        }
+    }
+
+    static class User{
+        String name;
+        Integer age;
+    }
+}
\ No newline at end of file
diff --git 
a/server/src/main/java/org/apache/seata/server/controller/ClusterController.java
 
b/server/src/main/java/org/apache/seata/server/controller/ClusterController.java
index b701f26537..b6edda641e 100644
--- 
a/server/src/main/java/org/apache/seata/server/controller/ClusterController.java
+++ 
b/server/src/main/java/org/apache/seata/server/controller/ClusterController.java
@@ -111,13 +111,9 @@ public class ClusterController {
     public void watch(HttpContext context, @RequestBody Map<String, Object> 
groupTerms,
         @RequestParam(defaultValue = "28000") Integer timeout) {
         context.setAsync(true);
-        if (timeout == null) {
-            timeout = 28000;
-        }
-        Integer finalTimeout = timeout;
         groupTerms.forEach((group, term) -> {
             Watcher<HttpContext> watcher =
-                new Watcher<>(group, context, finalTimeout, 
Long.parseLong(String.valueOf(term)));
+                new Watcher<>(group, context, timeout, 
Long.parseLong(String.valueOf(term)));
             clusterWatcherManager.registryWatcher(watcher);
         });
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org
For additional commands, e-mail: notifications-h...@seata.apache.org


Reply via email to