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

dengliming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 1fcecc4d19 fix(dubbo): use Dubbo @Service in ServiceProcessor to set 
beanPath (#6895)
1fcecc4d19 is described below

commit 1fcecc4d19cd3513646391286aa30023df67c7c2
Author: hengyuss <[email protected]>
AuthorDate: Wed Sep 16 14:22:00 2026 +0800

    fix(dubbo): use Dubbo @Service in ServiceProcessor to set beanPath (#6895)
    
    * fix: ServiceProcessor imports Spring @Service instead of Dubbo @Service — 
old-style Dubbo beans never get beanPath
    
    * fix: ServiceProcessor uses Dubbo @Service, extract shared 
DubboRpcExtBuilders
    
    ---------
    
    Co-authored-by: zhengpeng <[email protected]>
    Co-authored-by: Liming Deng <[email protected]>
---
 .../dubbo/ApacheDubboServiceBeanListener.java      |  37 +----
 .../processor/extractor/DubboServiceProcessor.java |  28 +---
 .../processor/extractor/ServiceProcessor.java      |  29 +++-
 .../extractor/DubboServiceProcessorTest.java       | 175 +++++++++++++++++++++
 .../processor/extractor/ServiceProcessorTest.java  | 175 +++++++++++++++++++++
 .../shenyu-client-dubbo-common/pom.xml             |   6 +
 .../dubbo/common/dto/DubboRpcExtBuilders.java      |  65 ++++++++
 .../dubbo/common/dto/DubboRpcExtBuildersTest.java  | 140 +++++++++++++++++
 8 files changed, 593 insertions(+), 62 deletions(-)

diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/ApacheDubboServiceBeanListener.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/ApacheDubboServiceBeanListener.java
index c537b04c3d..4606103c86 100644
--- 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/ApacheDubboServiceBeanListener.java
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/ApacheDubboServiceBeanListener.java
@@ -18,19 +18,14 @@
 package org.apache.shenyu.client.apache.dubbo;
 
 import org.apache.commons.lang3.StringUtils;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.config.MethodConfig;
 import org.apache.dubbo.config.spring.ServiceBean;
 import 
org.apache.shenyu.client.core.client.AbstractContextRefreshedEventListener;
 import org.apache.shenyu.client.core.constant.ShenyuClientConstants;
 import org.apache.shenyu.client.dubbo.common.annotation.ShenyuDubboClient;
-import org.apache.shenyu.client.dubbo.common.dto.DubboRpcExt;
-import org.apache.shenyu.client.dubbo.common.dto.DubboRpcMethodExt;
-import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.client.dubbo.common.dto.DubboRpcExtBuilders;
 import org.apache.shenyu.common.enums.ApiHttpMethodEnum;
 import org.apache.shenyu.common.enums.RpcTypeEnum;
 import org.apache.shenyu.common.exception.ShenyuException;
-import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
 import org.apache.shenyu.register.common.config.ShenyuClientConfig;
 import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
@@ -46,7 +41,6 @@ import org.springframework.util.ReflectionUtils;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -54,8 +48,6 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT;
-
 /**
  * The Apache Dubbo ServiceBean Listener.
  */
@@ -223,31 +215,6 @@ public class ApacheDubboServiceBeanListener extends 
AbstractContextRefreshedEven
     }
     
     private String buildRpcExt(final ServiceBean<?> serviceBean, final String 
methodName) {
-        DubboRpcExt build = DubboRpcExt.builder()
-                
.protocol(StringUtils.isNotEmpty(serviceBean.getProtocol().getName()) ? 
serviceBean.getProtocol().getName() : "")
-                .group(StringUtils.isNotEmpty(serviceBean.getGroup()) ? 
serviceBean.getGroup() : "")
-                .version(StringUtils.isNotEmpty(serviceBean.getVersion()) ? 
serviceBean.getVersion() : "")
-                
.loadbalance(StringUtils.isNotEmpty(serviceBean.getLoadbalance()) ? 
serviceBean.getLoadbalance() : CommonConstants.DEFAULT_LOADBALANCE)
-                
.retries(Optional.ofNullable(serviceBean.getRetries()).orElse(CommonConstants.DEFAULT_RETRIES))
-                
.timeout(Optional.ofNullable(serviceBean.getTimeout()).orElse(DEFAULT_CONNECT_TIMEOUT))
-                
.sent(Optional.ofNullable(serviceBean.getSent()).orElse(Boolean.FALSE))
-                .cluster(StringUtils.isNotEmpty(serviceBean.getCluster()) ? 
serviceBean.getCluster() : Constants.DEFAULT_CLUSTER)
-                .url("")
-                .serialization(serviceBean.getSerialization())
-                .build();
-        // method config: loadbalance,retries,timeout,sent
-        if (Objects.nonNull(serviceBean.getMethods())) {
-            build.setMethods(new ArrayList<>());
-            for (MethodConfig methodConfig : serviceBean.getMethods()) {
-                DubboRpcMethodExt methodExt = new DubboRpcMethodExt();
-                methodExt.setName(methodConfig.getName());
-                methodExt.setLoadbalance(methodConfig.getLoadbalance());
-                methodExt.setRetries(methodConfig.getRetries());
-                methodExt.setTimeout(methodConfig.getTimeout());
-                methodExt.setSent(methodConfig.getSent());
-                build.getMethods().add(methodExt);
-            }
-        }
-        return GsonUtils.getInstance().toJson(build);
+        return DubboRpcExtBuilders.buildRpcExt(serviceBean);
     }
 }
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessor.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessor.java
index 71e4602c68..bfe672ac3c 100644
--- 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessor.java
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessor.java
@@ -17,23 +17,16 @@
 
 package org.apache.shenyu.client.apache.dubbo.processor.extractor;
 
-import org.apache.commons.lang3.StringUtils;
-import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.apache.dubbo.config.spring.ServiceBean;
 import org.apache.shenyu.client.core.register.ApiBean;
 import org.apache.shenyu.client.core.register.matcher.ApiAnnotationProcessor;
 import org.apache.shenyu.client.core.register.matcher.ExtractorProcessor;
-import org.apache.shenyu.client.dubbo.common.dto.DubboRpcExt;
-import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.client.dubbo.common.dto.DubboRpcExtBuilders;
 import org.apache.shenyu.common.enums.RpcTypeEnum;
-import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.common.utils.ListUtil;
 
 import java.util.List;
-import java.util.Optional;
-
-import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT;
 
 /**
  * DubboServiceProcessor.
@@ -70,7 +63,7 @@ public class DubboServiceProcessor implements 
ApiAnnotationProcessor<DubboServic
     private String getRpcExt(final ApiBean apiBean) {
         final Object beanInstance = apiBean.getBeanInstance();
         if (beanInstance instanceof ServiceBean) {
-            return getRpcExt((ServiceBean<?>) beanInstance);
+            return DubboRpcExtBuilders.buildRpcExt((ServiceBean<?>) 
beanInstance);
         }
         return "{}";
     }
@@ -78,23 +71,8 @@ public class DubboServiceProcessor implements 
ApiAnnotationProcessor<DubboServic
     private String getRpcExt(final ApiBean.ApiDefinition definition) {
         final Object beanInstance = definition.getApiBean().getBeanInstance();
         if (beanInstance instanceof ServiceBean) {
-            return getRpcExt((ServiceBean<?>) beanInstance);
+            return DubboRpcExtBuilders.buildRpcExt((ServiceBean<?>) 
beanInstance);
         }
         return "{}";
     }
-    
-    private static String getRpcExt(final ServiceBean<?> serviceBean) {
-        DubboRpcExt build = DubboRpcExt.builder()
-                
.protocol(StringUtils.isNotEmpty(serviceBean.getProtocol().getName()) ? 
serviceBean.getProtocol().getName() : "")
-                .group(StringUtils.isNotEmpty(serviceBean.getGroup()) ? 
serviceBean.getGroup() : "")
-                .version(StringUtils.isNotEmpty(serviceBean.getVersion()) ? 
serviceBean.getVersion() : "")
-                
.loadbalance(StringUtils.isNotEmpty(serviceBean.getLoadbalance()) ? 
serviceBean.getLoadbalance() : CommonConstants.DEFAULT_LOADBALANCE)
-                
.retries(Optional.ofNullable(serviceBean.getRetries()).orElse(CommonConstants.DEFAULT_RETRIES))
-                
.timeout(Optional.ofNullable(serviceBean.getTimeout()).orElse(DEFAULT_CONNECT_TIMEOUT))
-                
.sent(Optional.ofNullable(serviceBean.getSent()).orElse(Boolean.FALSE))
-                .cluster(StringUtils.isNotEmpty(serviceBean.getCluster()) ? 
serviceBean.getCluster() : Constants.DEFAULT_CLUSTER)
-                .url("")
-                .build();
-        return GsonUtils.getInstance().toJson(build);
-    }
 }
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessor.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessor.java
index 4473573346..83c332282b 100644
--- 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessor.java
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/main/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessor.java
@@ -17,12 +17,14 @@
 
 package org.apache.shenyu.client.apache.dubbo.processor.extractor;
 
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.config.spring.ServiceBean;
 import org.apache.shenyu.client.core.register.ApiBean;
 import org.apache.shenyu.client.core.register.matcher.ApiAnnotationProcessor;
 import org.apache.shenyu.client.core.register.matcher.ExtractorProcessor;
+import org.apache.shenyu.client.dubbo.common.dto.DubboRpcExtBuilders;
 import org.apache.shenyu.common.enums.RpcTypeEnum;
 import org.apache.shenyu.common.utils.ListUtil;
-import org.springframework.stereotype.Service;
 
 import java.util.List;
 
@@ -38,7 +40,14 @@ public class ServiceProcessor implements 
ApiAnnotationProcessor<Service>, Extrac
     
     @Override
     public void process(final ApiBean apiBean, final Service annotation) {
-        apiBean.setBeanPath(annotation.value());
+        apiBean.setBeanPath(annotation.path());
+        
+        apiBean.addProperties("rpcExt", getRpcExt(apiBean));
+    }
+    
+    @Override
+    public void process(final ApiBean.ApiDefinition definition) {
+        definition.addProperties("rpcExt", getRpcExt(definition));
     }
     
     @Override
@@ -50,4 +59,20 @@ public class ServiceProcessor implements 
ApiAnnotationProcessor<Service>, Extrac
     public Class<Service> matchAnnotation() {
         return Service.class;
     }
+    
+    private String getRpcExt(final ApiBean apiBean) {
+        final Object beanInstance = apiBean.getBeanInstance();
+        if (beanInstance instanceof ServiceBean) {
+            return DubboRpcExtBuilders.buildRpcExt((ServiceBean<?>) 
beanInstance);
+        }
+        return "{}";
+    }
+    
+    private String getRpcExt(final ApiBean.ApiDefinition definition) {
+        final Object beanInstance = definition.getApiBean().getBeanInstance();
+        if (beanInstance instanceof ServiceBean) {
+            return DubboRpcExtBuilders.buildRpcExt((ServiceBean<?>) 
beanInstance);
+        }
+        return "{}";
+    }
 }
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/test/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessorTest.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/test/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessorTest.java
new file mode 100644
index 0000000000..31087b93d4
--- /dev/null
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/test/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/DubboServiceProcessorTest.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.client.apache.dubbo.processor.extractor;
+
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.apache.dubbo.config.spring.ServiceBean;
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class DubboServiceProcessorTest {
+
+    private DubboServiceProcessor dubboServiceProcessor;
+
+    @Mock
+    private ServiceBean<?> serviceBean;
+
+    @Mock
+    private ProtocolConfig protocolConfig;
+
+    @BeforeEach
+    void setUp() {
+        dubboServiceProcessor = new DubboServiceProcessor();
+    }
+
+    @Test
+    void testSupportedClient() {
+        List<String> clients = dubboServiceProcessor.supportedClient();
+        assertEquals(1, clients.size());
+        assertEquals(RpcTypeEnum.DUBBO.getName(), clients.get(0));
+    }
+
+    @Test
+    void testMatchAnnotation() {
+        assertEquals(DubboService.class, 
dubboServiceProcessor.matchAnnotation());
+    }
+
+    @Test
+    void testProcessWithServiceBeanSetsBeanPathAndRpcExt() throws Exception {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("dubbo");
+        when(serviceBean.getGroup()).thenReturn("testGroup");
+        when(serviceBean.getVersion()).thenReturn("1.0.0");
+        when(serviceBean.getLoadbalance()).thenReturn("random");
+        when(serviceBean.getRetries()).thenReturn(3);
+        when(serviceBean.getTimeout()).thenReturn(1000);
+        when(serviceBean.getSent()).thenReturn(true);
+        when(serviceBean.getCluster()).thenReturn("failover");
+        when(serviceBean.getSerialization()).thenReturn("fastjson");
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
serviceBean);
+
+        DubboService dubboServiceAnnotation = 
TestDubboServiceBean.class.getAnnotation(DubboService.class);
+        dubboServiceProcessor.process(apiBean, dubboServiceAnnotation);
+
+        assertEquals(dubboServiceAnnotation.path(), apiBean.getBeanPath());
+        String rpcExt = apiBean.getPropertiesValue("rpcExt");
+        assertNotNull(rpcExt);
+        assertTrue(rpcExt.contains("\"serialization\":\"fastjson\""));
+        assertTrue(rpcExt.contains("\"protocol\":\"dubbo\""));
+        assertTrue(rpcExt.contains("\"group\":\"testGroup\""));
+    }
+
+    @Test
+    void testProcessWithNonServiceBeanReturnsEmptyRpcExt() {
+        Object plainBean = new Object();
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
plainBean);
+
+        DubboService dubboServiceAnnotation = 
TestDubboServiceBean.class.getAnnotation(DubboService.class);
+        dubboServiceProcessor.process(apiBean, dubboServiceAnnotation);
+
+        String rpcExt = apiBean.getPropertiesValue("rpcExt");
+        assertEquals("{}", rpcExt);
+    }
+
+    @Test
+    void testProcessApiDefinitionWithServiceBean() throws Exception {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("dubbo");
+        when(serviceBean.getGroup()).thenReturn("myGroup");
+        when(serviceBean.getVersion()).thenReturn("2.0.0");
+        when(serviceBean.getLoadbalance()).thenReturn(null);
+        when(serviceBean.getRetries()).thenReturn(null);
+        when(serviceBean.getTimeout()).thenReturn(null);
+        when(serviceBean.getSent()).thenReturn(null);
+        when(serviceBean.getCluster()).thenReturn(null);
+        when(serviceBean.getSerialization()).thenReturn("fastjson");
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
serviceBean);
+        Method method = TestDubboServiceBean.class.getMethod("testMethod");
+        ApiBean.ApiDefinition definition = new ApiBean.ApiDefinition(apiBean, 
method);
+
+        dubboServiceProcessor.process(definition);
+
+        String rpcExt = definition.getPropertiesValue("rpcExt");
+        assertNotNull(rpcExt);
+        assertTrue(rpcExt.contains("\"serialization\":\"fastjson\""));
+        assertTrue(rpcExt.contains("\"protocol\":\"dubbo\""));
+        assertTrue(rpcExt.contains("\"group\":\"myGroup\""));
+        assertTrue(rpcExt.contains("\"version\":\"2.0.0\""));
+    }
+
+    @Test
+    void testProcessApiDefinitionWithNonServiceBean() throws Exception {
+        Object plainBean = new Object();
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
plainBean);
+        Method method = TestDubboServiceBean.class.getMethod("testMethod");
+        ApiBean.ApiDefinition definition = new ApiBean.ApiDefinition(apiBean, 
method);
+
+        dubboServiceProcessor.process(definition);
+
+        String rpcExt = definition.getPropertiesValue("rpcExt");
+        assertEquals("{}", rpcExt);
+    }
+
+    @Test
+    void testProcessWithNullProtocol() {
+        when(serviceBean.getProtocol()).thenReturn(null);
+        when(serviceBean.getGroup()).thenReturn(null);
+        when(serviceBean.getVersion()).thenReturn(null);
+        when(serviceBean.getLoadbalance()).thenReturn(null);
+        when(serviceBean.getRetries()).thenReturn(null);
+        when(serviceBean.getTimeout()).thenReturn(null);
+        when(serviceBean.getSent()).thenReturn(null);
+        when(serviceBean.getCluster()).thenReturn(null);
+        when(serviceBean.getSerialization()).thenReturn(null);
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
serviceBean);
+
+        DubboService dubboServiceAnnotation = 
TestDubboServiceBean.class.getAnnotation(DubboService.class);
+        dubboServiceProcessor.process(apiBean, dubboServiceAnnotation);
+
+        String rpcExt = apiBean.getPropertiesValue("rpcExt");
+        assertNotNull(rpcExt);
+        assertTrue(rpcExt.contains("\"protocol\":\"\""));
+    }
+
+    @DubboService(path = "/test-dubbo-path")
+    private static class TestDubboServiceBean {
+        public void testMethod() {
+        }
+    }
+}
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/test/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessorTest.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/test/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessorTest.java
new file mode 100644
index 0000000000..3d8e1c0152
--- /dev/null
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-apache-dubbo/src/test/java/org/apache/shenyu/client/apache/dubbo/processor/extractor/ServiceProcessorTest.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.client.apache.dubbo.processor.extractor;
+
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.config.spring.ServiceBean;
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ServiceProcessorTest {
+
+    private ServiceProcessor serviceProcessor;
+
+    @Mock
+    private ServiceBean<?> serviceBean;
+
+    @Mock
+    private ProtocolConfig protocolConfig;
+
+    @BeforeEach
+    void setUp() {
+        serviceProcessor = new ServiceProcessor();
+    }
+
+    @Test
+    void testSupportedClient() {
+        List<String> clients = serviceProcessor.supportedClient();
+        assertEquals(1, clients.size());
+        assertEquals(RpcTypeEnum.DUBBO.getName(), clients.get(0));
+    }
+
+    @Test
+    void testMatchAnnotation() {
+        assertEquals(Service.class, serviceProcessor.matchAnnotation());
+    }
+
+    @Test
+    void testProcessWithServiceBeanSetsBeanPathAndRpcExt() throws Exception {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("dubbo");
+        when(serviceBean.getGroup()).thenReturn("testGroup");
+        when(serviceBean.getVersion()).thenReturn("1.0.0");
+        when(serviceBean.getLoadbalance()).thenReturn("random");
+        when(serviceBean.getRetries()).thenReturn(3);
+        when(serviceBean.getTimeout()).thenReturn(1000);
+        when(serviceBean.getSent()).thenReturn(true);
+        when(serviceBean.getCluster()).thenReturn("failover");
+        when(serviceBean.getSerialization()).thenReturn("hessian2");
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
serviceBean);
+
+        Service serviceAnnotation = 
TestServiceBean.class.getAnnotation(Service.class);
+        serviceProcessor.process(apiBean, serviceAnnotation);
+
+        assertEquals(serviceAnnotation.path(), apiBean.getBeanPath());
+        String rpcExt = apiBean.getPropertiesValue("rpcExt");
+        assertNotNull(rpcExt);
+        assertTrue(rpcExt.contains("\"serialization\":\"hessian2\""));
+        assertTrue(rpcExt.contains("\"protocol\":\"dubbo\""));
+        assertTrue(rpcExt.contains("\"group\":\"testGroup\""));
+    }
+
+    @Test
+    void testProcessWithNonServiceBeanReturnsEmptyRpcExt() {
+        Object plainBean = new Object();
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
plainBean);
+
+        Service serviceAnnotation = 
TestServiceBean.class.getAnnotation(Service.class);
+        serviceProcessor.process(apiBean, serviceAnnotation);
+
+        String rpcExt = apiBean.getPropertiesValue("rpcExt");
+        assertEquals("{}", rpcExt);
+    }
+
+    @Test
+    void testProcessApiDefinitionWithServiceBean() throws Exception {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("dubbo");
+        when(serviceBean.getGroup()).thenReturn("myGroup");
+        when(serviceBean.getVersion()).thenReturn("2.0.0");
+        when(serviceBean.getLoadbalance()).thenReturn(null);
+        when(serviceBean.getRetries()).thenReturn(null);
+        when(serviceBean.getTimeout()).thenReturn(null);
+        when(serviceBean.getSent()).thenReturn(null);
+        when(serviceBean.getCluster()).thenReturn(null);
+        when(serviceBean.getSerialization()).thenReturn("fastjson");
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
serviceBean);
+        Method method = TestServiceBean.class.getMethod("testMethod");
+        ApiBean.ApiDefinition definition = new ApiBean.ApiDefinition(apiBean, 
method);
+
+        serviceProcessor.process(definition);
+
+        String rpcExt = definition.getPropertiesValue("rpcExt");
+        assertNotNull(rpcExt);
+        assertTrue(rpcExt.contains("\"serialization\":\"fastjson\""));
+        assertTrue(rpcExt.contains("\"protocol\":\"dubbo\""));
+        assertTrue(rpcExt.contains("\"group\":\"myGroup\""));
+        assertTrue(rpcExt.contains("\"version\":\"2.0.0\""));
+    }
+
+    @Test
+    void testProcessApiDefinitionWithNonServiceBean() throws Exception {
+        Object plainBean = new Object();
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
plainBean);
+        Method method = TestServiceBean.class.getMethod("testMethod");
+        ApiBean.ApiDefinition definition = new ApiBean.ApiDefinition(apiBean, 
method);
+
+        serviceProcessor.process(definition);
+
+        String rpcExt = definition.getPropertiesValue("rpcExt");
+        assertEquals("{}", rpcExt);
+    }
+
+    @Test
+    void testProcessWithNullProtocol() {
+        when(serviceBean.getProtocol()).thenReturn(null);
+        when(serviceBean.getGroup()).thenReturn(null);
+        when(serviceBean.getVersion()).thenReturn(null);
+        when(serviceBean.getLoadbalance()).thenReturn(null);
+        when(serviceBean.getRetries()).thenReturn(null);
+        when(serviceBean.getTimeout()).thenReturn(null);
+        when(serviceBean.getSent()).thenReturn(null);
+        when(serviceBean.getCluster()).thenReturn(null);
+        when(serviceBean.getSerialization()).thenReturn(null);
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        ApiBean apiBean = new ApiBean(RpcTypeEnum.DUBBO.getName(), "testBean", 
serviceBean);
+
+        Service serviceAnnotation = 
TestServiceBean.class.getAnnotation(Service.class);
+        serviceProcessor.process(apiBean, serviceAnnotation);
+
+        String rpcExt = apiBean.getPropertiesValue("rpcExt");
+        assertNotNull(rpcExt);
+        assertTrue(rpcExt.contains("\"protocol\":\"\""));
+    }
+
+    @Service(path = "/test-path")
+    private static class TestServiceBean {
+        public void testMethod() {
+        }
+    }
+}
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/pom.xml 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/pom.xml
index cd8a537bb6..53e2926153 100644
--- a/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/pom.xml
+++ b/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/pom.xml
@@ -36,5 +36,11 @@
             <artifactId>spring-web</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo</artifactId>
+            <version>${apache.dubbo.version}</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/src/main/java/org/apache/shenyu/client/dubbo/common/dto/DubboRpcExtBuilders.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/src/main/java/org/apache/shenyu/client/dubbo/common/dto/DubboRpcExtBuilders.java
new file mode 100644
index 0000000000..02d5f6d54c
--- /dev/null
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/src/main/java/org/apache/shenyu/client/dubbo/common/dto/DubboRpcExtBuilders.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.client.dubbo.common.dto;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.config.MethodConfig;
+import org.apache.dubbo.config.spring.ServiceBean;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.utils.GsonUtils;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Optional;
+
+import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT;
+
+public final class DubboRpcExtBuilders {
+
+    private DubboRpcExtBuilders() {
+    }
+
+    public static String buildRpcExt(final ServiceBean<?> serviceBean) {
+        DubboRpcExt build = DubboRpcExt.builder()
+                .protocol(Objects.nonNull(serviceBean.getProtocol()) && 
StringUtils.isNotEmpty(serviceBean.getProtocol().getName()) ? 
serviceBean.getProtocol().getName() : "")
+                .group(StringUtils.isNotEmpty(serviceBean.getGroup()) ? 
serviceBean.getGroup() : "")
+                .version(StringUtils.isNotEmpty(serviceBean.getVersion()) ? 
serviceBean.getVersion() : "")
+                
.loadbalance(StringUtils.isNotEmpty(serviceBean.getLoadbalance()) ? 
serviceBean.getLoadbalance() : CommonConstants.DEFAULT_LOADBALANCE)
+                
.retries(Optional.ofNullable(serviceBean.getRetries()).orElse(CommonConstants.DEFAULT_RETRIES))
+                
.timeout(Optional.ofNullable(serviceBean.getTimeout()).orElse(DEFAULT_CONNECT_TIMEOUT))
+                
.sent(Optional.ofNullable(serviceBean.getSent()).orElse(Boolean.FALSE))
+                .cluster(StringUtils.isNotEmpty(serviceBean.getCluster()) ? 
serviceBean.getCluster() : Constants.DEFAULT_CLUSTER)
+                .url("")
+                .serialization(serviceBean.getSerialization())
+                .build();
+        if (Objects.nonNull(serviceBean.getMethods())) {
+            build.setMethods(new ArrayList<>());
+            for (MethodConfig methodConfig : serviceBean.getMethods()) {
+                DubboRpcMethodExt methodExt = new DubboRpcMethodExt();
+                methodExt.setName(methodConfig.getName());
+                methodExt.setLoadbalance(methodConfig.getLoadbalance());
+                methodExt.setRetries(methodConfig.getRetries());
+                methodExt.setTimeout(methodConfig.getTimeout());
+                methodExt.setSent(methodConfig.getSent());
+                build.getMethods().add(methodExt);
+            }
+        }
+        return GsonUtils.getInstance().toJson(build);
+    }
+}
diff --git 
a/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/src/test/java/org/apache/shenyu/client/dubbo/common/dto/DubboRpcExtBuildersTest.java
 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/src/test/java/org/apache/shenyu/client/dubbo/common/dto/DubboRpcExtBuildersTest.java
new file mode 100644
index 0000000000..d695566723
--- /dev/null
+++ 
b/shenyu-client/shenyu-client-dubbo/shenyu-client-dubbo-common/src/test/java/org/apache/shenyu/client/dubbo/common/dto/DubboRpcExtBuildersTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.client.dubbo.common.dto;
+
+import org.apache.dubbo.config.MethodConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.spring.ServiceBean;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class DubboRpcExtBuildersTest {
+
+    @Mock
+    private ServiceBean<?> serviceBean;
+
+    @Mock
+    private ProtocolConfig protocolConfig;
+
+    @Test
+    void testBuildRpcExtWithFullConfig() {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("dubbo");
+        when(serviceBean.getGroup()).thenReturn("testGroup");
+        when(serviceBean.getVersion()).thenReturn("1.0.0");
+        when(serviceBean.getLoadbalance()).thenReturn("random");
+        when(serviceBean.getRetries()).thenReturn(3);
+        when(serviceBean.getTimeout()).thenReturn(2000);
+        when(serviceBean.getSent()).thenReturn(true);
+        when(serviceBean.getCluster()).thenReturn("failfast");
+        when(serviceBean.getSerialization()).thenReturn("hessian2");
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        String result = DubboRpcExtBuilders.buildRpcExt(serviceBean);
+        assertNotNull(result);
+        assertTrue(result.contains("\"serialization\":\"hessian2\""));
+        assertTrue(result.contains("\"protocol\":\"dubbo\""));
+        assertTrue(result.contains("\"group\":\"testGroup\""));
+        assertTrue(result.contains("\"version\":\"1.0.0\""));
+        assertTrue(result.contains("\"loadbalance\":\"random\""));
+        assertTrue(result.contains("\"retries\":3"));
+        assertTrue(result.contains("\"timeout\":2000"));
+        assertTrue(result.contains("\"sent\":true"));
+        assertTrue(result.contains("\"cluster\":\"failfast\""));
+    }
+
+    @Test
+    void testBuildRpcExtWithNullProtocol() {
+        when(serviceBean.getProtocol()).thenReturn(null);
+        when(serviceBean.getGroup()).thenReturn(null);
+        when(serviceBean.getVersion()).thenReturn(null);
+        when(serviceBean.getLoadbalance()).thenReturn(null);
+        when(serviceBean.getRetries()).thenReturn(null);
+        when(serviceBean.getTimeout()).thenReturn(null);
+        when(serviceBean.getSent()).thenReturn(null);
+        when(serviceBean.getCluster()).thenReturn(null);
+        when(serviceBean.getSerialization()).thenReturn(null);
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        String result = DubboRpcExtBuilders.buildRpcExt(serviceBean);
+        assertNotNull(result);
+        assertTrue(result.contains("\"protocol\":\"\""));
+    }
+
+    @Test
+    void testBuildRpcExtWithMethodConfigs() {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("dubbo");
+        when(serviceBean.getGroup()).thenReturn("testGroup");
+        when(serviceBean.getVersion()).thenReturn("1.0.0");
+        when(serviceBean.getLoadbalance()).thenReturn("random");
+        when(serviceBean.getRetries()).thenReturn(3);
+        when(serviceBean.getTimeout()).thenReturn(2000);
+        when(serviceBean.getSent()).thenReturn(true);
+        when(serviceBean.getCluster()).thenReturn("failover");
+        when(serviceBean.getSerialization()).thenReturn("hessian2");
+
+        MethodConfig methodConfig = new MethodConfig();
+        methodConfig.setName("sayHello");
+        methodConfig.setLoadbalance("roundrobin");
+        methodConfig.setRetries(5);
+        methodConfig.setTimeout(3000);
+        methodConfig.setSent(false);
+
+        List<MethodConfig> methods = new ArrayList<>();
+        methods.add(methodConfig);
+        when(serviceBean.getMethods()).thenReturn(methods);
+
+        String result = DubboRpcExtBuilders.buildRpcExt(serviceBean);
+        assertNotNull(result);
+        assertTrue(result.contains("\"methods\":[{"));
+        assertTrue(result.contains("\"name\":\"sayHello\""));
+        assertTrue(result.contains("\"loadbalance\":\"roundrobin\""));
+        assertTrue(result.contains("\"retries\":5"));
+        assertTrue(result.contains("\"timeout\":3000"));
+    }
+
+    @Test
+    void testBuildRpcExtWithEmptyProtocolName() {
+        when(serviceBean.getProtocol()).thenReturn(protocolConfig);
+        when(protocolConfig.getName()).thenReturn("");
+        when(serviceBean.getGroup()).thenReturn(null);
+        when(serviceBean.getVersion()).thenReturn(null);
+        when(serviceBean.getLoadbalance()).thenReturn(null);
+        when(serviceBean.getRetries()).thenReturn(null);
+        when(serviceBean.getTimeout()).thenReturn(null);
+        when(serviceBean.getSent()).thenReturn(null);
+        when(serviceBean.getCluster()).thenReturn(null);
+        when(serviceBean.getSerialization()).thenReturn(null);
+        when(serviceBean.getMethods()).thenReturn(null);
+
+        String result = DubboRpcExtBuilders.buildRpcExt(serviceBean);
+        assertNotNull(result);
+        assertTrue(result.contains("\"protocol\":\"\""));
+    }
+}

Reply via email to