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

liujun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git

commit b1324511d13aad36d14adee7bce71cdbe65a872a
Merge: 678cdb4 5184416
Author: ken.lj <[email protected]>
AuthorDate: Sun Dec 23 20:29:38 2018 +0800

    Merge branch 'dev-metadata'
    
    # Conflicts:
    #   dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Router.java
    #   
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java
    #   
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java
    #   
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
    #   
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java
    #   
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
    #   
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractConfig.java
    #   
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
    #   
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
    #   dubbo-config/dubbo-config-api/src/test/resources/log4j.xml
    #   
dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
    #   
dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
    #   
dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/ProviderConsumerRegTable.java
    #   
dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
    #   
dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RuntimeExceptionInvoker.java
    #   
dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
    #   
dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/service/GenericServiceTest.java
    #   
dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/validation/ValidationParameter.java
    #   
dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/validation/ValidationService.java
    #   
dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/validation/ValidationTest.java

 codestyle/checkstyle.xml                           |  10 +-
 dubbo-all/pom.xml                                  |  57 +++
 dubbo-bom/pom.xml                                  |  35 ++
 dubbo-cluster/pom.xml                              |  20 +
 ...rFactory.java => AbstractAppRouterFactory.java} |  24 +-
 ...uterFactory.java => AbstractRouterFactory.java} |  19 +-
 .../org/apache/dubbo/rpc/cluster/Configurator.java |  48 +++
 .../java/org/apache/dubbo/rpc/cluster/Router.java  |  45 ++-
 .../org/apache/dubbo/rpc/cluster/RouterChain.java  | 156 ++++++++
 .../apache/dubbo/rpc/cluster/RouterFactory.java    |   7 +-
 .../cluster/configurator/AbstractConfigurator.java |  75 +++-
 .../cluster/configurator/parser/ConfigParser.java  | 201 ++++++++++
 .../configurator/parser/model/ConfigItem.java      | 103 +++++
 .../parser/model/ConfiguratorConfig.java           |  74 ++++
 .../rpc/cluster/directory/AbstractDirectory.java   |  55 +--
 .../rpc/cluster/directory/StaticDirectory.java     |  35 +-
 .../dubbo/rpc/cluster/router/AbstractRouter.java   | 101 +++++
 .../rpc/cluster/router/AbstractRouterRule.java     | 105 +++++
 .../cluster/router/condition/ConditionRouter.java  |  46 ++-
 .../config/AbstractConfigConditionRouter.java      | 145 +++++++
 .../condition/config/AppConfigConditionRouter.java |  28 +-
 .../config/AppConfigConditionRouterFactory.java}   |  19 +-
 .../config/ServiceConfigConditionRouter.java       |  28 +-
 .../ServiceConfigConditionRouterFactory.java}      |  19 +-
 .../condition/config/model/BlackWhiteListRule.java |  73 ++--
 .../config/model/ConditionRouterRule.java          |  99 +++--
 .../config/model/ConditionRuleParser.java          |  57 +++
 .../router/{ => mock}/MockInvokersSelector.java    | 214 +++++-----
 .../MockRouterFactory.java}                        |  16 +-
 .../rpc/cluster/router/script/ScriptRouter.java    |  32 +-
 .../dubbo/rpc/cluster/router/tag/TagRouter.java    | 217 ++++++++--
 .../rpc/cluster/router/tag/TagRouterFactory.java   |  14 +-
 .../dubbo/rpc/cluster/router/tag/model/Tag.java    |  80 ++--
 .../cluster/router/tag/model/TagRouterRule.java    |  86 ++++
 .../cluster/router/tag/model/TagRuleParser.java    |  45 +++
 .../cluster/support/AbstractClusterInvoker.java    |   2 +-
 .../dubbo/rpc/cluster/support/ClusterUtils.java    |  15 +-
 .../cluster/support/MergeableClusterInvoker.java   |  29 +-
 .../rpc/cluster/support/RegistryAwareCluster.java  |  25 +-
 .../support/RegistryAwareClusterInvoker.java       |  59 +++
 .../internal/org.apache.dubbo.rpc.cluster.Cluster  |   3 +-
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |   5 +-
 .../configurator/parser/ConfigParserTest.java      | 167 ++++++++
 .../loadbalance/RoundRobinLoadBalanceTest.java     |  32 +-
 .../cluster/router/ConfigConditionRouterTest.java  | 134 +++++++
 .../dubbo/rpc/cluster/router/TagRouterTest.java    |  67 ++++
 .../cluster/router/file/FileRouterEngineTest.java  |  14 +-
 .../rpc/cluster/router/tag/TagRouterTest.java      | 170 --------
 .../support/AbstractClusterInvokerTest.java        |  10 +-
 .../support/wrapper/MockClusterInvokerTest.java    |  22 +-
 .../src/test/resources/AppAnyServices.yml          |  32 ++
 .../src/test/resources/AppMultiServices.yml        |  32 ++
 dubbo-cluster/src/test/resources/AppNoService.yml  |  32 ++
 dubbo-cluster/src/test/resources/ConditionRule.yml |  56 +++
 .../test/resources/ConsumerSpecificProviders.yml   |  34 ++
 .../src/test/resources/ServiceGroupVersion.yml     |  29 ++
 .../src/test/resources/ServiceMultiApps.yml        |  30 ++
 dubbo-cluster/src/test/resources/ServiceNoApp.yml  |  29 ++
 dubbo-cluster/src/test/resources/ServiceNoRule.yml |  28 ++
 dubbo-cluster/src/test/resources/TagRule.yml       |  31 ++
 .../java/org/apache/dubbo/common/Constants.java    |  89 ++++-
 .../src/main/java/org/apache/dubbo/common/URL.java |  62 ++-
 .../dubbo/common/config/AbstractConfiguration.java |  91 +++++
 .../common/config/AbstractPrefixConfiguration.java |  52 +++
 .../common/config/CompositeConfiguration.java      |  82 ++++
 .../apache/dubbo/common/config/Configuration.java  |  82 ++++
 .../dubbo/common/config/ConfigurationUtils.java    |  59 +++
 .../apache/dubbo/common/config/Environment.java    | 199 ++++++++++
 .../common/config/EnvironmentConfiguration.java    |  22 +-
 .../dubbo/common/config/InmemoryConfiguration.java |  67 ++++
 .../common/config/PropertiesConfiguration.java     |  28 +-
 .../dubbo/common/config/SystemConfiguration.java   |  27 +-
 .../dubbo/common/extension/ExtensionLoader.java    |  20 +-
 .../org/apache/dubbo/common/utils/ClassHelper.java |  70 ++++
 .../org/apache/dubbo/common/utils/ConfigUtils.java |  40 +-
 .../org/apache/dubbo/common/utils/UrlUtils.java    |  11 +-
 .../config/AbstractPrefixConfigurationTest.java    |  46 +--
 .../common/config/CompositeConfigurationTest.java  |  46 +--
 .../dubbo/common/config/EnvironmentTest.java       |  46 +--
 .../apache/dubbo/common/utils/ConfigUtilsTest.java |  27 --
 dubbo-compatible/pom.xml                           |   7 +-
 .../java/com/alibaba/dubbo/rpc/cluster/Router.java |  34 +-
 .../dubbo/config/RegistryDataConfigTest.java       |  87 ++++
 .../apache/dubbo/generic/GenericServiceTest.java   | 181 ++++++++-
 .../apache/dubbo/rpc/cluster/CompatibleRouter.java |  88 +++--
 .../org/apache/dubbo/service/ComplexObject.java    | 282 +++++++++++++
 .../java/org/apache/dubbo/service/DemoService.java |   8 +-
 .../org/apache/dubbo/service/DemoServiceImpl.java  |  15 +-
 dubbo-config/dubbo-config-api/pom.xml              |   5 +
 .../org/apache/dubbo/config/AbstractConfig.java    | 323 +++++++++------
 .../dubbo/config/AbstractInterfaceConfig.java      | 297 ++++++++++----
 .../apache/dubbo/config/AbstractMethodConfig.java  |  16 +-
 .../apache/dubbo/config/AbstractServiceConfig.java |  36 +-
 .../org/apache/dubbo/config/ApplicationConfig.java |  29 +-
 .../apache/dubbo/config/ConfigCenterConfig.java    | 313 +++++++++++++++
 .../org/apache/dubbo/config/ConsumerConfig.java    |   8 +-
 .../org/apache/dubbo/config/DubboShutdownHook.java |   9 +-
 ...onitorConfig.java => MetadataReportConfig.java} | 280 ++++++-------
 .../java/org/apache/dubbo/config/MethodConfig.java |  37 ++
 .../java/org/apache/dubbo/config/ModuleConfig.java |   8 +-
 .../org/apache/dubbo/config/MonitorConfig.java     |  13 +-
 .../org/apache/dubbo/config/ProtocolConfig.java    |  11 +-
 .../org/apache/dubbo/config/ReferenceConfig.java   | 121 +++---
 .../org/apache/dubbo/config/RegistryConfig.java    |  49 ++-
 .../apache/dubbo/config/RegistryDataConfig.java    |  86 ++++
 .../org/apache/dubbo/config/ServiceConfig.java     | 177 ++++++---
 .../invoker/DelegateProviderMetaDataInvoker.java   |   2 +-
 .../org/apache/dubbo/config/support/Parameter.java |   2 +
 .../apache/dubbo/config/AbstractConfigTest.java    | 280 ++++++++++++-
 .../dubbo/config/AbstractInterfaceConfigTest.java  |   5 +-
 .../dubbo/config/AbstractReferenceConfigTest.java  |   1 +
 .../dubbo/config/AbstractServiceConfigTest.java    |   1 +
 .../apache/dubbo/config/ApplicationConfigTest.java |   1 +
 .../org/apache/dubbo/config/MethodConfigTest.java  |   1 +
 .../apache/dubbo/config/ProtocolConfigTest.java    |   1 +
 .../apache/dubbo/config/ReferenceConfigTest.java   |   6 +
 .../apache/dubbo/config/RegistryConfigTest.java    |   2 +
 .../org/apache/dubbo/config/ServiceConfigTest.java |   6 +-
 .../org/apache/dubbo/config/api/DemoException.java |  84 ++--
 .../org/apache/dubbo/config/api/DemoService.java   |  72 ++--
 .../java/org/apache/dubbo/config/api/User.java     | 130 +++---
 .../apache/dubbo/config/cache/CacheService.java    |  52 +--
 .../dubbo/config/cache/CacheServiceImpl.java       |  64 +--
 .../org/apache/dubbo/config/cache/CacheTest.java   | 238 +++++------
 .../config/consumer/DemoActionByAnnotation.java    |  68 ++--
 .../dubbo/config/consumer/DemoActionBySetter.java  |  70 ++--
 .../dubbo/config/consumer/DemoInterceptor.java     |  60 +--
 .../DelegateProviderMetaDataInvokerTest.java       |   3 +-
 .../dubbo/config/mock/MockExporterListener.java    |   2 +-
 .../org/apache/dubbo/config/mock/MockRegistry.java |  16 +-
 .../dubbo/config/mock/MockRegistryFactory.java     |   2 +-
 .../apache/dubbo/config/mock/MockThreadPool.java   |   2 +-
 .../apache/dubbo/config/mock/MockTransporter.java  |   1 +
 .../config/provider/impl/DemoServiceImpl.java      | 100 ++---
 .../org.apache.dubbo.registry.RegistryFactory      |   4 +-
 .../src/test/resources/dubbo.properties            |   2 +
 .../dubbo-config-api/src/test/resources/log4j.xml  |  54 +--
 .../dubbo/config/spring/ConfigCenterBean.java      | 154 ++++++++
 .../apache/dubbo/config/spring/ReferenceBean.java  |  57 ++-
 .../apache/dubbo/config/spring/ServiceBean.java    |  76 +++-
 .../annotation/DubboConfigConfiguration.java       |  12 +-
 .../spring/schema/DubboBeanDefinitionParser.java   |  46 +--
 .../spring/schema/DubboNamespaceHandler.java       |   2 +
 .../src/main/resources/META-INF/compat/dubbo.xsd   | 137 ++++++-
 .../src/main/resources/META-INF/dubbo.xsd          | 128 ++++++
 .../dubbo-configcenter-api}/pom.xml                |  14 +-
 .../configcenter/AbstractDynamicConfiguration.java | 127 ++++++
 .../AbstractDynamicConfigurationFactory.java       |  26 +-
 .../dubbo/configcenter/ConfigChangeEvent.java      |  63 +++
 .../dubbo/configcenter/ConfigChangeType.java       |  63 +--
 .../dubbo/configcenter/ConfigurationListener.java  |  57 +--
 .../dubbo/configcenter/DynamicConfiguration.java   | 106 +++++
 .../configcenter/DynamicConfigurationFactory.java  |  56 +--
 .../support/nop/NopDynamicConfiguration.java       |  66 ++++
 .../nop/NopDynamicConfigurationFactory.java        |  17 +-
 ....dubbo.configcenter.DynamicConfigurationFactory |   1 +
 .../mock/AbstractDynamicConfigurationTest.java     |  47 +++
 .../mock/MockDynamicConfiguration.java             |  64 +++
 .../mock/MockDynamicConfigurationFactory.java      |  17 +-
 ....dubbo.configcenter.DynamicConfigurationFactory |   1 +
 .../dubbo-configcenter-apollo}/pom.xml             |  16 +-
 .../support/apollo/ApolloDynamicConfiguration.java | 191 +++++++++
 .../apollo/ApolloDynamicConfigurationFactory.java  |  18 +-
 ....dubbo.configcenter.DynamicConfigurationFactory |   1 +
 .../dubbo-configcenter-zookeeper}/pom.xml          |  43 +-
 .../support/zookeeper/CacheListener.java           | 119 ++++++
 .../zookeeper/ZookeeperDynamicConfiguration.java   | 162 ++++++++
 .../ZookeeperDynamicConfigurationFactory.java      |  20 +-
 ....dubbo.configcenter.DynamicConfigurationFactory |   1 +
 .../ZookeeperDynamicConfigurationTest.java         | 151 +++++++
 {dubbo-cluster => dubbo-configcenter}/pom.xml      |  22 +-
 dubbo-container/dubbo-container-log4j/pom.xml      |   5 +
 .../dubbo/container/log4j/Log4jContainer.java      |   8 +-
 .../java/org/apache/dubbo/demo/DemoService.java    |   4 +
 dubbo-demo/dubbo-demo-consumer/pom.xml             |  14 +-
 .../org/apache/dubbo/demo/consumer/Consumer.java   |   8 +
 .../dubbo/demo/consumer/DemoServiceMock.java       |  24 +-
 .../META-INF/spring/dubbo-demo-consumer.xml        |   8 +-
 dubbo-demo/dubbo-demo-provider/pom.xml             |  18 +-
 .../dubbo/demo/provider/DemoServiceImpl.java       |  12 +
 .../META-INF/spring/dubbo-demo-provider.xml        |  86 ++--
 dubbo-dependencies-bom/pom.xml                     |  54 ++-
 .../dubbo-metadata-definition}/pom.xml             |  27 +-
 .../definition/ServiceDefinitionBuilder.java       | 107 +++++
 .../metadata/definition/TypeDefinitionBuilder.java |  79 ++++
 .../definition/builder/ArrayTypeBuilder.java       | 119 +++---
 .../definition/builder/CollectionTypeBuilder.java  |  79 ++++
 .../definition/builder/DefaultTypeBuilder.java     |  70 ++++
 .../definition/builder/EnumTypeBuilder.java        |  63 +++
 .../definition/builder/MapTypeBuilder.java         |  76 ++++
 .../metadata/definition/builder/TypeBuilder.java   |  82 ++--
 .../definition/model/FullServiceDefinition.java    |  30 +-
 .../definition/model/MethodDefinition.java         |  92 +++++
 .../definition/model/ServiceDefinition.java        |  96 +++++
 .../metadata/definition/model/TypeDefinition.java  | 124 ++++++
 .../dubbo/metadata/definition/util/ClassUtils.java | 111 ++++++
 .../definition/util/JaketConfigurationUtils.java   | 100 +++++
 .../dubbo/metadata/definition/MetadataTest.java    | 143 +++++++
 .../dubbo/metadata/definition/MetadataUtils.java   |  75 ++++
 .../definition/ServiceDefinitionBuildderTest.java  |  98 +++++
 .../definition/common/ClassExtendsMap.java         |  19 +-
 .../metadata/definition/common/ColorEnum.java      |  45 ++-
 .../metadata/definition/common/OuterClass.java     |  72 ++--
 .../common/ResultWithRawCollections.java           |  96 +++--
 .../metadata/definition/common/TestService.java    |  98 +++--
 .../metadata/definition/service/ComplexObject.java | 296 ++++++++++++++
 .../metadata/definition/service}/DemoService.java  |  67 ++--
 .../dubbo-metadata-report-api}/pom.xml             |  53 ++-
 .../metadata/identifier/MetadataIdentifier.java    | 125 ++++++
 .../integration/MetadataReportService.java         | 103 +++++
 .../dubbo/metadata/store/MetadataReport.java       |  73 ++--
 .../metadata/store/MetadataReportFactory.java      |  56 +--
 .../metadata/support/AbstractMetadataReport.java   | 407 +++++++++++++++++++
 .../support/AbstractMetadataReportFactory.java     |  64 +++
 .../integration/InterfaceNameTestService.java      |  48 +--
 .../integration/InterfaceNameTestService2.java     |  48 +--
 .../integration/MetadataReportServiceTest.java     | 137 +++++++
 .../metadata/integration/RetryTestService.java     |  53 +--
 .../store/test/JTestMetadataReport4Test.java       |  69 ++++
 .../test/JTestMetadataReportFactory4Test.java      |  25 +-
 .../support/AbstractMetadataReportFactoryTest.java |  94 +++++
 .../support/AbstractMetadataReportTest.java        | 323 +++++++++++++++
 ...ache.dubbo.metadata.store.MetadataReportFactory |   1 +
 .../dubbo-metadata-report-redis}/pom.xml           |  35 +-
 .../metadata/store/redis/RedisMetadataReport.java  |  63 +++
 .../store/redis/RedisMetadataReportFactory.java    |  17 +-
 ...ache.dubbo.metadata.store.MetadataReportFactory |   1 +
 .../store/redis/RedisMetadata4TstService.java      |  12 +
 .../store/redis/RedisMetadataReportTest.java       | 130 ++++++
 .../dubbo-metadata-report-zookeeper}/pom.xml       |  29 +-
 .../store/zookeeper/ZookeeperMetadataReport.java   |  94 +++++
 .../zookeeper/ZookeeperMetadataReportFactory.java  |  25 +-
 ...ache.dubbo.metadata.store.MetadataReportFactory |   1 +
 .../ZookeeperMetadataReport4TstService.java        |  11 +
 .../zookeeper/ZookeeperMetadataReportTest.java     | 125 ++++++
 {dubbo-cluster => dubbo-metadata-report}/pom.xml   |  35 +-
 dubbo-plugin/dubbo-qos/pom.xml                     |   2 +-
 dubbo-registry/dubbo-registry-api/pom.xml          |  23 +-
 .../integration/AbstractConfiguratorListener.java  |  71 ++++
 .../registry/integration/RegistryDirectory.java    | 439 +++++++++++++--------
 .../registry/integration/RegistryProtocol.java     | 317 +++++++++++----
 .../registry/support/ProviderConsumerRegTable.java |  48 ++-
 .../registry/support/ProviderInvokerWrapper.java   |   9 +
 .../java/org/apache/dubbo/registry/ZKTools.java    | 307 ++++++++++++++
 dubbo-registry/dubbo-registry-default/pom.xml      |   5 +
 .../registry/dubbo/RegistryDirectoryTest.java      |  18 +-
 .../dubbo/registry/dubbo/RegistryProtocolTest.java |   1 -
 .../registry/integration/RegistryProtocolTest.java |  23 ++
 .../dubbo/remoting/zookeeper/ZookeeperClient.java  |   4 +
 .../zookeeper/curator/CuratorZookeeperClient.java  |  40 ++
 .../zookeeper/support/AbstractZookeeperClient.java |  28 ++
 .../zookeeper/zkclient/ZkClientWrapper.java        |  15 +
 .../zkclient/ZkclientZookeeperClient.java          |  25 ++
 .../curator/CuratorZookeeperClientTest.java        |  30 +-
 .../zkclient/ZkclientZookeeperClientTest.java      |  30 +-
 .../java/org/apache/dubbo/rpc/AsyncContext.java    |   1 +
 .../java/org/apache/dubbo/rpc/AsyncRpcResult.java  |   4 +-
 .../main/java/org/apache/dubbo/rpc/RpcContext.java |   5 +-
 .../java/org/apache/dubbo/rpc/RpcException.java    |   5 +
 .../org/apache/dubbo/rpc/filter/ContextFilter.java |   1 -
 .../apache/dubbo/rpc/model/ApplicationModel.java   |  10 +
 dubbo-rpc/dubbo-rpc-dubbo/pom.xml                  |   5 +
 .../dubbo/rpc/protocol/dubbo/DubboInvoker.java     |   4 +-
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |   8 +-
 .../protocol/dubbo/DubboInvokerAvilableTest.java   |   3 +-
 .../dubbo/rpc/protocol/thrift/ThriftProtocol.java  |   4 +-
 .../webservice/WebserviceProtocolTest.java         |   4 +-
 .../dubbo/test/provider/DefaultDemoService.java    |  10 +
 pom.xml                                            |   2 +
 269 files changed, 13777 insertions(+), 3060 deletions(-)

diff --cc 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index f650b6e,20b0a0e..f44c398
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@@ -16,24 -16,18 +16,20 @@@
   */
  package org.apache.dubbo.rpc.cluster.directory;
  
- import org.apache.dubbo.common.Constants;
  import org.apache.dubbo.common.URL;
- import org.apache.dubbo.common.extension.ExtensionLoader;
  import org.apache.dubbo.common.logger.Logger;
  import org.apache.dubbo.common.logger.LoggerFactory;
 +import org.apache.dubbo.common.utils.StringUtils;
  import org.apache.dubbo.rpc.Invocation;
  import org.apache.dubbo.rpc.Invoker;
  import org.apache.dubbo.rpc.RpcException;
  import org.apache.dubbo.rpc.cluster.Directory;
  import org.apache.dubbo.rpc.cluster.Router;
- import org.apache.dubbo.rpc.cluster.RouterFactory;
- import org.apache.dubbo.rpc.cluster.router.MockInvokersSelector;
+ import org.apache.dubbo.rpc.cluster.RouterChain;
  
  import java.util.ArrayList;
- import java.util.Collections;
  import java.util.List;
 +import java.util.Map;
  
  /**
   * Abstract implementation of Directory: Invoker list returned from this 
Directory's list method have been filtered by Routers
@@@ -64,16 -58,9 +60,16 @@@ public abstract class AbstractDirectory
          if (url == null) {
              throw new IllegalArgumentException("url == null");
          }
 -        this.url = url;
 +
 +        if (url.getProtocol().equals(Constants.REGISTRY_PROTOCOL)) {
 +            Map<String, String> queryMap = 
StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
 +            this.url = 
url.clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
 +        } else {
 +            this.url = url;
 +        }
 +
          this.consumerUrl = consumerUrl;
-         setRouters(routers);
+         setRouterChain(routerChain);
      }
  
      @Override
diff --cc 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
index 7c3070a,05815df..f95573c
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
@@@ -40,9 -40,10 +40,9 @@@ import java.util.concurrent.ConcurrentH
  
  /**
   * ScriptRouter
 - *
   */
- public class ScriptRouter implements Router {
- 
+ public class ScriptRouter extends AbstractRouter {
+     public static final String NAME = "SCRIPT_ROUTER";
      private static final Logger logger = 
LoggerFactory.getLogger(ScriptRouter.class);
  
      private static final Map<String, ScriptEngine> engines = new 
ConcurrentHashMap<String, ScriptEngine>();
diff --cc 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
index 2d3602a,14e9f07..63486aa
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@@ -256,8 -257,8 +256,8 @@@ public abstract class AbstractClusterIn
      }
  
      protected void checkInvokers(List<Invoker<T>> invokers, Invocation 
invocation) {
 -        if (invokers == null || invokers.isEmpty()) {
 +        if (CollectionUtils.isEmpty(invokers)) {
-             throw new RpcException("Failed to invoke the method "
+             throw new 
RpcException(RpcException.NO_INVOKER_AVAILABLE_AFTER_FILTER, "Failed to invoke 
the method "
                      + invocation.getMethodName() + " in the service " + 
getInterface().getName()
                      + ". No provider available for the service " + 
directory.getUrl().getServiceKey()
                      + " from registry " + directory.getUrl().getAddress()
diff --cc dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java
index c6f8b1f,5c1006b..1750e31
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java
@@@ -671,11 -715,41 +725,42 @@@ public class Constants 
  
      public static final String MULTICAST = "multicast";
  
-     public static final String TAG_KEY = "tag";
+     public static final String TAG_KEY = "dubbo.tag";
+ 
+     public static final String FORCE_USE_TAG = "dubbo.force.tag";
+ 
+     public static final String HOST_KEY = "host";
+ 
+     public static final String ADDRESS_KEY = "address";
+ 
+     public static final String RETRY_TIMES_KEY = "retry.times";
+ 
+     public static final String RETRY_PERIOD_KEY = "retry.period";
+ 
+     public static final String SYNC_REPORT_KEY = "sync.report";
+ 
+     public static final String CYCLE_REPORT_KEY = "cycle.report";
+ 
+     public static final String CONFIG_VERSION_KEY = "configVersion";
+ 
+     public static final String COMPATIBLE_CONFIG_KEY = "compatible_config";
+     // package version in the manifest
+     public static final String SPECIFICATION_VERSION_KEY = "specVersion";
+ 
+     public static final String OVERRIDE_PROVIDERS_KEY = "providerAddreses";
+ 
+     public static final String PROTOCOLS_SUFFIX = "dubbo.protocols.";
+ 
+     public static final String PROTOCOL_SUFFIX = "dubbo.protocol.";
+ 
+     public static final String REGISTRIES_SUFFIX = "dubbo.registries.";
+ 
+     public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = 
{APPLICATION_KEY, CODEC_KEY, EXCHANGER_KEY, SERIALIZATION_KEY, CLUSTER_KEY, 
CONNECTIONS_KEY, DEPRECATED_KEY,
+             GROUP_KEY, LOADBALANCE_KEY, MOCK_KEY, PATH_KEY, TIMEOUT_KEY, 
TOKEN_KEY, VERSION_KEY, WARMUP_KEY, WEIGHT_KEY, TIMESTAMP_KEY, 
DUBBO_VERSION_KEY, SPECIFICATION_VERSION_KEY};
  
-     public static final String REQUEST_TAG_KEY = "request.tag";
+     public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = 
{APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, 
SPECIFICATION_VERSION_KEY};
  
 +    public static final String TELNET = "telnet";
      /*
       * private Constants(){ }
       */
diff --cc 
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 7cddb97,a64c73e..952853d
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@@ -1,535 -1,606 +1,606 @@@
 -/*
 - * 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;
 -
 -import org.apache.dubbo.common.Constants;
 -import org.apache.dubbo.common.URL;
 -import org.apache.dubbo.common.config.Configuration;
 -import org.apache.dubbo.common.config.Environment;
 -import org.apache.dubbo.common.extension.ExtensionLoader;
 -import org.apache.dubbo.common.logger.Logger;
 -import org.apache.dubbo.common.logger.LoggerFactory;
 -import org.apache.dubbo.common.utils.ClassHelper;
 -import org.apache.dubbo.common.utils.CollectionUtils;
 -import org.apache.dubbo.common.utils.ReflectUtils;
 -import org.apache.dubbo.common.utils.StringUtils;
 -import org.apache.dubbo.config.support.Parameter;
 -import org.apache.dubbo.rpc.model.ConsumerMethodModel;
 -
 -import java.io.Serializable;
 -import java.lang.reflect.Method;
 -import java.lang.reflect.Modifier;
 -import java.util.HashMap;
 -import java.util.Map;
 -import java.util.Set;
 -import java.util.regex.Matcher;
 -import java.util.regex.Pattern;
 -import java.util.stream.Collectors;
 -
 -/**
 - * Utility methods and public methods for parsing configuration
 - *
 - * @export
 - */
 -public abstract class AbstractConfig implements Serializable {
 -
 -    protected static final Logger logger = 
LoggerFactory.getLogger(AbstractConfig.class);
 -    private static final long serialVersionUID = 4267533505537413570L;
 -    private static final int MAX_LENGTH = 200;
 -
 -    private static final int MAX_PATH_LENGTH = 200;
 -
 -    private static final Pattern PATTERN_NAME = 
Pattern.compile("[\\-._0-9a-zA-Z]+");
 -
 -    private static final Pattern PATTERN_MULTI_NAME = 
Pattern.compile("[,\\-._0-9a-zA-Z]+");
 -
 -    private static final Pattern PATTERN_METHOD_NAME = 
Pattern.compile("[a-zA-Z][0-9a-zA-Z]*");
 -
 -    private static final Pattern PATTERN_PATH = 
Pattern.compile("[/\\-$._0-9a-zA-Z]+");
 -
 -    private static final Pattern PATTERN_NAME_HAS_SYMBOL = 
Pattern.compile("[:*,\\s/\\-._0-9a-zA-Z]+");
 -
 -    private static final Pattern PATTERN_KEY = 
Pattern.compile("[*,\\-._0-9a-zA-Z]+");
 -    private static final Map<String, String> legacyProperties = new 
HashMap<String, String>();
 -    private static final String[] SUFFIXES = new String[]{"Config", "Bean"};
 -
 -    static {
 -        legacyProperties.put("dubbo.protocol.name", "dubbo.service.protocol");
 -        legacyProperties.put("dubbo.protocol.host", 
"dubbo.service.server.host");
 -        legacyProperties.put("dubbo.protocol.port", 
"dubbo.service.server.port");
 -        legacyProperties.put("dubbo.protocol.threads", 
"dubbo.service.max.thread.pool.size");
 -        legacyProperties.put("dubbo.consumer.timeout", 
"dubbo.service.invoke.timeout");
 -        legacyProperties.put("dubbo.consumer.retries", 
"dubbo.service.max.retry.providers");
 -        legacyProperties.put("dubbo.consumer.check", 
"dubbo.service.allow.no.provider");
 -        legacyProperties.put("dubbo.service.url", "dubbo.service.address");
 -
 -        // this is only for compatibility
 -        
Runtime.getRuntime().addShutdownHook(DubboShutdownHook.getDubboShutdownHook());
 -    }
 -
 -    protected String id;
 -    protected String prefix;
 -
 -    private static String convertLegacyValue(String key, String value) {
 -        if (value != null && value.length() > 0) {
 -            if ("dubbo.service.max.retry.providers".equals(key)) {
 -                return String.valueOf(Integer.parseInt(value) - 1);
 -            } else if ("dubbo.service.allow.no.provider".equals(key)) {
 -                return String.valueOf(!Boolean.parseBoolean(value));
 -            }
 -        }
 -        return value;
 -    }
 -
 -    private static String getTagName(Class<?> cls) {
 -        String tag = cls.getSimpleName();
 -        for (String suffix : SUFFIXES) {
 -            if (tag.endsWith(suffix)) {
 -                tag = tag.substring(0, tag.length() - suffix.length());
 -                break;
 -            }
 -        }
 -        return tag.substring(0, 1).toLowerCase() + tag.substring(1);
 -    }
 -
 -    protected static void appendParameters(Map<String, String> parameters, 
Object config) {
 -        appendParameters(parameters, config, null);
 -    }
 -
 -    @SuppressWarnings("unchecked")
 -    protected static void appendParameters(Map<String, String> parameters, 
Object config, String prefix) {
 -        if (config == null) {
 -            return;
 -        }
 -        Method[] methods = config.getClass().getMethods();
 -        for (Method method : methods) {
 -            try {
 -                String name = method.getName();
 -                if (ClassHelper.isGetter(method)) {
 -                    Parameter parameter = 
method.getAnnotation(Parameter.class);
 -                    if (method.getReturnType() == Object.class || parameter 
!= null && parameter.excluded()) {
 -                        continue;
 -                    }
 -                    String key;
 -                    if (parameter != null && parameter.key().length() > 0) {
 -                        key = parameter.key();
 -                    } else {
 -                        key = calculatePropertyFromGetter(name);
 -                    }
 -                    Object value = method.invoke(config);
 -                    String str = String.valueOf(value).trim();
 -                    if (value != null && str.length() > 0) {
 -                        if (parameter != null && parameter.escaped()) {
 -                            str = URL.encode(str);
 -                        }
 -                        if (parameter != null && parameter.append()) {
 -                            String pre = parameters.get(Constants.DEFAULT_KEY 
+ "." + key);
 -                            if (pre != null && pre.length() > 0) {
 -                                str = pre + "," + str;
 -                            }
 -                            pre = parameters.get(key);
 -                            if (pre != null && pre.length() > 0) {
 -                                str = pre + "," + str;
 -                            }
 -                        }
 -                        if (prefix != null && prefix.length() > 0) {
 -                            key = prefix + "." + key;
 -                        }
 -                        parameters.put(key, str);
 -                    } else if (parameter != null && parameter.required()) {
 -                        throw new 
IllegalStateException(config.getClass().getSimpleName() + "." + key + " == 
null");
 -                    }
 -                } else if ("getParameters".equals(name)
 -                        && Modifier.isPublic(method.getModifiers())
 -                        && method.getParameterTypes().length == 0
 -                        && method.getReturnType() == Map.class) {
 -                    Map<String, String> map = (Map<String, String>) 
method.invoke(config, new Object[0]);
 -                    if (map != null && map.size() > 0) {
 -                        String pre = (prefix != null && prefix.length() > 0 ? 
prefix + "." : "");
 -                        for (Map.Entry<String, String> entry : 
map.entrySet()) {
 -                            parameters.put(pre + entry.getKey().replace('-', 
'.'), entry.getValue());
 -                        }
 -                    }
 -                }
 -            } catch (Exception e) {
 -                throw new IllegalStateException(e.getMessage(), e);
 -            }
 -        }
 -    }
 -
 -    protected static void appendAttributes(Map<String, Object> parameters, 
Object config) {
 -        appendAttributes(parameters, config, null);
 -    }
 -
 -    protected static void appendAttributes(Map<String, Object> parameters, 
Object config, String prefix) {
 -        if (config == null) {
 -            return;
 -        }
 -        Method[] methods = config.getClass().getMethods();
 -        for (Method method : methods) {
 -            try {
 -                Parameter parameter = method.getAnnotation(Parameter.class);
 -                if (parameter == null || !parameter.attribute()) {
 -                    continue;
 -                }
 -                String name = method.getName();
 -                if (ClassHelper.isGetter(method)) {
 -                    String key;
 -                    if (parameter.key().length() > 0) {
 -                        key = parameter.key();
 -                    } else {
 -                        key = calculateAttributeFromGetter(name);
 -                    }
 -                    Object value = method.invoke(config);
 -                    if (value != null) {
 -                        if (prefix != null && prefix.length() > 0) {
 -                            key = prefix + "." + key;
 -                        }
 -                        parameters.put(key, value);
 -                    }
 -                }
 -            } catch (Exception e) {
 -                throw new IllegalStateException(e.getMessage(), e);
 -            }
 -        }
 -    }
 -
 -    protected static ConsumerMethodModel.AsyncMethodInfo 
convertMethodConfig2AyncInfo(MethodConfig methodConfig) {
 -        if (methodConfig == null || (methodConfig.getOninvoke() == null && 
methodConfig.getOnreturn() == null && methodConfig.getOnthrow() == null)) {
 -            return null;
 -        }
 -
 -        //check config conflict
 -        if (Boolean.FALSE.equals(methodConfig.isReturn()) && 
(methodConfig.getOnreturn() != null || methodConfig.getOnthrow() != null)) {
 -            throw new IllegalStateException("method config error : return 
attribute must be set true when onreturn or onthrow has been set.");
 -        }
 -
 -        ConsumerMethodModel.AsyncMethodInfo asyncMethodInfo = new 
ConsumerMethodModel.AsyncMethodInfo();
 -
 -        asyncMethodInfo.setOninvokeInstance(methodConfig.getOninvoke());
 -        asyncMethodInfo.setOnreturnInstance(methodConfig.getOnreturn());
 -        asyncMethodInfo.setOnthrowInstance(methodConfig.getOnthrow());
 -
 -        try {
 -            String oninvokeMethod = methodConfig.getOninvokeMethod();
 -            if (StringUtils.isNotEmpty(oninvokeMethod)) {
 -                
asyncMethodInfo.setOninvokeMethod(getMethodByName(methodConfig.getOninvoke().getClass(),
 oninvokeMethod));
 -            }
 -
 -            String onreturnMethod = methodConfig.getOnreturnMethod();
 -            if (StringUtils.isNotEmpty(onreturnMethod)) {
 -                
asyncMethodInfo.setOnreturnMethod(getMethodByName(methodConfig.getOnreturn().getClass(),
 onreturnMethod));
 -            }
 -
 -            String onthrowMethod = methodConfig.getOnthrowMethod();
 -            if (StringUtils.isNotEmpty(onthrowMethod)) {
 -                
asyncMethodInfo.setOnthrowMethod(getMethodByName(methodConfig.getOnthrow().getClass(),
 onthrowMethod));
 -            }
 -        } catch (Exception e) {
 -            throw new IllegalStateException(e.getMessage(), e);
 -        }
 -
 -        return asyncMethodInfo;
 -    }
 -
 -    private static Method getMethodByName(Class<?> clazz, String methodName) {
 -        try {
 -            return ReflectUtils.findMethodByMethodName(clazz, methodName);
 -        } catch (Exception e) {
 -            throw new IllegalStateException(e);
 -        }
 -    }
 -
 -    /**
 -     * We only check boolean value at this moment.
 -     *
 -     * @param type
 -     * @param value
 -     * @return
 -     */
 -    private static boolean isTypeMatch(Class<?> type, String value) {
 -        if ((type == boolean.class || type == Boolean.class)
 -                && !("true".equals(value) || "false".equals(value))) {
 -            return false;
 -        }
 -        return true;
 -    }
 -
 -    protected static void checkExtension(Class<?> type, String property, 
String value) {
 -        checkName(property, value);
 -        if (value != null && value.length() > 0
 -                && 
!ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {
 -            throw new IllegalStateException("No such extension " + value + " 
for " + property + "/" + type.getName());
 -        }
 -    }
 -
 -    protected static void checkMultiExtension(Class<?> type, String property, 
String value) {
 -        checkMultiName(property, value);
 -        if (value != null && value.length() > 0) {
 -            String[] values = value.split("\\s*[,]+\\s*");
 -            for (String v : values) {
 -                if (v.startsWith(Constants.REMOVE_VALUE_PREFIX)) {
 -                    v = v.substring(1);
 -                }
 -                if (Constants.DEFAULT_KEY.equals(v)) {
 -                    continue;
 -                }
 -                if 
(!ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {
 -                    throw new IllegalStateException("No such extension " + v 
+ " for " + property + "/" + type.getName());
 -                }
 -            }
 -        }
 -    }
 -
 -    protected static void checkLength(String property, String value) {
 -        checkProperty(property, value, MAX_LENGTH, null);
 -    }
 -
 -    protected static void checkPathLength(String property, String value) {
 -        checkProperty(property, value, MAX_PATH_LENGTH, null);
 -    }
 -
 -    protected static void checkName(String property, String value) {
 -        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME);
 -    }
 -
 -    protected static void checkNameHasSymbol(String property, String value) {
 -        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_SYMBOL);
 -    }
 -
 -    protected static void checkKey(String property, String value) {
 -        checkProperty(property, value, MAX_LENGTH, PATTERN_KEY);
 -    }
 -
 -    protected static void checkMultiName(String property, String value) {
 -        checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME);
 -    }
 -
 -    protected static void checkPathName(String property, String value) {
 -        checkProperty(property, value, MAX_PATH_LENGTH, PATTERN_PATH);
 -    }
 -
 -    protected static void checkMethodName(String property, String value) {
 -        checkProperty(property, value, MAX_LENGTH, PATTERN_METHOD_NAME);
 -    }
 -
 -    protected static void checkParameterName(Map<String, String> parameters) {
 -        if (parameters == null || parameters.size() == 0) {
 -            return;
 -        }
 -        for (Map.Entry<String, String> entry : parameters.entrySet()) {
 -            checkNameHasSymbol(entry.getKey(), entry.getValue());
 -        }
 -    }
 -
 -    protected static void checkProperty(String property, String value, int 
maxlength, Pattern pattern) {
 -        if (value == null || value.length() == 0) {
 -            return;
 -        }
 -        if (value.length() > maxlength) {
 -            throw new IllegalStateException("Invalid " + property + "=\"" + 
value + "\" is longer than " + maxlength);
 -        }
 -        if (pattern != null) {
 -            Matcher matcher = pattern.matcher(value);
 -            if (!matcher.matches()) {
 -                throw new IllegalStateException("Invalid " + property + "=\"" 
+ value + "\" contains illegal " +
 -                        "character, only digit, letter, '-', '_' or '.' is 
legal.");
 -            }
 -        }
 -    }
 -
 -    protected static Set<String> getSubProperties(Map<String, String> 
properties, String prefix) {
 -        return properties.keySet().stream().filter(k -> 
k.contains(prefix)).map(k -> {
 -            k = k.substring(prefix.length());
 -            return k.substring(0, k.indexOf("."));
 -        }).collect(Collectors.toSet());
 -    }
 -
 -    private static String extractPropertyName(Class<?> clazz, Method setter) 
throws Exception {
 -        String propertyName = setter.getName().substring("set".length());
 -        Method getter = null;
 -        try {
 -            getter = clazz.getMethod("get" + propertyName);
 -        } catch (NoSuchMethodException e) {
 -            getter = clazz.getMethod("is" + propertyName);
 -        }
 -        Parameter parameter = getter.getAnnotation(Parameter.class);
 -        if (parameter != null && StringUtils.isNotEmpty(parameter.key()) && 
parameter.useKeyAsProperty()) {
 -            propertyName = parameter.key();
 -        } else {
 -            propertyName = propertyName.substring(0, 1).toLowerCase() + 
propertyName.substring(1);
 -        }
 -        return propertyName;
 -    }
 -
 -    private static String calculatePropertyFromGetter(String name) {
 -        int i = name.startsWith("get") ? 3 : 2;
 -        return StringUtils.camelToSplitName(name.substring(i, i + 
1).toLowerCase() + name.substring(i + 1), ".");
 -    }
 -
 -    private static String calculateAttributeFromGetter(String getter) {
 -        int i = getter.startsWith("get") ? 3 : 2;
 -        return getter.substring(i, i + 1).toLowerCase() + getter.substring(i 
+ 1);
 -    }
 -
 -    @Parameter(excluded = true)
 -    public String getId() {
 -        return id;
 -    }
 -
 -    public void setId(String id) {
 -        this.id = id;
 -    }
 -
 -    public void updateIdIfAbsent(String value) {
 -        if (StringUtils.isNotEmpty(value) && StringUtils.isEmpty(id)) {
 -            this.id = value;
 -        }
 -    }
 -
 -    protected void appendAnnotation(Class<?> annotationClass, Object 
annotation) {
 -        Method[] methods = annotationClass.getMethods();
 -        for (Method method : methods) {
 -            if (method.getDeclaringClass() != Object.class
 -                    && method.getReturnType() != void.class
 -                    && method.getParameterTypes().length == 0
 -                    && Modifier.isPublic(method.getModifiers())
 -                    && !Modifier.isStatic(method.getModifiers())) {
 -                try {
 -                    String property = method.getName();
 -                    if ("interfaceClass".equals(property) || 
"interfaceName".equals(property)) {
 -                        property = "interface";
 -                    }
 -                    String setter = "set" + property.substring(0, 
1).toUpperCase() + property.substring(1);
 -                    Object value = method.invoke(annotation);
 -                    if (value != null && 
!value.equals(method.getDefaultValue())) {
 -                        Class<?> parameterType = 
ReflectUtils.getBoxedClass(method.getReturnType());
 -                        if ("filter".equals(property) || 
"listener".equals(property)) {
 -                            parameterType = String.class;
 -                            value = StringUtils.join((String[]) value, ",");
 -                        } else if ("parameters".equals(property)) {
 -                            parameterType = Map.class;
 -                            value = CollectionUtils.toStringMap((String[]) 
value);
 -                        }
 -                        try {
 -                            Method setterMethod = 
getClass().getMethod(setter, parameterType);
 -                            setterMethod.invoke(this, value);
 -                        } catch (NoSuchMethodException e) {
 -                            // ignore
 -                        }
 -                    }
 -                } catch (Throwable e) {
 -                    logger.error(e.getMessage(), e);
 -                }
 -            }
 -        }
 -    }
 -
 -    /**
 -     * Should be called after Config was fully initialized.
 -     * // FIXME: this method should be completely replaced by appendParameters
 -     *
 -     * @return
 -     * @see AbstractConfig#appendParameters(Map, Object, String)
 -     * <p>
 -     * Notice! This method should include all properties in the returning 
map, treat @Parameter differently compared to appendParameters.
 -     */
 -    public Map<String, String> getMetaData() {
 -        Map<String, String> metaData = new HashMap<>();
 -        Method[] methods = this.getClass().getMethods();
 -        for (Method method : methods) {
 -            try {
 -                String name = method.getName();
 -                if ((name.startsWith("get") || name.startsWith("is"))
 -                        && !name.equals("get")
 -                        && !"getClass".equals(name)
 -                        && Modifier.isPublic(method.getModifiers())
 -                        && method.getParameterTypes().length == 0
 -                        && ClassHelper.isPrimitive(method.getReturnType())) {
 -                    String prop = calculateAttributeFromGetter(name);
 -                    String key;
 -                    Parameter parameter = 
method.getAnnotation(Parameter.class);
 -                    if (parameter != null && parameter.key().length() > 0 && 
parameter.useKeyAsProperty()) {
 -                        key = parameter.key();
 -                    } else {
 -                        key = prop;
 -                    }
 -                    // treat url and configuration differently, the value 
should always present in configuration though it may not need to present in url.
 -                    //if (method.getReturnType() == Object.class || parameter 
!= null && parameter.excluded()) {
 -                    if (method.getReturnType() == Object.class) {
 -                        metaData.put(key, null);
 -                        continue;
 -                    }
 -                    Object value = method.invoke(this);
 -                    String str = String.valueOf(value).trim();
 -                    if (value != null && str.length() > 0) {
 -                        // ignore escape, keep the original value.
 -                        /*if (parameter != null && parameter.escaped()) {
 -                            str = URL.encode(str);
 -                        }*/
 -                        if (parameter != null && parameter.append()) {
 -                            String pre = 
String.valueOf(metaData.get(Constants.DEFAULT_KEY + "." + key));
 -                            if (pre != null && pre.length() > 0) {
 -                                str = pre + "," + str;
 -                            }
 -                            pre = String.valueOf(metaData.get(key));
 -                            if (pre != null && pre.length() > 0) {
 -                                str = pre + "," + str;
 -                            }
 -                        }
 -                      /*  if (prefix != null && prefix.length() > 0) {
 -                            key = prefix + "." + key;
 -                        }*/
 -                        metaData.put(key, str);
 -                    } else {
 -                        metaData.put(key, null);
 -                    }
 -                    // TODO check required somewhere else.
 -                    /*else if (parameter != null && parameter.required()) {
 -                        throw new 
IllegalStateException(this.getClass().getSimpleName() + "." + key + " == null");
 -                    }*/
 -                } else if ("getParameters".equals(name)
 -                        && Modifier.isPublic(method.getModifiers())
 -                        && method.getParameterTypes().length == 0
 -                        && method.getReturnType() == Map.class) {
 -                    Map<String, String> map = (Map<String, String>) 
method.invoke(this, new Object[0]);
 -                    if (map != null && map.size() > 0) {
 -//                            String pre = (prefix != null && prefix.length() 
> 0 ? prefix + "." : "");
 -                        for (Map.Entry<String, String> entry : 
map.entrySet()) {
 -                            metaData.put(entry.getKey().replace('-', '.'), 
entry.getValue());
 -                        }
 -                    }
 -                }
 -            } catch (Exception e) {
 -                throw new IllegalStateException(e.getMessage(), e);
 -            }
 -        }
 -        return metaData;
 -    }
 -
 -    @Parameter(excluded = true)
 -    public String getPrefix() {
 -        return StringUtils.isNotEmpty(prefix) ? prefix : (Constants.DUBBO + 
"." + getTagName(this.getClass()));
 -    }
 -
 -    public void setPrefix(String prefix) {
 -        this.prefix = prefix;
 -    }
 -
 -    /**
 -     * TODO: Currently, only support overriding of properties explicitly 
defined in Config class, doesn't support
 -     * overriding of customized parameters stored in 'parameters'.
 -     */
 -    public void refresh() {
 -        try {
 -            Environment env = Environment.getInstance();
 -            env.addAppConfig(getPrefix(), getId(), getMetaData());
 -            Configuration configuration = env.getConfiguration(getPrefix(), 
getId());
 -
 -            // loop methods, get override value and set the new value back to 
method
 -            Method[] methods = getClass().getMethods();
 -            for (Method method : methods) {
 -                if (ClassHelper.isSetter(method)) {
 -                    try {
 -                        String value = 
configuration.getString(extractPropertyName(getClass(), method));
 -                        // isTypeMatch() is called to avoid duplicate and 
incorrect update, for example, we have two 'setGeneric' methods in 
ReferenceConfig.
 -                        if (value != null && 
ClassHelper.isTypeMatch(method.getParameterTypes()[0], value)) {
 -                            method.invoke(this, 
ClassHelper.convertPrimitive(method.getParameterTypes()[0], value));
 -                        }
 -                    } catch (NoSuchMethodException e) {
 -                        logger.info("Failed to override the property " + 
method.getName() + " in " +
 -                                this.getClass().getSimpleName() +
 -                                ", please make sure every property has 
getter/setter method provided.");
 -                    }
 -                }
 -            }
 -        } catch (Exception e) {
 -            logger.error("Failed to override ", e);
 -        }
 -    }
 -
 -    @Override
 -    public String toString() {
 -        try {
 -            StringBuilder buf = new StringBuilder();
 -            buf.append("<dubbo:");
 -            buf.append(getTagName(getClass()));
 -            Method[] methods = getClass().getMethods();
 -            for (Method method : methods) {
 -                try {
 -                    if (ClassHelper.isGetter(method)) {
 -                        String name = method.getName();
 -                        String key = calculateAttributeFromGetter(name);
 -                        Object value = method.invoke(this);
 -                        if (value != null) {
 -                            buf.append(" ");
 -                            buf.append(key);
 -                            buf.append("=\"");
 -                            buf.append(value);
 -                            buf.append("\"");
 -                        }
 -                    }
 -                } catch (Exception e) {
 -                    logger.warn(e.getMessage(), e);
 -                }
 -            }
 -            buf.append(" />");
 -            return buf.toString();
 -        } catch (Throwable t) {
 -            logger.warn(t.getMessage(), t);
 -            return super.toString();
 -        }
 -    }
 -
 -    /**
 -     * FIXME check @Parameter(required=true) and any conditions that need to 
match.
 -     */
 -    @Parameter(excluded = true)
 -    public boolean isValid() {
 -        return true;
 -    }
 -
 -}
 +/*
 + * 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;
 +
 +import org.apache.dubbo.common.Constants;
 +import org.apache.dubbo.common.URL;
++import org.apache.dubbo.common.config.Configuration;
++import org.apache.dubbo.common.config.Environment;
 +import org.apache.dubbo.common.extension.ExtensionLoader;
 +import org.apache.dubbo.common.logger.Logger;
 +import org.apache.dubbo.common.logger.LoggerFactory;
++import org.apache.dubbo.common.utils.ClassHelper;
 +import org.apache.dubbo.common.utils.CollectionUtils;
- import org.apache.dubbo.common.utils.ConfigUtils;
 +import org.apache.dubbo.common.utils.ReflectUtils;
 +import org.apache.dubbo.common.utils.StringUtils;
 +import org.apache.dubbo.config.support.Parameter;
 +import org.apache.dubbo.rpc.model.ConsumerMethodModel;
 +
 +import java.io.Serializable;
 +import java.lang.reflect.Method;
 +import java.lang.reflect.Modifier;
 +import java.util.HashMap;
 +import java.util.Map;
++import java.util.Set;
 +import java.util.regex.Matcher;
 +import java.util.regex.Pattern;
++import java.util.stream.Collectors;
 +
 +/**
 + * Utility methods and public methods for parsing configuration
 + *
 + * @export
 + */
 +public abstract class AbstractConfig implements Serializable {
 +
 +    protected static final Logger logger = 
LoggerFactory.getLogger(AbstractConfig.class);
 +    private static final long serialVersionUID = 4267533505537413570L;
 +    private static final int MAX_LENGTH = 200;
 +
 +    private static final int MAX_PATH_LENGTH = 200;
 +
 +    private static final Pattern PATTERN_NAME = 
Pattern.compile("[\\-._0-9a-zA-Z]+");
 +
 +    private static final Pattern PATTERN_MULTI_NAME = 
Pattern.compile("[,\\-._0-9a-zA-Z]+");
 +
 +    private static final Pattern PATTERN_METHOD_NAME = 
Pattern.compile("[a-zA-Z][0-9a-zA-Z]*");
 +
 +    private static final Pattern PATTERN_PATH = 
Pattern.compile("[/\\-$._0-9a-zA-Z]+");
 +
 +    private static final Pattern PATTERN_NAME_HAS_SYMBOL = 
Pattern.compile("[:*,\\s/\\-._0-9a-zA-Z]+");
 +
 +    private static final Pattern PATTERN_KEY = 
Pattern.compile("[*,\\-._0-9a-zA-Z]+");
 +    private static final Map<String, String> legacyProperties = new 
HashMap<String, String>();
 +    private static final String[] SUFFIXES = new String[]{"Config", "Bean"};
 +
 +    static {
 +        legacyProperties.put("dubbo.protocol.name", "dubbo.service.protocol");
 +        legacyProperties.put("dubbo.protocol.host", 
"dubbo.service.server.host");
 +        legacyProperties.put("dubbo.protocol.port", 
"dubbo.service.server.port");
 +        legacyProperties.put("dubbo.protocol.threads", 
"dubbo.service.max.thread.pool.size");
 +        legacyProperties.put("dubbo.consumer.timeout", 
"dubbo.service.invoke.timeout");
 +        legacyProperties.put("dubbo.consumer.retries", 
"dubbo.service.max.retry.providers");
 +        legacyProperties.put("dubbo.consumer.check", 
"dubbo.service.allow.no.provider");
 +        legacyProperties.put("dubbo.service.url", "dubbo.service.address");
 +
 +        // this is only for compatibility
 +        DubboShutdownHook.getDubboShutdownHook().register();
 +    }
 +
 +    protected String id;
++    protected String prefix;
 +
 +    private static String convertLegacyValue(String key, String value) {
 +        if (value != null && value.length() > 0) {
 +            if ("dubbo.service.max.retry.providers".equals(key)) {
 +                return String.valueOf(Integer.parseInt(value) - 1);
 +            } else if ("dubbo.service.allow.no.provider".equals(key)) {
 +                return String.valueOf(!Boolean.parseBoolean(value));
 +            }
 +        }
 +        return value;
 +    }
 +
-     protected static void appendProperties(AbstractConfig config) {
-         if (config == null) {
-             return;
-         }
-         String prefix = "dubbo." + getTagName(config.getClass()) + ".";
-         Method[] methods = config.getClass().getMethods();
-         for (Method method : methods) {
-             try {
-                 String name = method.getName();
-                 if (name.length() > 3 && name.startsWith("set") && 
Modifier.isPublic(method.getModifiers())
-                         && method.getParameterTypes().length == 1 && 
isPrimitive(method.getParameterTypes()[0])) {
-                     String property = 
StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + 
name.substring(4), ".");
- 
-                     String value = null;
-                     if (config.getId() != null && config.getId().length() > 
0) {
-                         String pn = prefix + config.getId() + "." + property;
-                         value = System.getProperty(pn);
-                         if (!StringUtils.isBlank(value)) {
-                             logger.info("Use System Property " + pn + " to 
config dubbo");
-                         }
-                     }
-                     if (value == null || value.length() == 0) {
-                         String pn = prefix + property;
-                         value = System.getProperty(pn);
-                         if (!StringUtils.isBlank(value)) {
-                             logger.info("Use System Property " + pn + " to 
config dubbo");
-                         }
-                     }
-                     if (value == null || value.length() == 0) {
-                         Method getter;
-                         try {
-                             getter = config.getClass().getMethod("get" + 
name.substring(3));
-                         } catch (NoSuchMethodException e) {
-                             try {
-                                 getter = config.getClass().getMethod("is" + 
name.substring(3));
-                             } catch (NoSuchMethodException e2) {
-                                 getter = null;
-                             }
-                         }
-                         if (getter != null) {
-                             if (getter.invoke(config) == null) {
-                                 if (config.getId() != null && 
config.getId().length() > 0) {
-                                     value = ConfigUtils.getProperty(prefix + 
config.getId() + "." + property);
-                                 }
-                                 if (value == null || value.length() == 0) {
-                                     value = ConfigUtils.getProperty(prefix + 
property);
-                                 }
-                                 if (value == null || value.length() == 0) {
-                                     String legacyKey = 
legacyProperties.get(prefix + property);
-                                     if (legacyKey != null && 
legacyKey.length() > 0) {
-                                         value = convertLegacyValue(legacyKey, 
ConfigUtils.getProperty(legacyKey));
-                                     }
-                                 }
- 
-                             }
-                         }
-                     }
-                     if (value != null && value.length() > 0) {
-                         method.invoke(config, 
convertPrimitive(method.getParameterTypes()[0], value));
-                     }
-                 }
-             } catch (Exception e) {
-                 logger.error(e.getMessage(), e);
-             }
-         }
-     }
- 
 +    private static String getTagName(Class<?> cls) {
 +        String tag = cls.getSimpleName();
 +        for (String suffix : SUFFIXES) {
 +            if (tag.endsWith(suffix)) {
 +                tag = tag.substring(0, tag.length() - suffix.length());
 +                break;
 +            }
 +        }
-         tag = tag.toLowerCase();
-         return tag;
++        return tag.substring(0, 1).toLowerCase() + tag.substring(1);
 +    }
 +
 +    protected static void appendParameters(Map<String, String> parameters, 
Object config) {
 +        appendParameters(parameters, config, null);
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    protected static void appendParameters(Map<String, String> parameters, 
Object config, String prefix) {
 +        if (config == null) {
 +            return;
 +        }
 +        Method[] methods = config.getClass().getMethods();
 +        for (Method method : methods) {
 +            try {
 +                String name = method.getName();
-                 if ((name.startsWith("get") || name.startsWith("is"))
-                         && !"getClass".equals(name)
-                         && Modifier.isPublic(method.getModifiers())
-                         && method.getParameterTypes().length == 0
-                         && isPrimitive(method.getReturnType())) {
++                if (ClassHelper.isGetter(method)) {
 +                    Parameter parameter = 
method.getAnnotation(Parameter.class);
 +                    if (method.getReturnType() == Object.class || parameter 
!= null && parameter.excluded()) {
 +                        continue;
 +                    }
-                     int i = name.startsWith("get") ? 3 : 2;
-                     String prop = 
StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + 
name.substring(i + 1), ".");
 +                    String key;
 +                    if (parameter != null && parameter.key().length() > 0) {
 +                        key = parameter.key();
 +                    } else {
-                         key = prop;
++                        key = calculatePropertyFromGetter(name);
 +                    }
 +                    Object value = method.invoke(config);
 +                    String str = String.valueOf(value).trim();
 +                    if (value != null && str.length() > 0) {
 +                        if (parameter != null && parameter.escaped()) {
 +                            str = URL.encode(str);
 +                        }
 +                        if (parameter != null && parameter.append()) {
 +                            String pre = parameters.get(Constants.DEFAULT_KEY 
+ "." + key);
 +                            if (pre != null && pre.length() > 0) {
 +                                str = pre + "," + str;
 +                            }
 +                            pre = parameters.get(key);
 +                            if (pre != null && pre.length() > 0) {
 +                                str = pre + "," + str;
 +                            }
 +                        }
 +                        if (prefix != null && prefix.length() > 0) {
 +                            key = prefix + "." + key;
 +                        }
 +                        parameters.put(key, str);
 +                    } else if (parameter != null && parameter.required()) {
 +                        throw new 
IllegalStateException(config.getClass().getSimpleName() + "." + key + " == 
null");
 +                    }
 +                } else if ("getParameters".equals(name)
 +                        && Modifier.isPublic(method.getModifiers())
 +                        && method.getParameterTypes().length == 0
 +                        && method.getReturnType() == Map.class) {
 +                    Map<String, String> map = (Map<String, String>) 
method.invoke(config, new Object[0]);
 +                    if (map != null && map.size() > 0) {
 +                        String pre = (prefix != null && prefix.length() > 0 ? 
prefix + "." : "");
 +                        for (Map.Entry<String, String> entry : 
map.entrySet()) {
 +                            parameters.put(pre + entry.getKey().replace('-', 
'.'), entry.getValue());
 +                        }
 +                    }
 +                }
 +            } catch (Exception e) {
 +                throw new IllegalStateException(e.getMessage(), e);
 +            }
 +        }
 +    }
 +
 +    protected static void appendAttributes(Map<String, Object> parameters, 
Object config) {
 +        appendAttributes(parameters, config, null);
 +    }
 +
 +    protected static void appendAttributes(Map<String, Object> parameters, 
Object config, String prefix) {
 +        if (config == null) {
 +            return;
 +        }
 +        Method[] methods = config.getClass().getMethods();
 +        for (Method method : methods) {
 +            try {
 +                Parameter parameter = method.getAnnotation(Parameter.class);
 +                if (parameter == null || !parameter.attribute()) {
 +                    continue;
 +                }
 +                String name = method.getName();
-                 if ((name.startsWith("get") || name.startsWith("is"))
-                         && !"getClass".equals(name)
-                         && Modifier.isPublic(method.getModifiers())
-                         && method.getParameterTypes().length == 0
-                         && isPrimitive(method.getReturnType())) {
++                if (ClassHelper.isGetter(method)) {
 +                    String key;
 +                    if (parameter.key().length() > 0) {
 +                        key = parameter.key();
 +                    } else {
-                         int i = name.startsWith("get") ? 3 : 2;
-                         key = name.substring(i, i + 1).toLowerCase() + 
name.substring(i + 1);
++                        key = calculateAttributeFromGetter(name);
 +                    }
 +                    Object value = method.invoke(config);
 +                    if (value != null) {
 +                        if (prefix != null && prefix.length() > 0) {
 +                            key = prefix + "." + key;
 +                        }
 +                        parameters.put(key, value);
 +                    }
 +                }
 +            } catch (Exception e) {
 +                throw new IllegalStateException(e.getMessage(), e);
 +            }
 +        }
 +    }
 +
 +    protected static ConsumerMethodModel.AsyncMethodInfo 
convertMethodConfig2AyncInfo(MethodConfig methodConfig) {
 +        if (methodConfig == null || (methodConfig.getOninvoke() == null && 
methodConfig.getOnreturn() == null && methodConfig.getOnthrow() == null)) {
 +            return null;
 +        }
 +
 +        //check config conflict
 +        if (Boolean.FALSE.equals(methodConfig.isReturn()) && 
(methodConfig.getOnreturn() != null || methodConfig.getOnthrow() != null)) {
 +            throw new IllegalStateException("method config error : return 
attribute must be set true when onreturn or onthrow has been set.");
 +        }
 +
 +        ConsumerMethodModel.AsyncMethodInfo asyncMethodInfo = new 
ConsumerMethodModel.AsyncMethodInfo();
 +
 +        asyncMethodInfo.setOninvokeInstance(methodConfig.getOninvoke());
 +        asyncMethodInfo.setOnreturnInstance(methodConfig.getOnreturn());
 +        asyncMethodInfo.setOnthrowInstance(methodConfig.getOnthrow());
 +
 +        try {
 +            String oninvokeMethod = methodConfig.getOninvokeMethod();
 +            if (StringUtils.isNotEmpty(oninvokeMethod)) {
 +                
asyncMethodInfo.setOninvokeMethod(getMethodByName(methodConfig.getOninvoke().getClass(),
 oninvokeMethod));
 +            }
 +
 +            String onreturnMethod = methodConfig.getOnreturnMethod();
 +            if (StringUtils.isNotEmpty(onreturnMethod)) {
 +                
asyncMethodInfo.setOnreturnMethod(getMethodByName(methodConfig.getOnreturn().getClass(),
 onreturnMethod));
 +            }
 +
 +            String onthrowMethod = methodConfig.getOnthrowMethod();
 +            if (StringUtils.isNotEmpty(onthrowMethod)) {
 +                
asyncMethodInfo.setOnthrowMethod(getMethodByName(methodConfig.getOnthrow().getClass(),
 onthrowMethod));
 +            }
 +        } catch (Exception e) {
 +            throw new IllegalStateException(e.getMessage(), e);
 +        }
 +
 +        return asyncMethodInfo;
 +    }
 +
 +    private static Method getMethodByName(Class<?> clazz, String methodName) {
 +        try {
 +            return ReflectUtils.findMethodByMethodName(clazz, methodName);
 +        } catch (Exception e) {
 +            throw new IllegalStateException(e);
 +        }
 +    }
 +
-     private static boolean isPrimitive(Class<?> type) {
-         return type.isPrimitive()
-                 || type == String.class
-                 || type == Character.class
-                 || type == Boolean.class
-                 || type == Byte.class
-                 || type == Short.class
-                 || type == Integer.class
-                 || type == Long.class
-                 || type == Float.class
-                 || type == Double.class
-                 || type == Object.class;
-     }
- 
-     private static Object convertPrimitive(Class<?> type, String value) {
-         if (type == char.class || type == Character.class) {
-             return value.length() > 0 ? value.charAt(0) : '\0';
-         } else if (type == boolean.class || type == Boolean.class) {
-             return Boolean.valueOf(value);
-         } else if (type == byte.class || type == Byte.class) {
-             return Byte.valueOf(value);
-         } else if (type == short.class || type == Short.class) {
-             return Short.valueOf(value);
-         } else if (type == int.class || type == Integer.class) {
-             return Integer.valueOf(value);
-         } else if (type == long.class || type == Long.class) {
-             return Long.valueOf(value);
-         } else if (type == float.class || type == Float.class) {
-             return Float.valueOf(value);
-         } else if (type == double.class || type == Double.class) {
-             return Double.valueOf(value);
++    /**
++     * We only check boolean value at this moment.
++     *
++     * @param type
++     * @param value
++     * @return
++     */
++    private static boolean isTypeMatch(Class<?> type, String value) {
++        if ((type == boolean.class || type == Boolean.class)
++                && !("true".equals(value) || "false".equals(value))) {
++            return false;
 +        }
-         return value;
++        return true;
 +    }
 +
 +    protected static void checkExtension(Class<?> type, String property, 
String value) {
 +        checkName(property, value);
 +        if (value != null && value.length() > 0
 +                && 
!ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {
 +            throw new IllegalStateException("No such extension " + value + " 
for " + property + "/" + type.getName());
 +        }
 +    }
 +
 +    protected static void checkMultiExtension(Class<?> type, String property, 
String value) {
 +        checkMultiName(property, value);
 +        if (value != null && value.length() > 0) {
 +            String[] values = value.split("\\s*[,]+\\s*");
 +            for (String v : values) {
 +                if (v.startsWith(Constants.REMOVE_VALUE_PREFIX)) {
 +                    v = v.substring(1);
 +                }
 +                if (Constants.DEFAULT_KEY.equals(v)) {
 +                    continue;
 +                }
 +                if 
(!ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {
 +                    throw new IllegalStateException("No such extension " + v 
+ " for " + property + "/" + type.getName());
 +                }
 +            }
 +        }
 +    }
 +
 +    protected static void checkLength(String property, String value) {
 +        checkProperty(property, value, MAX_LENGTH, null);
 +    }
 +
 +    protected static void checkPathLength(String property, String value) {
 +        checkProperty(property, value, MAX_PATH_LENGTH, null);
 +    }
 +
 +    protected static void checkName(String property, String value) {
 +        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME);
 +    }
 +
 +    protected static void checkNameHasSymbol(String property, String value) {
 +        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_SYMBOL);
 +    }
 +
 +    protected static void checkKey(String property, String value) {
 +        checkProperty(property, value, MAX_LENGTH, PATTERN_KEY);
 +    }
 +
 +    protected static void checkMultiName(String property, String value) {
 +        checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME);
 +    }
 +
 +    protected static void checkPathName(String property, String value) {
 +        checkProperty(property, value, MAX_PATH_LENGTH, PATTERN_PATH);
 +    }
 +
 +    protected static void checkMethodName(String property, String value) {
 +        checkProperty(property, value, MAX_LENGTH, PATTERN_METHOD_NAME);
 +    }
 +
 +    protected static void checkParameterName(Map<String, String> parameters) {
 +        if (parameters == null || parameters.size() == 0) {
 +            return;
 +        }
 +        for (Map.Entry<String, String> entry : parameters.entrySet()) {
 +            checkNameHasSymbol(entry.getKey(), entry.getValue());
 +        }
 +    }
 +
 +    protected static void checkProperty(String property, String value, int 
maxlength, Pattern pattern) {
 +        if (value == null || value.length() == 0) {
 +            return;
 +        }
 +        if (value.length() > maxlength) {
 +            throw new IllegalStateException("Invalid " + property + "=\"" + 
value + "\" is longer than " + maxlength);
 +        }
 +        if (pattern != null) {
 +            Matcher matcher = pattern.matcher(value);
 +            if (!matcher.matches()) {
 +                throw new IllegalStateException("Invalid " + property + "=\"" 
+ value + "\" contains illegal " +
 +                        "character, only digit, letter, '-', '_' or '.' is 
legal.");
 +            }
 +        }
 +    }
 +
++    protected static Set<String> getSubProperties(Map<String, String> 
properties, String prefix) {
++        return properties.keySet().stream().filter(k -> 
k.contains(prefix)).map(k -> {
++            k = k.substring(prefix.length());
++            return k.substring(0, k.indexOf("."));
++        }).collect(Collectors.toSet());
++    }
++
++    private static String extractPropertyName(Class<?> clazz, Method setter) 
throws Exception {
++        String propertyName = setter.getName().substring("set".length());
++        Method getter = null;
++        try {
++            getter = clazz.getMethod("get" + propertyName);
++        } catch (NoSuchMethodException e) {
++            getter = clazz.getMethod("is" + propertyName);
++        }
++        Parameter parameter = getter.getAnnotation(Parameter.class);
++        if (parameter != null && StringUtils.isNotEmpty(parameter.key()) && 
parameter.useKeyAsProperty()) {
++            propertyName = parameter.key();
++        } else {
++            propertyName = propertyName.substring(0, 1).toLowerCase() + 
propertyName.substring(1);
++        }
++        return propertyName;
++    }
++
++    private static String calculatePropertyFromGetter(String name) {
++        int i = name.startsWith("get") ? 3 : 2;
++        return StringUtils.camelToSplitName(name.substring(i, i + 
1).toLowerCase() + name.substring(i + 1), ".");
++    }
++
++    private static String calculateAttributeFromGetter(String getter) {
++        int i = getter.startsWith("get") ? 3 : 2;
++        return getter.substring(i, i + 1).toLowerCase() + getter.substring(i 
+ 1);
++    }
++
 +    @Parameter(excluded = true)
 +    public String getId() {
 +        return id;
 +    }
 +
 +    public void setId(String id) {
 +        this.id = id;
 +    }
 +
++    public void updateIdIfAbsent(String value) {
++        if (StringUtils.isNotEmpty(value) && StringUtils.isEmpty(id)) {
++            this.id = value;
++        }
++    }
++
 +    protected void appendAnnotation(Class<?> annotationClass, Object 
annotation) {
 +        Method[] methods = annotationClass.getMethods();
 +        for (Method method : methods) {
 +            if (method.getDeclaringClass() != Object.class
 +                    && method.getReturnType() != void.class
 +                    && method.getParameterTypes().length == 0
 +                    && Modifier.isPublic(method.getModifiers())
 +                    && !Modifier.isStatic(method.getModifiers())) {
 +                try {
 +                    String property = method.getName();
 +                    if ("interfaceClass".equals(property) || 
"interfaceName".equals(property)) {
 +                        property = "interface";
 +                    }
 +                    String setter = "set" + property.substring(0, 
1).toUpperCase() + property.substring(1);
 +                    Object value = method.invoke(annotation);
 +                    if (value != null && 
!value.equals(method.getDefaultValue())) {
 +                        Class<?> parameterType = 
ReflectUtils.getBoxedClass(method.getReturnType());
 +                        if ("filter".equals(property) || 
"listener".equals(property)) {
 +                            parameterType = String.class;
 +                            value = StringUtils.join((String[]) value, ",");
 +                        } else if ("parameters".equals(property)) {
 +                            parameterType = Map.class;
 +                            value = CollectionUtils.toStringMap((String[]) 
value);
 +                        }
 +                        try {
 +                            Method setterMethod = 
getClass().getMethod(setter, parameterType);
 +                            setterMethod.invoke(this, value);
 +                        } catch (NoSuchMethodException e) {
 +                            // ignore
 +                        }
 +                    }
 +                } catch (Throwable e) {
 +                    logger.error(e.getMessage(), e);
 +                }
 +            }
 +        }
 +    }
 +
++    /**
++     * Should be called after Config was fully initialized.
++     * // FIXME: this method should be completely replaced by appendParameters
++     *
++     * @return
++     * @see AbstractConfig#appendParameters(Map, Object, String)
++     * <p>
++     * Notice! This method should include all properties in the returning 
map, treat @Parameter differently compared to appendParameters.
++     */
++    public Map<String, String> getMetaData() {
++        Map<String, String> metaData = new HashMap<>();
++        Method[] methods = this.getClass().getMethods();
++        for (Method method : methods) {
++            try {
++                String name = method.getName();
++                if ((name.startsWith("get") || name.startsWith("is"))
++                        && !name.equals("get")
++                        && !"getClass".equals(name)
++                        && Modifier.isPublic(method.getModifiers())
++                        && method.getParameterTypes().length == 0
++                        && ClassHelper.isPrimitive(method.getReturnType())) {
++                    String prop = calculateAttributeFromGetter(name);
++                    String key;
++                    Parameter parameter = 
method.getAnnotation(Parameter.class);
++                    if (parameter != null && parameter.key().length() > 0 && 
parameter.useKeyAsProperty()) {
++                        key = parameter.key();
++                    } else {
++                        key = prop;
++                    }
++                    // treat url and configuration differently, the value 
should always present in configuration though it may not need to present in url.
++                    //if (method.getReturnType() == Object.class || parameter 
!= null && parameter.excluded()) {
++                    if (method.getReturnType() == Object.class) {
++                        metaData.put(key, null);
++                        continue;
++                    }
++                    Object value = method.invoke(this);
++                    String str = String.valueOf(value).trim();
++                    if (value != null && str.length() > 0) {
++                        // ignore escape, keep the original value.
++                        /*if (parameter != null && parameter.escaped()) {
++                            str = URL.encode(str);
++                        }*/
++                        if (parameter != null && parameter.append()) {
++                            String pre = 
String.valueOf(metaData.get(Constants.DEFAULT_KEY + "." + key));
++                            if (pre != null && pre.length() > 0) {
++                                str = pre + "," + str;
++                            }
++                            pre = String.valueOf(metaData.get(key));
++                            if (pre != null && pre.length() > 0) {
++                                str = pre + "," + str;
++                            }
++                        }
++                      /*  if (prefix != null && prefix.length() > 0) {
++                            key = prefix + "." + key;
++                        }*/
++                        metaData.put(key, str);
++                    } else {
++                        metaData.put(key, null);
++                    }
++                    // TODO check required somewhere else.
++                    /*else if (parameter != null && parameter.required()) {
++                        throw new 
IllegalStateException(this.getClass().getSimpleName() + "." + key + " == null");
++                    }*/
++                } else if ("getParameters".equals(name)
++                        && Modifier.isPublic(method.getModifiers())
++                        && method.getParameterTypes().length == 0
++                        && method.getReturnType() == Map.class) {
++                    Map<String, String> map = (Map<String, String>) 
method.invoke(this, new Object[0]);
++                    if (map != null && map.size() > 0) {
++//                            String pre = (prefix != null && prefix.length() 
> 0 ? prefix + "." : "");
++                        for (Map.Entry<String, String> entry : 
map.entrySet()) {
++                            metaData.put(entry.getKey().replace('-', '.'), 
entry.getValue());
++                        }
++                    }
++                }
++            } catch (Exception e) {
++                throw new IllegalStateException(e.getMessage(), e);
++            }
++        }
++        return metaData;
++    }
++
++    @Parameter(excluded = true)
++    public String getPrefix() {
++        return StringUtils.isNotEmpty(prefix) ? prefix : (Constants.DUBBO + 
"." + getTagName(this.getClass()));
++    }
++
++    public void setPrefix(String prefix) {
++        this.prefix = prefix;
++    }
++
++    /**
++     * TODO: Currently, only support overriding of properties explicitly 
defined in Config class, doesn't support
++     * overriding of customized parameters stored in 'parameters'.
++     */
++    public void refresh() {
++        try {
++            Environment env = Environment.getInstance();
++            env.addAppConfig(getPrefix(), getId(), getMetaData());
++            Configuration configuration = env.getConfiguration(getPrefix(), 
getId());
++
++            // loop methods, get override value and set the new value back to 
method
++            Method[] methods = getClass().getMethods();
++            for (Method method : methods) {
++                if (ClassHelper.isSetter(method)) {
++                    try {
++                        String value = 
configuration.getString(extractPropertyName(getClass(), method));
++                        // isTypeMatch() is called to avoid duplicate and 
incorrect update, for example, we have two 'setGeneric' methods in 
ReferenceConfig.
++                        if (value != null && 
ClassHelper.isTypeMatch(method.getParameterTypes()[0], value)) {
++                            method.invoke(this, 
ClassHelper.convertPrimitive(method.getParameterTypes()[0], value));
++                        }
++                    } catch (NoSuchMethodException e) {
++                        logger.info("Failed to override the property " + 
method.getName() + " in " +
++                                this.getClass().getSimpleName() +
++                                ", please make sure every property has 
getter/setter method provided.");
++                    }
++                }
++            }
++        } catch (Exception e) {
++            logger.error("Failed to override ", e);
++        }
++    }
++
 +    @Override
 +    public String toString() {
 +        try {
 +            StringBuilder buf = new StringBuilder();
 +            buf.append("<dubbo:");
 +            buf.append(getTagName(getClass()));
 +            Method[] methods = getClass().getMethods();
 +            for (Method method : methods) {
 +                try {
-                     String name = method.getName();
-                     if ((name.startsWith("get") || name.startsWith("is"))
-                             && !"get".equals(name) && !"is".equals(name)
-                             && !"getClass".equals(name) && 
!"getObject".equals(name)
-                             && Modifier.isPublic(method.getModifiers())
-                             && method.getParameterTypes().length == 0
-                             && isPrimitive(method.getReturnType())) {
-                         int i = name.startsWith("get") ? 3 : 2;
-                         String key = name.substring(i, i + 1).toLowerCase() + 
name.substring(i + 1);
++                    if (ClassHelper.isGetter(method)) {
++                        String name = method.getName();
++                        String key = calculateAttributeFromGetter(name);
 +                        Object value = method.invoke(this);
 +                        if (value != null) {
 +                            buf.append(" ");
 +                            buf.append(key);
 +                            buf.append("=\"");
 +                            buf.append(value);
 +                            buf.append("\"");
 +                        }
 +                    }
 +                } catch (Exception e) {
 +                    logger.warn(e.getMessage(), e);
 +                }
 +            }
 +            buf.append(" />");
 +            return buf.toString();
 +        } catch (Throwable t) {
 +            logger.warn(t.getMessage(), t);
 +            return super.toString();
 +        }
 +    }
 +
++    /**
++     * FIXME check @Parameter(required=true) and any conditions that need to 
match.
++     */
++    @Parameter(excluded = true)
++    public boolean isValid() {
++        return true;
++    }
++
 +}
diff --cc 
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
index 57535ac,06c9e12..22d50dd
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
@@@ -35,24 -35,20 +35,23 @@@ public class DubboShutdownHook extends 
      private static final Logger logger = 
LoggerFactory.getLogger(DubboShutdownHook.class);
  
      private static final DubboShutdownHook dubboShutdownHook = new 
DubboShutdownHook("DubboShutdownHook");
- 
-     public static DubboShutdownHook getDubboShutdownHook() {
-         return dubboShutdownHook;
-     }
- 
      /**
 +     * Has it already been registered or not?
 +     */
 +    private final AtomicBoolean registered = new AtomicBoolean(false);
 +    /**
       * Has it already been destroyed or not?
       */
 -    private final AtomicBoolean destroyed;
 +    private final AtomicBoolean destroyed= new AtomicBoolean(false);
  
      private DubboShutdownHook(String name) {
          super(name);
 -        this.destroyed = new AtomicBoolean(false);
      }
  
+     public static DubboShutdownHook getDubboShutdownHook() {
+         return dubboShutdownHook;
+     }
+ 
      @Override
      public void run() {
          if (logger.isInfoEnabled()) {
diff --cc 
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index 7ec277d,2389b33..5144fc2
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
@@@ -490,46 -552,10 +513,52 @@@ public class ReferenceConfig<T> extend
          return buf.toString();
      }
  
+     @Override
+     @Parameter(excluded = true)
+     public String getPrefix() {
+         return Constants.DUBBO + ".reference." + interfaceName;
+     }
+ 
 +    private void resolveFile() {
 +        String resolve = System.getProperty(interfaceName);
 +        String resolveFile = null;
 +        if (resolve == null || resolve.length() == 0) {
 +            resolveFile = System.getProperty("dubbo.resolve.file");
 +            if (resolveFile == null || resolveFile.length() == 0) {
 +                File userResolveFile = new File(new 
File(System.getProperty("user.home")), "dubbo-resolve.properties");
 +                if (userResolveFile.exists()) {
 +                    resolveFile = userResolveFile.getAbsolutePath();
 +                }
 +            }
 +            if (resolveFile != null && resolveFile.length() > 0) {
 +                Properties properties = new Properties();
 +                FileInputStream fis = null;
 +                try {
 +                    fis = new FileInputStream(new File(resolveFile));
 +                    properties.load(fis);
 +                } catch (IOException e) {
 +                    throw new IllegalStateException("Unload " + resolveFile + 
", cause: " + e.getMessage(), e);
 +                } finally {
 +                    try {
 +                        if (null != fis) {
 +                            fis.close();
 +                        }
 +                    } catch (IOException e) {
 +                        logger.warn(e.getMessage(), e);
 +                    }
 +                }
 +                resolve = properties.getProperty(interfaceName);
 +            }
 +        }
 +        if (resolve != null && resolve.length() > 0) {
 +            url = resolve;
 +            if (logger.isWarnEnabled()) {
 +                if (resolveFile != null) {
 +                    logger.warn("Using default dubbo resolve file " + 
resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke 
remote service.");
 +                } else {
 +                    logger.warn("Using -D" + interfaceName + "=" + resolve + 
" to p2p invoke remote service.");
 +                }
 +            }
 +        }
 +    }
  }
diff --cc 
dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index 37539eb,fc5d251..1d20a57
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@@ -301,12 -285,46 +286,41 @@@ public class ServiceConfig<T> extends A
                  throw new IllegalStateException("The stub implementation 
class " + stubClass.getName() + " not implement interface " + interfaceName);
              }
          }
-         checkApplication();
-         checkRegistry();
-         checkProtocol();
-         appendProperties(this);
          checkStub(interfaceClass);
          checkMock(interfaceClass);
+     }
+ 
+     public synchronized void export() {
+         checkAndUpdateSubConfigs();
+ 
+         if (provider != null) {
+             if (export == null) {
+                 export = provider.getExport();
+             }
+             if (delay == null) {
+                 delay = provider.getDelay();
+             }
+         }
+         if (export != null && !export) {
+             return;
+         }
+ 
+         if (delay != null && delay > 0) {
 -            delayExportExecutor.schedule(new Runnable() {
 -                @Override
 -                public void run() {
 -                    doExport();
 -                }
 -            }, delay, TimeUnit.MILLISECONDS);
++            delayExportExecutor.schedule(this::doExport, delay, 
TimeUnit.MILLISECONDS);
+         } else {
+             doExport();
+         }
+     }
+ 
+     protected synchronized void doExport() {
+         if (unexported) {
+             throw new IllegalStateException("Already unexported!");
+         }
+         if (exported) {
+             return;
+         }
+         exported = true;
+ 
          if (path == null || path.length() == 0) {
              path = interfaceName;
          }
diff --cc dubbo-config/dubbo-config-api/src/test/resources/log4j.xml
index 5007dd1,8607f5d..bfc37a8
--- a/dubbo-config/dubbo-config-api/src/test/resources/log4j.xml
+++ b/dubbo-config/dubbo-config-api/src/test/resources/log4j.xml
@@@ -1,28 -1,28 +1,28 @@@
- <!--
-   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.
-   -->
- <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
- <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"; 
debug="false">
-     <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
-         <layout class="org.apache.log4j.PatternLayout">
-             <param name="ConversionPattern" value="[%d{dd/MM/yy HH:mm:ss:SSS 
z}] %t %5p %c{2}: %m%n"/>
-         </layout>
-     </appender>
-     <root>
-         <level value="INFO"/>
-         <appender-ref ref="CONSOLE"/>
-     </root>
+ <!--
+   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.
+   -->
+ <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+ <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"; 
debug="false">
+     <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+         <layout class="org.apache.log4j.PatternLayout">
 -            <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss 
z}] %t %5p %c{2}: %m%n"/>
++            <param name="ConversionPattern" value="[%d{dd/MM/yy HH:mm:ss:SSS 
z}] %t %5p %c{2}: %m%n"/>
+         </layout>
+     </appender>
+     <root>
+         <level value="INFO"/>
+         <appender-ref ref="CONSOLE"/>
+     </root>
  </log4j:configuration>
diff --cc 
dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/ProviderConsumerRegTable.java
index 5149760,3f3f018..620c534
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/ProviderConsumerRegTable.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/ProviderConsumerRegTable.java
@@@ -23,10 -22,10 +22,12 @@@ import org.apache.dubbo.registry.integr
  import org.apache.dubbo.rpc.Invoker;
  
  import java.util.Collections;
 +import java.util.Map;
 +import java.util.Objects;
+ import java.util.HashSet;
  import java.util.Set;
  import java.util.concurrent.ConcurrentHashMap;
+ import java.util.concurrent.ConcurrentMap;
  
  /**
   * @date 2017/11/23
diff --cc 
dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java
index 8c8b8ea,3a65eb9..b271773
--- 
a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java
+++ 
b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@@ -29,12 -29,10 +29,10 @@@ import org.apache.dubbo.rpc.Invoker
  import org.apache.dubbo.rpc.Protocol;
  import org.apache.dubbo.rpc.RpcException;
  import org.apache.dubbo.rpc.RpcInvocation;
- import org.apache.dubbo.rpc.cluster.Router;
  import org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
  import org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
- import org.apache.dubbo.rpc.cluster.router.script.ScriptRouter;
  import org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory;
 -
 +import org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
diff --cc 
dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
index 26669ce,7c63bc7..cfad21c
--- 
a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
+++ 
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
@@@ -73,11 -75,11 +75,19 @@@ public class ApplicationModel 
          }
      }
  
+     public static String getApplication() {
+         return application;
+     }
+ 
+     public static void setApplication(String application) {
+         ApplicationModel.application = application;
+     }
++
 +    /**
 +     * For unit test
 +     */
 +    public static void reset() {
 +        providedServices.clear();
 +        consumedServices.clear();
 +    }
  }
diff --cc 
dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
index 14ab124,4addbf3..08444f7
--- 
a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
+++ 
b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
@@@ -97,7 -92,7 +97,6 @@@ public class DubboInvokerAvilableTest 
  
          try{
              System.setProperty(Constants.SHUTDOWN_WAIT_KEY, "2000");
-             
System.out.println("------------ConfigUtils.getServerShutdownTimeout(): " + 
ConfigUtils.getServerShutdownTimeout());
 -            
System.out.println("------------ConfigUtils.getServerShutdownTimeout(): " + 
ConfigurationUtils.getServerShutdownTimeout());
              protocol.destroy();
          }finally {
              System.getProperties().remove(Constants.SHUTDOWN_WAIT_KEY);

Reply via email to