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/08/10 03:25:47 UTC

[dubbo] branch 3.0 updated: [3.0] compatible with spring 3.2.x and add tests (#8430)

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

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


The following commit(s) were added to refs/heads/3.0 by this push:
     new 649dc5b  [3.0] compatible with spring 3.2.x and add tests (#8430)
649dc5b is described below

commit 649dc5b396fe57e34cb13a1b6469d8ea496db387
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Tue Aug 10 11:25:11 2021 +0800

    [3.0] compatible with spring 3.2.x and add tests (#8430)
    
    * compatible with spring 3.2.x and add tests
    
    * add licence
    
    * polish spring tests, fix compatible issue of spring 4.1
    
    * polish code
    
    * add skip_maven_deploy
    
    * adjust spring test module
    
    * polish log
    
    * remove unused imports
    
    * Fix NPE
---
 .../apache/dubbo/common/utils/AnnotationUtils.java |   5 +
 .../ReferenceAnnotationBeanPostProcessor.java      |  42 +++-
 .../annotation/ServiceAnnotationPostProcessor.java |  66 ++++--
 .../context/DubboBootstrapApplicationListener.java |  10 +-
 .../DubboInfraBeanRegisterPostProcessor.java       |  17 +-
 .../annotation/DubboComponentScanRegistrar.java    |  38 +++-
 .../spring/reference/ReferenceBeanSupport.java     |  12 +-
 .../spring/schema/DubboBeanDefinitionParser.java   |  17 +-
 .../dubbo/config/spring/util/DubboBeanUtils.java   |   4 +-
 .../config/spring/util/SpringCompatUtils.java      | 124 +++++++++++
 .../config/ServiceBeanIdConflictProcessor.java     |   5 +-
 dubbo-test/dubbo-test-common/pom.xml               |  61 ++++++
 .../dubbo/test/common/EmbeddedZooKeeper.java       | 244 +++++++++++++++++++++
 .../org/apache/dubbo/test/common/ErrorHandler.java |  27 +++
 .../org/apache/dubbo/test/common/SysProps.java     |  45 ++++
 .../apache/dubbo/test/common/ZooKeeperServer.java  |  43 ++++
 .../apache/dubbo/test/common/api/DemoService.java  |  27 +++
 .../dubbo/test/common/api/GreetingService.java     |  24 ++
 .../dubbo/test/common/api/RestDemoService.java     |  45 ++++
 .../dubbo/test/common/impl/DemoServiceImpl.java    |  43 ++++
 .../test/common/impl/GreetingServiceImpl.java      |  30 +++
 .../test/common/impl/RestDemoServiceImpl.java      |  58 +++++
 dubbo-test/dubbo-test-spring/pom.xml               | 170 ++++++++++++++
 .../test/spring/SpringAnnotationBeanTest.java      |  67 ++++++
 .../test/spring/SpringJavaConfigBeanTest.java      | 186 ++++++++++++++++
 .../dubbo/test/spring/SpringXmlConfigTest.java     |  66 ++++++
 .../src/main/resources/demo-app.properties         |  11 +
 .../src/main/resources/log4j.properties            |   7 +
 .../src/main/resources/spring/dubbo-demo.xml       |  53 +++++
 dubbo-test/dubbo-test-spring3.2/pom.xml            |  70 ++++++
 dubbo-test/dubbo-test-spring4.1/pom.xml            |  70 ++++++
 dubbo-test/dubbo-test-spring4.2/pom.xml            |  70 ++++++
 dubbo-test/pom.xml                                 |  56 +++++
 pom.xml                                            |   2 +
 34 files changed, 1754 insertions(+), 61 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java
index ba2da5b..770694e 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java
@@ -495,6 +495,11 @@ public interface AnnotationUtils {
                 filteredAttributes.put(key, val);
             }
         });
+        // remove void class, compatible with spring 3.x
+        Object interfaceClassValue = filteredAttributes.get("interfaceClass");
+        if (interfaceClassValue instanceof String && StringUtils.isEquals((String) interfaceClassValue, "void")) {
+            filteredAttributes.remove("interfaceClass");
+        }
         return filteredAttributes;
     }
 
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 71591f7..fe5af61 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
@@ -30,6 +30,7 @@ import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
 import org.apache.dubbo.config.spring.ReferenceBean;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
+import org.apache.dubbo.config.spring.util.SpringCompatUtils;
 import org.apache.dubbo.rpc.service.GenericService;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.PropertyValue;
@@ -40,7 +41,9 @@ import org.springframework.beans.factory.annotation.InjectionMetadata;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.AbstractBeanFactory;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 import org.springframework.beans.factory.support.GenericBeanDefinition;
 import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -63,6 +66,7 @@ import java.util.concurrent.ConcurrentMap;
 
 import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
 import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues;
+import static org.apache.dubbo.config.spring.util.SpringCompatUtils.getPropertyValue;
 import static org.springframework.util.StringUtils.hasText;
 
 /**
@@ -156,16 +160,24 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
             }
         }
 
-        // This bean has bean register as BeanPostProcessor at org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor.postProcessBeanFactory()
-        // so destroy this bean here, prevent register it as BeanPostProcessor again, avoid cause BeanPostProcessorChecker detection error
-        beanDefinitionRegistry.removeBeanDefinition(BEAN_NAME);
+        if (beanFactory instanceof AbstractBeanFactory) {
+            List<BeanPostProcessor> beanPostProcessors = ((AbstractBeanFactory) beanFactory).getBeanPostProcessors();
+            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
+                if (beanPostProcessor == this) {
+                    // This bean has bean register as BeanPostProcessor at org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor.postProcessBeanFactory()
+                    // so destroy this bean here, prevent register it as BeanPostProcessor again, avoid cause BeanPostProcessorChecker detection error
+                    beanDefinitionRegistry.removeBeanDefinition(BEAN_NAME);
+                    break;
+                }
+            }
+        }
 
         try {
             // this is an early event, it will be notified at org.springframework.context.support.AbstractApplicationContext.registerListeners()
             applicationContext.publishEvent(new DubboAnnotationInitedEvent(applicationContext));
         } catch (Exception e) {
             // if spring version is less then 4.2, it does not support early application event
-            logger.error("publish early application event failed, please upgrade spring version to 4.2.x or later", e);
+            logger.warn("publish early application event failed, please upgrade spring version to 4.2.x or later: " + e);
         }
     }
 
@@ -175,11 +187,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
     private boolean isAnnotatedReferenceBean(BeanDefinition beanDefinition) {
         if (beanDefinition instanceof AnnotatedBeanDefinition) {
             AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
-            if (annotatedBeanDefinition.getFactoryMethodMetadata() == null) {
-                return false;
-            }
-            String beanClassName = annotatedBeanDefinition.getFactoryMethodMetadata().getReturnTypeName();
-            if (ReferenceBean.class.getName().equals(beanClassName)) {
+            String beanClassName = SpringCompatUtils.getFactoryMethodReturnType(annotatedBeanDefinition);
+            if (beanClassName != null && ReferenceBean.class.getName().equals(beanClassName)) {
                 return true;
             }
         }
@@ -205,10 +214,19 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
      */
     private void processReferenceAnnotatedBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition) {
 
+        MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(beanDefinition);
+
         // Extract beanClass from generic return type of java-config bean method: ReferenceBean<DemoService>
         // see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBeanFromMethod
         Class beanClass = getBeanFactory().getType(beanName);
-        MethodMetadata factoryMethodMetadata = beanDefinition.getFactoryMethodMetadata();
+        if (beanClass == Object.class) {
+            beanClass = SpringCompatUtils.getGenericTypeOfReturnType(factoryMethodMetadata);
+        }
+        if (beanClass == Object.class) {
+            // bean class is invalid, ignore it
+            return;
+        }
+
         if (beanClass == null) {
             String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName()+"#"+factoryMethodMetadata.getMethodName()+"()";
             throw new BeanCreationException("The ReferenceBean is missing necessary generic type, which returned by the @Bean method of Java-config class. " +
@@ -239,6 +257,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
             // get interface
             String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE);
+
             // check beanClass and reference interface class
             if (!StringUtils.isEquals(interfaceName, beanClass.getName()) && beanClass != GenericService.class) {
                 String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName()+"#"+factoryMethodMetadata.getMethodName()+"()";
@@ -466,7 +485,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
         // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
         GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
         targetDefinition.setBeanClass(interfaceClass);
-        String id = (String) beanDefinition.getPropertyValues().get(ReferenceAttributes.ID);
+        String id = getPropertyValue(beanDefinition.getPropertyValues(), ReferenceAttributes.ID);
+
         beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));
 
         // signal object type since Spring 5.2
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationPostProcessor.java
index a23e8be..9313315 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationPostProcessor.java
@@ -29,10 +29,12 @@ import org.apache.dubbo.config.spring.ServiceBean;
 import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner;
 import org.apache.dubbo.config.spring.schema.AnnotationBeanDefinitionParser;
 import org.apache.dubbo.config.spring.util.DubboAnnotationUtils;
+import org.apache.dubbo.config.spring.util.SpringCompatUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.MutablePropertyValues;
 import org.springframework.beans.factory.BeanClassLoaderAware;
 import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
@@ -77,11 +79,11 @@ import java.util.Set;
 import static com.alibaba.spring.util.ObjectUtils.of;
 import static java.util.Arrays.asList;
 import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
 import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create;
 import static org.apache.dubbo.config.spring.util.DubboAnnotationUtils.resolveInterfaceName;
 import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
 import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
-import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
 import static org.springframework.util.ClassUtils.resolveClassName;
 
 /**
@@ -94,7 +96,7 @@ import static org.springframework.util.ClassUtils.resolveClassName;
  * @since 2.7.7
  */
 public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
-        ResourceLoaderAware, BeanClassLoaderAware, ApplicationContextAware {
+        ResourceLoaderAware, BeanClassLoaderAware, ApplicationContextAware, InitializingBean {
 
     public static final String BEAN_NAME = "dubboServiceAnnotationPostProcessor";
 
@@ -111,6 +113,7 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
 
     protected final Set<String> packagesToScan;
 
+    private Set<String> resolvedPackagesToScan;
 
     private Environment environment;
 
@@ -122,6 +125,7 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
 
     private ServicePackagesHolder servicePackagesHolder;
 
+    private volatile boolean scaned = false;
 
     public ServiceAnnotationPostProcessor(String... packagesToScan) {
         this(asList(packagesToScan));
@@ -136,19 +140,38 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
     }
 
     @Override
+    public void afterPropertiesSet() throws Exception {
+        this.resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
+    }
+
+    @Override
     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
         this.registry = registry;
+        scanServiceBeans(resolvedPackagesToScan, registry);
+    }
 
-        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        if (this.registry == null) {
+            // In spring 3.x, may be not call postProcessBeanDefinitionRegistry()
+            this.registry = (BeanDefinitionRegistry) beanFactory;
+        }
 
-        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
-            scanServiceBeans(resolvedPackagesToScan, registry);
-        } else {
-            if (logger.isWarnEnabled()) {
-                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
+        // scan bean definitions
+        String[] beanNames = beanFactory.getBeanDefinitionNames();
+        for (String beanName : beanNames) {
+            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+            Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
+            if (annotationAttributes != null) {
+                // process @DubboService at java-config @bean method
+                processAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
             }
         }
 
+        if (!scaned) {
+            // In spring 3.x, may be not call postProcessBeanDefinitionRegistry(), so scan service class here
+            scanServiceBeans(resolvedPackagesToScan, registry);
+        }
     }
 
     /**
@@ -159,6 +182,14 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
      */
     private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
 
+        scaned = true;
+        if (CollectionUtils.isEmpty(packagesToScan)) {
+            if (logger.isWarnEnabled()) {
+                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
+            }
+            return;
+        }
+
         DubboClassPathBeanDefinitionScanner scanner =
                 new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
 
@@ -325,7 +356,7 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
     private Annotation findServiceAnnotation(Class<?> beanClass) {
         return serviceAnnotationTypes
                 .stream()
-                .map(annotationType -> findMergedAnnotation(beanClass, annotationType))
+                .map(annotationType -> findAnnotation(beanClass, annotationType))
                 .filter(Objects::nonNull)
                 .findFirst()
                 .orElse(null);
@@ -482,19 +513,6 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
         builder.addPropertyValue(propertyName, resolvedBeanName);
     }
 
-    @Override
-    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
-        String[] beanNames = beanFactory.getBeanDefinitionNames();
-        for (String beanName : beanNames) {
-            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
-            Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
-            if (annotationAttributes != null) {
-                // process @DubboService at java-config @bean method
-                processAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
-            }
-        }
-    }
-
     /**
      * Get dubbo service annotation class at java-config @bean method
      * @return return service annotation attributes map if found, or return null if not found.
@@ -502,7 +520,7 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
     private Map<String, Object> getServiceAnnotationAttributes(BeanDefinition beanDefinition) {
         if (beanDefinition instanceof AnnotatedBeanDefinition) {
             AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
-            MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
+            MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(annotatedBeanDefinition);
             if (factoryMethodMetadata != null) {
                 // try all dubbo service annotation types
                 for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
@@ -542,7 +560,7 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
         Map<String, Object> serviceAnnotationAttributes = new LinkedHashMap<>(attributes);
 
         // get bean class from return type
-        String returnTypeName = refServiceBeanDefinition.getFactoryMethodMetadata().getReturnTypeName();
+        String returnTypeName = SpringCompatUtils.getFactoryMethodReturnType(refServiceBeanDefinition);
         Class<?> beanClass = resolveClassName(returnTypeName, classLoader);
 
         String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
index 90d0680..ffb1335 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
@@ -54,11 +54,17 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
 
     private final DubboBootstrap dubboBootstrap;
     private ApplicationContext applicationContext;
+    private boolean shouldInitConfigBeans;
 
     public DubboBootstrapApplicationListener() {
         this.dubboBootstrap = initBootstrap();
     }
 
+    public DubboBootstrapApplicationListener(boolean shouldInitConfigBeans) {
+        this.dubboBootstrap = initBootstrap();
+        this.shouldInitConfigBeans = shouldInitConfigBeans;
+    }
+
     private DubboBootstrap initBootstrap() {
         DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance();
         if (dubboBootstrap.getTakeoverMode() != BootstrapTakeoverMode.MANUAL) {
@@ -142,7 +148,9 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         this.applicationContext = applicationContext;
 
-        checkCallStackAndInit();
+        if (shouldInitConfigBeans) {
+            checkCallStackAndInit();
+        }
     }
 
     private void checkCallStackAndInit() {
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
index 2c56100..7952982 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
@@ -58,14 +58,17 @@ public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegist
     @Override
     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
 
-        // register ReferenceAnnotationBeanPostProcessor early before PropertySourcesPlaceholderConfigurer/PropertyPlaceholderConfigurer
-        // for processing early init ReferenceBean
-        ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor = beanFactory.getBean(
-            ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
-        beanFactory.addBeanPostProcessor(referenceAnnotationBeanPostProcessor);
+        // In Spring 3.2.x, registry may be null because do not calling postProcessBeanDefinitionRegistry method before postProcessBeanFactory
+        if (registry != null) {
+            // register ReferenceAnnotationBeanPostProcessor early before PropertySourcesPlaceholderConfigurer/PropertyPlaceholderConfigurer
+            // for processing early init ReferenceBean
+            ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor = beanFactory.getBean(
+                ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
+            beanFactory.addBeanPostProcessor(referenceAnnotationBeanPostProcessor);
 
-        // register PropertySourcesPlaceholderConfigurer bean if not exits
-        DubboBeanUtils.registerBeansIfNotExists(beanFactory, registry);
+            // register PropertySourcesPlaceholderConfigurer bean if not exits
+            DubboBeanUtils.registerPlaceholderConfigurerBeanIfNotExists(beanFactory, registry);
+        }
 
         // Initialize dubbo Environment before ConfigManager
         // Extract dubbo props from Spring env and put them to app config
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrar.java
index 7807efe..25821d2 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrar.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrar.java
@@ -79,19 +79,41 @@ public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistra
     }
 
     private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
+        // get from @DubboComponentScan 
+        Set<String> packagesToScan = getPackagesToScan0(metadata, DubboComponentScan.class, "basePackages", "basePackageClasses");
+
+        // get from @EnableDubbo, compatible with spring 3.x
+        if (packagesToScan.isEmpty()) {
+            packagesToScan = getPackagesToScan0(metadata, EnableDubbo.class, "scanBasePackages", "scanBasePackageClasses");
+        }
+
+        if (packagesToScan.isEmpty()) {
+            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
+        }
+        return packagesToScan;
+    }
+
+    private Set<String> getPackagesToScan0(AnnotationMetadata metadata, Class annotationClass, String basePackagesName, String basePackageClassesName) {
+
         AnnotationAttributes attributes = AnnotationAttributes.fromMap(
-                metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
-        String[] basePackages = attributes.getStringArray("basePackages");
-        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
-        String[] value = attributes.getStringArray("value");
-        // Appends value array attributes
-        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
+                metadata.getAnnotationAttributes(annotationClass.getName()));
+        if (attributes == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> packagesToScan = new LinkedHashSet<>();
+        // basePackages
+        String[] basePackages = attributes.getStringArray(basePackagesName);
         packagesToScan.addAll(Arrays.asList(basePackages));
+        // basePackageClasses
+        Class<?>[] basePackageClasses = attributes.getClassArray(basePackageClassesName);
         for (Class<?> basePackageClass : basePackageClasses) {
             packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
         }
-        if (packagesToScan.isEmpty()) {
-            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
+        // value
+        if (attributes.containsKey("value")) {
+            String[] value = attributes.getStringArray("value");
+            packagesToScan.addAll(Arrays.asList(value));
         }
         return packagesToScan;
     }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanSupport.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanSupport.java
index dfdb19a..066ea0b 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanSupport.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanSupport.java
@@ -58,8 +58,16 @@ public class ReferenceBeanSupport {
             interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE_NAME);
         }
         if (interfaceName == null) {
-            Class clazz = (Class) attributes.get(ReferenceAttributes.INTERFACE_CLASS);
-            interfaceName = clazz != null ? clazz.getName() : null;
+            Object interfaceClassValue = attributes.get(ReferenceAttributes.INTERFACE_CLASS);
+            if (interfaceClassValue instanceof Class) {
+                interfaceName = ((Class) interfaceClassValue).getName();
+            } else if (interfaceClassValue instanceof String) {
+                if (interfaceClassValue.equals("void")) {
+                    attributes.remove(ReferenceAttributes.INTERFACE_CLASS);
+                } else {
+                    interfaceName = (String) interfaceClassValue;
+                }
+            }
         }
         if (interfaceName == null && defaultInterfaceClass != GenericService.class) {
             interfaceName = defaultInterfaceClass.getName();
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 85f2034..a28b497 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
@@ -44,7 +44,6 @@ import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.core.env.Environment;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
@@ -61,6 +60,7 @@ import java.util.Set;
 import java.util.regex.Pattern;
 
 import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
+import static org.apache.dubbo.config.spring.util.SpringCompatUtils.getPropertyValue;
 
 /**
  * AbstractBeanDefinitionParser
@@ -76,6 +76,7 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
     private static final String ONINVOKE = "oninvoke";
     private static final String METHOD = "Method";
     private static final String BEAN_NAME = "BEAN_NAME";
+    private static boolean resolvePlaceholdersEnabled = true;
     private final Class<?> beanClass;
     private static Map<String, Map<String, Class>> beanPropsCache = new HashMap<>();
 
@@ -247,11 +248,10 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
         String generic = resolveAttribute(element, ReferenceAttributes.GENERIC, parserContext);
         if (StringUtils.isBlank(generic) && consumerDefinition != null) {
             // get generic from consumerConfig
-            generic = (String) consumerDefinition.getPropertyValues().get(ReferenceAttributes.GENERIC);
+            generic = getPropertyValue(consumerDefinition.getPropertyValues(), ReferenceAttributes.GENERIC);
         }
         if (generic != null) {
-            Environment environment = parserContext.getReaderContext().getEnvironment();
-            generic = environment.resolvePlaceholders(generic);
+            generic = resolvePlaceholders(generic, parserContext);
             beanDefinition.getPropertyValues().add(ReferenceAttributes.GENERIC, generic);
         }
         beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
@@ -513,6 +513,13 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
     }
 
     private static String resolvePlaceholders(String str, ParserContext parserContext) {
-        return parserContext.getReaderContext().getEnvironment().resolveRequiredPlaceholders(str);
+        if (resolvePlaceholdersEnabled) {
+            try {
+                return parserContext.getReaderContext().getEnvironment().resolveRequiredPlaceholders(str);
+            } catch (NoSuchMethodError e) {
+                resolvePlaceholdersEnabled = false;
+            }
+        }
+        return str;
     }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
index 169af09..72f2177 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
@@ -117,7 +117,7 @@ public interface DubboBeanUtils {
     }
 
     /**
-     * Register some beans later
+     * Register a placeholder configurer beans if not exists.
      * Call this method in BeanDefinitionRegistryPostProcessor,
      * in order to enable the registered BeanFactoryPostProcessor bean to be loaded and executed.
      * @see DubboInfraBeanRegisterPostProcessor
@@ -125,7 +125,7 @@ public interface DubboBeanUtils {
      * @param beanFactory
      * @param registry
      */
-    static void registerBeansIfNotExists(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry) {
+    static void registerPlaceholderConfigurerBeanIfNotExists(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry) {
         // Auto register a PropertyPlaceholderConfigurer bean to resolve placeholders with Spring Environment PropertySources
         // when loading dubbo xml config with @ImportResource
         if (!checkBeanExists(beanFactory, PropertySourcesPlaceholderConfigurer.class)) {
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/SpringCompatUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/SpringCompatUtils.java
new file mode 100644
index 0000000..9faed82
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/SpringCompatUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.dubbo.config.spring.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.core.type.MethodMetadata;
+import org.springframework.core.type.StandardMethodMetadata;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * Spring Compatibility Utils for spring 3.x/4.x/5.x
+ */
+public class SpringCompatUtils {
+
+    private static volatile Boolean factoryMethodMetadataEnabled = null;
+
+    private static final Log logger = LogFactory.getLog(SpringCompatUtils.class);
+
+    public static <T> T getPropertyValue(PropertyValues pvs, String propertyName) {
+        PropertyValue pv = pvs.getPropertyValue(propertyName);
+        return (pv != null ? (T) pv.getValue() : null);
+    }
+
+    public static boolean isFactoryMethodMetadataEnabled() {
+        if (factoryMethodMetadataEnabled == null) {
+            try {
+                //check AnnotatedBeanDefinition.getFactoryMethodMetadata() since spring 4.1
+                AnnotatedBeanDefinition.class.getMethod("getFactoryMethodMetadata");
+
+                // check MethodMetadata.getReturnTypeName() since spring 4.2
+                MethodMetadata.class.getMethod("getReturnTypeName");
+
+                factoryMethodMetadataEnabled = true;
+            } catch (NoSuchMethodException e) {
+                factoryMethodMetadataEnabled = false;
+            }
+        }
+        return factoryMethodMetadataEnabled;
+    }
+
+    public static String getFactoryMethodReturnType(AnnotatedBeanDefinition annotatedBeanDefinition) {
+        try {
+            if (isFactoryMethodMetadataEnabled()) {
+                MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
+                return factoryMethodMetadata != null ? factoryMethodMetadata.getReturnTypeName() : null;
+            } else {
+                Object source = annotatedBeanDefinition.getSource();
+                if (source instanceof StandardMethodMetadata) {
+                    StandardMethodMetadata methodMetadata = (StandardMethodMetadata) source;
+                    Method introspectedMethod = methodMetadata.getIntrospectedMethod();
+                    if (introspectedMethod != null) {
+                        return introspectedMethod.getReturnType().getName();
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            if (logger.isInfoEnabled()) {
+                logger.info("get return type of AnnotatedBeanDefinition failed", e);
+            }
+        }
+        return null;
+    }
+
+    public static MethodMetadata getFactoryMethodMetadata(AnnotatedBeanDefinition annotatedBeanDefinition) {
+        if (isFactoryMethodMetadataEnabled()) {
+            return annotatedBeanDefinition.getFactoryMethodMetadata();
+        } else {
+            Object source = annotatedBeanDefinition.getSource();
+            if (source instanceof StandardMethodMetadata) {
+                return (MethodMetadata) source;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Get the generic type of return type of the method.
+     *
+     * <pre>
+     *  Source method:
+     *  ReferenceBean&lt;DemoService> demoService()
+     *
+     *  Result: DemoService.class
+     * </pre>
+     *
+     * @param factoryMethodMetadata
+     * @return
+     */
+    public static Class getGenericTypeOfReturnType(MethodMetadata factoryMethodMetadata) {
+        if (factoryMethodMetadata instanceof StandardMethodMetadata) {
+            Method introspectedMethod = ((StandardMethodMetadata) factoryMethodMetadata).getIntrospectedMethod();
+            Type returnType = introspectedMethod.getGenericReturnType();
+            if (returnType instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType) returnType;
+                Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
+                if (actualTypeArgument instanceof Class) {
+                    return (Class) actualTypeArgument;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java
index b6bf04b..77e0625 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java
+++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.spring.boot.beans.factory.config;
 import org.apache.dubbo.config.ServiceConfig;
 import org.apache.dubbo.config.spring.ServiceBean;
 
+import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
@@ -33,6 +34,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
+import static org.apache.dubbo.config.spring.util.SpringCompatUtils.getPropertyValue;
 import static org.springframework.util.ClassUtils.getUserClass;
 import static org.springframework.util.ClassUtils.isAssignable;
 
@@ -64,7 +66,8 @@ public class ServiceBeanIdConflictProcessor implements MergedBeanDefinitionPostP
         // Get raw bean type
         Class<?> rawBeanType = getUserClass(beanType);
         if (isAssignable(ServiceConfig.class, rawBeanType)) { // ServiceConfig type or sub-type
-            String interfaceName = (String) beanDefinition.getPropertyValues().get("interface");
+            String interfaceName = getPropertyValue(beanDefinition.getPropertyValues(), ReferenceAttributes.INTERFACE);
+
             String mappedBeanName = interfaceNamesToBeanNames.putIfAbsent(interfaceName, beanName);
             // If mapped bean name exists and does not equal current bean name
             if (mappedBeanName != null && !mappedBeanName.equals(beanName)) {
diff --git a/dubbo-test/dubbo-test-common/pom.xml b/dubbo-test/dubbo-test-common/pom.xml
new file mode 100644
index 0000000..9c52951
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/pom.xml
@@ -0,0 +1,61 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dubbo-test</artifactId>
+        <groupId>org.apache.dubbo</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-test-common</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <skip_maven_deploy>true</skip_maven_deploy>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.zookeeper</groupId>
+            <artifactId>zookeeper</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-rest</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/EmbeddedZooKeeper.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/EmbeddedZooKeeper.java
new file mode 100644
index 0000000..e91755a
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/EmbeddedZooKeeper.java
@@ -0,0 +1,244 @@
+/*
+ * 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.dubbo.test.common;
+
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.zookeeper.server.ServerConfig;
+import org.apache.zookeeper.server.ZooKeeperServerMain;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Properties;
+import java.util.UUID;
+
+/**
+ * from: https://github.com/spring-projects/spring-xd/blob/v1.3.1.RELEASE/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/zookeeper/ZooKeeperUtils.java
+ * <p>
+ * Helper class to start an embedded instance of standalone (non clustered) ZooKeeper.
+ * <p>
+ * NOTE: at least an external standalone server (if not an ensemble) are recommended, even for
+ * {@link org.springframework.xd.dirt.server.singlenode.SingleNodeApplication}
+ *
+ * @author Patrick Peralta
+ * @author Mark Fisher
+ * @author David Turanski
+ */
+public class EmbeddedZooKeeper {
+
+    /**
+     * Logger.
+     */
+    private static final Logger logger = LoggerFactory.getLogger(EmbeddedZooKeeper.class);
+
+    /**
+     * ZooKeeper client port. This will be determined dynamically upon startup.
+     */
+    private final int clientPort;
+
+    /**
+     * Whether to auto-start. Default is true.
+     */
+    private boolean autoStartup = true;
+
+    /**
+     * Lifecycle phase. Default is 0.
+     */
+    private int phase = 0;
+
+    /**
+     * Thread for running the ZooKeeper server.
+     */
+    private volatile Thread zkServerThread;
+
+    /**
+     * ZooKeeper server.
+     */
+    private volatile ZooKeeperServerMain zkServer;
+
+    /**
+     * {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread.
+     */
+    private ErrorHandler errorHandler;
+
+    private boolean daemon = true;
+
+    /**
+     * Construct an EmbeddedZooKeeper with a random port.
+     */
+    public EmbeddedZooKeeper() {
+        // clientPort = SocketUtils.findAvailableTcpPort();
+        clientPort = NetUtils.getAvailablePort();
+    }
+
+    /**
+     * Construct an EmbeddedZooKeeper with the provided port.
+     *
+     * @param clientPort port for ZooKeeper server to bind to
+     */
+    public EmbeddedZooKeeper(int clientPort, boolean daemon) {
+        this.clientPort = clientPort;
+        this.daemon = daemon;
+    }
+
+    /**
+     * Returns the port that clients should use to connect to this embedded server.
+     *
+     * @return dynamically determined client port
+     */
+    public int getClientPort() {
+        return this.clientPort;
+    }
+
+    /**
+     * Specify whether to start automatically. Default is true.
+     *
+     * @param autoStartup whether to start automatically
+     */
+    public void setAutoStartup(boolean autoStartup) {
+        this.autoStartup = autoStartup;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isAutoStartup() {
+        return this.autoStartup;
+    }
+
+    /**
+     * Specify the lifecycle phase for the embedded server.
+     *
+     * @param phase the lifecycle phase
+     */
+    public void setPhase(int phase) {
+        this.phase = phase;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getPhase() {
+        return this.phase;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isRunning() {
+        return (zkServerThread != null);
+    }
+
+    /**
+     * Start the ZooKeeper server in a background thread.
+     * <p>
+     * Register an error handler via {@link #setErrorHandler} in order to handle
+     * any exceptions thrown during startup or execution.
+     */
+    public synchronized void start() {
+        if (zkServerThread == null) {
+            zkServerThread = new Thread(new ServerRunnable(), "ZooKeeper Server Starter");
+            zkServerThread.setDaemon(daemon);
+            zkServerThread.start();
+        }
+    }
+
+    /**
+     * Shutdown the ZooKeeper server.
+     */
+    public synchronized void stop() {
+        if (zkServerThread != null) {
+            // The shutdown method is protected...thus this hack to invoke it.
+            // This will log an exception on shutdown; see
+            // https://issues.apache.org/jira/browse/ZOOKEEPER-1873 for details.
+            try {
+                Method shutdown = ZooKeeperServerMain.class.getDeclaredMethod("shutdown");
+                shutdown.setAccessible(true);
+                shutdown.invoke(zkServer);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+            // It is expected that the thread will exit after
+            // the server is shutdown; this will block until
+            // the shutdown is complete.
+            try {
+                zkServerThread.join(5000);
+                zkServerThread = null;
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                logger.warn("Interrupted while waiting for embedded ZooKeeper to exit");
+                // abandoning zk thread
+                zkServerThread = null;
+            }
+        }
+    }
+
+    /**
+     * Stop the server if running and invoke the callback when complete.
+     */
+    public void stop(Runnable callback) {
+        stop();
+        callback.run();
+    }
+
+    /**
+     * Provide an {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. If none
+     * is provided, only error-level logging will occur.
+     *
+     * @param errorHandler the {@link ErrorHandler} to be invoked
+     */
+    public void setErrorHandler(ErrorHandler errorHandler) {
+        this.errorHandler = errorHandler;
+    }
+
+    /**
+     * Runnable implementation that starts the ZooKeeper server.
+     */
+    private class ServerRunnable implements Runnable {
+
+        @Override
+        public void run() {
+            try {
+                Properties properties = new Properties();
+                File file = new File(System.getProperty("java.io.tmpdir")
+                        + File.separator + UUID.randomUUID());
+                file.deleteOnExit();
+                properties.setProperty("dataDir", file.getAbsolutePath());
+                properties.setProperty("clientPort", String.valueOf(clientPort));
+
+                QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig();
+                quorumPeerConfig.parseProperties(properties);
+
+                zkServer = new ZooKeeperServerMain();
+                ServerConfig configuration = new ServerConfig();
+                configuration.readFrom(quorumPeerConfig);
+
+                zkServer.runFromConfig(configuration);
+            } catch (Exception e) {
+                if (errorHandler != null) {
+                    errorHandler.handleError(e);
+                } else {
+                    logger.error("Exception running embedded ZooKeeper", e);
+                }
+            }
+        }
+    }
+
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ErrorHandler.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ErrorHandler.java
new file mode 100644
index 0000000..b44ff13
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ErrorHandler.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dubbo.test.common;
+
+@FunctionalInterface
+public interface ErrorHandler {
+
+    /**
+     * Handle the given error, possibly rethrowing it as a fatal exception.
+     */
+    void handleError(Throwable t);
+
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/SysProps.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/SysProps.java
new file mode 100644
index 0000000..dac5edd
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/SysProps.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dubbo.test.common;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Use to set and clear System property
+ */
+public class SysProps {
+
+    private static Map<String, String> map = new LinkedHashMap<String, String>();
+
+    public static void reset() {
+        map.clear();
+    }
+
+    public static void setProperty(String key, String value) {
+        map.put(key, value);
+        System.setProperty(key, value);
+    }
+
+    public static void clear() {
+        for (String key : map.keySet()) {
+            System.clearProperty(key);
+        }
+        reset();
+    }
+
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ZooKeeperServer.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ZooKeeperServer.java
new file mode 100644
index 0000000..ba34753
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ZooKeeperServer.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dubbo.test.common;
+
+public class ZooKeeperServer {
+
+    private static EmbeddedZooKeeper zookeeper1;
+    private static EmbeddedZooKeeper zookeeper2;
+
+    public static void start() {
+            if (zookeeper1 == null) {
+                zookeeper1 = new EmbeddedZooKeeper(2181, true);
+                zookeeper1.start();
+            }
+            if (zookeeper2 == null) {
+                zookeeper2 = new EmbeddedZooKeeper(2182, true);
+                zookeeper2.start();
+            }
+    }
+
+    public static void stop() {
+        if (zookeeper1 != null) {
+            zookeeper1.stop();
+        }
+        if (zookeeper2 != null) {
+            zookeeper2.stop();
+        }
+    }
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/DemoService.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/DemoService.java
new file mode 100644
index 0000000..9a5dfc1
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/DemoService.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dubbo.test.common.api;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface DemoService {
+
+    String sayHello(String name);
+
+    CompletableFuture<String> sayHelloAsync(String name);
+
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/GreetingService.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/GreetingService.java
new file mode 100644
index 0000000..16df570
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/GreetingService.java
@@ -0,0 +1,24 @@
+/*
+ * 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.dubbo.test.common.api;
+
+/**
+ *
+ */
+public interface GreetingService {
+    String hello();
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/RestDemoService.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/RestDemoService.java
new file mode 100644
index 0000000..809cd94
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/RestDemoService.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dubbo.test.common.api;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+
+@Path("/demoService")
+public interface RestDemoService {
+    @GET
+    @Path("/hello")
+    Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
+
+    @GET
+    @Path("/error")
+    String error();
+
+    @POST
+    @Path("/say")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String sayHello(String name);
+
+    @GET
+    @Path("/getRemoteApplicationName")
+    String getRemoteApplicationName();
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/DemoServiceImpl.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/DemoServiceImpl.java
new file mode 100644
index 0000000..4b80d8b
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/DemoServiceImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dubbo.test.common.impl;
+
+import org.apache.dubbo.config.annotation.DubboService;
+import org.apache.dubbo.test.common.api.DemoService;
+
+import java.util.concurrent.CompletableFuture;
+
+@DubboService
+public class DemoServiceImpl implements DemoService {
+    @Override
+    public String sayHello(String name) {
+        return "Hello " + name;
+    }
+
+    @Override
+    public CompletableFuture<String> sayHelloAsync(String name) {
+        CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
+//            try {
+//                Thread.sleep(1000);
+//            } catch (InterruptedException e) {
+//                e.printStackTrace();
+//            }
+            return "async result:" + name;
+        });
+        return cf;
+    }
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/GreetingServiceImpl.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/GreetingServiceImpl.java
new file mode 100644
index 0000000..0ad249a
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/GreetingServiceImpl.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dubbo.test.common.impl;
+
+
+import org.apache.dubbo.test.common.api.GreetingService;
+
+/**
+ *
+ */
+public class GreetingServiceImpl implements GreetingService {
+    @Override
+    public String hello() {
+        return "Greetings!";
+    }
+}
diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/RestDemoServiceImpl.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/RestDemoServiceImpl.java
new file mode 100644
index 0000000..c863314
--- /dev/null
+++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/RestDemoServiceImpl.java
@@ -0,0 +1,58 @@
+/*
+ * 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.dubbo.test.common.impl;
+
+
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.test.common.api.RestDemoService;
+
+import java.util.Map;
+
+public class RestDemoServiceImpl implements RestDemoService {
+    private static Map<String, Object> context;
+    private boolean called;
+
+    public String sayHello(String name) {
+        called = true;
+        return "Hello, " + name;
+    }
+
+
+    public boolean isCalled() {
+        return called;
+    }
+
+    @Override
+    public Integer hello(Integer a, Integer b) {
+        context = RpcContext.getServerAttachment().getObjectAttachments();
+        return a + b;
+    }
+
+    @Override
+    public String error() {
+        throw new RuntimeException();
+    }
+
+    public static Map<String, Object> getAttachments() {
+        return context;
+    }
+
+    @Override
+    public String getRemoteApplicationName() {
+        return RpcContext.getServiceContext().getRemoteApplicationName();
+    }
+}
diff --git a/dubbo-test/dubbo-test-spring/pom.xml b/dubbo-test/dubbo-test-spring/pom.xml
new file mode 100644
index 0000000..3e417d3
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/pom.xml
@@ -0,0 +1,170 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dubbo-test</artifactId>
+        <groupId>org.apache.dubbo</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-test-spring</artifactId>
+
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+        <slf4j-log4j12.version>1.7.25</slf4j-log4j12.version>
+        <spring_version>3.2.18.RELEASE</spring_version>
+        <!--<spring_version>4.0.9.RELEASE</spring_version>-->
+        <!--<spring_version>4.1.9.RELEASE</spring_version>-->
+        <!--<spring_version>4.2.4.RELEASE</spring_version>-->
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-framework-bom</artifactId>
+                <version>${spring_version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- test common -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-test-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+        <!-- Dubbo -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-multicast</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-zookeeper</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-nacos</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-configcenter-zookeeper</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-configcenter-nacos</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-report-nacos</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-dubbo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-rest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-config-spring</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-remoting-netty4</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-serialization-hessian2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-serialization-jdk</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>${slf4j-log4j12.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+        </dependency>
+
+
+        <!--JUnit Jupiter Engine to depend on the JUnit5 engine and JUnit 5 API -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit_jupiter_version}</version>
+            <!--<scope>test</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit_jupiter_version}</version>
+            <!--<scope>test</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>${hamcrest_version}</version>
+            <!--<scope>test</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito_version}</version>
+            <!--<scope>test</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <version>${mockito_version}</version>
+            <!--<scope>test</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>${cglib_version}</version>
+            <!--<scope>test</scope>-->
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java
new file mode 100644
index 0000000..d0cc44c
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.dubbo.test.spring;
+
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.apache.dubbo.test.common.ZooKeeperServer;
+import org.apache.dubbo.test.common.api.DemoService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+public class SpringAnnotationBeanTest {
+    @BeforeAll
+    public static void beforeAll() {
+        ZooKeeperServer.start();
+        DubboBootstrap.reset();
+    }
+
+    @Test
+    public void test() {
+        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfiguration.class);
+        TestService testService = applicationContext.getBean(TestService.class);
+        testService.test();
+    }
+
+    @EnableDubbo(scanBasePackages = "org.apache.dubbo.test.common.impl")
+    @Configuration
+    @PropertySource("/demo-app.properties")
+    static class TestConfiguration {
+
+        @Bean
+        public TestService testService() {
+            return new TestService();
+        }
+    }
+
+    static class TestService {
+
+        @DubboReference
+        private DemoService demoService;
+
+        public void test() {
+            String result = demoService.sayHello("dubbo");
+            Assertions.assertEquals("Hello dubbo", result);
+        }
+    }
+}
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java
new file mode 100644
index 0000000..bcd2e5d
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.dubbo.test.spring;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ConsumerConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.apache.dubbo.rpc.Constants;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.test.common.SysProps;
+import org.apache.dubbo.test.common.ZooKeeperServer;
+import org.apache.dubbo.test.common.api.DemoService;
+import org.apache.dubbo.test.common.impl.DemoServiceImpl;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class SpringJavaConfigBeanTest {
+
+    private static final String MY_PROTOCOL_ID = "myProtocol";
+    private static final String MY_REGISTRY_ID = "my-registry";
+
+    @BeforeAll
+    public static void beforeAll() {
+        ZooKeeperServer.start();
+        DubboBootstrap.reset();
+    }
+
+    @BeforeEach
+    public void beforeEach() {
+        DubboBootstrap.reset();
+    }
+
+    @AfterEach
+    public void afterEach() {
+        SysProps.clear();
+    }
+
+    @Test
+    public void testBean() {
+
+        SysProps.setProperty("dubbo.application.owner", "Tom");
+        SysProps.setProperty("dubbo.application.qos-enable", "false");
+        SysProps.setProperty("dubbo.protocol.name", "dubbo");
+        SysProps.setProperty("dubbo.protocol.port", "2346");
+        String registryAddress = "zookeeper://127.0.0.1:2181";
+        SysProps.setProperty("dubbo.registry.address", registryAddress);
+        SysProps.setProperty("dubbo.provider.group", "test");
+
+        AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(
+                TestConfiguration.class, ConsumerConfiguration.class, ProviderConfiguration.class);
+        try {
+            consumerContext.start();
+
+            ConfigManager configManager = ApplicationModel.getConfigManager();
+            ApplicationConfig application = configManager.getApplication().get();
+            Assertions.assertEquals(false, application.getQosEnable());
+            Assertions.assertEquals("Tom", application.getOwner());
+
+            RegistryConfig registry = configManager.getRegistry(MY_REGISTRY_ID).get();
+            Assertions.assertEquals(registryAddress, registry.getAddress());
+
+            Collection<ProtocolConfig> protocols = configManager.getProtocols();
+            Assertions.assertEquals(1, protocols.size());
+            ProtocolConfig protocolConfig = protocols.iterator().next();
+            Assertions.assertEquals("dubbo", protocolConfig.getName());
+            Assertions.assertEquals(2346, protocolConfig.getPort());
+            Assertions.assertEquals(MY_PROTOCOL_ID, protocolConfig.getId());
+
+            ConsumerConfig consumerConfig = configManager.getDefaultConsumer().get();
+            Assertions.assertEquals(1000, consumerConfig.getTimeout());
+            Assertions.assertEquals("demo", consumerConfig.getGroup());
+            Assertions.assertEquals(false, consumerConfig.isCheck());
+            Assertions.assertEquals(2, consumerConfig.getRetries());
+
+            Map<String, ReferenceBean> referenceBeanMap = consumerContext.getBeansOfType(ReferenceBean.class);
+            Assertions.assertEquals(1, referenceBeanMap.size());
+            ReferenceBean referenceBean = referenceBeanMap.get("&demoService");
+            Assertions.assertNotNull(referenceBean);
+            ReferenceConfig referenceConfig = referenceBean.getReferenceConfig();
+            // use consumer's attributes as default value
+            Assertions.assertEquals(consumerConfig.getTimeout(), referenceConfig.getTimeout());
+            Assertions.assertEquals(consumerConfig.getGroup(), referenceConfig.getGroup());
+            // consumer cannot override reference's attribute
+            Assertions.assertEquals(5, referenceConfig.getRetries());
+
+            DemoService referProxy = (DemoService) referenceConfig.get();
+            Assertions.assertTrue( referProxy instanceof DemoService);
+            String result = referProxy.sayHello("dubbo");
+            Assertions.assertEquals("Hello dubbo", result);
+
+        } finally {
+            consumerContext.close();
+        }
+
+    }
+
+
+    @EnableDubbo(scanBasePackages = "")
+    @Configuration
+    static class TestConfiguration {
+
+        @Bean(name = "dubbo-demo-application")
+        public ApplicationConfig applicationConfig() {
+            ApplicationConfig applicationConfig = new ApplicationConfig();
+            applicationConfig.setName("dubbo-demo-application");
+            return applicationConfig;
+        }
+
+        @Bean(name = MY_PROTOCOL_ID)
+        public ProtocolConfig protocolConfig() {
+            ProtocolConfig protocolConfig = new ProtocolConfig();
+            protocolConfig.setName("rest");
+            protocolConfig.setPort(1234);
+            return protocolConfig;
+        }
+
+        @Bean(name = MY_REGISTRY_ID)
+        public RegistryConfig registryConfig() {
+            RegistryConfig registryConfig = new RegistryConfig();
+            registryConfig.setAddress("N/A");
+            return registryConfig;
+        }
+
+        @Bean
+        public ConsumerConfig consumerConfig() {
+            ConsumerConfig consumer = new ConsumerConfig();
+            consumer.setTimeout(1000);
+            consumer.setGroup("demo");
+            consumer.setCheck(false);
+            consumer.setRetries(2);
+            return consumer;
+        }
+    }
+
+    @Configuration
+    static class ConsumerConfiguration {
+
+        @Bean
+        @DubboReference(scope = Constants.SCOPE_LOCAL, retries = 5)
+        public ReferenceBean<DemoService> demoService() {
+            return new ReferenceBean<>();
+        }
+
+    }
+
+    @Configuration
+    static class ProviderConfiguration {
+
+        @Bean
+        @DubboService(group = "demo")
+        public DemoService demoServiceImpl() {
+            return new DemoServiceImpl();
+        }
+    }
+}
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java
new file mode 100644
index 0000000..492ee1a
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.dubbo.test.spring;
+
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.test.common.SysProps;
+import org.apache.dubbo.test.common.ZooKeeperServer;
+import org.apache.dubbo.test.common.api.DemoService;
+import org.apache.dubbo.test.common.api.GreetingService;
+import org.apache.dubbo.test.common.api.RestDemoService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY;
+
+public class SpringXmlConfigTest {
+
+    @BeforeAll
+    public static void beforeAll() {
+        ZooKeeperServer.start();
+        DubboBootstrap.reset();
+    }
+
+    @Test
+    public void test() {
+        SysProps.setProperty(SHUTDOWN_WAIT_KEY, "2000");
+        ClassPathXmlApplicationContext applicationContext = null;
+        try {
+            applicationContext = new ClassPathXmlApplicationContext("/spring/dubbo-demo.xml");
+
+            GreetingService greetingService = applicationContext.getBean("greetingService", GreetingService.class);
+            String greeting = greetingService.hello();
+            Assertions.assertEquals(greeting, "Greetings!");
+
+            DemoService demoService = applicationContext.getBean("demoService", DemoService.class);
+            String sayHelloResult = demoService.sayHello("dubbo");
+            Assertions.assertTrue(sayHelloResult.startsWith("Hello dubbo"), sayHelloResult);
+
+            RestDemoService restDemoService = applicationContext.getBean("restDemoService", RestDemoService.class);
+            String resetHelloResult = restDemoService.sayHello("dubbo");
+            Assertions.assertEquals("Hello, dubbo", resetHelloResult);
+        } finally {
+            SysProps.clear();
+            if (applicationContext != null) {
+                applicationContext.close();
+            }
+        }
+
+    }
+}
diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/demo-app.properties b/dubbo-test/dubbo-test-spring/src/main/resources/demo-app.properties
new file mode 100644
index 0000000..f5bfdff
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/resources/demo-app.properties
@@ -0,0 +1,11 @@
+
+dubbo.application.name=demo-app
+
+dubbo.registry.address=zookeeper://127.0.0.1:2181?registry-type=service
+
+dubbo.config-center.address=zookeeper://127.0.0.1:2181
+
+dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
+
+dubbo.protocols.dubbo.port=-1
+
diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/log4j.properties b/dubbo-test/dubbo-test-spring/src/main/resources/log4j.properties
new file mode 100644
index 0000000..8de4c4f
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/resources/log4j.properties
@@ -0,0 +1,7 @@
+###set log levels###
+log4j.rootLogger=info, stdout
+###output to the console###
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n
diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml b/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml
new file mode 100644
index 0000000..05392da
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
+
+    <dubbo:application name="demo-app" />
+
+    <dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
+    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
+    <dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
+
+    <dubbo:protocol name="dubbo" port="-1"/>
+    <dubbo:protocol name="rest" port="-1"/>
+
+    <bean id="demoServiceImpl" class="org.apache.dubbo.test.common.impl.DemoServiceImpl"/>
+    <bean id="greetingServiceImpl" class="org.apache.dubbo.test.common.impl.GreetingServiceImpl"/>
+    <bean id="restDemoServiceImpl" class="org.apache.dubbo.test.common.impl.RestDemoServiceImpl"/>
+
+    <dubbo:service interface="org.apache.dubbo.test.common.api.DemoService" timeout="3000" ref="demoServiceImpl" protocol="dubbo"/>
+    <dubbo:service version="1.0.0" group="greeting" timeout="5000" interface="org.apache.dubbo.test.common.api.GreetingService"
+                   ref="greetingServiceImpl" protocol="dubbo"/>
+    <dubbo:service version="1.0.0" timeout="5000" interface="org.apache.dubbo.test.common.api.RestDemoService"
+                   ref="restDemoServiceImpl" protocol="rest"/>
+
+    <!-- references -->
+    <dubbo:consumer check="false" scope="remote" />
+    <dubbo:reference id="demoService" scope="local"
+                     interface="org.apache.dubbo.test.common.api.DemoService"/>
+
+    <dubbo:reference  id="greetingService" version="1.0.0" group="greeting"
+                     interface="org.apache.dubbo.test.common.api.GreetingService"/>
+
+    <dubbo:reference  id="restDemoService" version="1.0.0" protocol="rest"
+                      interface="org.apache.dubbo.test.common.api.RestDemoService"/>
+</beans>
diff --git a/dubbo-test/dubbo-test-spring3.2/pom.xml b/dubbo-test/dubbo-test-spring3.2/pom.xml
new file mode 100644
index 0000000..c349bdd
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring3.2/pom.xml
@@ -0,0 +1,70 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dubbo-test</artifactId>
+        <groupId>org.apache.dubbo</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-test-spring3.2</artifactId>
+
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+        <spring_version>3.2.18.RELEASE</spring_version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-framework-bom</artifactId>
+                <version>${spring_version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- spring test -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-test-spring</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <dependenciesToScan>
+                        <dependency>org.apache.dubbo:dubbo-test-spring</dependency>
+                    </dependenciesToScan>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/dubbo-test/dubbo-test-spring4.1/pom.xml b/dubbo-test/dubbo-test-spring4.1/pom.xml
new file mode 100644
index 0000000..942e055
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring4.1/pom.xml
@@ -0,0 +1,70 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dubbo-test</artifactId>
+        <groupId>org.apache.dubbo</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-test-spring4.1</artifactId>
+
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+        <spring_version>4.1.9.RELEASE</spring_version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-framework-bom</artifactId>
+                <version>${spring_version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- spring test -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-test-spring</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <dependenciesToScan>
+                        <dependency>org.apache.dubbo:dubbo-test-spring</dependency>
+                    </dependenciesToScan>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/dubbo-test/dubbo-test-spring4.2/pom.xml b/dubbo-test/dubbo-test-spring4.2/pom.xml
new file mode 100644
index 0000000..e53c596
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring4.2/pom.xml
@@ -0,0 +1,70 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dubbo-test</artifactId>
+        <groupId>org.apache.dubbo</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-test-spring4.2</artifactId>
+
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+        <spring_version>4.2.4.RELEASE</spring_version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-framework-bom</artifactId>
+                <version>${spring_version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- spring test -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-test-spring</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <dependenciesToScan>
+                        <dependency>org.apache.dubbo:dubbo-test-spring</dependency>
+                    </dependenciesToScan>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/dubbo-test/pom.xml b/dubbo-test/pom.xml
new file mode 100644
index 0000000..bc6fee2
--- /dev/null
+++ b/dubbo-test/pom.xml
@@ -0,0 +1,56 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-parent</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dubbo-test</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>dubbo-test-common</module>
+        <module>dubbo-test-spring</module>
+        <module>dubbo-test-spring3.2</module>
+        <module>dubbo-test-spring4.1</module>
+        <module>dubbo-test-spring4.2</module>
+    </modules>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <skip_maven_deploy>true</skip_maven_deploy>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo-bom</artifactId>
+                <version>${project.parent.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+</project>
diff --git a/pom.xml b/pom.xml
index a0ed38e..034699f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -153,6 +153,7 @@
         <module>dubbo-build-tools</module>
         <module>dubbo-spring-boot</module>
         <module>dubbo-native</module>
+        <module>dubbo-test</module>
     </modules>
 
     <dependencyManagement>
@@ -691,6 +692,7 @@
             <plugins>
                 <!-- keep surefire and failsafe in sync -->
                 <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>${maven_surefire_version}</version>
                 </plugin>