You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/06/29 02:22:33 UTC

[dubbo] 01/02: Support generic call by reference without interface class locally (#8159)

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

albumenj pushed a commit to branch 3.0.1-release
in repository https://gitbox.apache.org/repos/asf/dubbo.git

commit 84d6bb74b3f876800aa85a83969d5040a8698292
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Mon Jun 28 19:00:36 2021 +0800

    Support generic call by reference without interface class locally (#8159)
---
 .../apache/dubbo/config/ReferenceConfigBase.java   |  6 +--
 .../org/apache/dubbo/rpc/model/ConsumerModel.java  | 11 +++-
 .../org/apache/dubbo/config/ReferenceConfig.java   |  2 +-
 .../apache/dubbo/config/spring/ReferenceBean.java  | 63 ++++++++++++----------
 .../ReferenceAnnotationBeanPostProcessor.java      | 18 ++-----
 .../spring/schema/DubboBeanDefinitionParser.java   | 20 +++----
 .../javaconfig/JavaConfigReferenceBeanTest.java    |  8 +--
 .../config/spring/schema/GenericServiceTest.java   | 38 +++++++++++--
 .../META-INF/spring/dubbo-generic-consumer.xml     |  7 ++-
 .../src/test/resources/log4j.xml                   |  4 +-
 10 files changed, 104 insertions(+), 73 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
index c3d2af7..2e6e719 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
@@ -144,17 +144,17 @@ public abstract class ReferenceConfigBase<T> extends AbstractReferenceConfig {
     }
 
     /**
-     * Get actual interface class of this reference.
+     * Get service interface class of this reference.
      * The actual service type of remote provider.
      * @return
      */
-    public Class<?> getActualInterface() {
+    public Class<?> getServiceInterfaceClass() {
         Class actualInterface = interfaceClass;
         if (interfaceClass == GenericService.class) {
             try {
                 actualInterface = Class.forName(interfaceName);
             } catch (ClassNotFoundException e) {
-                // ignore
+                return null;
             }
         }
         return actualInterface;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
index 34fe276..ce4ab6a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.model;
 
 import org.apache.dubbo.common.BaseServiceMetadata;
 import org.apache.dubbo.common.utils.Assert;
+import org.apache.dubbo.common.utils.ClassUtils;
 import org.apache.dubbo.config.ReferenceConfigBase;
 
 import java.lang.reflect.Method;
@@ -146,7 +147,12 @@ public class ConsumerModel {
     public void initMethodModels() {
         Class[] interfaceList = null;
         if (proxyObject == null) {
-            interfaceList = new Class[]{referenceConfig.getActualInterface()};
+            Class<?> serviceInterfaceClass = referenceConfig.getServiceInterfaceClass();
+            if (serviceInterfaceClass != null) {
+                interfaceList = new Class[]{serviceInterfaceClass};
+            } else {
+                interfaceList = new Class[0];
+            }
         } else {
             interfaceList = proxyObject.getClass().getInterfaces();
         }
@@ -158,7 +164,8 @@ public class ConsumerModel {
     }
 
     public ClassLoader getClassLoader() {
-        return serviceMetadata.getServiceType().getClassLoader();
+        Class<?> serviceType = serviceMetadata.getServiceType();
+        return serviceType != null ? serviceType.getClassLoader() : ClassUtils.getClassLoader();
     }
 
     /**
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index 442713d..ee1a448 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
@@ -250,7 +250,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
 
         //init serivceMetadata
         initServiceMetadata(consumer);
-        serviceMetadata.setServiceType(getActualInterface());
+        serviceMetadata.setServiceType(getServiceInterfaceClass());
         // TODO, uncomment this line once service key is unified
         serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version));
 
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
index f06caa3..212e05c 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
@@ -18,8 +18,8 @@ package org.apache.dubbo.config.spring;
 
 import org.apache.dubbo.common.utils.Assert;
 import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ReferenceConfig;
-import org.apache.dubbo.config.ReferenceConfigBase;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
 import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
@@ -115,17 +115,10 @@ public class ReferenceBean<T> implements FactoryBean,
      */
     private Class<?> interfaceClass;
 
-    /**
-     * Actual interface class of this reference.
-     * The actual service type of remote provider.
-     * see {@link ReferenceConfigBase#getActualInterface()}
-     */
-    private Class actualInterface;
-
     /*
-     * actual interface class name
+     * remote service interface class name
      */
-    // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
+    // 'interfaceName' field for compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
     private String interfaceName;
 
     //from annotation attributes
@@ -214,7 +207,7 @@ public class ReferenceBean<T> implements FactoryBean,
         Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty");
         BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId());
         this.interfaceClass = (Class<?>) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS);
-        this.actualInterface = (Class) beanDefinition.getAttribute(ReferenceAttributes.ACTUAL_INTERFACE);
+        this.interfaceName = (String) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_NAME);
         Assert.notNull(this.interfaceClass, "The interface class of ReferenceBean is not initialized");
 
         if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) {
@@ -228,20 +221,15 @@ public class ReferenceBean<T> implements FactoryBean,
                     referenceProps = new LinkedHashMap<>();
                 }
                 ReferenceBeanSupport.convertReferenceProps(referenceProps, interfaceClass);
-                if (this.actualInterface == null) {
-                    try {
-                        this.actualInterface = ClassUtils.forName((String) referenceProps.get(ReferenceAttributes.INTERFACE));
-                    } catch (ClassNotFoundException e) {
-                        throw new IllegalStateException(e.getMessage(), e);
-                    }
+                if (this.interfaceName == null) {
+                    this.interfaceName = (String) referenceProps.get(ReferenceAttributes.INTERFACE);
                 }
             } else {
                 // xml reference bean
                 propertyValues = beanDefinition.getPropertyValues();
             }
         }
-        Assert.notNull(this.actualInterface, "The actual interface of ReferenceBean is not initialized");
-        this.interfaceName = actualInterface.getName();
+        Assert.notNull(this.interfaceName, "The interface name of ReferenceBean is not initialized");
 
 //        this.sources = (List<Map<String, Object>>) beanDefinition.getAttribute(Constants.REFERENCE_SOURCES);
 //        Assert.notNull(this.sources, "The registration sources of this reference is empty");
@@ -267,22 +255,36 @@ public class ReferenceBean<T> implements FactoryBean,
         this.id = id;
     }
 
-    /* Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
+
+    /**
+     * The interface of this ReferenceBean, for injection purpose
+     * @return
+     */
     public Class<?> getInterfaceClass() {
+        // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
         return interfaceClass;
     }
 
-    public Class getActualInterface() {
-        return actualInterface;
+    /**
+     * The interface of remote service
+     */
+    public String getServiceInterface() {
+        return interfaceName;
     }
 
-    /* Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
+    /**
+     * The group of the service
+     */
     public String getGroup() {
+        // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
         return referenceConfig.getGroup();
     }
 
-    /* Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
+    /**
+     * The version of the service
+     */
     public String getVersion() {
+        // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
         return referenceConfig.getVersion();
     }
 
@@ -316,14 +318,19 @@ public class ReferenceBean<T> implements FactoryBean,
         //see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
         ProxyFactory proxyFactory = new ProxyFactory();
         proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
-        proxyFactory.addInterface(getInterfaceClass());
+        proxyFactory.addInterface(interfaceClass);
         Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
         for (Class<?> anInterface : internalInterfaces) {
             proxyFactory.addInterface(anInterface);
         }
-        if (actualInterface != interfaceClass){
-            //add actual interface
-            proxyFactory.addInterface(actualInterface);
+        if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
+            //add service interface
+            try {
+                Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
+                proxyFactory.addInterface(serviceInterface);
+            } catch (ClassNotFoundException e) {
+                // generic call maybe without service interface class locally
+            }
         }
 
         this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index 2f4166f..0c4d8b4 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -237,23 +237,17 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
             }
 
             Class interfaceClass = beanClass;
-            Class actualInterface = null;
-            try {
-                actualInterface = ClassUtils.forName(interfaceName);
-            } catch (ClassNotFoundException e) {
-                throw new IllegalStateException(e.getMessage(), e);
-            }
 
             // set attribute instead of property values
             beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
             beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
-            beanDefinition.setAttribute(ReferenceAttributes.ACTUAL_INTERFACE, actualInterface);
+            beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
         } else {
             // raw reference bean
             // the ReferenceBean is not yet initialized
             beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, beanClass);
             if (beanClass != GenericService.class) {
-                beanDefinition.setAttribute(ReferenceAttributes.ACTUAL_INTERFACE, beanClass);
+                beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, beanClass.getName());
             }
         }
 
@@ -442,12 +436,6 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
         }
 
         Class interfaceClass = injectedType;
-        Class actualInterface = null;
-        try {
-            actualInterface = ClassUtils.forName(interfaceName);
-        } catch (ClassNotFoundException e) {
-            throw new IllegalStateException(e.getMessage(), e);
-        }
 
         // TODO Only register one reference bean for same (group, interface, version)
 
@@ -459,7 +447,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
         // set attribute instead of property values
         beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
         beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
-        beanDefinition.setAttribute(ReferenceAttributes.ACTUAL_INTERFACE, actualInterface);
+        beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
 
         // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
         // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
index 305cfd4..c15512a 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.config.spring.schema;
 
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.ClassUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.AbstractServiceConfig;
@@ -32,6 +31,7 @@ import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.spring.Constants;
 import org.apache.dubbo.config.spring.ReferenceBean;
 import org.apache.dubbo.config.spring.ServiceBean;
+import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
 import org.springframework.beans.PropertyValue;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
@@ -246,27 +246,21 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
 
     private static void configReferenceBean(Element element, ParserContext parserContext, RootBeanDefinition beanDefinition, BeanDefinition consumerDefinition) {
         // process interface class
-        String interfaceName = resolveAttribute(element, "interface", parserContext);
-        String generic = resolveAttribute(element, "generic", parserContext);
+        String interfaceName = resolveAttribute(element, ReferenceAttributes.INTERFACE, parserContext);
+        String generic = resolveAttribute(element, ReferenceAttributes.GENERIC, parserContext);
         if (StringUtils.isBlank(generic) && consumerDefinition != null) {
             // get generic from consumerConfig
-            generic = (String) consumerDefinition.getPropertyValues().get("generic");
+            generic = (String) consumerDefinition.getPropertyValues().get(ReferenceAttributes.GENERIC);
         }
         if (generic != null) {
             Environment environment = parserContext.getReaderContext().getEnvironment();
             generic = environment.resolvePlaceholders(generic);
-            beanDefinition.getPropertyValues().add("generic", generic);
+            beanDefinition.getPropertyValues().add(ReferenceAttributes.GENERIC, generic);
         }
+        beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
 
         Class interfaceClass = ReferenceConfig.determineInterfaceClass(generic, interfaceName);
-        Class actualInterface = null;
-        try {
-            actualInterface = ClassUtils.forName(interfaceName);
-        } catch (ClassNotFoundException e) {
-            throw new IllegalStateException(e.getMessage(), e);
-        }
-        beanDefinition.setAttribute("interfaceClass", interfaceClass);
-        beanDefinition.setAttribute("actualInterface", actualInterface);
+        beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
 
         // TODO Only register one reference bean for same (group, interface, version)
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java
index 37d70f2..e0b2428 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java
@@ -71,12 +71,12 @@ public class JavaConfigReferenceBeanTest {
         ReferenceBean referenceBean = referenceBeanMap.get("&helloService");
         Assertions.assertEquals("demo", referenceBean.getGroup());
         Assertions.assertEquals(HelloService.class, referenceBean.getInterfaceClass());
-        Assertions.assertEquals(HelloService.class, referenceBean.getActualInterface());
+        Assertions.assertEquals(HelloService.class.getName(), referenceBean.getServiceInterface());
 
         ReferenceBean genericHelloServiceReferenceBean = referenceBeanMap.get("&genericHelloService");
         Assertions.assertEquals("demo", genericHelloServiceReferenceBean.getGroup());
         Assertions.assertEquals(GenericService.class, genericHelloServiceReferenceBean.getInterfaceClass());
-        Assertions.assertEquals(HelloService.class, genericHelloServiceReferenceBean.getActualInterface());
+        Assertions.assertEquals(HelloService.class.getName(), genericHelloServiceReferenceBean.getServiceInterface());
 
         context.close();
         Assertions.assertEquals(1, SpringExtensionFactory.getContexts().size());
@@ -101,12 +101,12 @@ public class JavaConfigReferenceBeanTest {
         Assertions.assertEquals(3, referenceBeanMap.size());
         ReferenceBean referenceBean = referenceBeanMap.get("&helloService");
         Assertions.assertEquals(HelloService.class, referenceBean.getInterfaceClass());
-        Assertions.assertEquals(HelloService.class, referenceBean.getActualInterface());
+        Assertions.assertEquals(HelloService.class.getName(), referenceBean.getServiceInterface());
 
         ReferenceBean genericHelloServiceReferenceBean = referenceBeanMap.get("&genericHelloService");
         Assertions.assertEquals("demo", genericHelloServiceReferenceBean.getGroup());
         Assertions.assertEquals(GenericService.class, genericHelloServiceReferenceBean.getInterfaceClass());
-        Assertions.assertEquals(HelloService.class, genericHelloServiceReferenceBean.getActualInterface());
+        Assertions.assertEquals(HelloService.class.getName(), genericHelloServiceReferenceBean.getServiceInterface());
 
         context.close();
         Assertions.assertEquals(1, SpringExtensionFactory.getContexts().size());
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java
index de4ab91..105bbdf 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java
@@ -16,10 +16,17 @@
  */
 package org.apache.dubbo.config.spring.schema;
 
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.config.ReferenceConfigBase;
+import org.apache.dubbo.config.ServiceConfigBase;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.context.ConfigManager;
 import org.apache.dubbo.config.spring.ServiceBean;
+import org.apache.dubbo.config.spring.ZooKeeperServer;
+import org.apache.dubbo.config.spring.api.DemoService;
+import org.apache.dubbo.rpc.service.GenericService;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -41,6 +48,7 @@ public class GenericServiceTest {
 
     @BeforeAll
     public static void setUp() {
+        ZooKeeperServer.start();
         DubboBootstrap.reset();
     }
 
@@ -51,15 +59,37 @@ public class GenericServiceTest {
 
     @Autowired
     @Qualifier("demoServiceRef")
-    private ReferenceBean referenceBean;
+    private GenericService demoServiceRef;
+
+    @Autowired
+    @Qualifier("genericServiceWithoutInterfaceRef")
+    private GenericService genericServiceWithoutInterfaceRef;
 
     @Autowired
     @Qualifier("demoService")
     private ServiceBean serviceBean;
 
     @Test
-    public void testBeanDefinitionParser() {
-        assertNotNull(referenceBean);
+    public void testGeneric() {
+        assertNotNull(demoServiceRef);
         assertNotNull(serviceBean);
+
+        ConfigManager configManager = DubboBootstrap.getInstance().getConfigManager();
+        ServiceConfigBase<Object> serviceConfig = configManager.getService("demoService");
+        Assertions.assertEquals(DemoService.class.getName(), serviceConfig.getInterface());
+        Assertions.assertEquals(true, serviceConfig.isExported());
+
+        Object result = demoServiceRef.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"dubbo"});
+        Assertions.assertEquals("Welcome dubbo", result);
+
+
+        // Test generic service without interface class locally
+        result = genericServiceWithoutInterfaceRef.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"generic"});
+        Assertions.assertEquals("Welcome generic", result);
+
+        ReferenceConfigBase<Object> reference = configManager.getReference("genericServiceWithoutInterfaceRef");
+        Assertions.assertNull(reference.getServiceInterfaceClass());
+        Assertions.assertThrows(ClassNotFoundException.class, () -> ClassUtils.forName(reference.getInterface()));
+
     }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml
index 72bf442..a103e4f 100644
--- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml
@@ -25,7 +25,7 @@
     <dubbo:application name="dubbo-generic-consumer"/>
 
     <!-- 连接注册中心配置 -->
-    <dubbo:registry address="N/A"/>
+    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
 
     <dubbo:reference id="demoServiceRef" interface="org.apache.dubbo.config.spring.api.DemoService" generic="true" init="false"/>
 
@@ -33,4 +33,9 @@
 
     <dubbo:service id="demoService" interface="org.apache.dubbo.config.spring.api.DemoService" generic="true"
                    ref="genericService"/>
+
+    <!-- generic service without interface class -->
+    <dubbo:reference id="genericServiceWithoutInterfaceRef" interface="org.apache.dubbo.config.spring.api.LocalMissService" generic="true" init="false"/>
+    <dubbo:service id="genericServiceWithoutInterface" interface="org.apache.dubbo.config.spring.api.LocalMissService" generic="true"
+                   ref="genericService"/>
 </beans>
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/log4j.xml b/dubbo-config/dubbo-config-spring/src/test/resources/log4j.xml
index 24e59e1..5d27202 100644
--- a/dubbo-config/dubbo-config-spring/src/test/resources/log4j.xml
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/log4j.xml
@@ -23,7 +23,7 @@
         </layout>
     </appender>
 
-    <logger name="org.apache.dubbo.config.spring" additivity="false">
+    <logger name="org.apache.dubbo.config" additivity="false">
         <level value="DEBUG"/>
         <appender-ref ref="CONSOLE"/>
     </logger>
@@ -32,4 +32,4 @@
         <level value="INFO"/>
         <appender-ref ref="CONSOLE"/>
     </root>
-</log4j:configuration>
\ No newline at end of file
+</log4j:configuration>