You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2021/03/24 07:25:01 UTC

[dubbo] branch 3.0 updated (631aedc -> 07d5286)

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

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


    from 631aedc  Set callback decode (#7429)
     new 489f488  Brand new routing rule (#7372)
     new d541548  3.0 enhancement, do not merge (#7381)
     new 552112e  Revert "3.0 enhancement, do not merge (#7381)" (#7387)
     new 70a9245  [3.0] fix service discovery implementation and introduce ClusterFiIlter (#7388)
     new 954e20f  Refactor dubbo reference processing (#7383)
     new a812a7a  [3.0-Actions] Add rat result upload & Fix rat in Ubuntu (#7394)
     new 9b0e30c  fix apache rat plugin configuration (#7396)
     new a6337a6  create reference proxy after export service in DubboBootstrap (#7402)
     new 3a3ef2c  Fix #7311, try put rpc contex attachments into invocation for Filters may have put new values. (#7399)
     new 68fdea8  Improve reference bean definition and prepare logic, fix injection problems of PropertyPlaceholderConfigurer (#7405)
     new c3e432c  fix reference cache (#7412)
     new 07d5286  bump version 3.0.0.preview

The 12 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/workflows/build-and-test-3.yml             |   17 +-
 dubbo-build-tools/pom.xml                          |   50 +
 dubbo-cluster/pom.xml                              |    4 +-
 .../org/apache/dubbo/registry/AddressListener.java |    0
 .../org/apache/dubbo/rpc/cluster/Directory.java    |    4 +
 .../dubbo/rpc/cluster/filter/ClusterFilter.java    |   21 +-
 .../cluster/filter/DefaultFilterChainBuilder.java  |   10 +-
 .../rpc/cluster/filter/FilterChainBuilder.java     |   36 +-
 .../rpc/cluster/filter/ProtocolFilterWrapper.java  |    9 +-
 .../filter/support}/ConsumerContextFilter.java     |   26 +-
 .../support/ZoneAwareFilter.java}                  |   19 +-
 .../cluster/interceptor/ClusterInterceptor.java    |    1 +
 .../ConsumerContextClusterInterceptor.java         |   60 -
 .../router/mesh/route/MeshAppRuleListener.java     |   98 ++
 .../route/MeshRuleAddressListenerInterceptor.java  |   51 +
 .../cluster/router/mesh/route/MeshRuleManager.java |   79 ++
 .../cluster/router/mesh/route/MeshRuleRouter.java  |  359 +++++
 .../router/mesh/route/MeshRuleRouterFactory.java   |   29 +-
 .../rpc/cluster/router/mesh/rule/BaseRule.java     |   60 +
 .../router/mesh/rule/VsDestinationGroup.java       |   55 +
 .../rule/destination/ConnectionPoolSettings.java   |   21 +-
 .../mesh/rule/destination/DestinationRule.java     |   36 +-
 .../mesh/rule/destination/DestinationRuleSpec.java |   60 +
 .../router/mesh/rule/destination/Subset.java       |   45 +-
 .../router/mesh/rule/destination/TCPSettings.java  |   24 +-
 .../router/mesh/rule/destination/TcpKeepalive.java |   23 +-
 .../mesh/rule/destination/TrafficPolicy.java       |   35 +-
 .../destination/loadbalance/ConsistentHashLB.java  |   21 +-
 .../loadbalance/LoadBalancerSettings.java          |   43 +-
 .../rule/destination/loadbalance/SimpleLB.java     |   25 +-
 .../rule/virtualservice/DubboMatchRequest.java     |  130 ++
 .../mesh/rule/virtualservice/DubboRoute.java       |   62 +
 .../mesh/rule/virtualservice/DubboRouteDetail.java |   62 +
 .../rule/virtualservice/VirtualServiceRule.java    |   36 +-
 .../rule/virtualservice/VirtualServiceSpec.java    |   43 +-
 .../destination/DubboDestination.java              |   59 +
 .../destination/DubboRouteDestination.java         |   35 +-
 .../mesh/rule/virtualservice/match/BoolMatch.java  |   33 +-
 .../rule/virtualservice/match/DoubleMatch.java     |   63 +
 .../virtualservice/match/DoubleRangeMatch.java     |   53 +
 .../virtualservice/match/DubboAttachmentMatch.java |   76 ++
 .../rule/virtualservice/match/DubboMethodArg.java  |   90 ++
 .../virtualservice/match/DubboMethodMatch.java     |  128 ++
 .../rule/virtualservice/match/ListBoolMatch.java   |   21 +-
 .../rule/virtualservice/match/ListDoubleMatch.java |   36 +-
 .../rule/virtualservice/match/ListStringMatch.java |   37 +-
 .../rule/virtualservice/match/StringMatch.java     |  105 ++
 .../util/VsDestinationGroupRuleDispatcher.java     |   53 +
 .../mesh/util/VsDestinationGroupRuleListener.java  |   22 +-
 .../cluster/support/AbstractClusterInvoker.java    |   10 +-
 .../cluster/support/wrapper/AbstractCluster.java   |  190 +--
 .../org.apache.dubbo.registry.AddressListener      |    1 +
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |    1 +
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |    2 +
 ...ubbo.rpc.cluster.interceptor.ClusterInterceptor |    2 -
 .../router/mesh/route/MeshAppRuleListenerTest.java |  180 +++
 .../router/mesh/route/MeshRuleManagerTest.java     |  160 +++
 .../mesh/route/MeshRuleRouterFactoryTest.java      |   27 +-
 .../router/mesh/route/MeshRuleRouterTest.java      | 1407 ++++++++++++++++++++
 .../router/mesh/rule/DestinationRuleTest.java      |  102 ++
 .../router/mesh/rule/VirtualServiceRuleTest.java   |   31 +-
 .../rule/virtualservice/DubboMatchRequestTest.java |  140 ++
 .../rule/virtualservice/match/BoolMatchTest.java   |   35 +-
 .../rule/virtualservice/match/DoubleMatchTest.java |  101 ++
 .../match/DubboAttachmentMatchTest.java            |  178 +++
 .../virtualservice/match/DubboMethodMatchTest.java |  156 +++
 .../virtualservice/match/ListDoubleMatchTest.java  |   52 +
 .../virtualservice/match/ListStringMatchTest.java  |   54 +
 .../rule/virtualservice/match/StringMatchTest.java |   81 ++
 .../util/VsDestinationGroupRuleDispatcherTest.java |   74 +
 .../support/AbstractClusterInvokerTest.java        |    4 +-
 .../src/test/resources/DestinationRuleTest.yaml    |   33 +
 .../src/test/resources/DestinationRuleTest2.yaml   |   58 +
 .../src/test/resources/VirtualServiceTest.yaml     |   41 +
 dubbo-common/pom.xml                               |    4 +
 .../java/org/apache/dubbo/common/URLStrParser.java |    9 +-
 .../apache/dubbo/common/config/Environment.java    |   20 +
 .../dubbo/common/constants/CommonConstants.java    |    6 +
 .../dubbo/common/url/component/URLAddress.java     |    2 +-
 .../dubbo/common/url/component/URLItemCache.java   |   44 +-
 .../dubbo/common/url/component/URLParam.java       |    4 +-
 .../org/apache/dubbo/common/utils/ConfigUtils.java |   47 +
 .../org/apache/dubbo/config/AbstractConfig.java    |   13 +-
 .../java/org/apache/dubbo/config/Constants.java    |    2 +
 .../apache/dubbo/config/ReferenceConfigBase.java   |   50 +-
 .../apache/dubbo/common/utils/NetUtilsTest.java    |    2 +-
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   11 +-
 .../ServiceInstanceHostPortCustomizer.java         |    5 +-
 .../apache/dubbo/config/AbstractConfigTest.java    |    4 +-
 .../DubboConfigInitializationPostProcessor.java    |  122 ++
 .../apache/dubbo/config/spring/ReferenceBean.java  |  226 +++-
 .../dubbo/config/spring/ReferenceBeanManager.java  |  235 ++++
 .../AbstractAnnotationBeanPostProcessor.java       |  489 +++++++
 .../AbstractAnnotationConfigBeanBuilder.java       |  215 ---
 .../AnnotatedInterfaceConfigBeanBuilder.java       |  215 ---
 .../AnnotationPropertyValuesAdapter.java           |    2 +-
 .../ReferenceAnnotationBeanPostProcessor.java      |  364 +++--
 .../factory/annotation/ReferenceBeanBuilder.java   |  241 ++--
 .../annotation/ServiceClassPostProcessor.java      |   12 +-
 .../spring/schema/DubboBeanDefinitionParser.java   |  246 ++--
 .../dubbo/config/spring/util/DubboBeanUtils.java   |  103 +-
 .../org/apache/dubbo/config/spring/ConfigTest.java |   96 +-
 .../AnnotationPropertyValuesAdapterTest.java       |   12 +-
 .../ReferenceAnnotationBeanPostProcessorTest.java  |  107 +-
 .../annotation/ReferenceBeanBuilderTest.java       |   15 +-
 .../DubboComponentScanRegistrarTest.java           |    6 +-
 .../annotation/DubboConfigConfigurationTest.java   |    2 +
 .../context/annotation/EnableDubboConfigTest.java  |   12 +
 .../spring/context/annotation/EnableDubboTest.java |    8 +-
 .../dubbo/config/spring/issues/Issue6252Test.java  |   12 +-
 .../spring/schema/DubboNamespaceHandlerTest.java   |   34 +-
 .../config/spring/schema/GenericServiceTest.java   |   10 +-
 .../resources/META-INF/init-reference.properties   |    5 +
 .../apache/dubbo/config/spring/demo-provider.xml   |    8 +-
 ...reference.xml => init-reference-properties.xml} |   14 +-
 .../apache/dubbo/config/spring/init-reference.xml  |   20 +-
 .../src/main/resources/dubbo-migration.yaml        |   22 +
 dubbo-dependencies-bom/pom.xml                     |    8 +-
 .../dubbo-dependencies-zookeeper/pom.xml           |    2 +-
 dubbo-distribution/dubbo-all/pom.xml               |    8 +
 .../org/apache/dubbo/metadata/MetadataInfo.java    |    4 +
 .../dubbo/metadata/definition/util/ClassUtils.java |    4 +-
 .../dubbo/monitor/support/MonitorFilter.java       |    3 +-
 .../dubbo/auth/filter/ConsumerSignFilter.java      |    2 +-
 .../org/apache/dubbo/registry/NotifyListener.java  |    4 +
 .../registry/client/DefaultServiceInstance.java    |   58 +-
 .../client/EventPublishingServiceDiscovery.java    |   15 +
 .../client/FileSystemServiceDiscovery.java         |    2 +-
 .../client/SelfHostMetaServiceDiscovery.java       |    2 +-
 .../dubbo/registry/client/ServiceDiscovery.java    |    5 +
 .../registry/client/ServiceDiscoveryRegistry.java  |    2 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |    5 +-
 .../dubbo/registry/client/ServiceInstance.java     |   13 +-
 .../listener/ServiceInstancesChangedListener.java  |  117 +-
 .../registry/client/metadata/MetadataUtils.java    |    2 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |    2 +-
 .../DefaultMigrationAddressComparator.java         |    4 +-
 .../client/migration/MigrationInvoker.java         |   89 +-
 .../client/migration/MigrationRuleHandler.java     |    4 +-
 .../client/migration/MigrationRuleListener.java    |    8 +-
 .../registry/integration/DynamicDirectory.java     |    8 +-
 .../client/DefaultServiceInstanceTest.java         |    3 +-
 .../client/FileSystemServiceDiscoveryTest.java     |    2 +
 .../registry/multicast/MulticastRegistryTest.java  |   11 +-
 .../multiple/MultipleServiceDiscovery.java         |   38 +-
 .../nacos/util/NacosNamingServiceUtils.java        |    4 +-
 .../zookeeper/ZookeeperServiceDiscovery.java       |   34 +-
 .../ZookeeperServiceDiscoveryChangeWatcher.java    |   38 +-
 .../zookeeper/util/CuratorFrameworkUtils.java      |    2 +-
 .../zookeeper/ZookeeperServiceDiscoveryTest.java   |    3 +-
 .../main/java/org/apache/dubbo/rpc/BaseFilter.java |   28 +-
 .../src/main/java/org/apache/dubbo/rpc/Filter.java |   40 +-
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |    7 +-
 .../dubbo/rpc/proxy/AbstractProxyFactory.java      |    4 +
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |    2 -
 .../rpc/protocol/dubbo/filter/FutureFilter.java    |    4 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |    3 +-
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |    1 +
 .../dubbo/rpc/protocol/dubbo/FutureFilterTest.java |    4 +-
 pom.xml                                            |  119 +-
 160 files changed, 7540 insertions(+), 1839 deletions(-)
 copy {dubbo-registry/dubbo-registry-api => dubbo-cluster}/src/main/java/org/apache/dubbo/registry/AddressListener.java (100%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java (65%)
 rename {dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support}/ConsumerContextFilter.java (83%)
 rename dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/{interceptor/ZoneAwareClusterInterceptor.java => filter/support/ZoneAwareFilter.java} (80%)
 delete mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java (64%)
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java (61%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java (58%)
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java (55%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java (61%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java (61%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java (57%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java (61%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java (51%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java (61%)
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java (58%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java (55%)
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java (61%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java (61%)
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java (61%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java (58%)
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java (58%)
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
 create mode 100644 dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java (61%)
 create mode 100644 dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener
 create mode 100644 dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
 delete mode 100644 dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java (63%)
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java (55%)
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
 copy dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java (55%)
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
 create mode 100644 dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java
 create mode 100644 dubbo-cluster/src/test/resources/DestinationRuleTest.yaml
 create mode 100644 dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml
 create mode 100644 dubbo-cluster/src/test/resources/VirtualServiceTest.yaml
 create mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
 create mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
 create mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java
 delete mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java
 delete mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java
 create mode 100644 dubbo-config/dubbo-config-spring/src/test/resources/META-INF/init-reference.properties
 copy dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/{init-reference.xml => init-reference-properties.xml} (68%)
 create mode 100644 dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
 rename dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java => dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java (62%)
 create mode 100644 dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter

[dubbo] 06/12: [3.0-Actions] Add rat result upload & Fix rat in Ubuntu (#7394)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a812a7a9bf9cbe5c154dca61e899823f16936476
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Wed Mar 17 19:59:50 2021 +0800

    [3.0-Actions] Add rat result upload & Fix rat in Ubuntu (#7394)
---
 .github/workflows/build-and-test-3.yml                | 17 +++++++++++++----
 .../src/main/resources/dubbo-migration.yaml           | 19 +++++++++++++++++++
 pom.xml                                               |  3 ++-
 3 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/build-and-test-3.yml b/.github/workflows/build-and-test-3.yml
index 57b28d8..b1e6302 100644
--- a/.github/workflows/build-and-test-3.yml
+++ b/.github/workflows/build-and-test-3.yml
@@ -45,11 +45,11 @@ jobs:
       - name: "Build Dubbo with Maven"
         run: |
           cd ./dubbo
-          ./mvnw --batch-mode -U -e --no-transfer-progress  clean install -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true
+          ./mvnw --batch-mode -U -e --no-transfer-progress clean source:jar install -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true
       - name: "Build Dubbo Spring Boot with Maven"
         run: |
           cd ./dubbo-spring-boot-project
-          ./mvnw --batch-mode --no-transfer-progress  clean install -Dmaven.test.skip=true -Dmaven.test.skip.exec=true
+          ./mvnw --batch-mode --no-transfer-progress clean source:jar install -Dmaven.test.skip=true -Dmaven.test.skip.exec=true
       - name: "Calculate Dubbo Version"
         id: dubbo-version
         run: |
@@ -82,13 +82,22 @@ jobs:
       - name: "Test with Maven with Integration Tests"
         timeout-minutes: 30
         if: ${{ startsWith( matrix.os, 'ubuntu') }}
-        run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true
+        run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true
       - name: "Test with Maven without Integration Tests"
         env:
           DISABLE_FILE_SYSTEM_TEST: true
         timeout-minutes: 30
         if: ${{ startsWith( matrix.os, 'windows') }}
-        run: ./mvnw --batch-mode -U -e --no-transfer-progress clean install -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true"
+        run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true"
+      - name: "Pack rat file if failure"
+        if: failure()
+        run: 7z a ${{ github.workspace }}/rat.zip *rat.txt -r
+      - name: "Upload rat file if failure"
+        if: failure()
+        uses: actions/upload-artifact@v2
+        with:
+          name: "rat-file-${{ matrix.os }}-JDK${{ matrix.jdk }}"
+          path: ${{ github.workspace }}/rat.zip
       - name: "Upload coverage to Codecov"
         uses: codecov/codecov-action@v1
 
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
index dc2e8f2..b33d32c 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
@@ -1,3 +1,22 @@
+#
+#
+#   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.
+#
+#
+
 key: demo-consumer
 step: FORCE_APPLICATION
 threshold: 0.1
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0fdcc22..b1a1695 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,7 +122,7 @@
         <maven_jacoco_version>0.8.2</maven_jacoco_version>
         <maven_flatten_version>1.1.0</maven_flatten_version>
         <maven_enforce_version>3.0.0-M2</maven_enforce_version>
-        <apache-rat-plugin.version>0.12</apache-rat-plugin.version>
+        <apache-rat-plugin.version>0.13</apache-rat-plugin.version>
         <arguments />
         <checkstyle.skip>true</checkstyle.skip>
         <rat.skip>true</rat.skip>
@@ -280,6 +280,7 @@
                                         **/org/apache/dubbo/common/utils/Utf8Utils.java,
                                         **/org/apache/dubbo/common/serialize/protobuf/support/wrapper/MapValue.java,
                                         **/org/apache/dubbo/common/serialize/protobuf/support/wrapper/ThrowablePB.java,
+                                        **/org/apache/dubbo/triple/TripleWrapper.java,
                                     </excludes>
                                 </configuration>
                                 <goals>

[dubbo] 04/12: [3.0] fix service discovery implementation and introduce ClusterFiIlter (#7388)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 70a92457159e1b27bb7ee367118b7e32e20eb05c
Author: ken.lj <ke...@gmail.com>
AuthorDate: Wed Mar 17 00:03:09 2021 +0800

    [3.0] fix service discovery implementation and introduce ClusterFiIlter (#7388)
---
 .../org/apache/dubbo/rpc/cluster/Directory.java    |   4 +
 .../dubbo/rpc/cluster/filter/ClusterFilter.java    |  24 +++
 .../cluster/filter/DefaultFilterChainBuilder.java  |  10 +-
 .../rpc/cluster/filter/FilterChainBuilder.java     |  36 +++-
 .../rpc/cluster/filter/ProtocolFilterWrapper.java  |   9 +-
 .../filter/support}/ConsumerContextFilter.java     |  26 ++-
 .../support/ZoneAwareFilter.java}                  |  19 ++-
 .../cluster/interceptor/ClusterInterceptor.java    |   1 +
 .../ConsumerContextClusterInterceptor.java         |  60 -------
 .../cluster/support/wrapper/AbstractCluster.java   | 190 +++++++++++----------
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   2 +
 ...ubbo.rpc.cluster.interceptor.ClusterInterceptor |   2 -
 dubbo-common/pom.xml                               |   4 +
 .../java/org/apache/dubbo/common/URLStrParser.java |   9 +-
 .../apache/dubbo/common/config/Environment.java    |  20 +++
 .../dubbo/common/constants/CommonConstants.java    |   6 +
 .../dubbo/common/url/component/URLAddress.java     |   2 +-
 .../dubbo/common/url/component/URLItemCache.java   |  44 +++--
 .../dubbo/common/url/component/URLParam.java       |   4 +-
 .../org/apache/dubbo/common/utils/ConfigUtils.java |  47 +++++
 .../org/apache/dubbo/config/AbstractConfig.java    |   3 +-
 .../java/org/apache/dubbo/config/Constants.java    |   2 +
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   3 +-
 .../ServiceInstanceHostPortCustomizer.java         |   5 +-
 .../apache/dubbo/config/AbstractConfigTest.java    |   4 +-
 .../src/main/resources/dubbo-migration.yaml        |   3 +
 dubbo-dependencies-bom/pom.xml                     |   6 +
 dubbo-distribution/dubbo-all/pom.xml               |   8 +
 .../org/apache/dubbo/metadata/MetadataInfo.java    |   4 +
 .../dubbo/monitor/support/MonitorFilter.java       |   3 +-
 .../dubbo/auth/filter/ConsumerSignFilter.java      |   2 +-
 .../org/apache/dubbo/registry/NotifyListener.java  |   4 +
 .../registry/client/DefaultServiceInstance.java    |  58 +++----
 .../client/EventPublishingServiceDiscovery.java    |  15 ++
 .../client/FileSystemServiceDiscovery.java         |   2 +-
 .../client/SelfHostMetaServiceDiscovery.java       |   2 +-
 .../dubbo/registry/client/ServiceDiscovery.java    |   5 +
 .../registry/client/ServiceDiscoveryRegistry.java  |   2 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |   5 +-
 .../dubbo/registry/client/ServiceInstance.java     |  13 +-
 .../listener/ServiceInstancesChangedListener.java  | 117 +++++++------
 .../registry/client/metadata/MetadataUtils.java    |   2 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |   2 +-
 .../DefaultMigrationAddressComparator.java         |   4 +-
 .../client/migration/MigrationInvoker.java         |  89 +++++-----
 .../client/migration/MigrationRuleHandler.java     |   4 +-
 .../client/migration/MigrationRuleListener.java    |   8 +-
 .../registry/integration/DynamicDirectory.java     |   8 +-
 .../client/DefaultServiceInstanceTest.java         |   3 +-
 .../client/FileSystemServiceDiscoveryTest.java     |   2 +
 .../multiple/MultipleServiceDiscovery.java         |  38 +++--
 .../nacos/util/NacosNamingServiceUtils.java        |   4 +-
 .../zookeeper/ZookeeperServiceDiscovery.java       |  34 ++--
 .../ZookeeperServiceDiscoveryChangeWatcher.java    |  38 ++++-
 .../zookeeper/util/CuratorFrameworkUtils.java      |   2 +-
 .../zookeeper/ZookeeperServiceDiscoveryTest.java   |   3 +-
 .../main/java/org/apache/dubbo/rpc/BaseFilter.java |  31 ++++
 .../src/main/java/org/apache/dubbo/rpc/Filter.java |  40 +++--
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |   2 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   2 -
 .../rpc/protocol/dubbo/filter/FutureFilter.java    |   4 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   3 +-
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   1 +
 .../dubbo/rpc/protocol/dubbo/FutureFilterTest.java |   4 +-
 64 files changed, 688 insertions(+), 425 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
index 9fd3ca2..5a92d97 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
@@ -65,4 +65,8 @@ public interface Directory<T> extends Node {
     void discordAddresses();
 
     RouterChain<T> getRouterChain();
+
+    default boolean isNotificationReceived() {
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java
new file mode 100644
index 0000000..7d48dc9
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.filter;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.BaseFilter;
+
+@SPI
+public interface ClusterFilter extends BaseFilter {
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
index 982008b..e2e26d2 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
@@ -27,6 +27,9 @@ import java.util.List;
 @Activate(order = 0)
 public class DefaultFilterChainBuilder implements FilterChainBuilder {
 
+    /**
+     * build consumer/provider filter chain
+     */
     @Override
     public <T> Invoker<T> buildInvokerChain(final Invoker<T> originalInvoker, String key, String group) {
         Invoker<T> last = originalInvoker;
@@ -43,14 +46,17 @@ public class DefaultFilterChainBuilder implements FilterChainBuilder {
         return last;
     }
 
+    /**
+     * build consumer cluster filter chain
+     */
     @Override
     public <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> originalInvoker, String key, String group) {
         ClusterInvoker<T> last = originalInvoker;
-        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
+        List<ClusterFilter> filters = ExtensionLoader.getExtensionLoader(ClusterFilter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
 
         if (!filters.isEmpty()) {
             for (int i = filters.size() - 1; i >= 0; i--) {
-                final Filter filter = filters.get(i);
+                final ClusterFilter filter = filters.get(i);
                 final Invoker<T> next = last;
                 last = new ClusterFilterChainNode<>(originalInvoker, next, filter);
             }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
index 20275e2..949a66c 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.filter;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.BaseFilter;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -29,16 +30,27 @@ import org.apache.dubbo.rpc.cluster.Directory;
 
 @SPI("default")
 public interface FilterChainBuilder {
+    /**
+     * build consumer/provider filter chain
+     */
     <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group);
 
+    /**
+     * build consumer cluster filter chain
+     */
     <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> invoker, String key, String group);
 
-    class FilterChainNode<T, TYPE extends Invoker<T>> implements Invoker<T>{
+    /**
+     * Works on provider side
+     * @param <T>
+     * @param <TYPE>
+     */
+    class FilterChainNode<T, TYPE extends Invoker<T>, FILTER extends BaseFilter> implements Invoker<T>{
         TYPE originalInvoker;
         Invoker<T> nextNode;
-        Filter filter;
+        FILTER filter;
 
-        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
+        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, FILTER filter) {
             this.originalInvoker = originalInvoker;
             this.nextNode = nextNode;
             this.filter = filter;
@@ -79,8 +91,8 @@ public interface FilterChainBuilder {
                     } finally {
                         listenableFilter.removeListener(invocation);
                     }
-                } else if (filter instanceof Filter.Listener) {
-                    Filter.Listener listener = (Filter.Listener) filter;
+                } else if (filter instanceof FILTER.Listener) {
+                    FILTER.Listener listener = (FILTER.Listener) filter;
                     listener.onError(e, originalInvoker, invocation);
                 }
                 throw e;
@@ -102,8 +114,8 @@ public interface FilterChainBuilder {
                     } finally {
                         listenableFilter.removeListener(invocation);
                     }
-                } else if (filter instanceof Filter.Listener) {
-                    Filter.Listener listener = (Filter.Listener) filter;
+                } else if (filter instanceof FILTER.Listener) {
+                    FILTER.Listener listener = (FILTER.Listener) filter;
                     if (t == null) {
                         listener.onResponse(r, originalInvoker, invocation);
                     } else {
@@ -124,8 +136,14 @@ public interface FilterChainBuilder {
         }
     }
 
-    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>> extends FilterChainNode<T, TYPE> implements ClusterInvoker<T> {
-        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
+    /**
+     * Works on consumer side
+     * @param <T>
+     * @param <TYPE>
+     */
+    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>, FILTER extends BaseFilter>
+            extends FilterChainNode<T, TYPE, FILTER> implements ClusterInvoker<T> {
+        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, FILTER filter) {
             super(originalInvoker, nextNode, filter);
         }
 
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
index 65e8813..389b173 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
@@ -28,10 +28,9 @@ import org.apache.dubbo.rpc.ProtocolServer;
 import org.apache.dubbo.rpc.RpcException;
 
 import java.util.List;
-import java.util.Objects;
 
+import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY;
 
 /**
  * ListenerProtocol
@@ -68,11 +67,7 @@ public class ProtocolFilterWrapper implements Protocol {
         if (UrlUtils.isRegistry(url)) {
             return protocol.refer(type, url);
         }
-        // if it's peer-to-peer url
-        if (!Objects.isNull(url.getAttribute(PEER_KEY))) {
-            return builder.buildInvokerChain(protocol.refer(type, url), SERVICE_FILTER_KEY, CommonConstants.CONSUMER);
-        }
-        return protocol.refer(type, url);
+        return builder.buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
     }
 
     @Override
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
similarity index 83%
rename from dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
index 0fead6b..4a1ed3b 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.filter;
+package org.apache.dubbo.rpc.cluster.filter.support;
 
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -28,6 +28,7 @@ import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.TimeoutCountDown;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 
 import java.util.Map;
 
@@ -39,11 +40,11 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIME_COUNTDOWN_K
  * ConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port
  * for consumer invoker.It does it to make the requires info available to execution thread's RpcContext.
  *
- * @see org.apache.dubbo.rpc.Filter
+ * @see Filter
  * @see RpcContext
  */
 @Activate(group = CONSUMER, order = -10000)
-public class ConsumerContextFilter implements Filter {
+public class ConsumerContextFilter implements ClusterFilter, ClusterFilter.Listener {
 
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@@ -76,7 +77,24 @@ public class ConsumerContextFilter implements Filter {
                                 + invocation.getMethodName() + ", terminate directly."), invocation);
             }
         }
-        return invoker.invoke(invocation);
+
+        try {
+            RpcContext.removeServerContext();
+            return invoker.invoke(invocation);
+        } finally {
+            RpcContext.removeContext();
+        }
+    }
+
+    @Override
+    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
+        // pass attachments to result
+        RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
+    }
+
+    @Override
+    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
+
     }
 
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
similarity index 80%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
index 6daec08..cd0a7ab 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
@@ -14,15 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.interceptor;
+package org.apache.dubbo.rpc.cluster.filter.support;
 
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.ZoneDetector;
-import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_FORCE;
@@ -32,11 +36,11 @@ import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_
  *
  * active only when url has key 'cluster=zone-aware'
  */
-@Activate(value = "cluster:zone-aware")
-public class ZoneAwareClusterInterceptor implements ClusterInterceptor {
+@Activate(group = CommonConstants.CONSUMER, value = "cluster:zone-aware")
+public class ZoneAwareFilter implements ClusterFilter {
 
     @Override
-    public void before(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         RpcContext rpcContext = RpcContext.getContext();
         String zone = (String) rpcContext.getAttachment(REGISTRY_ZONE);
         String force = (String) rpcContext.getAttachment(REGISTRY_ZONE_FORCE);
@@ -53,10 +57,7 @@ public class ZoneAwareClusterInterceptor implements ClusterInterceptor {
         if (StringUtils.isNotEmpty(force)) {
             invocation.setAttachment(REGISTRY_ZONE_FORCE, force);
         }
-    }
-
-    @Override
-    public void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
 
+        return invoker.invoke(invocation);
     }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
index 199361f..821dd2e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 /**
  * Different from {@link Filter}, ClusterInterceptor works at the outmost layer, before one specific address/invoker is picked.
  */
+@Deprecated
 @SPI
 public interface ClusterInterceptor {
 
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
deleted file mode 100644
index 053bc87..0000000
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.rpc.cluster.interceptor;
-
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.utils.NetUtils;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
-
-@Activate
-public class ConsumerContextClusterInterceptor implements ClusterInterceptor, ClusterInterceptor.Listener {
-
-    @Override
-    public void before(AbstractClusterInvoker<?> invoker, Invocation invocation) {
-        RpcContext context = RpcContext.getContext();
-        context.setInvocation(invocation).setLocalAddress(NetUtils.getLocalHost(), 0);
-        if (invocation instanceof RpcInvocation) {
-            ((RpcInvocation) invocation).setInvoker(invoker);
-        }
-        RpcContext.removeServerContext();
-    }
-
-    @Override
-    public void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
-        RpcContext.removeContext(true);
-    }
-
-    @Override
-    public Result intercept(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) throws RpcException {
-        return clusterInvoker.invoke(invocation);
-    }
-
-    @Override
-    public void onMessage(Result appResponse, AbstractClusterInvoker<?> invoker, Invocation invocation) {
-        RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
-    }
-
-    @Override
-    public void onError(Throwable t, AbstractClusterInvoker<?> invoker, Invocation invocation) {
-
-    }
-}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
index 9ce920d..1c2d047 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.rpc.cluster.support.wrapper;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -36,6 +37,7 @@ import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
 import java.util.List;
 
+import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_INTERCEPTOR_COMPATIBLE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.INVOCATION_INTERCEPTOR_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_INTERCEPTOR_KEY;
@@ -44,15 +46,10 @@ public abstract class AbstractCluster implements Cluster {
 
     private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
 //        AbstractClusterInvoker<T> last = clusterInvoker;
-        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new FilterInvoker<>(clusterInvoker));
-        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
+        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new ClusterFilterInvoker<>(clusterInvoker));
 
-        if (!interceptors.isEmpty()) {
-            for (int i = interceptors.size() - 1; i >= 0; i--) {
-                final ClusterInterceptor interceptor = interceptors.get(i);
-                final AbstractClusterInvoker<T> next = last;
-                last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
-            }
+        if (Boolean.parseBoolean(ConfigurationUtils.getProperty(CLUSTER_INTERCEPTOR_COMPATIBLE_KEY, "false"))) {
+            return build27xCompatibleClusterInterceptors(clusterInvoker, last);
         }
         return last;
     }
@@ -70,90 +67,15 @@ public abstract class AbstractCluster implements Cluster {
         if (CollectionUtils.isEmpty(builders)) {
             return invoker;
         }
-        return new InterceptorInvoker<>(invoker, builders);
+        return new InvocationInterceptorInvoker<>(invoker, builders);
     }
 
     protected abstract <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException;
 
-    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
-
-        private AbstractClusterInvoker<T> clusterInvoker;
-        private ClusterInterceptor interceptor;
-        private AbstractClusterInvoker<T> next;
-
-        public InterceptorInvokerNode(AbstractClusterInvoker<T> clusterInvoker,
-                                      ClusterInterceptor interceptor,
-                                      AbstractClusterInvoker<T> next) {
-            this.clusterInvoker = clusterInvoker;
-            this.interceptor = interceptor;
-            this.next = next;
-        }
-
-        @Override
-        public Class<T> getInterface() {
-            return clusterInvoker.getInterface();
-        }
-
-        @Override
-        public URL getUrl() {
-            return clusterInvoker.getUrl();
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return clusterInvoker.isAvailable();
-        }
-
-        @Override
-        public Result invoke(Invocation invocation) throws RpcException {
-            Result asyncResult;
-            try {
-                interceptor.before(next, invocation);
-                asyncResult = interceptor.intercept(next, invocation);
-            } catch (Exception e) {
-                // onError callback
-                if (interceptor instanceof ClusterInterceptor.Listener) {
-                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
-                    listener.onError(e, clusterInvoker, invocation);
-                }
-                throw e;
-            } finally {
-                interceptor.after(next, invocation);
-            }
-            return asyncResult.whenCompleteWithContext((r, t) -> {
-                // onResponse callback
-                if (interceptor instanceof ClusterInterceptor.Listener) {
-                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
-                    if (t == null) {
-                        listener.onMessage(r, clusterInvoker, invocation);
-                    } else {
-                        listener.onError(t, clusterInvoker, invocation);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void destroy() {
-            clusterInvoker.destroy();
-        }
-
-        @Override
-        public String toString() {
-            return clusterInvoker.toString();
-        }
-
-        @Override
-        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
-            // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
-            return null;
-        }
-    }
-
-    static class FilterInvoker<T> extends AbstractClusterInvoker<T> {
+    static class ClusterFilterInvoker<T> extends AbstractClusterInvoker<T> {
         private ClusterInvoker<T> filterInvoker;
 
-        public FilterInvoker(AbstractClusterInvoker<T> invoker) {
+        public ClusterFilterInvoker(AbstractClusterInvoker<T> invoker) {
             List<FilterChainBuilder> builders = ExtensionLoader.getExtensionLoader(FilterChainBuilder.class).getActivateExtensions();
             if (CollectionUtils.isEmpty(builders)) {
                 filterInvoker = invoker;
@@ -203,10 +125,10 @@ public abstract class AbstractCluster implements Cluster {
         }
     }
 
-    static class InterceptorInvoker<T> extends AbstractClusterInvoker<T> {
+    static class InvocationInterceptorInvoker<T> extends AbstractClusterInvoker<T> {
         private ClusterInvoker<T> interceptorInvoker;
 
-        public InterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
+        public InvocationInterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
             ClusterInvoker<T> tmpInvoker = invoker;
             for (InvocationInterceptorBuilder builder : builders) {
                 tmpInvoker = builder.buildClusterInterceptorChain(tmpInvoker, INVOCATION_INTERCEPTOR_KEY, CommonConstants.CONSUMER);
@@ -248,4 +170,96 @@ public abstract class AbstractCluster implements Cluster {
             return null;
         }
     }
+
+    @Deprecated
+    private <T> ClusterInvoker<T> build27xCompatibleClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, AbstractClusterInvoker<T> last) {
+        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
+
+        if (!interceptors.isEmpty()) {
+            for (int i = interceptors.size() - 1; i >= 0; i--) {
+                final ClusterInterceptor interceptor = interceptors.get(i);
+                final AbstractClusterInvoker<T> next = last;
+                last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
+            }
+        }
+        return last;
+    }
+
+    @Deprecated
+    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
+
+        private AbstractClusterInvoker<T> clusterInvoker;
+        private ClusterInterceptor interceptor;
+        private AbstractClusterInvoker<T> next;
+
+        public InterceptorInvokerNode(AbstractClusterInvoker<T> clusterInvoker,
+                                      ClusterInterceptor interceptor,
+                                      AbstractClusterInvoker<T> next) {
+            this.clusterInvoker = clusterInvoker;
+            this.interceptor = interceptor;
+            this.next = next;
+        }
+
+        @Override
+        public Class<T> getInterface() {
+            return clusterInvoker.getInterface();
+        }
+
+        @Override
+        public URL getUrl() {
+            return clusterInvoker.getUrl();
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return clusterInvoker.isAvailable();
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            Result asyncResult;
+            try {
+                interceptor.before(next, invocation);
+                asyncResult = interceptor.intercept(next, invocation);
+            } catch (Exception e) {
+                // onError callback
+                if (interceptor instanceof ClusterInterceptor.Listener) {
+                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
+                    listener.onError(e, clusterInvoker, invocation);
+                }
+                throw e;
+            } finally {
+                interceptor.after(next, invocation);
+            }
+            return asyncResult.whenCompleteWithContext((r, t) -> {
+                // onResponse callback
+                if (interceptor instanceof ClusterInterceptor.Listener) {
+                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
+                    if (t == null) {
+                        listener.onMessage(r, clusterInvoker, invocation);
+                    } else {
+                        listener.onError(t, clusterInvoker, invocation);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void destroy() {
+            clusterInvoker.destroy();
+        }
+
+        @Override
+        public String toString() {
+            return clusterInvoker.toString();
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+            // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
+            return null;
+        }
+    }
+
+
 }
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
new file mode 100644
index 0000000..8f70d31
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
@@ -0,0 +1,2 @@
+zone-aware=org.apache.dubbo.rpc.cluster.filter.support.ZoneAwareFilter
+consumercontext=org.apache.dubbo.rpc.cluster.filter.support.ConsumerContextFilter
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
deleted file mode 100644
index 3f3f008..0000000
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
+++ /dev/null
@@ -1,2 +0,0 @@
-context=org.apache.dubbo.rpc.cluster.interceptor.ConsumerContextClusterInterceptor
-zone-aware=org.apache.dubbo.rpc.cluster.interceptor.ZoneAwareClusterInterceptor
\ No newline at end of file
diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
index 44d9fab..b424661 100644
--- a/dubbo-common/pom.xml
+++ b/dubbo-common/pom.xml
@@ -72,6 +72,10 @@
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.collections</groupId>
+            <artifactId>eclipse-collections</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
index 619b1bb..5bd2970 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
@@ -21,8 +21,9 @@ import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
 import org.apache.dubbo.common.url.component.URLItemCache;
 
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
+
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Map;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
@@ -73,7 +74,7 @@ public final class URLStrParser {
         }
 
         TempBuf tempBuf = DECODE_TEMP_BUF.get();
-        Map<String, String> params = new HashMap<>();
+        Map<String, String> params = new UnifiedMap<>();
         int nameStart = from;
         int valueStart = -1;
         int i;
@@ -169,7 +170,7 @@ public final class URLStrParser {
         }
 
         // check cache
-        protocol = URLItemCache.checkProtocol(protocol);
+        protocol = URLItemCache.intern(protocol);
         path = URLItemCache.checkPath(path);
 
         return new ServiceConfigURL(protocol, username, password, host, port, path, parameters);
@@ -233,7 +234,7 @@ public final class URLStrParser {
         }
 
         TempBuf tempBuf = DECODE_TEMP_BUF.get();
-        Map<String, String> params = new HashMap<>();
+        Map<String, String> params = new UnifiedMap<>();
         int nameStart = from;
         int valueStart = -1;
         int i;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
index ea4b4e5..3da6cca 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
@@ -17,11 +17,13 @@
 package org.apache.dubbo.common.config;
 
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.context.FrameworkExt;
 import org.apache.dubbo.common.context.LifecycleAdapter;
 import org.apache.dubbo.common.extension.DisableInject;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ConfigUtils;
 import org.apache.dubbo.config.AbstractConfig;
 import org.apache.dubbo.config.ConfigCenterConfig;
 import org.apache.dubbo.config.context.ConfigConfigurationAdapter;
@@ -54,6 +56,7 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
     private boolean configCenterFirst = true;
 
     private DynamicConfiguration dynamicConfiguration;
+    private String localMigrationRule;
 
     public Environment() {
         this.propertiesConfiguration = new PropertiesConfiguration();
@@ -76,6 +79,19 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
 
         this.externalConfiguration.setProperties(externalConfigurationMap);
         this.appExternalConfiguration.setProperties(appExternalConfigurationMap);
+
+        loadMigrationRule();
+    }
+
+    private void loadMigrationRule() {
+        String path = System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY);
+        if (path == null || path.length() == 0) {
+            path = System.getenv(CommonConstants.DUBBO_MIGRATION_KEY);
+            if (path == null || path.length() == 0) {
+                path = CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE;
+            }
+        }
+        this.localMigrationRule = ConfigUtils.loadMigrationRule(path);
     }
 
     @DisableInject
@@ -220,6 +236,10 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
         return appExternalConfiguration;
     }
 
+    public String getLocalMigrationRule() {
+        return localMigrationRule;
+    }
+
     // For test
     public void clearExternalConfigs() {
         this.externalConfiguration.clear();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 268c313..953b11b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -45,6 +45,10 @@ public interface CommonConstants {
 
     String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties";
 
+    String DUBBO_MIGRATION_KEY = "dubbo.migration.file";
+
+    String DEFAULT_DUBBO_MIGRATION_FILE = "dubbo-migration.yaml";
+
     String ANY_VALUE = "*";
 
     /**
@@ -376,4 +380,6 @@ public interface CommonConstants {
     String CACHE_CLEAR_TASK_INTERVAL = "dubbo.application.url.cache.task.interval";
     String CACHE_CLEAR_WAITING_THRESHOLD = "dubbo.application.url.cache.clear.waiting";
 
+    String CLUSTER_INTERCEPTOR_COMPATIBLE_KEY = "dubbo.application.cluster.interceptor.compatible";
+
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
index 63d140b..a0e89f1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
@@ -252,7 +252,7 @@ public class URLAddress implements Serializable {
         }
 
         // check cache
-        protocol = URLItemCache.checkProtocol(protocol);
+        protocol = URLItemCache.intern(protocol);
         path = URLItemCache.checkPath(path);
 
         return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
index 2384493..54372c2 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
@@ -19,14 +19,13 @@ package org.apache.dubbo.common.url.component;
 import org.apache.dubbo.common.utils.LRUCache;
 
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 
 public class URLItemCache {
     // thread safe with limited size, by default 1000
     private static final Map<String, String> PARAM_KEY_CACHE = new LRUCache<>(10000);
-    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(100000);
+    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(50000);
     private static final Map<String, String> PATH_CACHE = new LRUCache<>(10000);
-    private static final Map<String, String> PROTOCOL_CACHE = new ConcurrentHashMap<>();
+    private static final Map<String, String> REVISION_CACHE = new LRUCache<>(10000);
 
     public static void putParams(Map<String, String> params, String key, String value) {
         String cachedKey = PARAM_KEY_CACHE.get(key);
@@ -43,17 +42,6 @@ public class URLItemCache {
         params.put(cachedKey, cachedValue);
     }
 
-    public static String checkProtocol(String _protocol) {
-        if (_protocol == null) {
-            return _protocol;
-        }
-        String cachedProtocol = PROTOCOL_CACHE.putIfAbsent(_protocol, _protocol);
-        if (cachedProtocol != null) {
-            return cachedProtocol;
-        }
-        return _protocol;
-    }
-
     public static String checkPath(String _path) {
         if (_path == null) {
             return _path;
@@ -64,4 +52,32 @@ public class URLItemCache {
         }
         return _path;
     }
+
+    public static String checkRevision(String _revision) {
+        if (_revision == null) {
+            return _revision;
+        }
+        String revision = REVISION_CACHE.putIfAbsent(_revision, _revision);
+        if (revision != null) {
+            return revision;
+        }
+        return _revision;
+    }
+
+    public static String intern(String _protocol) {
+        if (_protocol == null) {
+            return _protocol;
+        }
+        return _protocol.intern();
+    }
+
+    public static void putParamsIntern(Map<String, String> params, String key, String value) {
+        if (key == null || value == null) {
+            params.put(key, value);
+            return;
+        }
+        key = key.intern();
+        value = value.intern();
+        params.put(key, value);
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
index b2dca0c..cea92cb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
@@ -21,6 +21,8 @@ import org.apache.dubbo.common.URLStrParser;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
+
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashMap;
@@ -263,7 +265,7 @@ public class URLParam implements Serializable {
 
     public static URLParam parse(String rawParam) {
         String[] parts = rawParam.split("&");
-        Map<String, String> parameters = new HashMap<>((int) (parts.length/.75f) + 1);
+        Map<String, String> parameters = new UnifiedMap<>((int) (parts.length/.75f) + 1);
         for (String part : parts) {
             part = part.trim();
             if (part.length() > 0) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
index c1f4711..d0d0693 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
@@ -21,12 +21,16 @@ import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
@@ -296,6 +300,49 @@ public class ConfigUtils {
         return properties;
     }
 
+    public static String loadMigrationRule(String fileName) {
+        String rawRule = "";
+        if (checkFileNameExist(fileName)) {
+            try {
+                try (FileInputStream input = new FileInputStream(fileName)) {
+                    rawRule = readString(input);
+                }
+            } catch (Throwable e) {
+                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
+            }
+            return rawRule;
+        }
+
+        try {
+            InputStream is = ClassUtils.getClassLoader().getResourceAsStream(fileName);
+            if (is != null) {
+                rawRule = readString(is);
+            }
+        } catch (Throwable e) {
+            logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
+        }
+        return rawRule;
+    }
+
+    private static String readString(InputStream is) {
+        StringBuilder stringBuilder = new StringBuilder();
+        char[] buffer = new char[10];
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))){
+            int n;
+            while ((n = reader.read(buffer)) != -1) {
+                if (n < 10) {
+                    buffer = Arrays.copyOf(buffer, n);
+                }
+                stringBuilder.append(String.valueOf(buffer));
+                buffer = new char[10];
+            }
+        } catch (IOException e) {
+            logger.error("Read migration file error.", e);
+        }
+
+        return stringBuilder.toString();
+    }
+
     /**
      * check if the fileName can be found in filesystem
      *
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 4e7bbf4..47556c5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -36,6 +36,7 @@ import javax.annotation.PostConstruct;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -336,7 +337,7 @@ public abstract class AbstractConfig implements Serializable {
             String value = entry.getValue();
             result.put(pre + key, value);
             // For compatibility, key like "registry-type" will has a duplicate key "registry.type"
-            if (key.contains("-")) {
+            if (Arrays.binarySearch(Constants.DOT_COMPATIBLE_KEYS, key) != -1) {
                 result.put(pre + key.replace('-', '.'), value);
             }
         }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
index f8fed84..894f138 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
@@ -117,4 +117,6 @@ public interface Constants {
     String REGISTER_KEY = "register";
 
     String MULTI_SERIALIZATION_KEY = "serialize.multiple";
+
+    String[] DOT_COMPATIBLE_KEYS = new String[]{"qos-enable", "qos-port", "qos-accept-foreign-ip"};
 }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index a4116f4..cdf66dd 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -520,6 +520,7 @@ public class DubboBootstrap extends GenericEventListener {
         }
 
         ApplicationModel.initFrameworkExts();
+        
 
         startConfigCenter();
 
@@ -1162,7 +1163,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private void doRegisterServiceInstance(ServiceInstance serviceInstance) {
         // register instance only when at least one service is exported.
-        if (serviceInstance.getPort() != null && serviceInstance.getPort() != -1) {
+        if (serviceInstance.getPort() > 0) {
             publishMetadataToRemote(serviceInstance);
             logger.info("Start registering instance address to registry.");
             getServiceDiscoveries().forEach(serviceDiscovery ->
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
index 3831337..8693828 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
@@ -36,14 +36,14 @@ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomi
     @Override
     public void customize(ServiceInstance serviceInstance) {
 
-        if (serviceInstance.getPort() != null) {
+        if (serviceInstance.getPort() > 0) {
             return;
         }
 
         WritableMetadataService writableMetadataService = WritableMetadataService.getDefaultExtension();
 
         String host = null;
-        Integer port = null;
+        int port = -1;
         Set<URL> urls = writableMetadataService.getExportedServiceURLs();
         if (CollectionUtils.isNotEmpty(urls)) {
             String preferredProtocol = ApplicationModel.getApplicationConfig().getProtocol();
@@ -64,7 +64,6 @@ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomi
                 DefaultServiceInstance instance = (DefaultServiceInstance) serviceInstance;
                 instance.setHost(host);
                 instance.setPort(port);
-                instance.setId(host + ":" + port);
             }
         }
     }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
index 041cd75..04d5f72 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
@@ -116,8 +116,6 @@ public class AbstractConfigTest {
         Assertions.assertEquals("ONE,1", parameters.get("prefix.num"));
         Assertions.assertEquals("hello%2Fworld", parameters.get("prefix.naming"));
         Assertions.assertEquals("30", parameters.get("prefix.age"));
-        Assertions.assertTrue(parameters.containsKey("prefix.key-2"));
-        Assertions.assertTrue(parameters.containsKey("prefix.key.2"));
         Assertions.assertFalse(parameters.containsKey("prefix.secret"));
     }
 
@@ -807,7 +805,7 @@ public class AbstractConfigTest {
         public Map getParameters() {
             Map<String, String> map = new HashMap<String, String>();
             map.put("key.1", "one");
-            map.put("key-2", "two");
+            map.put("key.2", "two");
             return map;
         }
     }
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
new file mode 100644
index 0000000..dc2e8f2
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
@@ -0,0 +1,3 @@
+key: demo-consumer
+step: FORCE_APPLICATION
+threshold: 0.1
\ No newline at end of file
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index e6b67c1..8cc1d43 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -91,6 +91,7 @@
         <!-- Common libs -->
         <spring_version>4.3.16.RELEASE</spring_version>
         <javassist_version>3.20.0-GA</javassist_version>
+        <eclipse_collections_version>10.4.0</eclipse_collections_version>
         <netty_version>3.2.5.Final</netty_version>
         <netty4_version>4.1.56.Final</netty4_version>
         <mina_version>1.1.7</mina_version>
@@ -186,6 +187,11 @@
                 <version>${javassist_version}</version>
             </dependency>
             <dependency>
+                <groupId>org.eclipse.collections</groupId>
+                <artifactId>eclipse-collections</artifactId>
+                <version>${eclipse_collections_version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.jboss.netty</groupId>
                 <artifactId>netty</artifactId>
                 <version>${netty_version}</version>
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index 5729680..b464e41 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -312,6 +312,10 @@
             <artifactId>javassist</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.collections</groupId>
+            <artifactId>eclipse-collections</artifactId>
+        </dependency>
+        <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
         </dependency>
@@ -513,6 +517,10 @@
                                 </transformer>
                                 <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter</resource>
+                                </transformer>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                     <resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener</resource>
                                 </transformer>
                                 <transformer
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
index 024328e..4106ad2 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
@@ -42,6 +42,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPAR
 import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
 
 public class MetadataInfo implements Serializable {
+    public static final MetadataInfo EMPTY = new MetadataInfo();
+
     private String app;
     private String revision;
     private Map<String, ServiceInfo> services;
@@ -50,6 +52,8 @@ public class MetadataInfo implements Serializable {
     private transient Map<String, String> extendParams;
     private transient AtomicBoolean reported = new AtomicBoolean(false);
 
+    public MetadataInfo() {}
+
     public MetadataInfo(String app) {
         this(app, null, null);
     }
diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
index 1492e0b..47e4bc7 100644
--- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
+++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
@@ -36,7 +36,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
@@ -49,7 +48,7 @@ import static org.apache.dubbo.rpc.Constants.OUTPUT_KEY;
 /**
  * MonitorFilter. (SPI, Singleton, ThreadSafe)
  */
-@Activate(group = {PROVIDER, CONSUMER})
+@Activate(group = {PROVIDER})
 public class MonitorFilter implements Filter, Filter.Listener {
 
     private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
diff --git a/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java b/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
index cf984a5..96438c5 100644
--- a/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
+++ b/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
@@ -33,7 +33,7 @@ import org.apache.dubbo.rpc.RpcException;
  *
  * @see org.apache.dubbo.rpc.Filter
  */
-@Activate(group = CommonConstants.CONSUMER, order = -10000)
+@Activate(group = CommonConstants.CONSUMER, value = Constants.SERVICE_AUTH, order = -10000)
 public class ConsumerSignFilter implements Filter {
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
index 89e3e75..4c02cdc 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
@@ -45,4 +45,8 @@ public interface NotifyListener {
     default void addServiceListener(ServiceInstancesChangedListener instanceListener) {
     }
 
+    default URL getConsumerUrl() {
+        return null;
+    }
+
 }
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 93ba70e..1bcf284 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.client;
 import org.apache.dubbo.metadata.MetadataInfo;
 
 import com.alibaba.fastjson.JSON;
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
 
 import java.util.HashMap;
 import java.util.List;
@@ -39,24 +40,23 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private static final long serialVersionUID = 1149677083747278100L;
 
-    private String id;
-
     private String serviceName;
 
     private String host;
 
-    private Integer port;
+    private int port;
 
     private boolean enabled;
 
     private boolean healthy;
 
-    private Map<String, String> metadata = new HashMap<>();
+    private Map<String, String> metadata = new UnifiedMap<>();
 
     private transient String address;
     private transient MetadataInfo serviceMetadata;
     // used at runtime
-    private transient Map<String, String> extendParams = new HashMap<>();
+    private transient String registryCluster; // extendParams can be more flexiable, but one single property uses less space
+    private transient Map<String, String> extendParams;
     private transient List<Endpoint> endpoints;
 
     public DefaultServiceInstance() {
@@ -70,17 +70,16 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.healthy = other.healthy;
         this.metadata = other.metadata;
         this.serviceMetadata = other.serviceMetadata;
+        this.registryCluster = other.registryCluster;
         this.extendParams = other.extendParams;
         this.endpoints = other.endpoints;
         this.address = null;
-        this.id = null;
     }
 
-    public DefaultServiceInstance(String id, String serviceName, String host, Integer port) {
+    public DefaultServiceInstance(String serviceName, String host, Integer port) {
         if (port != null && port.intValue() < 1) {
             throw new IllegalArgumentException("The port must be greater than zero!");
         }
-        this.id = id;
         this.serviceName = serviceName;
         this.host = host;
         this.port = port;
@@ -88,18 +87,10 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.healthy = true;
     }
 
-    public DefaultServiceInstance(String serviceName, String host, Integer port) {
-        this(host + ":" + port, serviceName, host, port);
-    }
-
     public DefaultServiceInstance(String serviceName) {
         this.serviceName = serviceName;
     }
 
-    public void setId(String id) {
-        this.id = id;
-    }
-
     public void setServiceName(String serviceName) {
         this.serviceName = serviceName;
     }
@@ -109,11 +100,6 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
-    public String getId() {
-        return id;
-    }
-
-    @Override
     public String getServiceName() {
         return serviceName;
     }
@@ -123,12 +109,12 @@ public class DefaultServiceInstance implements ServiceInstance {
         return host;
     }
 
-    public void setPort(Integer port) {
+    public void setPort(int port) {
         this.port = port;
     }
 
     @Override
-    public Integer getPort() {
+    public int getPort() {
         return port;
     }
 
@@ -173,7 +159,19 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
+    public String getRegistryCluster() {
+        return registryCluster;
+    }
+
+    public void setRegistryCluster(String registryCluster) {
+        this.registryCluster = registryCluster;
+    }
+
+    @Override
     public Map<String, String> getExtendParams() {
+        if (extendParams == null) {
+            extendParams = new HashMap<>();
+        }
         return extendParams;
     }
 
@@ -187,16 +185,19 @@ public class DefaultServiceInstance implements ServiceInstance {
     public DefaultServiceInstance copy(Endpoint endpoint) {
         DefaultServiceInstance copyOfInstance = new DefaultServiceInstance(this);
         copyOfInstance.setPort(endpoint.getPort());
-        copyOfInstance.setId(copyOfInstance.getAddress());
         return copyOfInstance;
     }
 
     @Override
     public Map<String, String> getAllParams() {
-        Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
-        allParams.putAll(metadata);
-        allParams.putAll(extendParams);
-        return allParams;
+        if (extendParams == null) {
+            return metadata;
+        } else {
+            Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
+            allParams.putAll(metadata);
+            allParams.putAll(extendParams);
+            return allParams;
+        }
     }
 
     public void setMetadata(Map<String, String> metadata) {
@@ -249,7 +250,6 @@ public class DefaultServiceInstance implements ServiceInstance {
     @Override
     public String toString() {
         return "DefaultServiceInstance{" +
-                "id='" + id + '\'' +
                 ", serviceName='" + serviceName + '\'' +
                 ", host='" + host + '\'' +
                 ", port=" + port +
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
index ee99000..f9c8019 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
@@ -224,6 +224,21 @@ final class EventPublishingServiceDiscovery implements ServiceDiscovery {
     }
 
     @Override
+    public ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
+        return serviceDiscovery.createListener(serviceNames);
+    }
+
+    @Override
+    public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException {
+        serviceDiscovery.removeServiceInstancesChangedListener(listener);
+    }
+
+    @Override
+    public long getDelay() {
+        return serviceDiscovery.getDelay();
+    }
+
+    @Override
     public URL getUrl() {
         return serviceDiscovery.getUrl();
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
index 2a51168..1482a9d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
@@ -110,7 +110,7 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen
     }
 
     private String getServiceInstanceId(ServiceInstance serviceInstance) {
-        String id = serviceInstance.getId();
+        String id = serviceInstance.getAddress();
         if (StringUtils.isBlank(id)) {
             return serviceInstance.getHost() + "." + serviceInstance.getPort();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
index 1034420..7537090 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
@@ -207,7 +207,7 @@ public abstract class SelfHostMetaServiceDiscovery implements ServiceDiscovery {
 
     @SuppressWarnings("unchecked")
     public final void fillServiceInstance(DefaultServiceInstance serviceInstance) {
-        String hostId = serviceInstance.getId();
+        String hostId = serviceInstance.getAddress();
         if (metadataMap.containsKey(hostId)) {
             // Use cached metadata.
             // Metadata will be updated by provider callback
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
index 90ba196..aa318d4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
@@ -219,6 +219,7 @@ public interface ServiceDiscovery extends Prioritized {
 
     /**
      * unsubscribe to instances change event.
+     *
      * @param listener
      * @throws IllegalArgumentException
      */
@@ -226,6 +227,10 @@ public interface ServiceDiscovery extends Prioritized {
             throws IllegalArgumentException {
     }
 
+    default ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
+        return new ServiceInstancesChangedListener(serviceNames, this);
+    }
+
     /**
      * Dispatch the {@link ServiceInstancesChangedEvent}
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index d19c8de..31ae6f9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -291,7 +291,7 @@ public class ServiceDiscoveryRegistry implements Registry {
 
         // register ServiceInstancesChangedListener
         ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceNamesKey, k -> {
-            ServiceInstancesChangedListener serviceInstancesChangedListener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery);
+            ServiceInstancesChangedListener serviceInstancesChangedListener = serviceDiscovery.createListener(serviceNames);
             serviceInstancesChangedListener.setUrl(url);
             serviceNames.forEach(serviceName -> {
                 List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index c41e028..6232097 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -32,9 +32,10 @@ import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.cluster.RouterChain;
 
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
+
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -152,7 +153,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
      * @return invokers
      */
     private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
-        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
+        Map<String, Invoker<T>> newUrlInvokerMap = new UnifiedMap<>();
         if (urls == null || urls.isEmpty()) {
             return newUrlInvokerMap;
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index 5bb2bfe..3a55c4e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -29,13 +29,6 @@ import java.util.SortedMap;
 public interface ServiceInstance extends Serializable {
 
     /**
-     * The id of the registered service instance.
-     *
-     * @return nullable
-     */
-    String getId();
-
-    /**
      * The name of service that current instance belongs to.
      *
      * @return non-null
@@ -54,7 +47,7 @@ public interface ServiceInstance extends Serializable {
      *
      * @return the positive integer if present
      */
-    Integer getPort();
+    int getPort();
 
     String getAddress();
 
@@ -87,6 +80,10 @@ public interface ServiceInstance extends Serializable {
 
     SortedMap<String, String> getSortedMetadata();
 
+    String getRegistryCluster();
+
+    void setRegistryCluster(String registryCluster);
+
     Map<String, String> getExtendParams();
 
     Map<String, String> getAllParams();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index 22280bd..dd0a0cf 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -53,7 +53,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
@@ -67,14 +66,14 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     private static final Logger logger = LoggerFactory.getLogger(ServiceInstancesChangedListener.class);
 
-    private final Set<String> serviceNames;
-    private final ServiceDiscovery serviceDiscovery;
-    private URL url;
-    private Map<String, NotifyListener> listeners;
+    protected final Set<String> serviceNames;
+    protected final ServiceDiscovery serviceDiscovery;
+    protected URL url;
+    protected Map<String, NotifyListener> listeners;
 
     private Map<String, List<ServiceInstance>> allInstances;
 
-    private Map<String, List<URL>> serviceUrls;
+    private Map<String, Object> serviceUrls;
 
     private Map<String, MetadataInfo> revisionToMetadata;
 
@@ -112,8 +111,8 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
         Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
         Map<ServiceInfo, Set<String>> localServiceToRevisions = new HashMap<>();
-        Map<String, Map<Set<String>, List<URL>>> protocolRevisionsToUrls = new HashMap<>();
-        Map<String, List<URL>> newServiceUrls = new HashMap<>();//TODO
+        Map<String, Map<Set<String>, Object>> protocolRevisionsToUrls = new HashMap<>();
+        Map<String, Object> newServiceUrls = new HashMap<>();//TODO
         Map<String, MetadataInfo> newRevisionToMetadata = new HashMap<>();
 
         for (Map.Entry<String, List<ServiceInstance>> entry : allInstances.entrySet()) {
@@ -151,27 +150,14 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
         localServiceToRevisions.forEach((serviceInfo, revisions) -> {
             String protocol = serviceInfo.getProtocol();
-            Map<Set<String>, List<URL>> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> {
+            Map<Set<String>, Object> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> {
                 return new HashMap<>();
             });
-            List<URL> urls = revisionsToUrls.get(revisions);
+            Object urls = revisionsToUrls.get(revisions);
             if (urls != null) {
                 newServiceUrls.put(serviceInfo.getMatchKey(), urls);
             } else {
-                urls = new ArrayList<>();
-                for (String r : revisions) {
-                    for (ServiceInstance i : revisionToInstances.get(r)) {
-                        // different protocols may have ports specified in meta
-                        if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
-                            DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
-                            if (endpoint != null && !endpoint.getPort().equals(i.getPort())) {
-                                urls.add(((DefaultServiceInstance)i).copy(endpoint).toURL());
-                                break;
-                            }
-                        }
-                        urls.add(i.toURL());
-                    }
-                }
+                urls = getServiceUrlsCache(revisionToInstances, revisions, protocol);
                 revisionsToUrls.put(revisions, urls);
                 newServiceUrls.put(serviceInfo.getMatchKey(), urls);
             }
@@ -183,7 +169,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     public synchronized void addListenerAndNotify(String serviceKey, NotifyListener listener) {
         this.listeners.put(serviceKey, listener);
-        List<URL> urls = this.serviceUrls.get(serviceKey);
+        List<URL> urls = getAddresses(serviceKey);
         if (CollectionUtils.isNotEmpty(urls)) {
             listener.notify(urls);
         }
@@ -197,7 +183,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     }
 
     public List<URL> getUrls(String serviceKey) {
-        return toUrlsWithEmpty(serviceUrls.get(serviceKey));
+        return toUrlsWithEmpty(getAddresses(serviceKey));
     }
 
     /**
@@ -241,7 +227,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return serviceNames.contains(event.getServiceName());
     }
 
-    private boolean isRetryAndExpired(ServiceInstancesChangedEvent event) {
+    protected boolean isRetryAndExpired(ServiceInstancesChangedEvent event) {
         String appName = event.getServiceName();
         List<ServiceInstance> appInstances = event.getServiceInstances();
 
@@ -261,13 +247,13 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return false;
     }
 
-    private boolean hasEmptyMetadata(Map<String, MetadataInfo> revisionToMetadata) {
+    protected boolean hasEmptyMetadata(Map<String, MetadataInfo> revisionToMetadata) {
         if (revisionToMetadata == null) {
             return false;
         }
         boolean result = false;
         for (Map.Entry<String, MetadataInfo> entry : revisionToMetadata.entrySet()) {
-            if (entry.getValue() == null) {
+            if (entry.getValue() == MetadataInfo.EMPTY) {
                 result = true;
                 break;
             }
@@ -275,31 +261,31 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return result;
     }
 
-    private MetadataInfo getRemoteMetadata(ServiceInstance instance, String revision, Map<ServiceInfo, Set<String>> localServiceToRevisions, List<ServiceInstance> subInstances) {
+    protected MetadataInfo getRemoteMetadata(ServiceInstance instance, String revision, Map<ServiceInfo, Set<String>> localServiceToRevisions, List<ServiceInstance> subInstances) {
         MetadataInfo metadata = revisionToMetadata.get(revision);
-        if (metadata == null) {
-            if (failureCounter.get() < 3 || (System.currentTimeMillis() - lastFailureTime > 10000)) {
-                metadata = getMetadataInfo(instance);
-                if (metadata != null) {
-                    logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
-                    failureCounter.set(0);
-                    revisionToMetadata.putIfAbsent(revision, metadata);
-                    parseMetadata(revision, metadata, localServiceToRevisions);
-                } else {
-                    logger.error("Failed to get MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision
-                            + ", wait for retry.");
-                    lastFailureTime = System.currentTimeMillis();
-                    failureCounter.incrementAndGet();
-                }
+        if (metadata == null
+                || (metadata == MetadataInfo.EMPTY && (failureCounter.get() < 3 || (System.currentTimeMillis() - lastFailureTime > 10000)))) {
+            metadata = getMetadataInfo(instance);
+
+            if (metadata != MetadataInfo.EMPTY) {
+                logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
+                failureCounter.set(0);
+                revisionToMetadata.putIfAbsent(revision, metadata);
+                parseMetadata(revision, metadata, localServiceToRevisions);
+            } else {
+                logger.error("Failed to get MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision
+                        + ", wait for retry.");
+                lastFailureTime = System.currentTimeMillis();
+                failureCounter.incrementAndGet();
             }
-        } else if (subInstances.size() == 1) {
+        } else if (metadata != MetadataInfo.EMPTY && subInstances.size() == 1) {
             // "subInstances.size() >= 2" means metadata of this revision has been parsed, ignore
             parseMetadata(revision, metadata, localServiceToRevisions);
         }
         return metadata;
     }
 
-    private Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
+    protected Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
         Map<String, ServiceInfo> serviceInfos = metadata.getServices();
         for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
             Set<String> set = localServiceToRevisions.computeIfAbsent(entry.getValue(), k -> new TreeSet<>());
@@ -309,10 +295,12 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return localServiceToRevisions;
     }
 
-    private MetadataInfo getMetadataInfo(ServiceInstance instance) {
+    protected MetadataInfo getMetadataInfo(ServiceInstance instance) {
         String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
         // FIXME, check "REGISTRY_CLUSTER_KEY" must be set by every registry implementation.
-        instance.getExtendParams().putIfAbsent(REGISTRY_CLUSTER_KEY, RegistryClusterIdentifier.getExtension(url).consumerKey(url));
+        if (instance.getRegistryCluster() == null) {
+            instance.setRegistryCluster(RegistryClusterIdentifier.getExtension(url).consumerKey(url));
+        }
         MetadataInfo metadataInfo;
         try {
             if (logger.isDebugEnabled()) {
@@ -332,19 +320,46 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
             logger.error("Failed to load service metadata, meta type is " + metadataType, e);
             metadataInfo = null;
         }
+
+        if (metadataInfo == null) {
+            metadataInfo = MetadataInfo.EMPTY;
+        }
         return metadataInfo;
     }
 
-    private void notifyAddressChanged() {
+    protected Object getServiceUrlsCache(Map<String, List<ServiceInstance>> revisionToInstances, Set<String> revisions, String protocol) {
+        List<URL> urls;
+        urls = new ArrayList<>();
+        for (String r : revisions) {
+            for (ServiceInstance i : revisionToInstances.get(r)) {
+                // different protocols may have ports specified in meta
+                if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
+                    DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
+                    if (endpoint != null && !endpoint.getPort().equals(i.getPort())) {
+                        urls.add(((DefaultServiceInstance) i).copy(endpoint).toURL());
+                        break;
+                    }
+                }
+                urls.add(i.toURL());
+            }
+        }
+        return urls;
+    }
+
+    protected List<URL> getAddresses(String serviceProtocolKey) {
+        return (List<URL>) serviceUrls.get(serviceProtocolKey);
+    }
+
+    protected void notifyAddressChanged() {
         listeners.forEach((key, notifyListener) -> {
             //FIXME, group wildcard match
-            List<URL> urls = toUrlsWithEmpty(serviceUrls.get(key));
+            List<URL> urls = toUrlsWithEmpty(getAddresses(key));
             logger.info("Notify service " + key + " with urls " + urls.size());
             notifyListener.notify(urls);
         });
     }
 
-    private List<URL> toUrlsWithEmpty(List<URL> urls) {
+    protected List<URL> toUrlsWithEmpty(List<URL> urls) {
         if (urls == null) {
             urls = Collections.emptyList();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
index 27f66f5..42382f8 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -86,7 +86,7 @@ public class MetadataUtils {
     }
 
     public static String computeKey(ServiceInstance serviceInstance) {
-        return serviceInstance.getServiceName() + "##" + serviceInstance.getId() + "##" +
+        return serviceInstance.getServiceName() + "##" + serviceInstance.getAddress() + "##" +
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance);
     }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
index bca273b..d217e2d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
@@ -85,7 +85,7 @@ public class RemoteMetadataServiceImpl {
         SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(),
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
 
-        String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER_KEY);
+        String registryCluster = instance.getRegistryCluster();
 
         checkRemoteConfigured();
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
index 30f1ea1..a9c7b8c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
@@ -39,11 +39,11 @@ public class DefaultMigrationAddressComparator implements MigrationAddressCompar
     @Override
     public <T> boolean shouldMigrate(ClusterInvoker<T> serviceDiscoveryInvoker, ClusterInvoker<T> invoker, MigrationRule rule) {
         if (!serviceDiscoveryInvoker.hasProxyInvokers()) {
-            logger.info("No instance address available, will not migrate.");
+            logger.info("No instance address available, stop compare.");
             return false;
         }
         if (!invoker.hasProxyInvokers()) {
-            logger.info("No interface address available, will migrate.");
+            logger.info("No interface address available, stop compare.");
             return true;
         }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
index 15e2434..2e94d3f 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
@@ -27,7 +27,6 @@ import org.apache.dubbo.registry.client.migration.model.MigrationStep;
 import org.apache.dubbo.registry.integration.DynamicDirectory;
 import org.apache.dubbo.registry.integration.RegistryProtocol;
 import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
@@ -36,7 +35,6 @@ import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 
-import java.util.List;
 import java.util.Set;
 
 import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
@@ -288,6 +286,9 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
 
     private volatile boolean invokersChanged;
 
+    /**
+     * Need to know which invoker change triggered this compare.
+     */
     private synchronized void compareAddresses(ClusterInvoker<T> serviceDiscoveryInvoker, ClusterInvoker<T> invoker) {
         this.invokersChanged = true;
         if (logger.isDebugEnabled()) {
@@ -297,10 +298,10 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
         Set<MigrationAddressComparator> detectors = ExtensionLoader.getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();
         if (detectors != null && detectors.stream().allMatch(migrationDetector -> migrationDetector.shouldMigrate(serviceDiscoveryInvoker, invoker, rule))) {
             logger.info("serviceKey:" + invoker.getUrl().getServiceKey() + " switch to APP Level address");
-            discardInterfaceInvokerAddress(invoker);
+            destroyInterfaceInvoker(invoker);
         } else {
             logger.info("serviceKey:" + invoker.getUrl().getServiceKey() + " switch to Service Level address");
-            discardServiceDiscoveryInvokerAddress(serviceDiscoveryInvoker);
+            destroyServiceDiscoveryInvoker(serviceDiscoveryInvoker);
         }
     }
 
@@ -310,26 +311,28 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
         }
         if (serviceDiscoveryInvoker != null) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+            if (serviceDiscoveryInvoker.getDirectory().isNotificationReceived()) {
+                if (logger.isInfoEnabled()) {
+                    logger.info("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+                }
+                serviceDiscoveryInvoker.destroy();
             }
-            serviceDiscoveryInvoker.destroy();
         }
     }
 
-    protected synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker<T> serviceDiscoveryInvoker) {
-        if (this.invoker != null) {
-            this.currentAvailableInvoker = this.invoker;
-            updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
-        }
-        if (serviceDiscoveryInvoker != null) {
-            if (logger.isDebugEnabled()) {
-                List<Invoker<T>> invokers = serviceDiscoveryInvoker.getDirectory().getAllInvokers();
-                logger.debug("Discarding instance addresses, total size " + (invokers == null ? 0 : invokers.size()));
-            }
-//            serviceDiscoveryInvoker.getDirectory().discordAddresses();
-        }
-    }
+//    protected synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker<T> serviceDiscoveryInvoker) {
+//        if (this.invoker != null) {
+//            this.currentAvailableInvoker = this.invoker;
+//            updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
+//        }
+//        if (serviceDiscoveryInvoker != null) {
+//            if (logger.isDebugEnabled()) {
+//                List<Invoker<T>> invokers = serviceDiscoveryInvoker.getDirectory().getAllInvokers();
+//                logger.debug("Discarding instance addresses, total size " + (invokers == null ? 0 : invokers.size()));
+//            }
+////            serviceDiscoveryInvoker.getDirectory().discordAddresses();
+//        }
+//    }
 
     protected void refreshServiceDiscoveryInvoker() {
         clearListener(serviceDiscoveryInvoker);
@@ -338,8 +341,6 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
                 logger.debug("Re-subscribing instance addresses, current interface " + type.getName());
             }
             serviceDiscoveryInvoker = registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url);
-        } else {
-            ((DynamicDirectory) serviceDiscoveryInvoker.getDirectory()).markInvokersChanged();
         }
     }
 
@@ -352,8 +353,6 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             }
 
             invoker = registryProtocol.getInvoker(cluster, registry, type, url);
-        } else {
-            ((DynamicDirectory) invoker.getDirectory()).markInvokersChanged();
         }
     }
 
@@ -363,26 +362,28 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             updateConsumerModel(currentAvailableInvoker, invoker);
         }
         if (invoker != null) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName());
-            }
-            invoker.destroy();
-        }
-    }
-
-    protected synchronized void discardInterfaceInvokerAddress(ClusterInvoker<T> invoker) {
-        if (this.serviceDiscoveryInvoker != null) {
-            this.currentAvailableInvoker = this.serviceDiscoveryInvoker;
-            updateConsumerModel(currentAvailableInvoker, invoker);
-        }
-        if (invoker != null) {
-            if (logger.isDebugEnabled()) {
-                List<Invoker<T>> invokers = invoker.getDirectory().getAllInvokers();
-                logger.debug("Discarding interface addresses, total address size " + (invokers == null ? 0 : invokers.size()));
+            if (invoker.getDirectory().isNotificationReceived()) {
+                if (logger.isInfoEnabled()) {
+                    logger.info("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+                }
+                invoker.destroy();
             }
-            //invoker.getDirectory().discordAddresses();
         }
     }
+//
+//    protected synchronized void discardInterfaceInvokerAddress(ClusterInvoker<T> invoker) {
+//        if (this.serviceDiscoveryInvoker != null) {
+//            this.currentAvailableInvoker = this.serviceDiscoveryInvoker;
+//            updateConsumerModel(currentAvailableInvoker, invoker);
+//        }
+//        if (invoker != null) {
+//            if (logger.isDebugEnabled()) {
+//                List<Invoker<T>> invokers = invoker.getDirectory().getAllInvokers();
+//                logger.debug("Discarding interface addresses, total address size " + (invokers == null ? 0 : invokers.size()));
+//            }
+//            //invoker.getDirectory().discordAddresses();
+//        }
+//    }
 
     private void clearListener(ClusterInvoker<T> invoker) {
         if (invoker == null) return;
@@ -410,9 +411,9 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             if (workingInvoker != null) {
                 consumerModel.getServiceMetadata().addAttribute("currentClusterInvoker", workingInvoker);
             }
-            if (backInvoker != null) {
-                consumerModel.getServiceMetadata().addAttribute("backupClusterInvoker", backInvoker);
-            }
+//            if (backInvoker != null) {
+//                consumerModel.getServiceMetadata().addAttribute("backupClusterInvoker", backInvoker);
+//            }
         }
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
index 5972e06..b9718f4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
@@ -30,8 +30,8 @@ import java.util.Set;
 
 @Activate
 public class MigrationRuleHandler<T> {
+    public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
     private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class);
-    private static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
 
     private MigrationClusterInvoker<T> migrationInvoker;
     private MigrationStep currentStep;
@@ -119,8 +119,10 @@ public class MigrationRuleHandler<T> {
         }
 
         if (step == MigrationStep.APPLICATION_FIRST) {
+            setCurrentStepAndThreshold(step, threshold);
             migrationInvoker.refreshServiceDiscoveryInvokerOnMappingCallback(false);
         } else if (step == MigrationStep.FORCE_APPLICATION) {
+            setCurrentStepAndThreshold(step, threshold);
             migrationInvoker.refreshServiceDiscoveryInvokerOnMappingCallback(true);
         }
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
index 399a260..85c7349 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
@@ -67,20 +67,24 @@ public class MigrationRuleListener implements RegistryProtocolListener, Configur
 
     public MigrationRuleListener() {
         this.configuration = ApplicationModel.getEnvironment().getDynamicConfiguration().orElse(null);
+
+        String localRawRule = ApplicationModel.getEnvironment().getLocalMigrationRule();
+        String defaultRawRule = StringUtils.isEmpty(localRawRule) ? INIT : localRawRule;
+
         if (this.configuration != null) {
             logger.info("Listening for migration rules on dataId " + RULE_KEY + ", group " + DUBBO_SERVICEDISCOVERY_MIGRATION);
             configuration.addListener(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION, this);
 
             String rawRule = configuration.getConfig(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION);
             if (StringUtils.isEmpty(rawRule)) {
-                rawRule = INIT;
+                rawRule = defaultRawRule;
             }
             this.rawRule = rawRule;
         } else {
             if (logger.isWarnEnabled()) {
                 logger.warn("Using default configuration rule because config center is not configured!");
             }
-            rawRule = INIT;
+            rawRule = defaultRawRule;
         }
 //        process(new ConfigChangedEvent(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION, rawRule));
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
index 8b29483..c7b0daa 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -251,20 +251,18 @@ public abstract class DynamicDirectory<T> extends AbstractDirectory<T> implement
         this.invokersChangedListener = listener;
         if (invokersChangedListener != null && invokersChanged) {
             invokersChangedListener.onChange();
-            invokersChanged = false;
         }
     }
 
     protected synchronized void invokersChanged() {
         invokersChanged = true;
-        if (invokersChangedListener != null && invokersChanged) {
+        if (invokersChangedListener != null) {
             invokersChangedListener.onChange();
-            invokersChanged = false;
         }
     }
 
-    public synchronized void markInvokersChanged() {
-        this.invokersChanged = true;
+    public boolean isNotificationReceived() {
+        return invokersChanged;
     }
 
     protected abstract void destroyAllInvokers();
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
index 85b63de..e2909ce 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
@@ -19,7 +19,6 @@ package org.apache.dubbo.registry.client;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static java.lang.String.valueOf;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,7 +36,7 @@ public class DefaultServiceInstanceTest {
     public DefaultServiceInstance instance;
 
     public static DefaultServiceInstance createInstance() {
-        DefaultServiceInstance instance = new DefaultServiceInstance(valueOf(System.nanoTime()), "A", "127.0.0.1", 8080);
+        DefaultServiceInstance instance = new DefaultServiceInstance("A", "127.0.0.1", 8080);
         instance.getMetadata().put("dubbo.metadata-service.urls", "[ \"dubbo://192.168.0.102:20881/com.alibaba.cloud.dubbo.service.DubboMetadataService?anyhost=true&application=spring-cloud-alibaba-dubbo-provider&bind.ip=192.168.0.102&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=spring-cloud-alibaba-dubbo-provider&interface=com.alibaba.cloud.dubbo.service.DubboMetadataService&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedU [...]
         instance.getMetadata().put("dubbo.metadata-service.url-params", "{\"dubbo\":{\"application\":\"dubbo-provider-demo\",\"deprecated\":\"false\",\"group\":\"dubbo-provider-demo\",\"version\":\"1.0.0\",\"timestamp\":\"1564845042651\",\"dubbo\":\"2.0.2\",\"provider.host\":\"192.168.0.102\",\"provider.port\":\"20880\"}}");
         return instance;
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
index 8677381..65527df 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URLBuilder;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.dubbo.registry.client.DefaultServiceInstanceTest.createInstance;
@@ -29,6 +30,7 @@ import static org.apache.dubbo.registry.client.DefaultServiceInstanceTest.create
  *
  * @since 2.7.5
  */
+@Disabled("FileSystemServiceDiscovery implementation is not stable enough at present")
 public class FileSystemServiceDiscoveryTest {
 
     private FileSystemServiceDiscovery serviceDiscovery;
diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
index 084376b..15bf2d1 100644
--- a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
@@ -20,7 +20,6 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.utils.DefaultPage;
 import org.apache.dubbo.common.utils.Page;
-import org.apache.dubbo.event.ConditionalEventListener;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscoveryFactory;
 import org.apache.dubbo.registry.client.ServiceInstance;
@@ -33,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
 
 public class MultipleServiceDiscovery implements ServiceDiscovery {
     public static final String REGISTRY_PREFIX_KEY = "child.";
@@ -88,16 +88,23 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
 
     @Override
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
-        MultiServiceInstancesChangedListener multiListener = new MultiServiceInstancesChangedListener(listener);
+        MultiServiceInstancesChangedListener multiListener = (MultiServiceInstancesChangedListener) listener;
 
         for (String registryKey : serviceDiscoveries.keySet()) {
-            SingleServiceInstancesChangedListener singleListener = new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscoveries.get(registryKey), multiListener);
-            multiListener.putSingleListener(registryKey, singleListener);
-            serviceDiscoveries.get(registryKey).addServiceInstancesChangedListener(singleListener);
+            ServiceDiscovery serviceDiscovery = serviceDiscoveries.get(registryKey);
+            SingleServiceInstancesChangedListener singleListener = multiListener.getAndComputeIfAbsent(registryKey, k -> {
+                return new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscovery, multiListener);
+            });
+            serviceDiscovery.addServiceInstancesChangedListener(singleListener);
         }
     }
 
     @Override
+    public ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
+        return new MultiServiceInstancesChangedListener(serviceNames, this);
+    }
+
+    @Override
     public Page<ServiceInstance> getInstances(String serviceName, int offset, int pageSize, boolean healthyOnly) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
 
         List<ServiceInstance> serviceInstanceList = new ArrayList<>();
@@ -123,17 +130,12 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
         return serviceInstance;
     }
 
-    protected static class MultiServiceInstancesChangedListener implements ConditionalEventListener<ServiceInstancesChangedEvent> {
-        private final ServiceInstancesChangedListener sourceListener;
-        private final Map<String, SingleServiceInstancesChangedListener> singleListenerMap = new ConcurrentHashMap<>();
-
-        public MultiServiceInstancesChangedListener(ServiceInstancesChangedListener sourceListener) {
-            this.sourceListener = sourceListener;
-        }
+    protected static class MultiServiceInstancesChangedListener extends ServiceInstancesChangedListener {
+        private final Map<String, SingleServiceInstancesChangedListener> singleListenerMap;
 
-        @Override
-        public boolean accept(ServiceInstancesChangedEvent event) {
-            return sourceListener.getServiceNames().contains(event.getServiceName());
+        public MultiServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
+            super(serviceNames, serviceDiscovery);
+            this.singleListenerMap = new ConcurrentHashMap<>();
         }
 
         @Override
@@ -149,12 +151,16 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
                 }
             }
 
-            sourceListener.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances));
+            super.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances));
         }
 
         public void putSingleListener(String registryKey, SingleServiceInstancesChangedListener singleListener) {
             singleListenerMap.put(registryKey, singleListener);
         }
+
+        public SingleServiceInstancesChangedListener getAndComputeIfAbsent(String registryKey, Function<String, SingleServiceInstancesChangedListener> func) {
+            return singleListenerMap.computeIfAbsent(registryKey, func);
+        }
     }
 
     protected static class SingleServiceInstancesChangedListener extends ServiceInstancesChangedListener {
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
index 7542b5e..abc7467 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
@@ -57,7 +57,6 @@ public class NacosNamingServiceUtils {
      */
     public static Instance toInstance(ServiceInstance serviceInstance) {
         Instance instance = new Instance();
-        instance.setInstanceId(serviceInstance.getId());
         instance.setServiceName(serviceInstance.getServiceName());
         instance.setIp(serviceInstance.getHost());
         instance.setPort(serviceInstance.getPort());
@@ -75,8 +74,7 @@ public class NacosNamingServiceUtils {
      * @since 2.7.5
      */
     public static ServiceInstance toServiceInstance(Instance instance) {
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(instance.getInstanceId(),
-                NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort());
+        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort());
         serviceInstance.setMetadata(instance.getMetadata());
         serviceInstance.setEnabled(instance.isEnabled());
         serviceInstance.setHealthy(instance.isHealthy());
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
index dce7d20..549b803 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.Page;
 import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.rpc.RpcException;
 
@@ -40,6 +41,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
 
 import static org.apache.dubbo.common.function.ThrowableFunction.execute;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isInstanceUpdated;
@@ -199,18 +201,28 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery {
             throw new IllegalStateException("registerServiceWatcher create path=" + path + " fail.", e);
         }
 
-        CuratorWatcher watcher = watcherCaches.computeIfAbsent(path, key ->
-                new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, listener));
-        try {
-            curatorFramework.getChildren().usingWatcher(watcher).forPath(path);
-        } catch (KeeperException.NoNodeException e) {
-            // ignored
-            if (logger.isErrorEnabled()) {
-                logger.error(e.getMessage());
+        CountDownLatch latch = new CountDownLatch(1);
+        ZookeeperServiceDiscoveryChangeWatcher watcher = watcherCaches.computeIfAbsent(path, key -> {
+            ZookeeperServiceDiscoveryChangeWatcher tmpWatcher = new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, path, latch);
+            try {
+                curatorFramework.getChildren().usingWatcher(tmpWatcher).forPath(path);
+            } catch (KeeperException.NoNodeException e) {
+                // ignored
+                if (logger.isErrorEnabled()) {
+                    logger.error(e.getMessage());
+                }
+            } catch (Exception e) {
+                throw new IllegalStateException(e.getMessage(), e);
             }
-        } catch (Exception e) {
-            throw new IllegalStateException(e.getMessage(), e);
-        }
+            return tmpWatcher;
+        });
+        watcher.addListener(listener);
+        listener.onEvent(new ServiceInstancesChangedEvent(serviceName, this.getInstances(serviceName)));
+        latch.countDown();
+    }
+
+    public void reRegisterWatcher(ZookeeperServiceDiscoveryChangeWatcher watcher) throws Exception {
+        curatorFramework.getChildren().usingWatcher(watcher).forPath(watcher.getPath());
     }
 
     private String buildServicePath(String serviceName) {
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
index e34d3e4..8cffc3e 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
@@ -16,8 +16,10 @@
  */
 package org.apache.dubbo.registry.zookeeper;
 
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.registry.RegistryNotifier;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
@@ -25,6 +27,10 @@ import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
 
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
 import static org.apache.dubbo.rpc.model.ApplicationModel.getExecutorRepository;
 import static org.apache.zookeeper.Watcher.Event.EventType.NodeChildrenChanged;
 import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
@@ -37,7 +43,7 @@ import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
  * @since 2.7.5
  */
 public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
-    private ServiceInstancesChangedListener listener;
+    private Set<ServiceInstancesChangedListener> listeners = new ConcurrentHashSet<>();
 
     private final ZookeeperServiceDiscovery zookeeperServiceDiscovery;
 
@@ -47,34 +53,52 @@ public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
 
     private final String serviceName;
 
+    private final String path;
+
+    private CountDownLatch latch;
+
     public ZookeeperServiceDiscoveryChangeWatcher(ZookeeperServiceDiscovery zookeeperServiceDiscovery,
                                                   String serviceName,
-                                                  ServiceInstancesChangedListener listener) {
+                                                  String path,
+                                                  CountDownLatch latch) {
         this.zookeeperServiceDiscovery = zookeeperServiceDiscovery;
         this.serviceName = serviceName;
-        this.listener = listener;
+        this.path = path;
+        this.latch = latch;
         this.notifier = new RegistryNotifier(zookeeperServiceDiscovery.getDelay(), getExecutorRepository().getServiceDiscoveryAddressNotificationExecutor()) {
             @Override
             protected void doNotify(Object rawAddresses) {
-                listener.onEvent((ServiceInstancesChangedEvent)rawAddresses);
+                listeners.forEach(listener -> listener.onEvent((ServiceInstancesChangedEvent)rawAddresses));
             }
         };
     }
 
     @Override
     public void process(WatchedEvent event) throws Exception {
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+        }
 
         Watcher.Event.EventType eventType = event.getType();
 
         if (NodeChildrenChanged.equals(eventType) || NodeDataChanged.equals(eventType)) {
             if (shouldKeepWatching()) {
-                notifier.notify(new ServiceInstancesChangedEvent(serviceName, zookeeperServiceDiscovery.getInstances(serviceName)));
-                zookeeperServiceDiscovery.registerServiceWatcher(serviceName, listener);
-                zookeeperServiceDiscovery.dispatchServiceInstancesChangedEvent(serviceName);
+                zookeeperServiceDiscovery.reRegisterWatcher(this);
+                List<ServiceInstance> instanceList = zookeeperServiceDiscovery.getInstances(serviceName);
+                notifier.notify(new ServiceInstancesChangedEvent(serviceName, instanceList));
             }
         }
     }
 
+    public String getPath() {
+        return path;
+    }
+
+    public void addListener(ServiceInstancesChangedListener listener) {
+        listeners.add(listener);
+    }
+
     public boolean shouldKeepWatching() {
         return keepWatching;
     }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
index 278aae7..ed9be54 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
@@ -85,7 +85,7 @@ public abstract class CuratorFrameworkUtils {
         String host = instance.getAddress();
         int port = instance.getPort();
         ZookeeperInstance zookeeperInstance = instance.getPayload();
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(instance.getId(), name, host, port);
+        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(name, host, port);
         serviceInstance.setMetadata(zookeeperInstance.getMetadata());
         return serviceInstance;
     }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
index 5f3b680..359d62a 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
@@ -34,7 +34,6 @@ import java.util.Map;
 import static java.util.Arrays.asList;
 import static org.apache.dubbo.common.utils.NetUtils.getAvailablePort;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.INSTANCE_REVISION_UPDATED_KEY;
-import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.generateId;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -104,7 +103,7 @@ public class ZookeeperServiceDiscoveryTest {
     }
 
     private DefaultServiceInstance createServiceInstance(String serviceName, String host, int port) {
-        return new DefaultServiceInstance(generateId(host, port), serviceName, host, port);
+        return new DefaultServiceInstance(serviceName, host, port);
     }
 
 //    @Test
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java
new file mode 100644
index 0000000..d0b7848
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.rpc;
+
+public interface BaseFilter {
+    /**
+     * Make sure call invoker.invoke() in your implementation.
+     */
+    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
+
+    interface Listener {
+
+        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
+
+        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
index 58e8215..74acb51 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
@@ -33,6 +33,32 @@ import org.apache.dubbo.common.extension.SPI;
  *    Caching is implemented in dubbo using filter approach. If cache is configured for invocation then before
  *    remote call configured caching type's (e.g. Thread Local, JCache etc) implementation invoke method gets called.
  * </pre>
+ *
+ * Start from 3.0, the semantics of the Filter component at the consumer side has changed.
+ * Instead of intercepting a specific instance of invoker, Filter in 3.0 now intercepts ClusterInvoker. A new SPI named
+ * InstanceFilter is introduced to work as the same semantic as Filter in 2.x.
+ *
+ * The difference of Filter is as follows:
+ *
+ * 3.x Filter
+ *
+ *                                             -> InstanceFilter -> Invoker
+ *
+ * Proxy -> Filter -> Filter -> ClusterInvoker -> InstanceFilter -> Invoker
+ *
+ *                                             -> InstanceFilter -> Invoker
+ *
+ *
+ * 2.x Filter
+ *
+ *                            Filter -> Filter -> Invoker
+ *
+ * Proxy -> ClusterInvoker -> Filter -> Filter -> Invoker
+ *
+ *                            Filter -> Filter -> Invoker
+ *
+ * If you want to a Filter
+ *
  * Filter. (SPI, Singleton, ThreadSafe)
  *
  * @see org.apache.dubbo.rpc.filter.GenericFilter
@@ -41,17 +67,5 @@ import org.apache.dubbo.common.extension.SPI;
  * @see org.apache.dubbo.rpc.filter.TpsLimitFilter
  */
 @SPI
-public interface Filter {
-    /**
-     * Make sure call invoker.invoke() in your implementation.
-     */
-    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
-
-    interface Listener {
-
-        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
-
-        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
-    }
-
+public interface Filter extends BaseFilter {
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index d7e8c44..33a9b64 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -52,7 +52,7 @@ import java.util.concurrent.TimeUnit;
  */
 public abstract class AbstractInvoker<T> implements Invoker<T> {
 
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
+    protected static final Logger logger = LoggerFactory.getLogger(AbstractInvoker.class);
 
     private final Class<T> type;
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index 5255f55..7c526c2 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -3,10 +3,8 @@ generic=org.apache.dubbo.rpc.filter.GenericFilter
 genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
 token=org.apache.dubbo.rpc.filter.TokenFilter
 accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter
-activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
 classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter
 context=org.apache.dubbo.rpc.filter.ContextFilter
-consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
 exception=org.apache.dubbo.rpc.filter.ExceptionFilter
 executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter
 deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
index ee922af..ad95481 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -20,11 +20,11 @@ import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.AsyncMethodInfo;
 import org.apache.dubbo.rpc.model.ConsumerModel;
@@ -39,7 +39,7 @@ import static org.apache.dubbo.rpc.protocol.dubbo.Constants.ASYNC_METHOD_INFO;
  * EventFilter
  */
 @Activate(group = CommonConstants.CONSUMER)
-public class FutureFilter implements Filter, Filter.Listener {
+public class FutureFilter implements ClusterFilter, ClusterFilter.Listener {
 
     protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index 79a7a38..ee41594 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -1,2 +1 @@
-trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
-future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
+trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
new file mode 100644
index 0000000..4783214
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
@@ -0,0 +1 @@
+future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
index 1a1b0fd..d34f49a 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
@@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.protocol.dubbo;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.rpc.AppResponse;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService;
 
@@ -40,7 +40,7 @@ import static org.mockito.Mockito.mock;
  */
 public class FutureFilterTest {
     private static RpcInvocation invocation;
-    private Filter eventFilter = new FutureFilter();
+    private ClusterFilter eventFilter = new FutureFilter();
 
     @BeforeAll
     public static void setUp() {

[dubbo] 09/12: Fix #7311, try put rpc contex attachments into invocation for Filters may have put new values. (#7399)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3a3ef2c60ed9b6319c5c5ae99d72375870405903
Author: ken.lj <ke...@gmail.com>
AuthorDate: Thu Mar 18 16:40:41 2021 +0800

    Fix #7311, try put rpc contex attachments into invocation for Filters may have put new values. (#7399)
---
 .../dubbo/rpc/cluster/support/AbstractClusterInvoker.java      | 10 ++++------
 .../dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java  |  4 ++--
 .../java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java    |  5 +++++
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git 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
index 0974fcc..d0544cf 100644
--- 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
@@ -29,7 +29,6 @@ import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.cluster.ClusterInvoker;
 import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
@@ -37,7 +36,6 @@ import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_LOADBALANCE;
@@ -256,10 +254,10 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
         checkWhetherDestroyed();
 
         // binding attachments into invocation.
-        Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
-        if (contextAttachments != null && contextAttachments.size() != 0) {
-            ((RpcInvocation) invocation).addObjectAttachmentsIfAbsent(contextAttachments);
-        }
+//        Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
+//        if (contextAttachments != null && contextAttachments.size() != 0) {
+//            ((RpcInvocation) invocation).addObjectAttachmentsIfAbsent(contextAttachments);
+//        }
 
         List<Invoker<T>> invokers = list(invocation);
         LoadBalance loadbalance = initLoadBalance(invokers, invocation);
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
index 18796417..b5a235e 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.rpc.cluster.support;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
@@ -39,6 +38,7 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
@@ -156,7 +156,7 @@ public class AbstractClusterInvokerTest {
 
     }
 
-
+    @Disabled("RpcContext attachments will be set to Invocation twice, first in ConsumerContextFilter, second AbstractInvoker")
     @Test
     public void testBindingAttachment() {
         final String attachKey = "attach";
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index 33a9b64..c157c8d 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -145,6 +145,11 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
             invocation.addObjectAttachmentsIfAbsent(attachment);
         }
 
+        Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
+        if (contextAttachments != null && contextAttachments.size() != 0) {
+            invocation.addObjectAttachmentsIfAbsent(contextAttachments);
+        }
+
         invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
         RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
 

[dubbo] 08/12: create reference proxy after export service in DubboBootstrap (#7402)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a6337a67a24f5e07abd889dadf744d707d28236b
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Thu Mar 18 14:40:18 2021 +0800

    create reference proxy after export service in DubboBootstrap (#7402)
---
 .../org/apache/dubbo/config/spring/ReferenceBeanManager.java  | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
index 122440c..83765f4 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
@@ -25,7 +25,6 @@ import org.apache.dubbo.config.ReferenceConfig;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationPropertyValuesAdapter;
 import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceBeanBuilder;
-import org.apache.dubbo.config.utils.ReferenceConfigCache;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.MutablePropertyValues;
 import org.springframework.beans.PropertyValue;
@@ -73,7 +72,7 @@ public class ReferenceBeanManager implements ApplicationContextAware {
 
         // if add reference after prepareReferenceBeans(), should init it immediately.
         if (initialized) {
-            initReferenceBean(referenceBean, true);
+            initReferenceBean(referenceBean);
         }
     }
 
@@ -107,7 +106,7 @@ public class ReferenceBeanManager implements ApplicationContextAware {
     public void prepareReferenceBeans() throws Exception {
         initialized = true;
         for (ReferenceBean referenceBean : getReferences()) {
-            initReferenceBean(referenceBean, false);
+            initReferenceBean(referenceBean);
         }
     }
 
@@ -117,7 +116,7 @@ public class ReferenceBeanManager implements ApplicationContextAware {
      * @param referenceBean
      * @throws Exception
      */
-    private void initReferenceBean(ReferenceBean referenceBean, boolean canCreateProxy) throws Exception {
+    private void initReferenceBean(ReferenceBean referenceBean) throws Exception {
 
         if (referenceBean.getReferenceConfig() != null) {
             return;
@@ -146,10 +145,6 @@ public class ReferenceBeanManager implements ApplicationContextAware {
         // register ReferenceConfig
         DubboBootstrap.getInstance().reference(referenceConfig);
 
-        //TODO add after DubboBootstrap is started
-        if (canCreateProxy && referenceConfig.shouldInit()) {
-            ReferenceConfigCache.getCache().get(referenceConfig);
-        }
     }
 
     private void resolvePlaceholders(Map<String, Object> referenceProps, PropertyResolver propertyResolver) {

[dubbo] 07/12: fix apache rat plugin configuration (#7396)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 9b0e30c7e2554df420ff00f568f2d8d950af4afd
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Wed Mar 17 21:21:50 2021 +0800

    fix apache rat plugin configuration (#7396)
---
 dubbo-build-tools/pom.xml |  50 ++++++++++++++++++++
 pom.xml                   | 114 +++++++++++++++++++++++-----------------------
 2 files changed, 107 insertions(+), 57 deletions(-)

diff --git a/dubbo-build-tools/pom.xml b/dubbo-build-tools/pom.xml
index 28bae80..966218d 100644
--- a/dubbo-build-tools/pom.xml
+++ b/dubbo-build-tools/pom.xml
@@ -27,4 +27,54 @@
       <maven.deploy.skip>true</maven.deploy.skip>
     </properties>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <version>0.13</version>
+                <executions>
+                    <execution>
+                        <id>verify.rat</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <excludes>
+                        <exclude>**/*.versionsBackup</exclude>
+                        <exclude>**/.idea/</exclude>
+                        <exclude>**/*.iml</exclude>
+                        <exclude>**/*.txt</exclude>
+                        <exclude>**/*.load</exclude>
+                        <exclude>**/*.flex</exclude>
+                        <exclude>**/*.fc</exclude>
+                        <exclude>**/*.javascript</exclude>
+                        <exclude>**/*.properties</exclude>
+                        <exclude>**/*.thrift</exclude>
+                        <exclude>**/*.sh</exclude>
+                        <exclude>**/*.bat</exclude>
+                        <exclude>**/*.md</exclude>
+                        <exclude>.git/</exclude>
+                        <exclude>.gitignore</exclude>
+                        <exclude>.repository/</exclude>
+                        <exclude>**/.settings/*</exclude>
+                        <exclude>**/.classpath</exclude>
+                        <exclude>**/.project</exclude>
+                        <exclude>**/target/**</exclude>
+                        <exclude>**/*.log</exclude>
+                        <exclude>CONTRIBUTING.md</exclude>
+                        <exclude>README.md</exclude>
+                        <exclude>**/codestyle/*</exclude>
+                        <exclude>**/resources/META-INF/**</exclude>
+                        <exclude>.github/**</exclude>
+                        <exclude>compiler/**</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b1a1695..155da8c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -529,65 +529,65 @@
                         <goals>
                             <goal>check</goal>
                         </goals>
-                        <configuration>
-                            <excludes>
-                                <exclude>**/*.versionsBackup</exclude>
-                                <exclude>**/.idea/</exclude>
-                                <exclude>**/*.iml</exclude>
-                                <exclude>**/*.txt</exclude>
-                                <exclude>**/*.load</exclude>
-                                <exclude>**/*.flex</exclude>
-                                <exclude>**/*.fc</exclude>
-                                <exclude>**/*.javascript</exclude>
-                                <exclude>**/*.properties</exclude>
-                                <exclude>**/*.thrift</exclude>
-                                <exclude>**/*.sh</exclude>
-                                <exclude>**/*.bat</exclude>
-                                <exclude>**/*.md</exclude>
-                                <exclude>.git/</exclude>
-                                <exclude>.gitignore</exclude>
-                                <!-- ASF jenkins box puts the Maven repo in our root directory. -->
-                                <exclude>.repository/</exclude>
-                                <exclude>**/.settings/*</exclude>
-                                <exclude>**/.classpath</exclude>
-                                <exclude>**/.project</exclude>
-                                <exclude>**/target/**</exclude>
-                                <exclude>**/*.log</exclude>
-                                <exclude>CODE_OF_CONDUCT.md</exclude>
-                                <exclude>.codecov.yml</exclude>
-                                <exclude>.travis.yml</exclude>
-                                <exclude>PULL_REQUEST_TEMPLATE.md</exclude>
-                                <exclude>CONTRIBUTING.md</exclude>
-                                <exclude>README.md</exclude>
-                                <exclude>Jenkinsfile</exclude>
-                                <exclude>**/codestyle/*</exclude>
-                                <exclude>**/resources/META-INF/**</exclude>
-                                <!-- exclude the netty files -->
-                                <exclude>**/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java</exclude>
-                                <exclude>**/org/apache/dubbo/common/threadlocal/InternalThreadLocalMap.java</exclude>
-                                <exclude>**/org/apache/dubbo/common/timer/TimerTask.java</exclude>
-                                <exclude>**/org/apache/dubbo/common/timer/Timer.java</exclude>
-                                <exclude>**/org/apache/dubbo/common/timer/Timeout.java</exclude>
-                                <exclude>**/org/apache/dubbo/common/timer/HashedWheelTimer.java</exclude>
-                                <!-- exclude the edazdarevic files -->
-                                <exclude>**/org/apache/dubbo/common/utils/CIDRUtils.java</exclude>
-                                <!-- exclude protobuf generated files -->
-                                <exclude>**/org/apache/dubbo/common/serialize/protobuf/support/wrapper/MapValue.java
-                                </exclude>
-                                <exclude>
-                                    **/org/apache/dubbo/common/serialize/protobuf/support/wrapper/ThrowablePB.java
-                                </exclude>
-                                <exclude>
-                                    **/org/apache/dubbo/common/utils/Utf8Utils.java
-                                </exclude>
-                                <exclude>.github/**</exclude>
-                                <exclude>compiler/**</exclude>
-                                <!-- exclude mockito extensions spi files -->
-                                <exclude>**/mockito-extensions/*</exclude>
-                            </excludes>
-                        </configuration>
                     </execution>
                 </executions>
+                <configuration>
+                    <excludes>
+                        <exclude>**/*.versionsBackup</exclude>
+                        <exclude>**/.idea/</exclude>
+                        <exclude>**/*.iml</exclude>
+                        <exclude>**/*.txt</exclude>
+                        <exclude>**/*.load</exclude>
+                        <exclude>**/*.flex</exclude>
+                        <exclude>**/*.fc</exclude>
+                        <exclude>**/*.javascript</exclude>
+                        <exclude>**/*.properties</exclude>
+                        <exclude>**/*.thrift</exclude>
+                        <exclude>**/*.sh</exclude>
+                        <exclude>**/*.bat</exclude>
+                        <exclude>**/*.md</exclude>
+                        <exclude>.git/</exclude>
+                        <exclude>.gitignore</exclude>
+                        <!-- ASF jenkins box puts the Maven repo in our root directory. -->
+                        <exclude>.repository/</exclude>
+                        <exclude>**/.settings/*</exclude>
+                        <exclude>**/.classpath</exclude>
+                        <exclude>**/.project</exclude>
+                        <exclude>**/target/**</exclude>
+                        <exclude>**/*.log</exclude>
+                        <exclude>CODE_OF_CONDUCT.md</exclude>
+                        <exclude>.codecov.yml</exclude>
+                        <exclude>.travis.yml</exclude>
+                        <exclude>PULL_REQUEST_TEMPLATE.md</exclude>
+                        <exclude>CONTRIBUTING.md</exclude>
+                        <exclude>README.md</exclude>
+                        <exclude>Jenkinsfile</exclude>
+                        <exclude>**/codestyle/*</exclude>
+                        <exclude>**/resources/META-INF/**</exclude>
+                        <!-- exclude the netty files -->
+                        <exclude>**/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java</exclude>
+                        <exclude>**/org/apache/dubbo/common/threadlocal/InternalThreadLocalMap.java</exclude>
+                        <exclude>**/org/apache/dubbo/common/timer/TimerTask.java</exclude>
+                        <exclude>**/org/apache/dubbo/common/timer/Timer.java</exclude>
+                        <exclude>**/org/apache/dubbo/common/timer/Timeout.java</exclude>
+                        <exclude>**/org/apache/dubbo/common/timer/HashedWheelTimer.java</exclude>
+                        <!-- exclude the edazdarevic files -->
+                        <exclude>**/org/apache/dubbo/common/utils/CIDRUtils.java</exclude>
+                        <!-- exclude protobuf generated files -->
+                        <exclude>**/org/apache/dubbo/common/serialize/protobuf/support/wrapper/MapValue.java
+                        </exclude>
+                        <exclude>
+                            **/org/apache/dubbo/common/serialize/protobuf/support/wrapper/ThrowablePB.java
+                        </exclude>
+                        <exclude>
+                            **/org/apache/dubbo/common/utils/Utf8Utils.java
+                        </exclude>
+                        <exclude>.github/**</exclude>
+                        <exclude>compiler/**</exclude>
+                        <!-- exclude mockito extensions spi files -->
+                        <exclude>**/mockito-extensions/*</exclude>
+                    </excludes>
+                </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>

[dubbo] 05/12: Refactor dubbo reference processing (#7383)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 954e20f74f8effa74909d4dca7aa8039a27b59c4
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Wed Mar 17 19:42:41 2021 +0800

    Refactor dubbo reference processing (#7383)
---
 .../org/apache/dubbo/config/AbstractConfig.java    |  10 +
 .../apache/dubbo/config/ReferenceConfigBase.java   |  50 ++-
 .../apache/dubbo/common/utils/NetUtilsTest.java    |   2 +-
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   8 +
 .../DubboConfigInitializationPostProcessor.java    | 131 ++++++
 .../apache/dubbo/config/spring/ReferenceBean.java  | 226 +++++++---
 .../dubbo/config/spring/ReferenceBeanManager.java  | 244 ++++++++++
 .../AbstractAnnotationBeanPostProcessor.java       | 489 +++++++++++++++++++++
 .../AbstractAnnotationConfigBeanBuilder.java       | 215 ---------
 .../AnnotatedInterfaceConfigBeanBuilder.java       | 215 ---------
 .../AnnotationPropertyValuesAdapter.java           |   2 +-
 .../ReferenceAnnotationBeanPostProcessor.java      | 364 +++++++++------
 .../factory/annotation/ReferenceBeanBuilder.java   | 241 ++++++----
 .../annotation/ServiceClassPostProcessor.java      |  12 +-
 .../spring/schema/DubboBeanDefinitionParser.java   | 239 ++++++----
 .../dubbo/config/spring/util/DubboBeanUtils.java   | 103 ++++-
 .../org/apache/dubbo/config/spring/ConfigTest.java |  96 ++--
 .../AnnotationPropertyValuesAdapterTest.java       |  12 +-
 .../ReferenceAnnotationBeanPostProcessorTest.java  | 107 ++---
 .../annotation/ReferenceBeanBuilderTest.java       |  15 +-
 .../DubboComponentScanRegistrarTest.java           |   6 +-
 .../annotation/DubboConfigConfigurationTest.java   |   2 +
 .../context/annotation/EnableDubboConfigTest.java  |  12 +
 .../spring/context/annotation/EnableDubboTest.java |   8 +-
 .../dubbo/config/spring/issues/Issue6252Test.java  |  12 +-
 .../spring/schema/DubboNamespaceHandlerTest.java   |  34 +-
 .../config/spring/schema/GenericServiceTest.java   |  10 +-
 .../resources/META-INF/init-reference.properties   |   5 +
 .../apache/dubbo/config/spring/demo-provider.xml   |   8 +-
 ...reference.xml => init-reference-properties.xml} |  14 +-
 .../apache/dubbo/config/spring/init-reference.xml  |  20 +-
 .../dubbo/metadata/definition/util/ClassUtils.java |   4 +-
 .../registry/multicast/MulticastRegistryTest.java  |  11 +-
 .../dubbo/rpc/proxy/AbstractProxyFactory.java      |   4 +
 34 files changed, 1968 insertions(+), 963 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 47556c5..3e0c9f6 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -161,6 +161,11 @@ public abstract class AbstractConfig implements Serializable {
         }
     }
 
+    /**
+     * Put attributes of specify 'config' into 'parameters' argument
+     * @param parameters
+     * @param config
+     */
     @Deprecated
     protected static void appendAttributes(Map<String, Object> parameters, Object config) {
         appendAttributes(parameters, config, null);
@@ -359,6 +364,11 @@ public abstract class AbstractConfig implements Serializable {
         }
     }
 
+    /**
+     * Copy attributes from annotation
+     * @param annotationClass
+     * @param annotation
+     */
     protected void appendAnnotation(Class<?> annotationClass, Object annotation) {
         Method[] methods = annotationClass.getMethods();
         for (Method method : methods) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
index 966b384..3a149aa 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
@@ -119,6 +119,11 @@ public abstract class ReferenceConfigBase<T> extends AbstractReferenceConfig {
         }
     }
 
+    /**
+     * Get actual interface class of this reference.
+     * The actual service type of remote provider.
+     * @return
+     */
     public Class<?> getActualInterface() {
         Class actualInterface = interfaceClass;
         if (interfaceClass == GenericService.class) {
@@ -131,33 +136,43 @@ public abstract class ReferenceConfigBase<T> extends AbstractReferenceConfig {
         return actualInterface;
     }
 
+    /**
+     * Get proxy interface class of this reference.
+     * The proxy interface class is used to create proxy instance.
+     * @return
+     */
     public Class<?> getInterfaceClass() {
         if (interfaceClass != null) {
             return interfaceClass;
         }
-        if (ProtocolUtils.isGeneric(getGeneric())
-                || (getConsumer() != null && ProtocolUtils.isGeneric(getConsumer().getGeneric()))) {
+
+        String generic = getGeneric();
+        if (StringUtils.isBlank(generic) && getConsumer() != null) {
+            generic = getConsumer().getGeneric();
+        }
+        interfaceClass = determineInterfaceClass(generic, interfaceName);
+
+        return interfaceClass;
+    }
+
+    /**
+     * Determine the interface of the proxy class
+     * @param generic
+     * @param interfaceName
+     * @return
+     */
+    public static Class<?> determineInterfaceClass(String generic, String interfaceName) {
+        if (ProtocolUtils.isGeneric(generic)) {
             return GenericService.class;
         }
         try {
             if (interfaceName != null && interfaceName.length() > 0) {
-                interfaceClass = Class.forName(interfaceName, true, ClassUtils.getClassLoader());
+                return Class.forName(interfaceName, true, ClassUtils.getClassLoader());
             }
         } catch (ClassNotFoundException t) {
             throw new IllegalStateException(t.getMessage(), t);
         }
-
-        return interfaceClass;
-    }
-
-    /**
-     * @param interfaceClass
-     * @see #setInterface(Class)
-     * @deprecated
-     */
-    @Deprecated
-    public void setInterfaceClass(Class<?> interfaceClass) {
-        setInterface(interfaceClass);
+        return null;
     }
 
     public String getInterface() {
@@ -166,17 +181,12 @@ public abstract class ReferenceConfigBase<T> extends AbstractReferenceConfig {
 
     public void setInterface(String interfaceName) {
         this.interfaceName = interfaceName;
-        // FIXME, add id strategy in ConfigManager
-//        if (StringUtils.isEmpty(id)) {
-//            id = interfaceName;
-//        }
     }
 
     public void setInterface(Class<?> interfaceClass) {
         if (interfaceClass != null && !interfaceClass.isInterface()) {
             throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
         }
-        this.interfaceClass = interfaceClass;
         setInterface(interfaceClass == null ? null : interfaceClass.getName());
     }
 
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
index c4f51d7..ea2c54e 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
@@ -162,7 +162,7 @@ public class NetUtilsTest {
     @Test
     public void testGetIpByHost() throws Exception {
         assertThat(NetUtils.getIpByHost("localhost"), equalTo("127.0.0.1"));
-        assertThat(NetUtils.getIpByHost("dubbo"), equalTo("dubbo"));
+        assertThat(NetUtils.getIpByHost("dubbo.local"), equalTo("dubbo.local"));
     }
 
     @Test
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index cdf66dd..0ef8ef4 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -202,6 +202,14 @@ public class DubboBootstrap extends GenericEventListener {
         return instance;
     }
 
+    public static void reset() {
+        if (instance != null) {
+            instance.destroy();
+        }
+        ApplicationModel.reset();
+        instance = null;
+    }
+
     private DubboBootstrap() {
         configManager = ApplicationModel.getConfigManager();
         environment = ApplicationModel.getEnvironment();
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
new file mode 100644
index 0000000..068d700
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.spring;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ConsumerConfig;
+import org.apache.dubbo.config.MetadataReportConfig;
+import org.apache.dubbo.config.MetricsConfig;
+import org.apache.dubbo.config.ModuleConfig;
+import org.apache.dubbo.config.MonitorConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ProviderConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.SslConfig;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.Ordered;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors;
+
+/**
+ *
+ * Post-processor Dubbo config bean initialization.
+ *
+ * NOTE: Dubbo config beans MUST be initialized after registering all BeanPostProcessors,
+ * that is after the AbstractApplicationContext#registerBeanPostProcessors() method.
+ */
+public class DubboConfigInitializationPostProcessor implements BeanPostProcessor, BeanFactoryAware, Ordered {
+
+    public static String BEAN_NAME = "dubboBeanFactoryPostProcessor";
+
+    /**
+     * This bean post processor should run before seata GlobalTransactionScanner(1024)
+     */
+    @Value("${dubbo.config-initialization-post-processor.order:1000}")
+    private int order = 1000;
+
+    private AtomicBoolean initialized = new AtomicBoolean(false);
+    private ConfigurableListableBeanFactory beanFactory;
+    private ReferenceBeanManager referenceBeanManager;
+
+    @Override
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        if (initialized.compareAndSet(false, true)) {
+            try {
+                prepareDubboConfigBeans(beanFactory);
+                prepareReferenceBeans(beanFactory);
+            } catch (Throwable e) {
+                throw new FatalBeanException("Initialization dubbo config beans failed", e);
+            }
+        }
+        return bean;
+    }
+
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        try {
+            if (bean instanceof ReferenceBean) {
+                ReferenceBean referenceBean = (ReferenceBean) bean;
+                referenceBeanManager.addReference(referenceBean);
+            }
+        } catch (Exception e) {
+            throw new BeanInitializationException("Initialization reference bean failed", e);
+        }
+        return bean;
+    }
+
+    @Override
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
+        referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
+    }
+
+    @Override
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    private void prepareReferenceBeans(ConfigurableListableBeanFactory beanFactory) throws Exception {
+        ReferenceBeanManager referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
+        referenceBeanManager.prepareReferenceBeans();
+    }
+
+    /**
+     * Initializes there Dubbo's Config Beans before @Reference bean autowiring
+     */
+    private void prepareDubboConfigBeans(ConfigurableListableBeanFactory beanFactory) {
+        //Make sure all these config beans are inited and registered to ConfigManager
+        beansOfTypeIncludingAncestors(beanFactory, ApplicationConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, ModuleConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, RegistryConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, ProtocolConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, MonitorConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, ProviderConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, ConsumerConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, ConfigCenterBean.class);
+        beansOfTypeIncludingAncestors(beanFactory, MetadataReportConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, MetricsConfig.class);
+        beansOfTypeIncludingAncestors(beanFactory, SslConfig.class);
+
+        //SHOULD NOT init service beans here, avoid conflicts with seata
+        //beansOfTypeIncludingAncestors(beanFactory, ServiceBean.class);
+    }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
index be9cd71..f8f834b 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
@@ -16,56 +16,79 @@
  */
 package org.apache.dubbo.config.spring;
 
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.config.ConsumerConfig;
-import org.apache.dubbo.config.MetadataReportConfig;
-import org.apache.dubbo.config.MetricsConfig;
-import org.apache.dubbo.config.ModuleConfig;
-import org.apache.dubbo.config.MonitorConfig;
-import org.apache.dubbo.config.ProtocolConfig;
-import org.apache.dubbo.config.ProviderConfig;
+import org.apache.dubbo.common.utils.Assert;
+import org.apache.dubbo.common.utils.ReflectUtils;
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ReferenceConfig;
-import org.apache.dubbo.config.RegistryConfig;
-import org.apache.dubbo.config.SslConfig;
-import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
 import org.apache.dubbo.config.support.Parameter;
-
+import org.apache.dubbo.config.utils.ReferenceConfigCache;
+import org.apache.dubbo.rpc.proxy.AbstractProxyFactory;
+import org.apache.dubbo.rpc.support.ProtocolUtils;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.target.AbstractLazyCreationTargetSource;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.BeanClassLoaderAware;
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 
-import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors;
+import java.util.Map;
+
 
 /**
  * ReferenceFactoryBean
  */
-public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean,
-        ApplicationContextAware, InitializingBean, DisposableBean {
-
-    private static final long serialVersionUID = 213195494150089726L;
+public class ReferenceBean<T> implements FactoryBean,
+        ApplicationContextAware, BeanClassLoaderAware, InitializingBean, DisposableBean {
 
     private transient ApplicationContext applicationContext;
+    private ClassLoader beanClassLoader;
+    private DubboReferenceLazyInitTargetSource referenceTargetSource;
+    private Object referenceLazyProxy;
+    /**
+     * The interface class of the reference service
+     */
+    protected Class<?> interfaceClass;
+
+    //beanName
+    protected String id;
+    //from annotation attributes
+    private Map<String, Object> referenceProps;
+    //from bean definition
+    private MutablePropertyValues propertyValues;
+    //actual reference config
+    private ReferenceConfig referenceConfig;
+    private String generic;
+    private String interfaceName;
 
     public ReferenceBean() {
         super();
     }
 
-    public ReferenceBean(Reference reference) {
-        super(reference);
+    public ReferenceBean(Map<String, Object> referenceProps) {
+        this.referenceProps = referenceProps;
     }
 
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) {
         this.applicationContext = applicationContext;
-        SpringExtensionFactory.addApplicationContext(applicationContext);
+    }
+
+    @Override
+    public void setBeanClassLoader(ClassLoader classLoader) {
+        this.beanClassLoader = classLoader;
     }
 
     @Override
     public Object getObject() {
-        return get();
+        if (referenceLazyProxy == null) {
+            createReferenceLazyProxy();
+        }
+        return referenceLazyProxy;
     }
 
     @Override
@@ -79,43 +102,148 @@ public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean,
         return true;
     }
 
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        if (referenceProps == null) {
+            Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty");
+            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+            BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(getId());
+            propertyValues = beanDefinition.getPropertyValues();
+        }
+    }
+
+    private ConfigurableListableBeanFactory getBeanFactory() {
+        return (ConfigurableListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
+    }
+
+    @Override
+    public void destroy() {
+        // do nothing
+    }
+
     /**
-     * Initializes there Dubbo's Config Beans before @Reference bean autowiring
+     * TODO remove get() method
+     *
+     * @return
      */
-    private void prepareDubboConfigBeans() {
-        beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, ConfigCenterBean.class);
-        beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class);
-        beansOfTypeIncludingAncestors(applicationContext, SslConfig.class);
+    @Deprecated
+    public Object get() {
+        throw new UnsupportedOperationException("Should not call this method");
     }
 
-    @Override
-    @SuppressWarnings({"unchecked"})
-    public void afterPropertiesSet() throws Exception {
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
 
-        // Initializes Dubbo's Config Beans before @Reference bean autowiring
-        prepareDubboConfigBeans();
+    /* Compatible with seata: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
+    public String getGroup() {
+        return referenceConfig.getGroup();
+    }
+
+    public String getVersion() {
+        return referenceConfig.getVersion();
+    }
+
+    public Map<String, Object> getReferenceProps() {
+        return referenceProps;
+    }
+
+    public MutablePropertyValues getPropertyValues() {
+        return propertyValues;
+    }
+
+    public ReferenceConfig getReferenceConfig() {
+        return referenceConfig;
+    }
+
+    public void setReferenceConfig(ReferenceConfig referenceConfig) {
+        this.referenceConfig = referenceConfig;
+    }
+
+    public Class<?> getInterfaceClass() {
+        // get interface class
+        if (interfaceClass == null) {
+            if (referenceProps != null) {
+                //get interface class name of @DubboReference
+                String interfaceName = (String) referenceProps.get("interfaceName");
+                if (interfaceName == null) {
+                    Class clazz = (Class) referenceProps.get("interfaceClass");
+                    if (clazz != null) {
+                        interfaceName = clazz.getName();
+                    }
+                }
+                if (StringUtils.isBlank(interfaceName)) {
+                    throw new RuntimeException("Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference'");
+                }
+                this.interfaceName = interfaceName;
+
+                //get generic
+                Object genericValue = referenceProps.get("generic");
+                generic = genericValue != null ? genericValue.toString() : null;
+                String consumer = (String) referenceProps.get("consumer");
+                if (StringUtils.isBlank(generic) && consumer != null) {
+                    // get generic from consumerConfig
+                    BeanDefinition consumerBeanDefinition = getBeanFactory().getMergedBeanDefinition(consumer);
+                    if (consumerBeanDefinition != null) {
+                        generic = (String) consumerBeanDefinition.getPropertyValues().get("generic");
+                    }
+                }
+            } else if (propertyValues != null) {
+                generic = (String) propertyValues.get("generic");
+                interfaceName = (String) propertyValues.get("interface");
+            } else {
+                throw new RuntimeException("Required 'referenceProps' or beanDefinition");
+            }
+
+            interfaceClass = ReferenceConfig.determineInterfaceClass(generic, interfaceName);
+        }
+        return interfaceClass;
+    }
 
-        // lazy init by default.
-        if (init == null) {
-            init = false;
+    private void createReferenceLazyProxy() {
+        this.referenceTargetSource = new DubboReferenceLazyInitTargetSource();
+
+        //set proxy interfaces
+        //see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
+        ProxyFactory proxyFactory = new ProxyFactory();
+        proxyFactory.setTargetSource(referenceTargetSource);
+        proxyFactory.addInterface(getInterfaceClass());
+        Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
+        for (Class<?> anInterface : internalInterfaces) {
+            proxyFactory.addInterface(anInterface);
+        }
+        if (ProtocolUtils.isGeneric(generic)){
+            //add actual interface
+            proxyFactory.addInterface(ReflectUtils.forName(interfaceName));
         }
 
-        // eager init if necessary.
-        if (shouldInit()) {
-            getObject();
+        this.referenceLazyProxy = proxyFactory.getProxy(this.beanClassLoader);
+    }
+
+    private Object getCallProxy() throws Exception {
+
+        if (referenceConfig == null) {
+            throw new IllegalStateException("ReferenceBean is not ready yet, maybe dubbo engine is not started");
         }
+        //get reference proxy
+        return ReferenceConfigCache.getCache().get(referenceConfig);
     }
 
-    @Override
-    public void destroy() {
-        // do nothing
+    private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource {
+
+        @Override
+        protected Object createObject() throws Exception {
+            return getCallProxy();
+        }
+
+        @Override
+        public synchronized Class<?> getTargetClass() {
+            return getInterfaceClass();
+        }
     }
+
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
new file mode 100644
index 0000000..122440c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.spring;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.dubbo.common.utils.Assert;
+import org.apache.dubbo.config.ArgumentConfig;
+import org.apache.dubbo.config.MethodConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationPropertyValuesAdapter;
+import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceBeanBuilder;
+import org.apache.dubbo.config.utils.ReferenceConfigCache;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.ManagedMap;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.PropertyResolver;
+import org.springframework.validation.DataBinder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ReferenceBeanManager implements ApplicationContextAware {
+    public static final String BEAN_NAME = "dubboReferenceBeanManager";
+    private final Log logger = LogFactory.getLog(getClass());
+    private Map<String, ReferenceBean> configMap = new ConcurrentHashMap<>();
+    private ApplicationContext applicationContext;
+    private volatile boolean initialized = false;
+
+
+    public void addReference(ReferenceBean referenceBean) throws Exception {
+        Assert.notNull(referenceBean.getId(), "The id of ReferenceBean cannot be empty");
+        //TODO generate reference bean id and unique cache key
+        String key = referenceBean.getId();
+        ReferenceBean oldReferenceBean = configMap.get(key);
+        if (oldReferenceBean != null) {
+            if (referenceBean != oldReferenceBean) {
+                logger.warn("Found duplicated ReferenceBean id: " + key);
+            }
+            return;
+        }
+        configMap.put(key, referenceBean);
+
+        // if add reference after prepareReferenceBeans(), should init it immediately.
+        if (initialized) {
+            initReferenceBean(referenceBean, true);
+        }
+    }
+
+    public ReferenceBean get(String id) {
+        return configMap.get(id);
+    }
+
+//    public ReferenceBean getOrCreateReference(Map<String, String> referenceProps) {
+//        Integer key = referenceProps.hashCode();
+//        return configMap.computeIfAbsent(key, k -> {
+//            ReferenceBean referenceBean = new ReferenceBean();
+//            referenceBean.setReferenceProps(referenceProps);
+//            //referenceBean.setId();
+//            return referenceBean;
+//        });
+//    }
+
+    public Collection<ReferenceBean> getReferences() {
+        return configMap.values();
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    /**
+     * Initialize all reference beans, call at Dubbo starting
+     * @throws Exception
+     */
+    public void prepareReferenceBeans() throws Exception {
+        initialized = true;
+        for (ReferenceBean referenceBean : getReferences()) {
+            initReferenceBean(referenceBean, false);
+        }
+    }
+
+    /**
+     * NOTE: This method should only call after all dubbo config beans and all property resolvers is loaded.
+     *
+     * @param referenceBean
+     * @throws Exception
+     */
+    private void initReferenceBean(ReferenceBean referenceBean, boolean canCreateProxy) throws Exception {
+
+        if (referenceBean.getReferenceConfig() != null) {
+            return;
+        }
+
+        Environment environment = applicationContext.getEnvironment();
+        Map<String, Object> referenceProps = referenceBean.getReferenceProps();
+        if (referenceProps == null) {
+            MutablePropertyValues propertyValues = referenceBean.getPropertyValues();
+            if (propertyValues == null) {
+                throw new RuntimeException("ReferenceBean is invalid, missing 'propertyValues'");
+            }
+            referenceProps = toReferenceProps(propertyValues, environment);
+        }
+
+        //resolve placeholders
+        resolvePlaceholders(referenceProps, environment);
+
+        //create real ReferenceConfig
+        ReferenceConfig referenceConfig = ReferenceBeanBuilder.create(new AnnotationAttributes(new LinkedHashMap<>(referenceProps)), applicationContext)
+                .defaultInterfaceClass(referenceBean.getObjectType())
+                .build();
+
+        referenceBean.setReferenceConfig(referenceConfig);
+
+        // register ReferenceConfig
+        DubboBootstrap.getInstance().reference(referenceConfig);
+
+        //TODO add after DubboBootstrap is started
+        if (canCreateProxy && referenceConfig.shouldInit()) {
+            ReferenceConfigCache.getCache().get(referenceConfig);
+        }
+    }
+
+    private void resolvePlaceholders(Map<String, Object> referenceProps, PropertyResolver propertyResolver) {
+        for (Map.Entry<String, Object> entry : referenceProps.entrySet()) {
+            Object value = entry.getValue();
+            if (value instanceof String) {
+                String valueToResovle = (String) value;
+                entry.setValue(propertyResolver.resolveRequiredPlaceholders(valueToResovle));
+            } else if (value instanceof String[]) {
+                String[] strings = (String[]) value;
+                for (int i = 0; i < strings.length; i++) {
+                    strings[i] = propertyResolver.resolveRequiredPlaceholders(strings[i]);
+                }
+                entry.setValue(strings);
+            }
+        }
+    }
+
+    private Map<String, Object> toReferenceProps(MutablePropertyValues propertyValues, PropertyResolver propertyResolver) {
+        Map<String, Object> referenceProps;
+        referenceProps = new LinkedHashMap<>();
+        for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {
+            String propertyName = propertyValue.getName();
+            Object value = propertyValue.getValue();
+            if ("methods".equals(propertyName)) {
+                ManagedList managedList = (ManagedList) value;
+                List<MethodConfig> methodConfigs = new ArrayList<>();
+                for (Object el : managedList) {
+                    MethodConfig methodConfig = createMethodConfig(((BeanDefinitionHolder) el).getBeanDefinition(), propertyResolver);
+                    methodConfigs.add(methodConfig);
+                }
+                value = methodConfigs.toArray(new MethodConfig[0]);
+            } else if ("parameters".equals(propertyName)) {
+                value = createParameterMap((ManagedMap) value, propertyResolver);
+            }
+            if (value instanceof RuntimeBeanReference) {
+                RuntimeBeanReference beanReference = (RuntimeBeanReference) value;
+                value = applicationContext.getBean(beanReference.getBeanName());
+            }
+            referenceProps.put(propertyName, value);
+        }
+        return referenceProps;
+    }
+
+    private MethodConfig createMethodConfig(BeanDefinition beanDefinition, PropertyResolver propertyResolver) {
+        Map<String, Object> attributes = new LinkedHashMap<>();
+        MutablePropertyValues pvs = beanDefinition.getPropertyValues();
+        for (PropertyValue propertyValue : pvs.getPropertyValueList()) {
+            String propertyName = propertyValue.getName();
+            Object value = propertyValue.getValue();
+            if ("arguments".equals(propertyName)) {
+                ManagedList managedList = (ManagedList) value;
+                List<ArgumentConfig> argumentConfigs = new ArrayList<>();
+                for (Object el : managedList) {
+                    ArgumentConfig argumentConfig = createArgumentConfig(((BeanDefinitionHolder) el).getBeanDefinition(), propertyResolver);
+                    argumentConfigs.add(argumentConfig);
+                }
+                value = argumentConfigs.toArray(new ArgumentConfig[0]);
+            } else if ("parameters".equals(propertyName)) {
+                value = createParameterMap((ManagedMap) value, propertyResolver);
+            }
+
+            if (value instanceof RuntimeBeanReference) {
+                RuntimeBeanReference beanReference = (RuntimeBeanReference) value;
+                value = applicationContext.getBean(beanReference.getBeanName());
+            }
+            attributes.put(propertyName, value);
+        }
+        MethodConfig methodConfig = new MethodConfig();
+        DataBinder dataBinder = new DataBinder(methodConfig);
+        dataBinder.bind(new AnnotationPropertyValuesAdapter(attributes, propertyResolver));
+        return methodConfig;
+    }
+
+    private ArgumentConfig createArgumentConfig(BeanDefinition beanDefinition, PropertyResolver propertyResolver) {
+        ArgumentConfig argumentConfig = new ArgumentConfig();
+        DataBinder dataBinder = new DataBinder(argumentConfig);
+        dataBinder.bind(beanDefinition.getPropertyValues());
+        return argumentConfig;
+    }
+
+    private Map<String, String> createParameterMap(ManagedMap managedMap, PropertyResolver propertyResolver) {
+        Map<String, String> map = new LinkedHashMap<>();
+        Set<Map.Entry<String, TypedStringValue>> entrySet = managedMap.entrySet();
+        for (Map.Entry<String, TypedStringValue> entry : entrySet) {
+            map.put(entry.getKey(), entry.getValue().getValue());
+        }
+        return map;
+    }
+
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java
new file mode 100644
index 0000000..2581b1e
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java
@@ -0,0 +1,489 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.spring.beans.factory.annotation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
+import org.springframework.beans.factory.annotation.InjectionMetadata;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
+import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.core.Ordered;
+import org.springframework.core.PriorityOrdered;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.env.Environment;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static com.alibaba.spring.util.AnnotationUtils.getAnnotationAttributes;
+import static org.springframework.core.BridgeMethodResolver.findBridgedMethod;
+import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMethodPair;
+
+/**
+ * Abstract common {@link BeanPostProcessor} implementation for customized annotation that annotated injected-object.
+ */
+@SuppressWarnings("unchecked")
+public abstract class AbstractAnnotationBeanPostProcessor extends
+        InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,
+        BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean {
+
+    private final static int CACHE_SIZE = Integer.getInteger("", 32);
+
+    private final Log logger = LogFactory.getLog(getClass());
+
+    private final Class<? extends Annotation>[] annotationTypes;
+
+    private final ConcurrentMap<String, AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata> injectionMetadataCache =
+            new ConcurrentHashMap<String, AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata>(CACHE_SIZE);
+
+    private final ConcurrentMap<String, Object> injectedObjectsCache = new ConcurrentHashMap<String, Object>(CACHE_SIZE);
+
+    private ConfigurableListableBeanFactory beanFactory;
+
+    private Environment environment;
+
+    private ClassLoader classLoader;
+
+    /**
+     * make sure higher priority than {@link AutowiredAnnotationBeanPostProcessor}
+     */
+    private int order = Ordered.LOWEST_PRECEDENCE - 3;
+
+    /**
+     * @param annotationTypes the multiple types of {@link Annotation annotations}
+     */
+    public AbstractAnnotationBeanPostProcessor(Class<? extends Annotation>... annotationTypes) {
+        Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
+        this.annotationTypes = annotationTypes;
+    }
+
+    private static <T> Collection<T> combine(Collection<? extends T>... elements) {
+        List<T> allElements = new ArrayList<T>();
+        for (Collection<? extends T> e : elements) {
+            allElements.addAll(e);
+        }
+        return allElements;
+    }
+
+    /**
+     * Annotation type
+     *
+     * @return non-null
+     * @deprecated 2.7.3, uses {@link #getAnnotationTypes()}
+     */
+    @Deprecated
+    public final Class<? extends Annotation> getAnnotationType() {
+        return annotationTypes[0];
+    }
+
+    protected final Class<? extends Annotation>[] getAnnotationTypes() {
+        return annotationTypes;
+    }
+
+    @Override
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
+                "AnnotationInjectedBeanPostProcessor requires a ConfigurableListableBeanFactory");
+        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
+    }
+
+    @Override
+    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
+        if (beanType != null) {
+            AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
+            metadata.checkConfigMembers(beanDefinition);
+            try {
+                prepareInjection(metadata);
+            } catch (Exception e) {
+                logger.warn("Prepare injection of @"+getAnnotationType().getSimpleName()+" failed", e);
+            }
+        }
+    }
+
+    @Override
+    public PropertyValues postProcessPropertyValues(
+            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
+
+        try {
+            AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
+            prepareInjection(metadata);
+            metadata.inject(bean, beanName, pvs);
+        } catch (BeanCreationException ex) {
+            throw ex;
+        } catch (Throwable ex) {
+            throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+                    + " dependencies is failed", ex);
+        }
+        return pvs;
+    }
+
+
+    /**
+     * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated fields
+     *
+     * @param beanClass The {@link Class} of Bean
+     * @return non-null {@link List}
+     */
+    private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
+
+        final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();
+
+        ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
+            @Override
+            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
+
+                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
+
+                    AnnotationAttributes attributes = getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);
+
+                    if (attributes != null) {
+
+                        if (Modifier.isStatic(field.getModifiers())) {
+                            if (logger.isWarnEnabled()) {
+                                logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);
+                            }
+                            return;
+                        }
+
+                        elements.add(new AnnotatedFieldElement(field, attributes));
+                    }
+                }
+            }
+        });
+
+        return elements;
+
+    }
+
+    /**
+     * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated methods
+     *
+     * @param beanClass The {@link Class} of Bean
+     * @return non-null {@link List}
+     */
+    private List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {
+
+        final List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement>();
+
+        ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
+            @Override
+            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
+
+                Method bridgedMethod = findBridgedMethod(method);
+
+                if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
+                    return;
+                }
+
+
+                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
+
+                    AnnotationAttributes attributes = getAnnotationAttributes(bridgedMethod, annotationType, getEnvironment(), true, true);
+
+                    if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
+                        if (Modifier.isStatic(method.getModifiers())) {
+                            if (logger.isWarnEnabled()) {
+                                logger.warn("@" + annotationType.getName() + " annotation is not supported on static methods: " + method);
+                            }
+                            return;
+                        }
+                        if (method.getParameterTypes().length == 0) {
+                            if (logger.isWarnEnabled()) {
+                                logger.warn("@" + annotationType.getName() + " annotation should only be used on methods with parameters: " +
+                                        method);
+                            }
+                        }
+                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
+                        elements.add(new AnnotatedMethodElement(method, pd, attributes));
+                    }
+                }
+            }
+        });
+
+        return elements;
+    }
+
+
+    private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
+        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
+        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
+        return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
+    }
+
+    protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
+        // Fall back to class name as cache key, for backwards compatibility with custom callers.
+        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
+        // Quick check on the concurrent map first, with minimal locking.
+        AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
+        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
+            synchronized (this.injectionMetadataCache) {
+                metadata = this.injectionMetadataCache.get(cacheKey);
+                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
+                    if (metadata != null) {
+                        metadata.clear(pvs);
+                    }
+                    try {
+                        metadata = buildAnnotatedMetadata(clazz);
+                        this.injectionMetadataCache.put(cacheKey, metadata);
+                    } catch (NoClassDefFoundError err) {
+                        throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
+                                "] for annotation metadata: could not find class that it depends on", err);
+                    }
+                }
+            }
+        }
+        return metadata;
+    }
+
+    @Override
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+
+        for (Object object : injectedObjectsCache.values()) {
+            if (logger.isInfoEnabled()) {
+                logger.info(object + " was destroying!");
+            }
+
+            if (object instanceof DisposableBean) {
+                ((DisposableBean) object).destroy();
+            }
+        }
+
+        injectionMetadataCache.clear();
+        injectedObjectsCache.clear();
+
+        if (logger.isInfoEnabled()) {
+            logger.info(getClass() + " was destroying!");
+        }
+
+    }
+
+    @Override
+    public void setBeanClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    @Override
+    public void setEnvironment(Environment environment) {
+        this.environment = environment;
+    }
+
+    protected Environment getEnvironment() {
+        return environment;
+    }
+
+    protected ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    protected ConfigurableListableBeanFactory getBeanFactory() {
+        return beanFactory;
+    }
+
+    /**
+     * Get injected-object from specified {@link AnnotationAttributes annotation attributes} and Bean Class
+     *
+     * @param attributes      {@link AnnotationAttributes the annotation attributes}
+     * @param bean            Current bean that will be injected
+     * @param beanName        Current bean name that will be injected
+     * @param injectedType    the type of injected-object
+     * @param injectedElement {@link AnnotatedInjectElement}
+     * @return An injected object
+     * @throws Exception If getting is failed
+     */
+    protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
+                                       AnnotatedInjectElement injectedElement) throws Exception {
+
+        String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
+
+        Object injectedObject = injectedObjectsCache.get(cacheKey);
+
+        if (injectedObject == null) {
+            injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
+            // Customized inject-object if necessary
+            injectedObjectsCache.put(cacheKey, injectedObject);
+        }
+
+        return injectedObject;
+
+    }
+
+    /**
+     * Prepare injection data after found injection elements
+     * @param metadata
+     * @throws Exception
+     */
+    protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws Exception {
+    }
+
+    /**
+     * Subclass must implement this method to get injected-object. The context objects could help this method if
+     * necessary :
+     * <ul>
+     * <li>{@link #getBeanFactory() BeanFactory}</li>
+     * <li>{@link #getClassLoader() ClassLoader}</li>
+     * <li>{@link #getEnvironment() Environment}</li>
+     * </ul>
+     *
+     * @param attributes      {@link AnnotationAttributes the annotation attributes}
+     * @param bean            Current bean that will be injected
+     * @param beanName        Current bean name that will be injected
+     * @param injectedType    the type of injected-object
+     * @param injectedElement {@link AnnotatedInjectElement}
+     * @return The injected object
+     * @throws Exception If resolving an injected object is failed.
+     */
+    protected abstract Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
+                                                AnnotatedInjectElement injectedElement) throws Exception;
+
+    /**
+     * Build a cache key for injected-object. The context objects could help this method if
+     * necessary :
+     * <ul>
+     * <li>{@link #getBeanFactory() BeanFactory}</li>
+     * <li>{@link #getClassLoader() ClassLoader}</li>
+     * <li>{@link #getEnvironment() Environment}</li>
+     * </ul>
+     *
+     * @param attributes      {@link AnnotationAttributes the annotation attributes}
+     * @param bean            Current bean that will be injected
+     * @param beanName        Current bean name that will be injected
+     * @param injectedType    the type of injected-object
+     * @param injectedElement {@link AnnotatedInjectElement}
+     * @return Bean cache key
+     */
+    protected abstract String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
+                                                          Class<?> injectedType,
+                                                          AnnotatedInjectElement injectedElement);
+
+    /**
+     * {@link Annotation Annotated} {@link InjectionMetadata} implementation
+     */
+    protected class AnnotatedInjectionMetadata extends InjectionMetadata {
+
+        private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements;
+
+        private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements;
+
+        public AnnotatedInjectionMetadata(Class<?> targetClass, Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements,
+                                          Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements) {
+            super(targetClass, combine(fieldElements, methodElements));
+            this.fieldElements = fieldElements;
+            this.methodElements = methodElements;
+        }
+
+        public Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> getFieldElements() {
+            return fieldElements;
+        }
+
+        public Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> getMethodElements() {
+            return methodElements;
+        }
+    }
+
+    /**
+     * {@link Annotation Annotated} {@link Method} {@link InjectionMetadata.InjectedElement}
+     */
+    protected class AnnotatedInjectElement extends InjectionMetadata.InjectedElement {
+
+        protected final AnnotationAttributes attributes;
+
+        protected volatile String refKey;
+
+        protected AnnotatedInjectElement(Member member, PropertyDescriptor pd, AnnotationAttributes attributes) {
+            super(member, pd);
+            this.attributes = attributes;
+        }
+
+        @Override
+        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
+
+            Class<?> injectedType = getResourceType();
+            Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
+
+            if (member instanceof Field) {
+                Field field = (Field) member;
+                ReflectionUtils.makeAccessible(field);
+                field.set(bean, injectedObject);
+            } else if (member instanceof Method) {
+                Method method = (Method) member;
+                ReflectionUtils.makeAccessible(method);
+                method.invoke(bean, injectedObject);
+            }
+        }
+
+        public Class<?> getInjectedType() {
+            return getResourceType();
+        }
+    }
+
+    protected class AnnotatedMethodElement extends AnnotatedInjectElement {
+
+        protected final Method method;
+
+        protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) {
+            super(method, pd, attributes);
+            this.method = method;
+        }
+    }
+
+    public class AnnotatedFieldElement extends AnnotatedInjectElement {
+
+        protected final Field field;
+
+        protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) {
+            super(field, null, attributes);
+            this.field = field;
+        }
+
+    }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java
deleted file mode 100644
index 01186af..0000000
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.config.spring.beans.factory.annotation;
-
-import org.apache.dubbo.config.AbstractInterfaceConfig;
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.config.ModuleConfig;
-import org.apache.dubbo.config.MonitorConfig;
-import org.apache.dubbo.config.RegistryConfig;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.util.Assert;
-
-import java.lang.annotation.Annotation;
-import java.util.List;
-
-import static com.alibaba.spring.util.BeanFactoryUtils.getBeans;
-import static com.alibaba.spring.util.BeanFactoryUtils.getOptionalBean;
-
-/**
- * Abstract Configurable {@link Annotation} Bean Builder
- *
- * @since 2.5.7
- * @deprecated use {@link AnnotatedInterfaceConfigBeanBuilder}
- */
-@Deprecated
-abstract class AbstractAnnotationConfigBeanBuilder<A extends Annotation, B extends AbstractInterfaceConfig> {
-
-    protected final Log logger = LogFactory.getLog(getClass());
-
-    protected final A annotation;
-
-    protected final ApplicationContext applicationContext;
-
-    protected final ClassLoader classLoader;
-
-    protected Object bean;
-
-    protected Class<?> interfaceClass;
-
-    protected AbstractAnnotationConfigBeanBuilder(A annotation, ClassLoader classLoader,
-                                                  ApplicationContext applicationContext) {
-        Assert.notNull(annotation, "The Annotation must not be null!");
-        Assert.notNull(classLoader, "The ClassLoader must not be null!");
-        Assert.notNull(applicationContext, "The ApplicationContext must not be null!");
-        this.annotation = annotation;
-        this.applicationContext = applicationContext;
-        this.classLoader = classLoader;
-
-    }
-
-    /**
-     * Build {@link B}
-     *
-     * @return non-null
-     * @throws Exception
-     */
-    public final B build() throws Exception {
-
-        checkDependencies();
-
-        B bean = doBuild();
-
-        configureBean(bean);
-
-        if (logger.isInfoEnabled()) {
-            logger.info("The bean[type:" + bean.getClass().getSimpleName() + "] has been built.");
-        }
-
-        return bean;
-
-    }
-
-    private void checkDependencies() {
-
-    }
-
-    /**
-     * Builds {@link B Bean}
-     *
-     * @return {@link B Bean}
-     */
-    protected abstract B doBuild();
-
-
-    protected void configureBean(B bean) throws Exception {
-
-        preConfigureBean(annotation, bean);
-
-        configureRegistryConfigs(bean);
-
-        configureMonitorConfig(bean);
-
-        configureApplicationConfig(bean);
-
-        configureModuleConfig(bean);
-
-        postConfigureBean(annotation, bean);
-
-    }
-
-    protected abstract void preConfigureBean(A annotation, B bean) throws Exception;
-
-
-    private void configureRegistryConfigs(B bean) {
-
-        String[] registryConfigBeanIds = resolveRegistryConfigBeanNames(annotation);
-
-        List<RegistryConfig> registryConfigs = getBeans(applicationContext, registryConfigBeanIds, RegistryConfig.class);
-
-        bean.setRegistries(registryConfigs);
-
-    }
-
-    private void configureMonitorConfig(B bean) {
-
-        String monitorBeanName = resolveMonitorConfigBeanName(annotation);
-
-        MonitorConfig monitorConfig = getOptionalBean(applicationContext, monitorBeanName, MonitorConfig.class);
-
-        bean.setMonitor(monitorConfig);
-
-    }
-
-    private void configureApplicationConfig(B bean) {
-
-        String applicationConfigBeanName = resolveApplicationConfigBeanName(annotation);
-
-        ApplicationConfig applicationConfig =
-                getOptionalBean(applicationContext, applicationConfigBeanName, ApplicationConfig.class);
-
-        bean.setApplication(applicationConfig);
-
-    }
-
-    private void configureModuleConfig(B bean) {
-
-        String moduleConfigBeanName = resolveModuleConfigBeanName(annotation);
-
-        ModuleConfig moduleConfig =
-                getOptionalBean(applicationContext, moduleConfigBeanName, ModuleConfig.class);
-
-        bean.setModule(moduleConfig);
-
-    }
-
-    /**
-     * Resolves the bean name of {@link ModuleConfig}
-     *
-     * @param annotation {@link A}
-     * @return
-     */
-    protected abstract String resolveModuleConfigBeanName(A annotation);
-
-    /**
-     * Resolves the bean name of {@link ApplicationConfig}
-     *
-     * @param annotation {@link A}
-     * @return
-     */
-    protected abstract String resolveApplicationConfigBeanName(A annotation);
-
-
-    /**
-     * Resolves the bean ids of {@link RegistryConfig}
-     *
-     * @param annotation {@link A}
-     * @return non-empty array
-     */
-    protected abstract String[] resolveRegistryConfigBeanNames(A annotation);
-
-    /**
-     * Resolves the bean name of {@link MonitorConfig}
-     *
-     * @param annotation {@link A}
-     * @return
-     */
-    protected abstract String resolveMonitorConfigBeanName(A annotation);
-
-    /**
-     * Configures Bean
-     *
-     * @param annotation
-     * @param bean
-     */
-    protected abstract void postConfigureBean(A annotation, B bean) throws Exception;
-
-
-    public <T extends AbstractAnnotationConfigBeanBuilder<A, B>> T bean(Object bean) {
-        this.bean = bean;
-        return (T) this;
-    }
-
-    public <T extends AbstractAnnotationConfigBeanBuilder<A, B>> T interfaceClass(Class<?> interfaceClass) {
-        this.interfaceClass = interfaceClass;
-        return (T) this;
-    }
-
-}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java
deleted file mode 100644
index be951ae..0000000
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.config.spring.beans.factory.annotation;
-
-import org.apache.dubbo.config.AbstractInterfaceConfig;
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.config.ModuleConfig;
-import org.apache.dubbo.config.MonitorConfig;
-import org.apache.dubbo.config.RegistryConfig;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.annotation.AnnotationAttributes;
-import org.springframework.util.Assert;
-
-import java.lang.annotation.Annotation;
-import java.util.List;
-
-import static com.alibaba.spring.util.BeanFactoryUtils.getBeans;
-import static com.alibaba.spring.util.BeanFactoryUtils.getOptionalBean;
-
-/**
- * An Abstract Builder to build {@link AbstractInterfaceConfig Interface Config} Bean that annotated
- * some {@link Annotation annotation}.
- *
- * @see ReferenceBeanBuilder
- * @see AbstractInterfaceConfig
- * @see AnnotationAttributes
- * @since 2.7.3
- */
-public abstract class AnnotatedInterfaceConfigBeanBuilder<C extends AbstractInterfaceConfig> {
-
-    protected final Log logger = LogFactory.getLog(getClass());
-
-    protected final AnnotationAttributes attributes;
-
-    protected final ApplicationContext applicationContext;
-
-    protected final ClassLoader classLoader;
-
-    protected Object configBean;
-
-    protected Class<?> interfaceClass;
-
-    protected AnnotatedInterfaceConfigBeanBuilder(AnnotationAttributes attributes, ApplicationContext applicationContext) {
-        Assert.notNull(attributes, "The Annotation attributes must not be null!");
-        Assert.notNull(applicationContext, "The ApplicationContext must not be null!");
-        this.attributes = attributes;
-        this.applicationContext = applicationContext;
-        this.classLoader = applicationContext.getClassLoader() != null ?
-                applicationContext.getClassLoader() : Thread.currentThread().getContextClassLoader();
-    }
-
-    /**
-     * Build {@link C}
-     *
-     * @return non-null
-     * @throws Exception
-     */
-    public final C build() throws Exception {
-
-        checkDependencies();
-
-        C configBean = doBuild();
-
-        configureBean(configBean);
-
-        if (logger.isInfoEnabled()) {
-            logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
-        }
-
-        return configBean;
-
-    }
-
-    private void checkDependencies() {
-
-    }
-
-    /**
-     * Builds {@link C Bean}
-     *
-     * @return {@link C Bean}
-     */
-    protected abstract C doBuild();
-
-
-    protected void configureBean(C configBean) throws Exception {
-
-        preConfigureBean(attributes, configBean);
-
-        configureRegistryConfigs(configBean);
-
-        configureMonitorConfig(configBean);
-
-        configureApplicationConfig(configBean);
-
-        configureModuleConfig(configBean);
-
-        postConfigureBean(attributes, configBean);
-
-    }
-
-    protected abstract void preConfigureBean(AnnotationAttributes attributes, C configBean) throws Exception;
-
-
-    private void configureRegistryConfigs(C configBean) {
-
-        String[] registryConfigBeanIds = resolveRegistryConfigBeanNames(attributes);
-
-        List<RegistryConfig> registryConfigs = getBeans(applicationContext, registryConfigBeanIds, RegistryConfig.class);
-
-        configBean.setRegistries(registryConfigs);
-
-    }
-
-    private void configureMonitorConfig(C configBean) {
-
-        String monitorBeanName = resolveMonitorConfigBeanName(attributes);
-
-        MonitorConfig monitorConfig = getOptionalBean(applicationContext, monitorBeanName, MonitorConfig.class);
-
-        configBean.setMonitor(monitorConfig);
-
-    }
-
-    private void configureApplicationConfig(C configBean) {
-
-        String applicationConfigBeanName = resolveApplicationConfigBeanName(attributes);
-
-        ApplicationConfig applicationConfig =
-                getOptionalBean(applicationContext, applicationConfigBeanName, ApplicationConfig.class);
-
-        configBean.setApplication(applicationConfig);
-
-    }
-
-    private void configureModuleConfig(C configBean) {
-
-        String moduleConfigBeanName = resolveModuleConfigBeanName(attributes);
-
-        ModuleConfig moduleConfig =
-                getOptionalBean(applicationContext, moduleConfigBeanName, ModuleConfig.class);
-
-        configBean.setModule(moduleConfig);
-
-    }
-
-    /**
-     * Resolves the configBean name of {@link ModuleConfig}
-     *
-     * @param attributes {@link AnnotationAttributes}
-     * @return
-     */
-    protected abstract String resolveModuleConfigBeanName(AnnotationAttributes attributes);
-
-    /**
-     * Resolves the configBean name of {@link ApplicationConfig}
-     *
-     * @param attributes {@link AnnotationAttributes}
-     * @return
-     */
-    protected abstract String resolveApplicationConfigBeanName(AnnotationAttributes attributes);
-
-
-    /**
-     * Resolves the configBean ids of {@link RegistryConfig}
-     *
-     * @param attributes {@link AnnotationAttributes}
-     * @return non-empty array
-     */
-    protected abstract String[] resolveRegistryConfigBeanNames(AnnotationAttributes attributes);
-
-    /**
-     * Resolves the configBean name of {@link MonitorConfig}
-     *
-     * @param attributes {@link AnnotationAttributes}
-     * @return
-     */
-    protected abstract String resolveMonitorConfigBeanName(AnnotationAttributes attributes);
-
-    /**
-     * Configures Bean
-     *
-     * @param attributes
-     * @param configBean
-     */
-    protected abstract void postConfigureBean(AnnotationAttributes attributes, C configBean) throws Exception;
-
-
-    public <T extends AnnotatedInterfaceConfigBeanBuilder<C>> T configBean(Object configBean) {
-        this.configBean = configBean;
-        return (T) this;
-    }
-
-    public <T extends AnnotatedInterfaceConfigBeanBuilder<C>> T interfaceClass(Class<?> interfaceClass) {
-        this.interfaceClass = interfaceClass;
-        return (T) this;
-    }
-}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java
index 80cdfb7..88280a5 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java
@@ -33,7 +33,7 @@ import static com.alibaba.spring.util.AnnotationUtils.getAttributes;
  * @see PropertyValues
  * @since 2.5.11
  */
-class AnnotationPropertyValuesAdapter implements PropertyValues {
+public class AnnotationPropertyValuesAdapter implements PropertyValues {
 
     private final PropertyValues delegate;
 
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index 07de2ee..7255264 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -16,33 +16,44 @@
  */
 package org.apache.dubbo.config.spring.beans.factory.annotation;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.dubbo.common.utils.ClassUtils;
 import org.apache.dubbo.config.annotation.DubboReference;
-import org.apache.dubbo.config.annotation.DubboService;
 import org.apache.dubbo.config.annotation.Reference;
 import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.config.context.ConfigManager;
 import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.spring.ReferenceBeanManager;
 import org.apache.dubbo.config.spring.ServiceBean;
-
-import com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor;
+import org.apache.dubbo.config.spring.util.DubboBeanUtils;
 import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.annotation.InjectionMetadata;
+import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.config.RuntimeBeanReference;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
-import static com.alibaba.spring.util.AnnotationUtils.getAttributes;
 import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create;
 import static org.springframework.util.StringUtils.hasText;
 
@@ -55,8 +66,8 @@ import static org.springframework.util.StringUtils.hasText;
  * @see com.alibaba.dubbo.config.annotation.Reference
  * @since 2.5.7
  */
-public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
-        ApplicationContextAware {
+public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor
+        implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
 
     /**
      * The bean name of {@link ReferenceAnnotationBeanPostProcessor}
@@ -68,8 +79,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
      */
     private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32);
 
-    private final ConcurrentMap<String, ReferenceBean<?>> referenceBeanCache =
-            new ConcurrentHashMap<>(CACHE_SIZE);
+    private final Log logger = LogFactory.getLog(getClass());
 
     private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedFieldReferenceBeanCache =
             new ConcurrentHashMap<>(CACHE_SIZE);
@@ -79,6 +89,9 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
     private ApplicationContext applicationContext;
 
+    private ReferenceBeanManager referenceBeanManager;
+    private BeanDefinitionRegistry beanDefinitionRegistry;
+
     /**
      * {@link com.alibaba.dubbo.config.annotation.Reference @com.alibaba.dubbo.config.annotation.Reference} has been supported since 2.7.3
      * <p>
@@ -88,96 +101,175 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
         super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
     }
 
-    /**
-     * Gets all beans of {@link ReferenceBean}
-     *
-     * @return non-null read-only {@link Collection}
-     * @since 2.5.9
-     */
-    public Collection<ReferenceBean<?>> getReferenceBeans() {
-        return referenceBeanCache.values();
+    @Override
+    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+        this.beanDefinitionRegistry = registry;
     }
 
-    /**
-     * Get {@link ReferenceBean} {@link Map} in injected field.
-     *
-     * @return non-null {@link Map}
-     * @since 2.5.11
-     */
-    public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedFieldReferenceBeanMap() {
-        return Collections.unmodifiableMap(injectedFieldReferenceBeanCache);
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        DubboBeanUtils.registerBeansIfNotExists(beanDefinitionRegistry);
+
+        String[] beanNames = beanFactory.getBeanDefinitionNames();
+        for (String beanName : beanNames) {
+            Class<?> beanType;
+            if (beanFactory.isFactoryBean(beanName)){
+                BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
+                if (isReferenceBean(beanDefinition)) {
+                    continue;
+                }
+                String beanClassName = beanDefinition.getBeanClassName();
+                beanType = ClassUtils.resolveClass(beanClassName, getClassLoader());
+            } else {
+                beanType = beanFactory.getType(beanName);
+            }
+            if (beanType != null) {
+                AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
+                try {
+                    prepareInjection(metadata);
+                } catch (Exception e) {
+                    logger.warn("Prepare dubbo reference injection element failed", e);
+                }
+            }
+        }
     }
 
-    /**
-     * Get {@link ReferenceBean} {@link Map} in injected method.
-     *
-     * @return non-null {@link Map}
-     * @since 2.5.11
-     */
-    public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedMethodReferenceBeanMap() {
-        return Collections.unmodifiableMap(injectedMethodReferenceBeanCache);
+    @Override
+    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
+        if (beanType != null) {
+            if (isReferenceBean(beanDefinition)) {
+                //mark property value as optional
+                List<PropertyValue> propertyValues = beanDefinition.getPropertyValues().getPropertyValueList();
+                for (PropertyValue propertyValue : propertyValues) {
+                    propertyValue.setOptional(true);
+                }
+            } else {
+                AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
+                metadata.checkConfigMembers(beanDefinition);
+                try {
+                    prepareInjection(metadata);
+                } catch (Exception e) {
+                    logger.warn("Prepare dubbo reference injection element failed", e);
+                }
+            }
+        }
     }
 
     @Override
-    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
-                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
-        /**
-         * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
-         */
-        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
-
-        /**
-         * The name of bean that is declared by {@link Reference @Reference} annotation injection
-         */
-        String referenceBeanName = getReferenceBeanName(attributes, injectedType);
+    public PropertyValues postProcessPropertyValues(
+            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
+
+        try {
+            AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
+            prepareInjection(metadata);
+            metadata.inject(bean, beanName, pvs);
+        } catch (BeanCreationException ex) {
+            throw ex;
+        } catch (Throwable ex) {
+            throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+                    + " dependencies is failed", ex);
+        }
+        return pvs;
+    }
 
-        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
+    private boolean isReferenceBean(BeanDefinition beanDefinition) {
+        return ReferenceBean.class.getName().equals(beanDefinition.getBeanClassName());
+    }
 
-        boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);
+    protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws Exception {
+        //find and registry bean definition for @DubboReference/@Reference
+        for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
+            if (fieldElement.refKey != null) {
+                continue;
+            }
+            Class<?> injectedType = fieldElement.field.getType();
+            AnnotationAttributes attributes = fieldElement.attributes;
+            ReferenceBean referenceBean = getReferenceBean(injectedType, attributes);
 
-        prepareReferenceBean(referencedBeanName, referenceBean, localServiceBean);
+            //associate fieldElement and reference bean
+            fieldElement.refKey = referenceBean.getId();
+            injectedFieldReferenceBeanCache.put(fieldElement, referenceBean);
 
-        registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);
+        }
 
-        cacheInjectedReferenceBean(referenceBean, injectedElement);
+        for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
+            if (methodElement.refKey != null) {
+                continue;
+            }
+            Class<?> injectedType = methodElement.getInjectedType();
+            AnnotationAttributes attributes = methodElement.attributes;
+            ReferenceBean referenceBean = getReferenceBean(injectedType, attributes);
 
-        return referenceBean.get();
+            //associate fieldElement and reference bean
+            methodElement.refKey = referenceBean.getId();
+            injectedMethodReferenceBeanCache.put(methodElement, referenceBean);
+        }
     }
 
-    /**
-     * Register an instance of {@link ReferenceBean} as a Spring Bean
-     *
-     * @param referencedBeanName The name of bean that annotated Dubbo's {@link Service @Service} in the Spring {@link ApplicationContext}
-     * @param referenceBean      the instance of {@link ReferenceBean} is about to register into the Spring {@link ApplicationContext}
-     * @param attributes         the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
-     * @param localServiceBean   Is Local Service bean or not
-     * @param interfaceClass     the {@link Class class} of Service interface
-     * @since 2.7.3
-     */
-    private void registerReferenceBean(String referencedBeanName, ReferenceBean referenceBean,
-                                       AnnotationAttributes attributes,
-                                       boolean localServiceBean, Class<?> interfaceClass) {
-
-        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+    private ReferenceBean getReferenceBean(Class<?> injectedType, AnnotationAttributes attributes) throws Exception {
+        // referenceBeanName
+        String referenceBeanName = getReferenceBeanName(attributes, injectedType);
 
-        String beanName = getReferenceBeanName(attributes, interfaceClass);
+        // reuse exist reference bean?
+        ReferenceBean referenceBean = referenceBeanManager.get(referenceBeanName);
 
-        if (localServiceBean) {  // If @Service bean is local one
+        //create referenceBean
+        if (referenceBean == null) {
+            //handle injvm/localServiceBean
             /**
-             * Get  the @Service's BeanDefinition from {@link BeanFactory}
-             * Refer to {@link ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition}
+             * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
              */
-            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(referencedBeanName);
-            RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) beanDefinition.getPropertyValues().get("ref");
-            // The name of bean annotated @Service
-            String serviceBeanName = runtimeBeanReference.getBeanName();
-            // register Alias rather than a new bean name, in order to reduce duplicated beans
-            beanFactory.registerAlias(serviceBeanName, beanName);
-        } else { // Remote @Service Bean
-            if (!beanFactory.containsBean(beanName)) {
-                beanFactory.registerSingleton(beanName, referenceBean);
+            String localServiceBeanName = buildReferencedBeanName(attributes, injectedType);
+            boolean localServiceBean = isLocalServiceBean(localServiceBeanName, attributes);
+            if (localServiceBean) { // If the local @Service Bean exists
+                attributes.put("injvm", Boolean.TRUE);
+                //  Issue : https://github.com/apache/dubbo/issues/6224
+                //exportServiceBeanIfNecessary(localServiceBeanName); // If the referenced ServiceBean exits, export it immediately
+            }
+
+            //check interfaceClass
+            if (attributes.get("interfaceName") == null && attributes.get("interfaceClass") == null) {
+                Class<?> interfaceClass = injectedType;
+                Assert.isTrue(interfaceClass.isInterface(),
+                        "The class of field or method that was annotated @DubboReference is not an interface!");
+                attributes.put("interfaceClass", interfaceClass);
+            }
+
+            //init reference bean
+            try {
+                //registry referenceBean
+                RootBeanDefinition beanDefinition = new RootBeanDefinition();
+                beanDefinition.setBeanClassName(ReferenceBean.class.getName());
+                //set autowireCandidate to false for local call, avoiding multiple candidate beans for @Autowire
+                beanDefinition.setAutowireCandidate(!localServiceBean);
+                //beanDefinition.getPropertyValues()
+
+                referenceBean = new ReferenceBean(attributes);
+                referenceBean.setId(referenceBeanName);
+                referenceBean.setApplicationContext(applicationContext);
+                referenceBean.setBeanClassLoader(getClassLoader());
+                referenceBean.afterPropertiesSet();
+
+                beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
+                getBeanFactory().registerSingleton(referenceBeanName, referenceBean);
+
+                referenceBeanManager.addReference(referenceBean);
+            } catch (Exception e) {
+                throw new Exception("Create dubbo reference bean failed", e);
             }
         }
+        return referenceBean;
+    }
+
+    @Override
+    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
+                                       AnnotatedInjectElement injectedElement) throws Exception {
+
+        if (injectedElement.refKey == null) {
+            throw new IllegalStateException("The AnnotatedInjectElement of @DubboReference should be inited before injection");
+        }
+
+        return getBeanFactory().getBean(injectedElement.refKey);
     }
 
     /**
@@ -211,10 +303,19 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
         if (!attributes.isEmpty()) {
             beanNameBuilder.append('(');
-            for (Map.Entry<String, Object> entry : attributes.entrySet()) {
-                beanNameBuilder.append(entry.getKey())
+            //sort attributes keys
+            List<String> sortedAttrKeys = new ArrayList<>(attributes.keySet());
+            Collections.sort(sortedAttrKeys);
+            for (String key : sortedAttrKeys) {
+                Object value = attributes.get(key);
+                //handle method array, generic array
+                if (value!=null && value.getClass().isArray()) {
+                    Object[] array = ObjectUtils.toObjectArray(value);
+                    value = Arrays.toString(array);
+                }
+                beanNameBuilder.append(key)
                         .append('=')
-                        .append(entry.getValue())
+                        .append(value)
                         .append(',');
             }
             // replace the latest "," to be ")"
@@ -223,6 +324,9 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
         beanNameBuilder.append(" ").append(interfaceClass.getName());
 
+        //TODO remove invalid chars
+        //TODO test @DubboReference with Method config
+        //.replaceAll("[<>]", "_")
         return beanNameBuilder.toString();
     }
 
@@ -233,8 +337,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
      * @return If the target referenced bean is existed, return <code>true</code>, or <code>false</code>
      * @since 2.7.6
      */
-    private boolean isLocalServiceBean(String referencedBeanName, ReferenceBean referenceBean, AnnotationAttributes attributes) {
-        return existsServiceBean(referencedBeanName) && !isRemoteReferenceBean(referenceBean, attributes);
+    private boolean isLocalServiceBean(String referencedBeanName, AnnotationAttributes attributes) {
+        return existsServiceBean(referencedBeanName) && !isRemoteReferenceBean(attributes);
     }
 
     /**
@@ -250,29 +354,12 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
     }
 
-    private boolean isRemoteReferenceBean(ReferenceBean referenceBean, AnnotationAttributes attributes) {
-        boolean remote = Boolean.FALSE.equals(referenceBean.isInjvm()) || Boolean.FALSE.equals(attributes.get("injvm"));
+    private boolean isRemoteReferenceBean(AnnotationAttributes attributes) {
+        //TODO Can the interface be called locally when injvm is empty? https://github.com/apache/dubbo/issues/6842
+        boolean remote = Boolean.FALSE.equals(attributes.get("injvm"));
         return remote;
     }
 
-    /**
-     * Prepare {@link ReferenceBean}
-     *
-     * @param referencedBeanName The name of bean that annotated Dubbo's {@link DubboService @DubboService}
-     *                           in the Spring {@link ApplicationContext}
-     * @param referenceBean      the instance of {@link ReferenceBean}
-     * @param localServiceBean   Is Local Service bean or not
-     * @since 2.7.8
-     */
-    private void prepareReferenceBean(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean) {
-        //  Issue : https://github.com/apache/dubbo/issues/6224
-        if (localServiceBean) { // If the local @Service Bean exists
-            referenceBean.setInjvm(Boolean.TRUE);
-            exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately
-        }
-    }
-
-
     private void exportServiceBeanIfNecessary(String referencedBeanName) {
         if (existsServiceBean(referencedBeanName)) {
             ServiceBean serviceBean = getServiceBean(referencedBeanName);
@@ -288,10 +375,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
     @Override
     protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
-                                                 Class<?> injectedType, InjectionMetadata.InjectedElement injectedElement) {
-        return buildReferencedBeanName(attributes, injectedType) +
-                "#source=" + (injectedElement.getMember()) +
-                "#attributes=" + getAttributes(attributes, getEnvironment());
+                                                 Class<?> injectedType, AnnotatedInjectElement injectedElement) {
+        return generateReferenceBeanName(attributes, injectedType);
     }
 
     /**
@@ -304,44 +389,45 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
         return serviceBeanNameBuilder.build();
     }
 
-    private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
-                                                     Class<?> referencedType)
-            throws Exception {
-
-        ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);
-
-        if (referenceBean == null) {
-            ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
-                    .create(attributes, applicationContext)
-                    .interfaceClass(referencedType);
-            referenceBean = beanBuilder.build();
-            referenceBeanCache.put(referenceBeanName, referenceBean);
-        } else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
-            throw new IllegalArgumentException("reference bean name " + referenceBeanName + " has been duplicated, but interfaceClass " +
-                    referenceBean.getInterfaceClass().getName() + " cannot be assigned to " + referencedType.getName());
-        }
-        return referenceBean;
-    }
-
-    private void cacheInjectedReferenceBean(ReferenceBean referenceBean,
-                                            InjectionMetadata.InjectedElement injectedElement) {
-        if (injectedElement.getMember() instanceof Field) {
-            injectedFieldReferenceBeanCache.put(injectedElement, referenceBean);
-        } else if (injectedElement.getMember() instanceof Method) {
-            injectedMethodReferenceBeanCache.put(injectedElement, referenceBean);
-        }
-    }
-
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         this.applicationContext = applicationContext;
+        this.referenceBeanManager = applicationContext.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
     }
 
     @Override
     public void destroy() throws Exception {
         super.destroy();
-        this.referenceBeanCache.clear();
         this.injectedFieldReferenceBeanCache.clear();
         this.injectedMethodReferenceBeanCache.clear();
     }
+
+    /**
+     * Gets all beans of {@link ReferenceBean}
+     * @deprecated  use {@link ConfigManager#getReferences()} instead
+     */
+    @Deprecated
+    public Collection<ReferenceBean<?>> getReferenceBeans() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Get {@link ReferenceBean} {@link Map} in injected field.
+     *
+     * @return non-null {@link Map}
+     * @since 2.5.11
+     */
+    public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedFieldReferenceBeanMap() {
+        return Collections.unmodifiableMap(injectedFieldReferenceBeanCache);
+    }
+
+    /**
+     * Get {@link ReferenceBean} {@link Map} in injected method.
+     *
+     * @return non-null {@link Map}
+     * @since 2.5.11
+     */
+    public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedMethodReferenceBeanMap() {
+        return Collections.unmodifiableMap(injectedMethodReferenceBeanCache);
+    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java
index f6aacf4..f58c93b 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java
@@ -16,13 +16,18 @@
  */
 package org.apache.dubbo.config.spring.beans.factory.annotation;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.ConsumerConfig;
 import org.apache.dubbo.config.MethodConfig;
+import org.apache.dubbo.config.ModuleConfig;
+import org.apache.dubbo.config.MonitorConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.dubbo.config.annotation.Method;
-import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.spring.ReferenceBean;
-
 import org.springframework.beans.propertyeditors.StringTrimmerEditor;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.annotation.AnnotationAttributes;
@@ -31,85 +36,195 @@ import org.springframework.util.StringUtils;
 import org.springframework.validation.DataBinder;
 
 import java.beans.PropertyEditorSupport;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
 import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
-import static com.alibaba.spring.util.AnnotationUtils.getAttributes;
+import static com.alibaba.spring.util.BeanFactoryUtils.getBeans;
 import static com.alibaba.spring.util.BeanFactoryUtils.getOptionalBean;
 import static com.alibaba.spring.util.ObjectUtils.of;
-import static org.apache.dubbo.config.spring.util.DubboAnnotationUtils.resolveServiceInterfaceClass;
-import static org.springframework.core.annotation.AnnotationAttributes.fromMap;
 import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
 
 /**
- * {@link ReferenceBean} Builder
+ * {@link ReferenceConfig} Builder for @{@link DubboReference}
  *
- * @since 2.5.7
+ * @since 3.0
  */
-class ReferenceBeanBuilder extends AnnotatedInterfaceConfigBeanBuilder<ReferenceBean> {
+public class ReferenceBeanBuilder {
 
     // Ignore those fields
-    static final String[] IGNORE_FIELD_NAMES = of("application", "module", "consumer", "monitor", "registry");
+    static final String[] IGNORE_FIELD_NAMES = of("application", "module", "consumer", "monitor", "registry", "interfaceClass");
+
+    protected final Log logger = LogFactory.getLog(getClass());
+
+    protected final AnnotationAttributes attributes;
+
+    protected final ApplicationContext applicationContext;
+
+    protected final ClassLoader classLoader;
+
+    protected Class<?> defaultInterfaceClass;
 
     private ReferenceBeanBuilder(AnnotationAttributes attributes, ApplicationContext applicationContext) {
-        super(attributes, applicationContext);
+        Assert.notNull(attributes, "The Annotation attributes must not be null!");
+        Assert.notNull(applicationContext, "The ApplicationContext must not be null!");
+        this.attributes = attributes;
+        this.applicationContext = applicationContext;
+        this.classLoader = applicationContext.getClassLoader() != null ?
+                applicationContext.getClassLoader() : Thread.currentThread().getContextClassLoader();
     }
 
-    private void configureInterface(AnnotationAttributes attributes, ReferenceBean referenceBean) {
-        Boolean generic = getAttribute(attributes, "generic");
-        if (generic != null && generic) {
-            // it's a generic reference
-            String interfaceClassName = getAttribute(attributes, "interfaceName");
-            Assert.hasText(interfaceClassName,
-                    "@Reference interfaceName() must be present when reference a generic service!");
-            referenceBean.setInterface(interfaceClassName);
-            return;
+    public final ReferenceConfig build() throws Exception {
+
+        ReferenceConfig configBean = new ReferenceConfig();
+
+        configureBean(configBean);
+
+        if (logger.isInfoEnabled()) {
+            logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
         }
 
-        Class<?> serviceInterfaceClass = resolveServiceInterfaceClass(attributes, interfaceClass);
+        return configBean;
+
+    }
+
+    protected void configureBean(ReferenceConfig configBean) throws Exception {
+
+        populateBean(attributes, configBean);
+
+        configureRegistryConfigs(configBean);
+
+        configureMonitorConfig(configBean);
+
+        configureApplicationConfig(configBean);
+
+        configureModuleConfig(configBean);
 
-        Assert.isTrue(serviceInterfaceClass.isInterface(),
-                "The class of field or method that was annotated @Reference is not an interface!");
+        //interfaceClass
+        configureInterface(attributes, configBean);
 
-        referenceBean.setInterface(serviceInterfaceClass);
+        configureConsumerConfig(attributes, configBean);
+
+        configureMethodConfig(attributes, configBean);
+
+        //bean.setApplicationContext(applicationContext);
+        //bean.afterPropertiesSet();
 
     }
 
+    private void configureRegistryConfigs(ReferenceConfig configBean) {
+
+        String[] registryConfigBeanIds = getAttribute(attributes, "registry");
+
+        List<RegistryConfig> registryConfigs = getBeans(applicationContext, registryConfigBeanIds, RegistryConfig.class);
+
+        configBean.setRegistries(registryConfigs);
+
+    }
+
+    private void configureMonitorConfig(ReferenceConfig configBean) {
+
+        String monitorBeanName = getAttribute(attributes, "monitor");
 
-    private void configureConsumerConfig(AnnotationAttributes attributes, ReferenceBean<?> referenceBean) {
+        MonitorConfig monitorConfig = getOptionalBean(applicationContext, monitorBeanName, MonitorConfig.class);
 
-        String consumerBeanName = getAttribute(attributes, "consumer");
+        configBean.setMonitor(monitorConfig);
 
-        ConsumerConfig consumerConfig = getOptionalBean(applicationContext, consumerBeanName, ConsumerConfig.class);
+    }
+
+    private void configureApplicationConfig(ReferenceConfig configBean) {
+
+        String applicationConfigBeanName = getAttribute(attributes, "application");
 
-        referenceBean.setConsumer(consumerConfig);
+        ApplicationConfig applicationConfig =
+                getOptionalBean(applicationContext, applicationConfigBeanName, ApplicationConfig.class);
+
+        configBean.setApplication(applicationConfig);
 
     }
 
-    void configureMethodConfig(AnnotationAttributes attributes, ReferenceBean<?> referenceBean) {
-        Method[] methods = (Method[]) attributes.get("methods");
-        List<MethodConfig> methodConfigs = MethodConfig.constructMethodConfig(methods);
-        if (!methodConfigs.isEmpty()) {
-            referenceBean.setMethods(methodConfigs);
+    private void configureModuleConfig(ReferenceConfig configBean) {
+
+        String moduleConfigBeanName = getAttribute(attributes, "module");
+
+        ModuleConfig moduleConfig =
+                getOptionalBean(applicationContext, moduleConfigBeanName, ModuleConfig.class);
+
+        configBean.setModule(moduleConfig);
+
+    }
+
+    private void configureInterface(AnnotationAttributes attributes, ReferenceConfig referenceBean) {
+        if (referenceBean.getInterface() == null) {
+
+            Object genericValue = getAttribute(attributes, "generic");
+            String generic = (genericValue != null) ? genericValue.toString() : null;
+            referenceBean.setGeneric(generic);
+
+            String interfaceClassName = getAttribute(attributes, "interfaceName");
+            if (StringUtils.hasText(interfaceClassName)) {
+                referenceBean.setInterface(interfaceClassName);
+            } else {
+                Class<?> interfaceClass = getAttribute(attributes, "interfaceClass");
+                if (void.class.equals(interfaceClass)) { // default or set void.class for purpose.
+                    interfaceClass = null;
+                }
+                if (interfaceClass != null) {
+                    Assert.isTrue(interfaceClass.isInterface(),
+                            "The interfaceClass of @DubboReference is not an interface: "+interfaceClass.getName());
+                }
+                // Not present 'interfaceClass' attribute, use default injection type of annotated
+                if (interfaceClass == null && defaultInterfaceClass != null) {
+                    interfaceClass = defaultInterfaceClass;
+                    Assert.isTrue(interfaceClass.isInterface(),
+                            "The class of field or method that was annotated @DubboReference is not an interface!");
+                }
+                // Convert to interface class name, InterfaceClass will be determined later
+                referenceBean.setInterface(interfaceClass.getName());
+            }
+        }
+    }
+
+
+    private void configureConsumerConfig(AnnotationAttributes attributes, ReferenceConfig<?> referenceBean) {
+        ConsumerConfig consumerConfig = null;
+        Object consumer = getAttribute(attributes, "consumer");
+        if (consumer != null) {
+            if (consumer instanceof String) {
+                consumerConfig = getOptionalBean(applicationContext, (String) consumer, ConsumerConfig.class);
+            } else if (consumer instanceof ConsumerConfig) {
+                consumerConfig = (ConsumerConfig) consumer;
+            } else {
+                throw new IllegalArgumentException("Unexpected 'consumer' attribute value: "+consumer);
+            }
+            referenceBean.setConsumer(consumerConfig);
         }
     }
 
-    @Override
-    protected ReferenceBean doBuild() {
-        return new ReferenceBean<Object>();
+    void configureMethodConfig(AnnotationAttributes attributes, ReferenceConfig<?> referenceBean) {
+        Object value = attributes.get("methods");
+        if (value instanceof Method[]) {
+            Method[] methods = (Method[]) value;
+            List<MethodConfig> methodConfigs = MethodConfig.constructMethodConfig(methods);
+            if (!methodConfigs.isEmpty()) {
+                referenceBean.setMethods(methodConfigs);
+            }
+        } else if (value instanceof MethodConfig[]) {
+            MethodConfig[] methodConfigs = (MethodConfig[]) value;
+            referenceBean.setMethods(Arrays.asList(methodConfigs));
+        }
     }
 
-    @Override
-    protected void preConfigureBean(AnnotationAttributes attributes, ReferenceBean referenceBean) {
-        Assert.notNull(interfaceClass, "The interface class must set first!");
+    protected void populateBean(AnnotationAttributes attributes, ReferenceConfig referenceBean) {
+        Assert.notNull(defaultInterfaceClass, "The default interface class must set first!");
         DataBinder dataBinder = new DataBinder(referenceBean);
         // Register CustomEditors for special fields
         dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true));
         dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true));
         dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() {
             @Override
-            public void setAsText(String text) throws java.lang.IllegalArgumentException {
+            public void setAsText(String text) throws IllegalArgumentException {
                 // Trim all whitespace
                 String content = StringUtils.trimAllWhitespace(text);
                 if (!StringUtils.hasText(content)) { // No content , ignore directly
@@ -130,49 +245,13 @@ class ReferenceBeanBuilder extends AnnotatedInterfaceConfigBeanBuilder<Reference
 
     }
 
-
-    @Override
-    protected String resolveModuleConfigBeanName(AnnotationAttributes attributes) {
-        return getAttribute(attributes, "module");
-    }
-
-    @Override
-    protected String resolveApplicationConfigBeanName(AnnotationAttributes attributes) {
-        return getAttribute(attributes, "application");
-    }
-
-    @Override
-    protected String[] resolveRegistryConfigBeanNames(AnnotationAttributes attributes) {
-        return getAttribute(attributes, "registry");
-    }
-
-    @Override
-    protected String resolveMonitorConfigBeanName(AnnotationAttributes attributes) {
-        return getAttribute(attributes, "monitor");
-    }
-
-    @Override
-    protected void postConfigureBean(AnnotationAttributes attributes, ReferenceBean bean) throws Exception {
-
-        bean.setApplicationContext(applicationContext);
-
-        configureInterface(attributes, bean);
-
-        configureConsumerConfig(attributes, bean);
-
-        configureMethodConfig(attributes, bean);
-
-        bean.afterPropertiesSet();
-
+    public static ReferenceBeanBuilder create(AnnotationAttributes attributes, ApplicationContext applicationContext) {
+        return new ReferenceBeanBuilder(attributes, applicationContext);
     }
 
-    @Deprecated
-    public static ReferenceBeanBuilder create(Reference reference, ClassLoader classLoader,
-                                              ApplicationContext applicationContext) {
-        return create(fromMap(getAttributes(reference, applicationContext.getEnvironment(), true)), applicationContext);
+    public ReferenceBeanBuilder defaultInterfaceClass(Class<?> interfaceClass) {
+        this.defaultInterfaceClass = interfaceClass;
+        return this;
     }
 
-    public static ReferenceBeanBuilder create(AnnotationAttributes attributes, ApplicationContext applicationContext) {
-        return new ReferenceBeanBuilder(attributes, applicationContext);
-    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java
index b1c9a30..f6dbe69 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java
@@ -289,12 +289,12 @@ public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProc
 
         String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
 
-        AbstractBeanDefinition serviceBeanDefinition =
-                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
-
         // ServiceBean Bean name
         String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
 
+        AbstractBeanDefinition serviceBeanDefinition =
+                buildServiceBeanDefinition(beanName, service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
+
         if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
             registry.registerBeanDefinition(beanName, serviceBeanDefinition);
 
@@ -376,6 +376,8 @@ public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProc
     /**
      * Build the {@link AbstractBeanDefinition Bean Definition}
      *
+     *
+     * @param beanName
      * @param serviceAnnotation
      * @param serviceAnnotationAttributes
      * @param interfaceClass
@@ -383,7 +385,7 @@ public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProc
      * @return
      * @since 2.7.3
      */
-    private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
+    private AbstractBeanDefinition buildServiceBeanDefinition(String beanName, Annotation serviceAnnotation,
                                                               AnnotationAttributes serviceAnnotationAttributes,
                                                               Class<?> interfaceClass,
                                                               String annotatedServiceBeanName) {
@@ -399,6 +401,8 @@ public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProc
 
         propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
 
+        //set config id, for ConfigManager cache key
+        builder.addPropertyValue("id", beanName);
         // References "ref" property to annotated-@Service Bean
         addPropertyReference(builder, "ref", annotatedServiceBeanName);
         // Set interface
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
index 084479a..70f4dfe 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -26,16 +26,17 @@ import org.apache.dubbo.config.ConsumerConfig;
 import org.apache.dubbo.config.MethodConfig;
 import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.ProviderConfig;
+import org.apache.dubbo.config.ReferenceConfig;
 import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.spring.ReferenceBean;
 import org.apache.dubbo.config.spring.ServiceBean;
-
 import org.springframework.beans.PropertyValue;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.config.TypedStringValue;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
 import org.springframework.beans.factory.support.ManagedList;
 import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -50,7 +51,9 @@ import org.w3c.dom.NodeList;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
 
@@ -71,6 +74,7 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
     private static final String METHOD = "Method";
     private final Class<?> beanClass;
     private final boolean required;
+    private static Map<String, Map<String, Class>> beanPropsCache = new HashMap<>();
 
     public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
         this.beanClass = beanClass;
@@ -101,6 +105,8 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
                 id = generatedBeanName + (counter++);
             }
         }
+
+        Set<String> processedProps = new HashSet<>();
         if (StringUtils.isNotEmpty(id)) {
             if (parserContext.getRegistry().containsBeanDefinition(id)) {
                 throw new IllegalStateException("Duplicate spring bean id " + id);
@@ -128,13 +134,133 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
                 parseProperties(element.getChildNodes(), classDefinition, parserContext);
                 beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
             }
-        } else if (ProviderConfig.class.equals(beanClass)) {
+
+        }
+
+
+        Map<String, Class> beanPropsMap = beanPropsCache.get(beanClass.getName());
+        if (beanPropsMap == null) {
+            beanPropsMap = new HashMap<>();
+            beanPropsCache.put(beanClass.getName(), beanPropsMap);
+
+            if (ReferenceBean.class.equals(beanClass)) {
+                //extract bean props from ReferenceConfig
+                getPropertyMap(ReferenceConfig.class, beanPropsMap);
+            } else {
+                getPropertyMap(beanClass, beanPropsMap);
+            }
+        }
+
+        ManagedMap parameters = null;
+        for (Map.Entry<String, Class> entry : beanPropsMap.entrySet()) {
+            String beanProperty = entry.getKey();
+            Class type = entry.getValue();
+            String property = StringUtils.camelToSplitName(beanProperty, "-");
+            processedProps.add(property);
+            if ("parameters".equals(property)) {
+                parameters = parseParameters(element.getChildNodes(), beanDefinition, parserContext);
+            } else if ("methods".equals(property)) {
+                parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
+            } else if ("arguments".equals(property)) {
+                parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
+            } else {
+                String value = resolveAttribute(element, property, parserContext);
+                if (value != null) {
+                    value = value.trim();
+                    if (value.length() > 0) {
+                        if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
+                            RegistryConfig registryConfig = new RegistryConfig();
+                            registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
+                            beanDefinition.getPropertyValues().addPropertyValue(beanProperty, registryConfig);
+                        } else if ("provider".equals(property) || "registry".equals(property) || ("protocol".equals(property) && AbstractServiceConfig.class.isAssignableFrom(beanClass))) {
+                            /**
+                             * For 'provider' 'protocol' 'registry', keep literal value (should be id/name) and set the value to 'registryIds' 'providerIds' protocolIds'
+                             * The following process should make sure each id refers to the corresponding instance, here's how to find the instance for different use cases:
+                             * 1. Spring, check existing bean by id, see{@link ServiceBean#afterPropertiesSet()}; then try to use id to find configs defined in remote Config Center
+                             * 2. API, directly use id to find configs defined in remote Config Center; if all config instances are defined locally, please use {@link ServiceConfig#setRegistries(List)}
+                             */
+                            beanDefinition.getPropertyValues().addPropertyValue(beanProperty + "Ids", value);
+                        } else {
+                            Object reference;
+                            if (isPrimitive(type)) {
+                                value = getCompatibleDefaultValue(property, value);
+                                reference = value;
+                            } else if (ONRETURN.equals(property) || ONTHROW.equals(property) || ONINVOKE.equals(property)) {
+                                int index = value.lastIndexOf(".");
+                                String ref = value.substring(0, index);
+                                String method = value.substring(index + 1);
+                                reference = new RuntimeBeanReference(ref);
+                                beanDefinition.getPropertyValues().addPropertyValue(property + METHOD, method);
+                            } else {
+                                if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
+                                    BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
+                                    if (!refBean.isSingleton()) {
+                                        throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
+                                    }
+                                }
+                                reference = new RuntimeBeanReference(value);
+                            }
+                            beanDefinition.getPropertyValues().addPropertyValue(beanProperty, reference);
+                        }
+                    }
+                }
+            }
+        }
+
+        NamedNodeMap attributes = element.getAttributes();
+        int len = attributes.getLength();
+        for (int i = 0; i < len; i++) {
+            Node node = attributes.item(i);
+            String name = node.getLocalName();
+            if (!processedProps.contains(name)) {
+                if (parameters == null) {
+                    parameters = new ManagedMap();
+                }
+                String value = node.getNodeValue();
+                parameters.put(name, new TypedStringValue(value, String.class));
+            }
+        }
+        if (parameters != null) {
+            beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
+        }
+
+        // post-process after parse attributes
+        if (ProviderConfig.class.equals(beanClass)) {
             parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
         } else if (ConsumerConfig.class.equals(beanClass)) {
             parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
+        } else if (ReferenceBean.class.equals(beanClass)) {
+            configReferenceBean(element, parserContext, beanDefinition, null);
         }
-        Set<String> props = new HashSet<>();
-        ManagedMap parameters = null;
+
+        return beanDefinition;
+    }
+
+    private static void configReferenceBean(Element element, ParserContext parserContext, RootBeanDefinition beanDefinition, BeanDefinition consumerDefinition) {
+        // process interface class
+        String interfaceClassName = resolveAttribute(element, "interface", parserContext);
+        String generic = resolveAttribute(element, "generic", parserContext);
+        if (StringUtils.isBlank(generic) && consumerDefinition != null) {
+            // get generic from consumerConfig
+            generic = (String) consumerDefinition.getPropertyValues().get("generic");
+        }
+        if (generic != null) {
+            Environment environment = parserContext.getReaderContext().getEnvironment();
+            generic = environment.resolvePlaceholders(generic);
+            beanDefinition.getPropertyValues().add("generic", generic);
+        }
+
+        Class interfaceClass = ReferenceConfig.determineInterfaceClass(generic, interfaceClassName);
+
+        // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
+        // refer to org.springframework.beans.factory.support.AbstractBeanFactory#getType()
+        GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
+        targetDefinition.setBeanClass(interfaceClass);
+        String id = (String) beanDefinition.getPropertyValues().get("id");
+        beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));
+    }
+
+    private static void getPropertyMap(Class<?> beanClass, Map<String, Class> beanPropsMap) {
         for (Method setter : beanClass.getMethods()) {
             String name = setter.getName();
             if (name.length() > 3 && name.startsWith("set")
@@ -142,8 +268,6 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
                     && setter.getParameterTypes().length == 1) {
                 Class<?> type = setter.getParameterTypes()[0];
                 String beanProperty = name.substring(3, 4).toLowerCase() + name.substring(4);
-                String property = StringUtils.camelToSplitName(beanProperty, "-");
-                props.add(property);
                 // check the setter/getter whether match
                 Method getter = null;
                 try {
@@ -161,81 +285,22 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
                         || !type.equals(getter.getReturnType())) {
                     continue;
                 }
-                if ("parameters".equals(property)) {
-                    parameters = parseParameters(element.getChildNodes(), beanDefinition, parserContext);
-                } else if ("methods".equals(property)) {
-                    parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
-                } else if ("arguments".equals(property)) {
-                    parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
-                } else {
-                    String value = resolveAttribute(element, property, parserContext);
-                    if (value != null) {
-                        value = value.trim();
-                        if (value.length() > 0) {
-                            if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
-                                RegistryConfig registryConfig = new RegistryConfig();
-                                registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
-                                beanDefinition.getPropertyValues().addPropertyValue(beanProperty, registryConfig);
-                            } else if ("provider".equals(property) || "registry".equals(property) || ("protocol".equals(property) && AbstractServiceConfig.class.isAssignableFrom(beanClass))) {
-                                /**
-                                 * For 'provider' 'protocol' 'registry', keep literal value (should be id/name) and set the value to 'registryIds' 'providerIds' protocolIds'
-                                 * The following process should make sure each id refers to the corresponding instance, here's how to find the instance for different use cases:
-                                 * 1. Spring, check existing bean by id, see{@link ServiceBean#afterPropertiesSet()}; then try to use id to find configs defined in remote Config Center
-                                 * 2. API, directly use id to find configs defined in remote Config Center; if all config instances are defined locally, please use {@link ServiceConfig#setRegistries(List)}
-                                 */
-                                beanDefinition.getPropertyValues().addPropertyValue(beanProperty + "Ids", value);
-                            } else {
-                                Object reference;
-                                if (isPrimitive(type)) {
-                                    if ("async".equals(property) && "false".equals(value)
-                                            || "timeout".equals(property) && "0".equals(value)
-                                            || "delay".equals(property) && "0".equals(value)
-                                            || "version".equals(property) && "0.0.0".equals(value)
-                                            || "stat".equals(property) && "-1".equals(value)
-                                            || "reliable".equals(property) && "false".equals(value)) {
-                                        // backward compatibility for the default value in old version's xsd
-                                        value = null;
-                                    }
-                                    reference = value;
-                                } else if (ONRETURN.equals(property) || ONTHROW.equals(property) || ONINVOKE.equals(property)) {
-                                    int index = value.lastIndexOf(".");
-                                    String ref = value.substring(0, index);
-                                    String method = value.substring(index + 1);
-                                    reference = new RuntimeBeanReference(ref);
-                                    beanDefinition.getPropertyValues().addPropertyValue(property + METHOD, method);
-                                } else {
-                                    if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
-                                        BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
-                                        if (!refBean.isSingleton()) {
-                                            throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
-                                        }
-                                    }
-                                    reference = new RuntimeBeanReference(value);
-                                }
-                                beanDefinition.getPropertyValues().addPropertyValue(beanProperty, reference);
-                            }
-                        }
-                    }
-                }
+                beanPropsMap.put(beanProperty, type);
             }
         }
-        NamedNodeMap attributes = element.getAttributes();
-        int len = attributes.getLength();
-        for (int i = 0; i < len; i++) {
-            Node node = attributes.item(i);
-            String name = node.getLocalName();
-            if (!props.contains(name)) {
-                if (parameters == null) {
-                    parameters = new ManagedMap();
-                }
-                String value = node.getNodeValue();
-                parameters.put(name, new TypedStringValue(value, String.class));
-            }
-        }
-        if (parameters != null) {
-            beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
+    }
+
+    private static String getCompatibleDefaultValue(String property, String value) {
+        if ("async".equals(property) && "false".equals(value)
+                || "timeout".equals(property) && "0".equals(value)
+                || "delay".equals(property) && "0".equals(value)
+                || "version".equals(property) && "0.0.0".equals(value)
+                || "stat".equals(property) && "-1".equals(value)
+                || "reliable".equals(property) && "false".equals(value)) {
+            // backward compatibility for the default value in old version's xsd
+            value = null;
         }
-        return beanDefinition;
+        return value;
     }
 
     private static boolean isPrimitive(Class<?> cls) {
@@ -265,9 +330,14 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
                         beanDefinition.getPropertyValues().addPropertyValue("default", "false");
                     }
                 }
-                BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
-                if (subDefinition != null && StringUtils.isNotEmpty(ref)) {
-                    subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
+                RootBeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
+                if (subDefinition != null) {
+                    if (StringUtils.isNotEmpty(ref)) {
+                        subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
+                    }
+                    if (ReferenceBean.class.equals(beanClass)) {
+                        configReferenceBean((Element) node, parserContext, subDefinition, beanDefinition);
+                    }
                 }
             }
         }
@@ -416,7 +486,10 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
 
     private static String resolveAttribute(Element element, String attributeName, ParserContext parserContext) {
         String attributeValue = element.getAttribute(attributeName);
-        Environment environment = parserContext.getReaderContext().getEnvironment();
-        return environment.resolvePlaceholders(attributeValue);
+        //https://github.com/apache/dubbo/pull/6079
+        //https://github.com/apache/dubbo/issues/6035
+//        Environment environment = parserContext.getReaderContext().getEnvironment();
+//        return environment.resolvePlaceholders(attributeValue);
+        return attributeValue;
     }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
index 3b2c31c..cb2fa00 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
@@ -16,15 +16,25 @@
  */
 package org.apache.dubbo.config.spring.util;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.dubbo.config.spring.DubboConfigInitializationPostProcessor;
+import org.apache.dubbo.config.spring.ReferenceBeanManager;
 import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigAliasPostProcessor;
 import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor;
 import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigDefaultPropertyValueBeanPostProcessor;
 import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener;
 import org.apache.dubbo.config.spring.context.DubboLifecycleComponentApplicationListener;
-
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
 
-import static com.alibaba.spring.util.BeanRegistrar.registerInfrastructureBean;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Dubbo Bean utilities class
@@ -33,6 +43,8 @@ import static com.alibaba.spring.util.BeanRegistrar.registerInfrastructureBean;
  */
 public interface DubboBeanUtils {
 
+    static final Log log = LogFactory.getLog(DubboBeanUtils.class);
+
     /**
      * Register the common beans
      *
@@ -45,6 +57,8 @@ public interface DubboBeanUtils {
      */
     static void registerCommonBeans(BeanDefinitionRegistry registry) {
 
+        registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
+
         // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
         registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
                 ReferenceAnnotationBeanPostProcessor.class);
@@ -64,5 +78,90 @@ public interface DubboBeanUtils {
         // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
         registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
                 DubboConfigDefaultPropertyValueBeanPostProcessor.class);
+
+        registerInfrastructureBean(registry, DubboConfigInitializationPostProcessor.BEAN_NAME, DubboConfigInitializationPostProcessor.class);
     }
+
+    /**
+     * Register Infrastructure Bean
+     *
+     * @param beanDefinitionRegistry {@link BeanDefinitionRegistry}
+     * @param beanType               the type of bean
+     * @param beanName               the name of bean
+     * @return if it's a first time to register, return <code>true</code>, or <code>false</code>
+     */
+    static boolean registerInfrastructureBean(BeanDefinitionRegistry beanDefinitionRegistry,
+                                                     String beanName,
+                                                     Class<?> beanType) {
+
+        boolean registered = false;
+
+        if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) {
+            RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
+            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+            beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
+            registered = true;
+
+            if (log.isDebugEnabled()) {
+                log.debug("The Infrastructure bean definition [" + beanDefinition
+                        + "with name [" + beanName + "] has been registered.");
+            }
+        }
+
+        return registered;
+    }
+
+    /**
+     * Call this method in postProcessBeanFactory()
+     *
+     * @param registry
+     */
+    static void registerBeansIfNotExists(BeanDefinitionRegistry registry) {
+        // Resolve ${...} placeholders of bean definition with Spring Environment
+
+        // If PropertyPlaceholderConfigurer already exists, PropertySourcesPlaceholderConfigurer cannot be registered.
+        // When both of them exist, a conflict will occur, and an exception will be thrown when encountering an unresolved placeholder
+
+        if (!checkBeanExists(registry, PropertyPlaceholderConfigurer.class)) {
+            Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
+            // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
+            propertySourcesPlaceholderPropertyValues.put("order", 0);
+            registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
+                    PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
+        }
+    }
+
+    static boolean registerBeanDefinitionIfNotExists(BeanDefinitionRegistry registry, String beanName,
+                                                     Class<?> beanClass, Map<String, Object> extraPropertyValues) {
+        if (registry.containsBeanDefinition(beanName)) {
+            return false;
+        }
+
+        if (checkBeanExists(registry, beanClass)) {
+            return false;
+        }
+
+        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanClass).getBeanDefinition();
+        if (extraPropertyValues != null) {
+            for (Map.Entry<String, Object> entry : extraPropertyValues.entrySet()) {
+                beanDefinition.getPropertyValues().add(entry.getKey(), entry.getValue());
+            }
+        }
+
+        registry.registerBeanDefinition(beanName, beanDefinition);
+
+        return true;
+    }
+
+    static boolean checkBeanExists(BeanDefinitionRegistry registry, Class<?> beanClass) {
+        String[] candidates = registry.getBeanDefinitionNames();
+        for (String candidate : candidates) {
+            BeanDefinition beanDefinition = registry.getBeanDefinition(candidate);
+            if (Objects.equals(beanDefinition.getBeanClassName(), beanClass.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java
index 6269258..882a9f5 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java
@@ -45,11 +45,14 @@ import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.service.GenericService;
 
 import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -74,9 +77,22 @@ import static org.junit.matchers.JUnitMatchers.containsString;
 @Ignore
 public class ConfigTest {
 
+    private static String resourcePath = ConfigTest.class.getPackage().getName().replace('.', '/');
+
+    @BeforeEach
+    public void setUp() {
+        ApplicationModel.reset();
+    }
+
+    @AfterEach
+    public void tearDown() {
+        ApplicationModel.reset();
+    }
+
+
     @Test
     public void testSpringExtensionInject() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/spring-extension-inject.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/spring-extension-inject.xml");
         ctx.start();
         try {
             MockFilter filter = (MockFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension("mymock");
@@ -91,7 +107,7 @@ public class ConfigTest {
 
     @Test
     public void testServiceClass() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/service-class.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/service-class.xml");
         ctx.start();
         try {
             DemoService demoService = refer("dubbo://127.0.0.1:30887");
@@ -129,7 +145,7 @@ public class ConfigTest {
     @Test
     @SuppressWarnings("unchecked")
     public void testProviderNestedService() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/provider-nested-service.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-nested-service.xml");
         ctx.start();
         try {
             ServiceConfig<DemoService> serviceConfig = (ServiceConfig<DemoService>) ctx.getBean("serviceConfig");
@@ -188,7 +204,7 @@ public class ConfigTest {
 
     @Test
     public void testMultiProtocol() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol.xml");
         ctx.start();
         try {
             DemoService demoService = refer("dubbo://127.0.0.1:20881");
@@ -202,7 +218,7 @@ public class ConfigTest {
 
     @Test
     public void testMultiProtocolDefault() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-default.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol-default.xml");
         ctx.start();
         try {
             DemoService demoService = refer("rmi://127.0.0.1:10991");
@@ -217,7 +233,7 @@ public class ConfigTest {
     @Test
     public void testMultiProtocolError() {
         try {
-            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-error.xml");
+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol-error.xml");
             ctx.start();
             ctx.stop();
             ctx.close();
@@ -230,7 +246,7 @@ public class ConfigTest {
     public void testMultiProtocolRegister() {
         SimpleRegistryService registryService = new SimpleRegistryService();
         Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4547, registryService);
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-register.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol-register.xml");
         ctx.start();
         try {
             List<URL> urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService");
@@ -250,7 +266,7 @@ public class ConfigTest {
         Exporter<RegistryService> exporter1 = SimpleRegistryExporter.export(4545, registryService1);
         SimpleRegistryService registryService2 = new SimpleRegistryService();
         Exporter<RegistryService> exporter2 = SimpleRegistryExporter.export(4546, registryService2);
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-registry.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-registry.xml");
         ctx.start();
         try {
             List<URL> urls1 = registryService1.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService");
@@ -271,7 +287,7 @@ public class ConfigTest {
     public void testDelayFixedTime() throws Exception {
         SimpleRegistryService registryService = new SimpleRegistryService();
         Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-fixed-time.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/delay-fixed-time.xml");
         ctx.start();
         try {
             List<URL> urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService");
@@ -295,7 +311,7 @@ public class ConfigTest {
     public void testDelayOnInitialized() throws Exception {
         SimpleRegistryService registryService = new SimpleRegistryService();
         Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-on-initialized.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/delay-on-initialized.xml");
         //ctx.start();
         try {
             List<URL> urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService");
@@ -323,10 +339,12 @@ public class ConfigTest {
 
     @Test
     public void testAutowireAndAOP() throws Exception {
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(
+                resourcePath + "/demo-provider.xml",
+                resourcePath + "/demo-provider-properties.xml");
         providerContext.start();
         try {
-            ClassPathXmlApplicationContext byNameContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/aop-autowire-byname.xml");
+            ClassPathXmlApplicationContext byNameContext = new ClassPathXmlApplicationContext(resourcePath + "/aop-autowire-byname.xml");
             byNameContext.start();
             try {
                 DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byNameContext.getBean("demoActionBySetter");
@@ -339,7 +357,7 @@ public class ConfigTest {
                 byNameContext.stop();
                 byNameContext.close();
             }
-            ClassPathXmlApplicationContext byTypeContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/aop-autowire-bytype.xml");
+            ClassPathXmlApplicationContext byTypeContext = new ClassPathXmlApplicationContext(resourcePath + "/aop-autowire-bytype.xml");
             byTypeContext.start();
             try {
                 DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byTypeContext.getBean("demoActionBySetter");
@@ -408,14 +426,23 @@ public class ConfigTest {
 
     @Test
     public void testInitReference() throws Exception {
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(
+                resourcePath + "/demo-provider.xml",
+                resourcePath + "/demo-provider-properties.xml");
         providerContext.start();
         try {
-            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/init-reference.xml");
+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/init-reference.xml",
+                    resourcePath + "/init-reference-properties.xml");
             ctx.start();
             try {
                 DemoService demoService = (DemoService) ctx.getBean("demoService");
                 assertEquals("say:world", demoService.sayName("world"));
+
+                GenericService demoService2 = (GenericService) ctx.getBean("demoService2");
+                assertEquals("say:world", demoService2.$invoke("sayName", new String[]{"java.lang.String"}, new Object[]{"world"}));
+
+            } catch (Throwable ex){
+                ex.printStackTrace();
             } finally {
                 ctx.stop();
                 ctx.close();
@@ -429,7 +456,7 @@ public class ConfigTest {
     // DUBBO-571 methods key in provider's URLONE doesn't contain the methods from inherited super interface
     @Test
     public void test_noMethodInterface_methodsKeyHasValue() throws Exception {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-no-methods-interface.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-no-methods-interface.xml");
         ctx.start();
         try {
             ServiceBean bean = (ServiceBean) ctx.getBean("service");
@@ -444,16 +471,14 @@ public class ConfigTest {
     }
 
     // DUBBO-147 find all invoker instances which have been tried from RpcContext
-    @Test
+    //@Test
     public void test_RpcContext_getUrls() throws Exception {
         ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(
-                ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-long-waiting.xml");
+                resourcePath + "/demo-provider-long-waiting.xml");
         providerContext.start();
 
         try {
-            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
-                    ConfigTest.class.getPackage().getName().replace('.', '/')
-                            + "/init-reference-getUrls.xml");
+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/init-reference-getUrls.xml");
             ctx.start();
             try {
                 DemoService demoService = (DemoService) ctx.getBean("demoService");
@@ -478,14 +503,12 @@ public class ConfigTest {
     // BUG: DUBBO-846 in version 2.0.9, config retry="false" on provider's method doesn't work
     @Test
     public void test_retrySettingFail() throws Exception {
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(
-                ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-long-waiting.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-long-waiting.xml");
         providerContext.start();
 
         try {
             ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
-                    ConfigTest.class.getPackage().getName().replace('.', '/')
-                            + "/init-reference-retry-false.xml");
+                    resourcePath + "/init-reference-retry-false.xml");
             ctx.start();
             try {
                 DemoService demoService = (DemoService) ctx.getBean("demoService");
@@ -511,10 +534,11 @@ public class ConfigTest {
     // for example, object transported on the wire doesn't implement Serializable
     @Test
     public void test_returnSerializationFail() throws Exception {
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-UnserializableBox.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-UnserializableBox.xml");
         providerContext.start();
         try {
-            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/init-reference.xml");
+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/init-reference.xml",
+                    resourcePath + "/init-reference-properties.xml");
             ctx.start();
             try {
                 DemoService demoService = (DemoService) ctx.getBean("demoService");
@@ -536,7 +560,7 @@ public class ConfigTest {
 
     @Test
     public void testXmlOverrideProperties() throws Exception {
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/xml-override-properties.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/xml-override-properties.xml");
         providerContext.start();
         try {
             ApplicationConfig application = (ApplicationConfig) providerContext.getBean("application");
@@ -602,7 +626,7 @@ public class ConfigTest {
     @Test
     public void testSystemPropertyOverrideProtocol() throws Exception {
         System.setProperty("dubbo.protocol.port", "20812");
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-protocol.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/override-protocol.xml");
         providerContext.start();
         try {
             ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");
@@ -618,7 +642,7 @@ public class ConfigTest {
     public void testSystemPropertyOverrideMultiProtocol() throws Exception {
         System.setProperty("dubbo.protocol.dubbo.port", "20814");
         System.setProperty("dubbo.protocol.rmi.port", "10914");
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-multi-protocol.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/override-multi-protocol.xml");
         providerContext.start();
         try {
             ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");
@@ -641,7 +665,7 @@ public class ConfigTest {
         System.setProperty("dubbo.registry.address", "N/A");
         System.setProperty("dubbo.protocol.name", "dubbo");
         System.setProperty("dubbo.protocol.port", "20819");
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/system-properties-override-default.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/system-properties-override-default.xml");
         providerContext.start();
         try {
             ServiceConfig<DemoService> service = (ServiceConfig<DemoService>) providerContext.getBean("demoServiceConfig");
@@ -670,7 +694,7 @@ public class ConfigTest {
         System.setProperty("dubbo.protocol.name", "dubbo");
         System.setProperty("dubbo.protocol.port", "20819");
         System.setProperty("dubbo.service.register", "false");
-        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/system-properties-override.xml");
+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/system-properties-override.xml");
         providerContext.start();
         try {
             ServiceConfig<DemoService> service = (ServiceConfig<DemoService>) providerContext.getBean("demoServiceConfig");
@@ -857,7 +881,7 @@ public class ConfigTest {
     @SuppressWarnings("unchecked")
     public void testCustomizeParameter() throws Exception {
         ClassPathXmlApplicationContext context =
-                new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/customize-parameter.xml");
+                new ClassPathXmlApplicationContext(resourcePath + "/customize-parameter.xml");
         context.start();
         ServiceBean<DemoService> serviceBean = (ServiceBean<DemoService>) context.getBean("demoServiceExport");
         URL url = (URL) serviceBean.getExportedUrls().get(0);
@@ -882,10 +906,10 @@ public class ConfigTest {
         SimpleRegistryService registryService = new SimpleRegistryService();
         Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);
         try {
-            ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/annotation-provider.xml");
+            ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/annotation-provider.xml");
             providerContext.start();
             try {
-                ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/annotation-consumer.xml");
+                ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(resourcePath + "/annotation-consumer.xml");
                 consumerContext.start();
                 try {
                     AnnotationAction annotationAction = (AnnotationAction) consumerContext.getBean("annotationAction");
@@ -1044,7 +1068,7 @@ public class ConfigTest {
 
     @Test
     public void testGenericServiceConfigThroughSpring() throws Exception {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/generic-export.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/generic-export.xml");
         try {
             ctx.start();
             ServiceConfig serviceConfig = (ServiceConfig) ctx.getBean("dubboDemoService");
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java
index d51fe3c..dfcf9aa 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java
@@ -18,12 +18,13 @@ package org.apache.dubbo.config.spring.beans.factory.annotation;
 
 
 import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.config.ReferenceConfig;
 import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.api.DemoService;
-
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.core.convert.support.DefaultConversionService;
@@ -44,6 +45,11 @@ import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
  */
 public class AnnotationPropertyValuesAdapterTest {
 
+    @BeforeEach
+    public void setUp() {
+        DubboBootstrap.reset();
+    }
+
     @Test
     public void test() {
 
@@ -59,7 +65,7 @@ public class AnnotationPropertyValuesAdapterTest {
 
         AnnotationPropertyValuesAdapter propertyValues = new AnnotationPropertyValuesAdapter(reference, mockEnvironment);
 
-        ReferenceBean referenceBean = new ReferenceBean();
+        ReferenceConfig referenceBean = new ReferenceConfig();
 
         DataBinder dataBinder = new DataBinder(referenceBean);
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
index 598933d..ef9e540 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
@@ -16,21 +16,22 @@
  */
 package org.apache.dubbo.config.spring.beans.factory.annotation;
 
+import org.apache.dubbo.config.ReferenceConfig;
 import org.apache.dubbo.config.annotation.Method;
 import org.apache.dubbo.config.annotation.Reference;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.spring.ReferenceBeanManager;
 import org.apache.dubbo.config.spring.api.DemoService;
 import org.apache.dubbo.config.spring.api.HelloService;
 import org.apache.dubbo.config.utils.ReferenceConfigCache;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
-import org.junit.After;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.InjectionMetadata;
@@ -44,7 +45,9 @@ import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import java.lang.reflect.Field;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Map;
 
 import static org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.BEAN_NAME;
@@ -70,14 +73,13 @@ import static org.junit.Assert.assertTrue;
 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
 public class ReferenceAnnotationBeanPostProcessorTest {
 
-    @Before
+    @BeforeEach
     public void setUp() {
-        ApplicationModel.reset();
+        DubboBootstrap.reset();
     }
 
-    @After
+    @AfterEach
     public void tearDown() {
-        ApplicationModel.reset();
     }
 
     private static final String AOP_SUFFIX = "(based on AOP)";
@@ -97,6 +99,11 @@ public class ReferenceAnnotationBeanPostProcessorTest {
         return new TestBean();
     }
 
+    @Bean(ReferenceBeanManager.BEAN_NAME)
+    public ReferenceBeanManager referenceBeanManager() {
+        return new ReferenceBeanManager();
+    }
+
     @Bean(BEAN_NAME)
     public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() {
         return new ReferenceAnnotationBeanPostProcessor();
@@ -122,7 +129,7 @@ public class ReferenceAnnotationBeanPostProcessorTest {
     private HelloService helloService2;
 
     @Test
-    public void test() throws Exception {
+    public void testAop() throws Exception {
 
         assertTrue(context.containsBean("helloService"));
 
@@ -135,7 +142,10 @@ public class ReferenceAnnotationBeanPostProcessorTest {
         Assert.assertNotNull(testBean.getDemoServiceFromParent());
         Assert.assertNotNull(testBean.getDemoService());
         Assert.assertNotNull(testBean.autowiredDemoService);
-        Assert.assertEquals(1, demoServicesMap.size());
+        Assert.assertEquals(3, demoServicesMap.size());
+        Assert.assertNotNull(demoServicesMap.get("demoServiceImpl"));
+        Assert.assertNotNull(demoServicesMap.get("my-reference-bean"));
+        Assert.assertNotNull(demoServicesMap.get("@Reference(url=dubbo://127.0.0.1:12345?version=2.5.7,version=2.5.7) org.apache.dubbo.config.spring.api.DemoService"));
 
         String expectedResult = "Hello,Mercy" + AOP_SUFFIX;
 
@@ -154,33 +164,6 @@ public class ReferenceAnnotationBeanPostProcessorTest {
 
         Assert.assertEquals(expectedResult, myDemoService.sayName("Mercy"));
 
-
-        for (DemoService demoService1 : demoServicesMap.values()) {
-
-            Assert.assertEquals(myDemoService, demoService1);
-
-            Assert.assertEquals(expectedResult, demoService1.sayName("Mercy"));
-        }
-
-    }
-
-    /**
-     * Test on {@link ReferenceAnnotationBeanPostProcessor#getReferenceBeans()}
-     */
-    @Test
-    public void testGetReferenceBeans() {
-
-        ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME,
-                ReferenceAnnotationBeanPostProcessor.class);
-
-        Collection<ReferenceBean<?>> referenceBeans = beanPostProcessor.getReferenceBeans();
-
-        Assert.assertEquals(4, referenceBeans.size());
-
-        ReferenceBean<?> referenceBean = referenceBeans.iterator().next();
-
-        Assert.assertNotNull(ReferenceConfigCache.getCache().get(referenceBean));
-
     }
 
     @Test
@@ -194,15 +177,24 @@ public class ReferenceAnnotationBeanPostProcessorTest {
 
         Assert.assertEquals(3, referenceBeanMap.size());
 
+        Map<String, Integer> checkingFieldNames = new HashMap<>();
+        checkingFieldNames.put("helloService", 0);
+        checkingFieldNames.put("helloService2", 0);
+        checkingFieldNames.put("demoServiceFromParent", 0);
+
         for (Map.Entry<InjectionMetadata.InjectedElement, ReferenceBean<?>> entry : referenceBeanMap.entrySet()) {
 
             InjectionMetadata.InjectedElement injectedElement = entry.getKey();
-
-            Assert.assertEquals("com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedFieldElement",
-                    injectedElement.getClass().getName());
-
+            Field field = (Field) injectedElement.getMember();
+            Integer count = checkingFieldNames.get(field.getName());
+            Assert.assertNotNull(count);
+            Assert.assertEquals(0, count.intValue());
+            checkingFieldNames.put(field.getName(), count+1);
         }
 
+        for (Map.Entry<String, Integer> entry : checkingFieldNames.entrySet()) {
+            Assert.assertEquals("check field element failed: "+entry.getKey(), 1, entry.getValue().intValue());
+        }
     }
 
     @Test
@@ -216,17 +208,26 @@ public class ReferenceAnnotationBeanPostProcessorTest {
 
         Assert.assertEquals(2, referenceBeanMap.size());
 
+        Map<String, Integer> checkingMethodNames = new HashMap<>();
+        checkingMethodNames.put("setDemoServiceFromAncestor", 0);
+        checkingMethodNames.put("setDemoService", 0);
+
         for (Map.Entry<InjectionMetadata.InjectedElement, ReferenceBean<?>> entry : referenceBeanMap.entrySet()) {
 
             InjectionMetadata.InjectedElement injectedElement = entry.getKey();
-
-            Assert.assertEquals("com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedMethodElement",
-                    injectedElement.getClass().getName());
-
+            java.lang.reflect.Method method = (java.lang.reflect.Method) injectedElement.getMember();
+            Integer count = checkingMethodNames.get(method.getName());
+            Assert.assertNotNull(count);
+            Assert.assertEquals(0, count.intValue());
+            checkingMethodNames.put(method.getName(), count+1);
         }
 
+        for (Map.Entry<String, Integer> entry : checkingMethodNames.entrySet()) {
+            Assert.assertEquals("check method element failed: "+entry.getKey(), 1, entry.getValue().intValue());
+        }
     }
 
+
     //    @Test
     //    public void testModuleInfo() {
     //
@@ -304,17 +305,23 @@ public class ReferenceAnnotationBeanPostProcessorTest {
     @Test
     public void testReferenceBeansMethodAnnotation() {
 
-        ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME,
-                ReferenceAnnotationBeanPostProcessor.class);
+        ReferenceBeanManager referenceBeanManager = context.getBean(ReferenceBeanManager.BEAN_NAME,
+                ReferenceBeanManager.class);
 
-        Collection<ReferenceBean<?>> referenceBeans = beanPostProcessor.getReferenceBeans();
+        Collection<ReferenceBean> referenceBeans = referenceBeanManager.getReferences();
 
         Assert.assertEquals(4, referenceBeans.size());
 
-        ReferenceBean<?> referenceBean = referenceBeans.iterator().next();
+        for (ReferenceBean referenceBean : referenceBeans) {
+            ReferenceConfig referenceConfig = referenceBean.getReferenceConfig();
+            Assert.assertNotNull(referenceConfig);
+            Assert.assertNotNull(ReferenceConfigCache.getCache().get(referenceConfig));
+        }
 
+        ReferenceBean referenceBean = referenceBeanManager.get("helloService");
         if ("helloService".equals(referenceBean.getId())) {
-            Assert.assertNotNull(referenceBean.getMethods());
+            ReferenceConfig referenceConfig = referenceBean.getReferenceConfig();
+            Assert.assertNotNull(referenceConfig.getMethods());
         }
     }
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java
index 23e754f..b95734f 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java
@@ -17,15 +17,14 @@
 package org.apache.dubbo.config.spring.beans.factory.annotation;
 
 
+import org.apache.dubbo.config.ReferenceConfig;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.spring.ReferenceBean;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
@@ -81,9 +80,9 @@ public class ReferenceBeanBuilderTest {
     @Autowired
     private ApplicationContext context;
 
-    @Before
+    @BeforeEach
     public void init() {
-        ApplicationModel.reset();
+        DubboBootstrap.reset();
     }
 
     @Test
@@ -91,8 +90,8 @@ public class ReferenceBeanBuilderTest {
         DubboReference reference = findAnnotation(findField(getClass(), "TEST_FIELD"), DubboReference.class);
         AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(reference, false, false);
         ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder.create(attributes, context);
-        beanBuilder.interfaceClass(CharSequence.class);
-        ReferenceBean referenceBean = beanBuilder.build();
+        beanBuilder.defaultInterfaceClass(CharSequence.class);
+        ReferenceConfig referenceBean = beanBuilder.build();
         Assert.assertEquals(CharSequence.class, referenceBean.getInterfaceClass());
         Assert.assertEquals("1.0.0", referenceBean.getVersion());
         Assert.assertEquals("TEST_GROUP", referenceBean.getGroup());
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java
index 80a8d40..2b89f0f 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java
@@ -16,12 +16,11 @@
  */
 package org.apache.dubbo.config.spring.context.annotation;
 
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.api.DemoService;
 import org.apache.dubbo.config.spring.context.annotation.consumer.ConsumerConfiguration;
 import org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl;
 import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -41,12 +40,11 @@ public class DubboComponentScanRegistrarTest {
 
     @BeforeEach
     public void setUp() {
-        ApplicationModel.reset();
+        DubboBootstrap.reset();
     }
 
     @AfterEach
     public void tearDown() {
-        ApplicationModel.reset();
     }
 
     @Test
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java
index 724f132..c2696fc 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.config.ModuleConfig;
 import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.RegistryConfig;
 
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -41,6 +42,7 @@ public class DubboConfigConfigurationTest {
 
     @BeforeEach
     public void before() throws IOException {
+        DubboBootstrap.reset();
 
         context = new AnnotationConfigApplicationContext();
         ResourcePropertySource propertySource = new ResourcePropertySource("META-INF/config.properties");
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java
index 5817185..6ecaffb 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java
@@ -24,8 +24,11 @@ import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.ProviderConfig;
 import org.apache.dubbo.config.RegistryConfig;
 
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.junit.Assert;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 import org.springframework.context.annotation.PropertySource;
@@ -44,6 +47,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  */
 public class EnableDubboConfigTest {
 
+    @BeforeEach
+    public void setUp() {
+        DubboBootstrap.reset();
+    }
+
+    @AfterEach
+    public void tearDown() {
+    }
+
     @Test
     public void testSingle() {
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java
index d9364d0..3a56d41 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java
@@ -17,11 +17,10 @@
 package org.apache.dubbo.config.spring.context.annotation;
 
 import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.api.DemoService;
 import org.apache.dubbo.config.spring.context.annotation.consumer.test.TestConsumerConfiguration;
 import org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -52,14 +51,13 @@ public class EnableDubboTest {
 
     @BeforeEach
     public void setUp() {
-        ApplicationModel.reset();
         context = new AnnotationConfigApplicationContext();
+        DubboBootstrap.reset();
     }
 
     @AfterEach
     public void tearDown() {
-        ApplicationModel.reset();
-        context.close();
+        //context.close();
     }
 
     @Test
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java
index 8c34e7e..d09407a 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java
@@ -16,9 +16,13 @@
  */
 package org.apache.dubbo.config.spring.issues;
 
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.apache.dubbo.config.annotation.DubboService;
 import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.spring.api.DemoService;
 import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig;
 
+import org.apache.dubbo.config.spring.impl.DemoServiceImpl;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 import org.springframework.context.annotation.Bean;
@@ -35,15 +39,13 @@ import org.springframework.context.annotation.PropertySource;
 @PropertySource("classpath:/META-INF/issue-6252-test.properties")
 public class Issue6252Test {
 
-    @Bean
-    public static ReferenceBean referenceBean() {
-        return new ReferenceBean();
-    }
+    @DubboReference
+    private DemoService demoService;
 
     @Test
     public void test() throws Exception {
         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Issue6252Test.class);
-        context.getBean(ReferenceBean.class);
+        DemoService demoService = context.getBean(DemoService.class);
         context.close();
     }
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java
index 705b890..31c16f5 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.config.ModuleConfig;
 import org.apache.dubbo.config.MonitorConfig;
 import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.ProviderConfig;
+import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.spring.ConfigTest;
 import org.apache.dubbo.config.spring.ServiceBean;
 import org.apache.dubbo.config.spring.api.DemoService;
@@ -47,6 +48,9 @@ import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 public class DubboNamespaceHandlerTest {
+
+    private static String resourcePath = ConfigTest.class.getPackage().getName().replace('.', '/');
+
     @BeforeEach
     public void setUp() {
         ApplicationModel.reset();
@@ -75,8 +79,8 @@ public class DubboNamespaceHandlerTest {
     @Test
     public void testProviderXml() {
         ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
-                ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml",
-                ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-properties.xml"
+                resourcePath + "/demo-provider.xml",
+                resourcePath + "/demo-provider-properties.xml"
         );
         ctx.start();
 
@@ -93,13 +97,17 @@ public class DubboNamespaceHandlerTest {
         assertThat(applicationConfig, not(nullValue()));
         assertThat(applicationConfig.getName(), is("demo-provider"));
 
+        RegistryConfig registryConfig = context.getBean(RegistryConfig.class);
+        assertThat(registryConfig, not(nullValue()));
+        assertThat(registryConfig.getAddress(), is("N/A"));
+
         DemoService service = context.getBean(DemoService.class);
         assertThat(service, not(nullValue()));
     }
 
     @Test
     public void testMultiProtocol() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol.xml");
         ctx.start();
 
         Map<String, ProtocolConfig> protocolConfigMap = ctx.getBeansOfType(ProtocolConfig.class);
@@ -114,7 +122,7 @@ public class DubboNamespaceHandlerTest {
 
     @Test
     public void testDefaultProtocol() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-protocol.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/override-protocol.xml");
         ctx.start();
 
         ProtocolConfig protocolConfig = ctx.getBean(ProtocolConfig.class);
@@ -124,7 +132,7 @@ public class DubboNamespaceHandlerTest {
 
     @Test
     public void testCustomParameter() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/customize-parameter.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/customize-parameter.xml");
         ctx.start();
 
         ProtocolConfig protocolConfig = ctx.getBean(ProtocolConfig.class);
@@ -139,7 +147,7 @@ public class DubboNamespaceHandlerTest {
 
     @Test
     public void testDelayFixedTime() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/" + ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-fixed-time.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/" + resourcePath + "/delay-fixed-time.xml");
         ctx.start();
 
         assertThat(ctx.getBean(ServiceBean.class).getDelay(), is(300));
@@ -147,7 +155,7 @@ public class DubboNamespaceHandlerTest {
 
     @Test
     public void testTimeoutConfig() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/provider-nested-service.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-nested-service.xml");
         ctx.start();
 
         Map<String, ProviderConfig> providerConfigMap = ctx.getBeansOfType(ProviderConfig.class);
@@ -157,7 +165,7 @@ public class DubboNamespaceHandlerTest {
 
     @Test
     public void testMonitor() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/provider-with-monitor.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-with-monitor.xml");
         ctx.start();
 
         assertThat(ctx.getBean(MonitorConfig.class), not(nullValue()));
@@ -166,7 +174,7 @@ public class DubboNamespaceHandlerTest {
 //    @Test
 //    public void testMultiMonitor() {
 //        Assertions.assertThrows(BeanCreationException.class, () -> {
-//            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-monitor.xml");
+//            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-monitor.xml");
 //            ctx.start();
 //        });
 //    }
@@ -174,14 +182,14 @@ public class DubboNamespaceHandlerTest {
 //    @Test
 //    public void testMultiProviderConfig() {
 //        Assertions.assertThrows(BeanCreationException.class, () -> {
-//            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/provider-multi.xml");
+//            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-multi.xml");
 //            ctx.start();
 //        });
 //    }
 
     @Test
     public void testModuleInfo() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/provider-with-module.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-with-module.xml");
         ctx.start();
 
         ModuleConfig moduleConfig = ctx.getBean(ModuleConfig.class);
@@ -191,14 +199,14 @@ public class DubboNamespaceHandlerTest {
     @Test
     public void testNotificationWithWrongBean() {
         Assertions.assertThrows(BeanCreationException.class, () -> {
-            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/consumer-notification.xml");
+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/consumer-notification.xml");
             ctx.start();
         });
     }
 
     @Test
     public void testProperty() {
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/service-class.xml");
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/service-class.xml");
         ctx.start();
 
         ServiceBean serviceBean = ctx.getBean(ServiceBean.class);
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java
index 6deb2ab..e89ff6c 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java
@@ -16,13 +16,12 @@
  */
 package org.apache.dubbo.config.spring.schema;
 
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.ReferenceBean;
 import org.apache.dubbo.config.spring.ServiceBean;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -37,14 +36,13 @@ import static org.junit.Assert.assertNotNull;
 @ImportResource(locations = "classpath:/META-INF/spring/dubbo-generic-consumer.xml")
 public class GenericServiceTest {
 
-    @Before
+    @BeforeEach
     public void setUp() {
-        ApplicationModel.reset();
+        DubboBootstrap.reset();
     }
 
     @After
     public void tearDown() {
-        ApplicationModel.reset();
     }
 
     @Autowired
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/init-reference.properties b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/init-reference.properties
new file mode 100644
index 0000000..c6f6d6a
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/init-reference.properties
@@ -0,0 +1,5 @@
+# The properties for org/apache/dubbo/config/spring/init-reference*.xml
+
+call.timeout=100
+connection.timeout=1000
+sayName.retry=false
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider.xml b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider.xml
index d5d3c52..dc77848 100644
--- a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider.xml
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider.xml
@@ -21,16 +21,16 @@
     http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
     <!-- current application configuration -->
-    <dubbo:application name="${dubbo.application.name}"/>
+    <dubbo:application name="${dubbo.application.name:foo-app}"/>
 
     <!-- registry center configuration -->
-    <dubbo:registry address="${dubbo.registry.address}"/>
+    <dubbo:registry address="${dubbo.registry.address:foo-address}"/>
 
     <!-- service protocol configuration -->
-    <dubbo:protocol name="${dubbo.protocol.name}" port="${dubbo.protocol.port}"/>
+    <dubbo:protocol name="${dubbo.protocol.name:foo-protocol}" port="${dubbo.protocol.port:20881}"/>
 
     <!-- service configuration -->
-    <dubbo:service interface="org.apache.dubbo.config.spring.api.DemoService" ref="demoService"/>
+    <dubbo:service interface="org.apache.dubbo.config.spring.api.DemoService" group="demo" version="1.2.3" ref="demoService"/>
 
     <bean id="demoService" class="org.apache.dubbo.config.spring.impl.DemoServiceImpl"/>
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-properties.xml
similarity index 68%
copy from dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml
copy to dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-properties.xml
index 360922f..08cb609 100644
--- a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-properties.xml
@@ -15,17 +15,11 @@
   limitations under the License.
   -->
 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
        xmlns="http://www.springframework.org/schema/beans"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
-    http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
-    ">
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
 
-    <!-- current application configuration -->
-    <dubbo:application name="demo-consumer"/>
-
-    <!-- service configuration -->
-    <dubbo:reference id="demoService" interface="org.apache.dubbo.config.spring.api.DemoService"
-                     url="dubbo://127.0.0.1:20813" init="true" scope="remote"/>
+    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
+        <property name="location" value="classpath:/META-INF/init-reference.properties"/>
+    </bean>
 
 </beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml
index 360922f..10e9f84 100644
--- a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml
@@ -24,8 +24,22 @@
     <!-- current application configuration -->
     <dubbo:application name="demo-consumer"/>
 
-    <!-- service configuration -->
-    <dubbo:reference id="demoService" interface="org.apache.dubbo.config.spring.api.DemoService"
-                     url="dubbo://127.0.0.1:20813" init="true" scope="remote"/>
+    <dubbo:registry id="my-registry" address="zookeeper://localhost:2181" />
 
+    <dubbo:consumer id="my-consumer" registry="my-registry" group="demo" version="1.2.3" scope="remote" init="true"
+                    timeout="${call.timeout:foo100}" >
+        <dubbo:reference id="demoService2" interface="org.apache.dubbo.config.spring.api.DemoService" generic="true"
+                         url="dubbo://127.0.0.1:20813" />
+    </dubbo:consumer>
+
+    <!-- service reference configuration -->
+    <dubbo:reference id="demoService" interface="org.apache.dubbo.config.spring.api.DemoService" group="demo" version="1.2.3"
+                     url="dubbo://127.0.0.1:20813" init="true" timeout="${call.timeout:foo100}"
+                     scope="remote" protocol="dubbo" registry="my-registry" consumer="my-consumer" >
+        <dubbo:parameter key="connec.timeout" value="${connection.timeout:foo1000}"/>
+        <dubbo:method name="sayName" retry="${sayName.retry:foo-retry}">
+            <dubbo:argument index="0" callback="true" />
+            <dubbo:parameter key="access-token" value="my-token" />
+        </dubbo:method>
+    </dubbo:reference>
 </beans>
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/util/ClassUtils.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/util/ClassUtils.java
index ffa7e2c..04096da 100755
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/util/ClassUtils.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/util/ClassUtils.java
@@ -16,8 +16,6 @@
  */
 package org.apache.dubbo.metadata.definition.util;
 
-import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
-
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -125,7 +123,7 @@ public final class ClassUtils {
 
             sb.append(".");
 
-            if (ownerType instanceof ParameterizedTypeImpl) {
+            if (ownerType instanceof ParameterizedType) {
                 // Find simple name of nested type by removing the
                 // shared prefix with owner.
                 sb.append(rawType.getName().replace(((Class) ((ParameterizedType) ownerType).getRawType()).getName() + "$",
diff --git a/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java b/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
index f0b9f1e..598f34a 100644
--- a/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
@@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test;
 
 import java.net.InetAddress;
 import java.net.MulticastSocket;
+import java.net.UnknownHostException;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -57,9 +58,13 @@ public class MulticastRegistryTest {
      */
     @Test
     public void testUrlError() {
-        Assertions.assertThrows(IllegalStateException.class, () -> {
-            URL errorUrl = URL.valueOf("multicast://mullticast/");
-            new MulticastRegistry(errorUrl);
+        Assertions.assertThrows(UnknownHostException.class, () -> {
+            try {
+                URL errorUrl = URL.valueOf("multicast://mullticast.local/");
+                new MulticastRegistry(errorUrl);
+            } catch (IllegalStateException e) {
+                throw e.getCause();
+            }
         });
     }
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
index 837750f..ab1ab44 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
@@ -79,6 +79,10 @@ public abstract class AbstractProxyFactory implements ProxyFactory {
         return getProxy(invoker, interfaces.toArray(new Class<?>[0]));
     }
 
+    public static Class<?>[] getInternalInterfaces() {
+        return INTERNAL_INTERFACES.clone();
+    }
+
     public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
 
 }

[dubbo] 02/12: 3.0 enhancement, do not merge (#7381)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d541548d15810e97319560a533b90f9eb2d507d8
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Mar 16 23:18:15 2021 +0800

    3.0 enhancement, do not merge (#7381)
---
 .../org/apache/dubbo/rpc/cluster/Directory.java    |   4 +
 .../dubbo/rpc/cluster/filter/ClusterFilter.java    |  24 +++
 .../cluster/filter/DefaultFilterChainBuilder.java  |  10 +-
 .../rpc/cluster/filter/FilterChainBuilder.java     |  36 +++-
 .../rpc/cluster/filter/ProtocolFilterWrapper.java  |   9 +-
 .../filter/support}/ConsumerContextFilter.java     |  26 ++-
 .../support/ZoneAwareFilter.java}                  |  19 ++-
 .../cluster/interceptor/ClusterInterceptor.java    |   1 +
 .../ConsumerContextClusterInterceptor.java         |  60 -------
 .../cluster/support/wrapper/AbstractCluster.java   | 190 +++++++++++----------
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   2 +
 ...ubbo.rpc.cluster.interceptor.ClusterInterceptor |   2 -
 dubbo-common/pom.xml                               |   4 +
 .../java/org/apache/dubbo/common/URLStrParser.java |   9 +-
 .../apache/dubbo/common/config/Environment.java    |  20 +++
 .../dubbo/common/constants/CommonConstants.java    |   6 +
 .../dubbo/common/url/component/URLAddress.java     |   2 +-
 .../dubbo/common/url/component/URLItemCache.java   |  44 +++--
 .../dubbo/common/url/component/URLParam.java       |   4 +-
 .../org/apache/dubbo/common/utils/ConfigUtils.java |  47 +++++
 .../org/apache/dubbo/config/AbstractConfig.java    |   3 +-
 .../java/org/apache/dubbo/config/Constants.java    |   2 +
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   3 +-
 .../ServiceInstanceHostPortCustomizer.java         |   5 +-
 .../apache/dubbo/config/AbstractConfigTest.java    |   4 +-
 .../src/main/resources/dubbo-migration.yaml        |   3 +
 dubbo-dependencies-bom/pom.xml                     |   6 +
 dubbo-distribution/dubbo-all/pom.xml               |   8 +
 .../org/apache/dubbo/metadata/MetadataInfo.java    |   4 +
 .../dubbo/monitor/support/MonitorFilter.java       |   3 +-
 .../dubbo/auth/filter/ConsumerSignFilter.java      |   2 +-
 .../org/apache/dubbo/registry/NotifyListener.java  |   4 +
 .../registry/client/DefaultServiceInstance.java    |  58 +++----
 .../client/EventPublishingServiceDiscovery.java    |  15 ++
 .../client/FileSystemServiceDiscovery.java         |   2 +-
 .../client/SelfHostMetaServiceDiscovery.java       |   2 +-
 .../dubbo/registry/client/ServiceDiscovery.java    |   5 +
 .../registry/client/ServiceDiscoveryRegistry.java  |   2 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |   5 +-
 .../dubbo/registry/client/ServiceInstance.java     |  13 +-
 .../listener/ServiceInstancesChangedListener.java  | 117 +++++++------
 .../registry/client/metadata/MetadataUtils.java    |   2 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |   2 +-
 .../DefaultMigrationAddressComparator.java         |   4 +-
 .../client/migration/MigrationInvoker.java         |  89 +++++-----
 .../client/migration/MigrationRuleHandler.java     |   4 +-
 .../client/migration/MigrationRuleListener.java    |   8 +-
 .../registry/integration/DynamicDirectory.java     |   8 +-
 .../client/DefaultServiceInstanceTest.java         |   3 +-
 .../client/FileSystemServiceDiscoveryTest.java     |   2 +
 .../multiple/MultipleServiceDiscovery.java         |  38 +++--
 .../nacos/util/NacosNamingServiceUtils.java        |   4 +-
 .../zookeeper/ZookeeperServiceDiscovery.java       |  34 ++--
 .../ZookeeperServiceDiscoveryChangeWatcher.java    |  38 ++++-
 .../zookeeper/util/CuratorFrameworkUtils.java      |   2 +-
 .../zookeeper/ZookeeperServiceDiscoveryTest.java   |   3 +-
 .../main/java/org/apache/dubbo/rpc/BaseFilter.java |  31 ++++
 .../src/main/java/org/apache/dubbo/rpc/Filter.java |  40 +++--
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |   2 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   2 -
 .../rpc/protocol/dubbo/filter/FutureFilter.java    |   4 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   3 +-
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   1 +
 .../dubbo/rpc/protocol/dubbo/FutureFilterTest.java |   4 +-
 64 files changed, 688 insertions(+), 425 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
index 9fd3ca2..5a92d97 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
@@ -65,4 +65,8 @@ public interface Directory<T> extends Node {
     void discordAddresses();
 
     RouterChain<T> getRouterChain();
+
+    default boolean isNotificationReceived() {
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java
new file mode 100644
index 0000000..7d48dc9
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.filter;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.BaseFilter;
+
+@SPI
+public interface ClusterFilter extends BaseFilter {
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
index 982008b..e2e26d2 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
@@ -27,6 +27,9 @@ import java.util.List;
 @Activate(order = 0)
 public class DefaultFilterChainBuilder implements FilterChainBuilder {
 
+    /**
+     * build consumer/provider filter chain
+     */
     @Override
     public <T> Invoker<T> buildInvokerChain(final Invoker<T> originalInvoker, String key, String group) {
         Invoker<T> last = originalInvoker;
@@ -43,14 +46,17 @@ public class DefaultFilterChainBuilder implements FilterChainBuilder {
         return last;
     }
 
+    /**
+     * build consumer cluster filter chain
+     */
     @Override
     public <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> originalInvoker, String key, String group) {
         ClusterInvoker<T> last = originalInvoker;
-        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
+        List<ClusterFilter> filters = ExtensionLoader.getExtensionLoader(ClusterFilter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
 
         if (!filters.isEmpty()) {
             for (int i = filters.size() - 1; i >= 0; i--) {
-                final Filter filter = filters.get(i);
+                final ClusterFilter filter = filters.get(i);
                 final Invoker<T> next = last;
                 last = new ClusterFilterChainNode<>(originalInvoker, next, filter);
             }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
index 20275e2..949a66c 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.filter;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.BaseFilter;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -29,16 +30,27 @@ import org.apache.dubbo.rpc.cluster.Directory;
 
 @SPI("default")
 public interface FilterChainBuilder {
+    /**
+     * build consumer/provider filter chain
+     */
     <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group);
 
+    /**
+     * build consumer cluster filter chain
+     */
     <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> invoker, String key, String group);
 
-    class FilterChainNode<T, TYPE extends Invoker<T>> implements Invoker<T>{
+    /**
+     * Works on provider side
+     * @param <T>
+     * @param <TYPE>
+     */
+    class FilterChainNode<T, TYPE extends Invoker<T>, FILTER extends BaseFilter> implements Invoker<T>{
         TYPE originalInvoker;
         Invoker<T> nextNode;
-        Filter filter;
+        FILTER filter;
 
-        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
+        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, FILTER filter) {
             this.originalInvoker = originalInvoker;
             this.nextNode = nextNode;
             this.filter = filter;
@@ -79,8 +91,8 @@ public interface FilterChainBuilder {
                     } finally {
                         listenableFilter.removeListener(invocation);
                     }
-                } else if (filter instanceof Filter.Listener) {
-                    Filter.Listener listener = (Filter.Listener) filter;
+                } else if (filter instanceof FILTER.Listener) {
+                    FILTER.Listener listener = (FILTER.Listener) filter;
                     listener.onError(e, originalInvoker, invocation);
                 }
                 throw e;
@@ -102,8 +114,8 @@ public interface FilterChainBuilder {
                     } finally {
                         listenableFilter.removeListener(invocation);
                     }
-                } else if (filter instanceof Filter.Listener) {
-                    Filter.Listener listener = (Filter.Listener) filter;
+                } else if (filter instanceof FILTER.Listener) {
+                    FILTER.Listener listener = (FILTER.Listener) filter;
                     if (t == null) {
                         listener.onResponse(r, originalInvoker, invocation);
                     } else {
@@ -124,8 +136,14 @@ public interface FilterChainBuilder {
         }
     }
 
-    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>> extends FilterChainNode<T, TYPE> implements ClusterInvoker<T> {
-        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
+    /**
+     * Works on consumer side
+     * @param <T>
+     * @param <TYPE>
+     */
+    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>, FILTER extends BaseFilter>
+            extends FilterChainNode<T, TYPE, FILTER> implements ClusterInvoker<T> {
+        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, FILTER filter) {
             super(originalInvoker, nextNode, filter);
         }
 
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
index 65e8813..389b173 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
@@ -28,10 +28,9 @@ import org.apache.dubbo.rpc.ProtocolServer;
 import org.apache.dubbo.rpc.RpcException;
 
 import java.util.List;
-import java.util.Objects;
 
+import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY;
 
 /**
  * ListenerProtocol
@@ -68,11 +67,7 @@ public class ProtocolFilterWrapper implements Protocol {
         if (UrlUtils.isRegistry(url)) {
             return protocol.refer(type, url);
         }
-        // if it's peer-to-peer url
-        if (!Objects.isNull(url.getAttribute(PEER_KEY))) {
-            return builder.buildInvokerChain(protocol.refer(type, url), SERVICE_FILTER_KEY, CommonConstants.CONSUMER);
-        }
-        return protocol.refer(type, url);
+        return builder.buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
     }
 
     @Override
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
similarity index 83%
rename from dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
index 0fead6b..4a1ed3b 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.filter;
+package org.apache.dubbo.rpc.cluster.filter.support;
 
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -28,6 +28,7 @@ import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.TimeoutCountDown;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 
 import java.util.Map;
 
@@ -39,11 +40,11 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIME_COUNTDOWN_K
  * ConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port
  * for consumer invoker.It does it to make the requires info available to execution thread's RpcContext.
  *
- * @see org.apache.dubbo.rpc.Filter
+ * @see Filter
  * @see RpcContext
  */
 @Activate(group = CONSUMER, order = -10000)
-public class ConsumerContextFilter implements Filter {
+public class ConsumerContextFilter implements ClusterFilter, ClusterFilter.Listener {
 
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@@ -76,7 +77,24 @@ public class ConsumerContextFilter implements Filter {
                                 + invocation.getMethodName() + ", terminate directly."), invocation);
             }
         }
-        return invoker.invoke(invocation);
+
+        try {
+            RpcContext.removeServerContext();
+            return invoker.invoke(invocation);
+        } finally {
+            RpcContext.removeContext();
+        }
+    }
+
+    @Override
+    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
+        // pass attachments to result
+        RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
+    }
+
+    @Override
+    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
+
     }
 
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
similarity index 80%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
index 6daec08..cd0a7ab 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
@@ -14,15 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.interceptor;
+package org.apache.dubbo.rpc.cluster.filter.support;
 
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.ZoneDetector;
-import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_FORCE;
@@ -32,11 +36,11 @@ import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_
  *
  * active only when url has key 'cluster=zone-aware'
  */
-@Activate(value = "cluster:zone-aware")
-public class ZoneAwareClusterInterceptor implements ClusterInterceptor {
+@Activate(group = CommonConstants.CONSUMER, value = "cluster:zone-aware")
+public class ZoneAwareFilter implements ClusterFilter {
 
     @Override
-    public void before(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         RpcContext rpcContext = RpcContext.getContext();
         String zone = (String) rpcContext.getAttachment(REGISTRY_ZONE);
         String force = (String) rpcContext.getAttachment(REGISTRY_ZONE_FORCE);
@@ -53,10 +57,7 @@ public class ZoneAwareClusterInterceptor implements ClusterInterceptor {
         if (StringUtils.isNotEmpty(force)) {
             invocation.setAttachment(REGISTRY_ZONE_FORCE, force);
         }
-    }
-
-    @Override
-    public void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
 
+        return invoker.invoke(invocation);
     }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
index 199361f..821dd2e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 /**
  * Different from {@link Filter}, ClusterInterceptor works at the outmost layer, before one specific address/invoker is picked.
  */
+@Deprecated
 @SPI
 public interface ClusterInterceptor {
 
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
deleted file mode 100644
index 053bc87..0000000
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.rpc.cluster.interceptor;
-
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.utils.NetUtils;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
-
-@Activate
-public class ConsumerContextClusterInterceptor implements ClusterInterceptor, ClusterInterceptor.Listener {
-
-    @Override
-    public void before(AbstractClusterInvoker<?> invoker, Invocation invocation) {
-        RpcContext context = RpcContext.getContext();
-        context.setInvocation(invocation).setLocalAddress(NetUtils.getLocalHost(), 0);
-        if (invocation instanceof RpcInvocation) {
-            ((RpcInvocation) invocation).setInvoker(invoker);
-        }
-        RpcContext.removeServerContext();
-    }
-
-    @Override
-    public void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
-        RpcContext.removeContext(true);
-    }
-
-    @Override
-    public Result intercept(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) throws RpcException {
-        return clusterInvoker.invoke(invocation);
-    }
-
-    @Override
-    public void onMessage(Result appResponse, AbstractClusterInvoker<?> invoker, Invocation invocation) {
-        RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
-    }
-
-    @Override
-    public void onError(Throwable t, AbstractClusterInvoker<?> invoker, Invocation invocation) {
-
-    }
-}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
index 9ce920d..1c2d047 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.rpc.cluster.support.wrapper;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -36,6 +37,7 @@ import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
 import java.util.List;
 
+import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_INTERCEPTOR_COMPATIBLE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.INVOCATION_INTERCEPTOR_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_INTERCEPTOR_KEY;
@@ -44,15 +46,10 @@ public abstract class AbstractCluster implements Cluster {
 
     private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
 //        AbstractClusterInvoker<T> last = clusterInvoker;
-        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new FilterInvoker<>(clusterInvoker));
-        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
+        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new ClusterFilterInvoker<>(clusterInvoker));
 
-        if (!interceptors.isEmpty()) {
-            for (int i = interceptors.size() - 1; i >= 0; i--) {
-                final ClusterInterceptor interceptor = interceptors.get(i);
-                final AbstractClusterInvoker<T> next = last;
-                last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
-            }
+        if (Boolean.parseBoolean(ConfigurationUtils.getProperty(CLUSTER_INTERCEPTOR_COMPATIBLE_KEY, "false"))) {
+            return build27xCompatibleClusterInterceptors(clusterInvoker, last);
         }
         return last;
     }
@@ -70,90 +67,15 @@ public abstract class AbstractCluster implements Cluster {
         if (CollectionUtils.isEmpty(builders)) {
             return invoker;
         }
-        return new InterceptorInvoker<>(invoker, builders);
+        return new InvocationInterceptorInvoker<>(invoker, builders);
     }
 
     protected abstract <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException;
 
-    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
-
-        private AbstractClusterInvoker<T> clusterInvoker;
-        private ClusterInterceptor interceptor;
-        private AbstractClusterInvoker<T> next;
-
-        public InterceptorInvokerNode(AbstractClusterInvoker<T> clusterInvoker,
-                                      ClusterInterceptor interceptor,
-                                      AbstractClusterInvoker<T> next) {
-            this.clusterInvoker = clusterInvoker;
-            this.interceptor = interceptor;
-            this.next = next;
-        }
-
-        @Override
-        public Class<T> getInterface() {
-            return clusterInvoker.getInterface();
-        }
-
-        @Override
-        public URL getUrl() {
-            return clusterInvoker.getUrl();
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return clusterInvoker.isAvailable();
-        }
-
-        @Override
-        public Result invoke(Invocation invocation) throws RpcException {
-            Result asyncResult;
-            try {
-                interceptor.before(next, invocation);
-                asyncResult = interceptor.intercept(next, invocation);
-            } catch (Exception e) {
-                // onError callback
-                if (interceptor instanceof ClusterInterceptor.Listener) {
-                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
-                    listener.onError(e, clusterInvoker, invocation);
-                }
-                throw e;
-            } finally {
-                interceptor.after(next, invocation);
-            }
-            return asyncResult.whenCompleteWithContext((r, t) -> {
-                // onResponse callback
-                if (interceptor instanceof ClusterInterceptor.Listener) {
-                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
-                    if (t == null) {
-                        listener.onMessage(r, clusterInvoker, invocation);
-                    } else {
-                        listener.onError(t, clusterInvoker, invocation);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void destroy() {
-            clusterInvoker.destroy();
-        }
-
-        @Override
-        public String toString() {
-            return clusterInvoker.toString();
-        }
-
-        @Override
-        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
-            // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
-            return null;
-        }
-    }
-
-    static class FilterInvoker<T> extends AbstractClusterInvoker<T> {
+    static class ClusterFilterInvoker<T> extends AbstractClusterInvoker<T> {
         private ClusterInvoker<T> filterInvoker;
 
-        public FilterInvoker(AbstractClusterInvoker<T> invoker) {
+        public ClusterFilterInvoker(AbstractClusterInvoker<T> invoker) {
             List<FilterChainBuilder> builders = ExtensionLoader.getExtensionLoader(FilterChainBuilder.class).getActivateExtensions();
             if (CollectionUtils.isEmpty(builders)) {
                 filterInvoker = invoker;
@@ -203,10 +125,10 @@ public abstract class AbstractCluster implements Cluster {
         }
     }
 
-    static class InterceptorInvoker<T> extends AbstractClusterInvoker<T> {
+    static class InvocationInterceptorInvoker<T> extends AbstractClusterInvoker<T> {
         private ClusterInvoker<T> interceptorInvoker;
 
-        public InterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
+        public InvocationInterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
             ClusterInvoker<T> tmpInvoker = invoker;
             for (InvocationInterceptorBuilder builder : builders) {
                 tmpInvoker = builder.buildClusterInterceptorChain(tmpInvoker, INVOCATION_INTERCEPTOR_KEY, CommonConstants.CONSUMER);
@@ -248,4 +170,96 @@ public abstract class AbstractCluster implements Cluster {
             return null;
         }
     }
+
+    @Deprecated
+    private <T> ClusterInvoker<T> build27xCompatibleClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, AbstractClusterInvoker<T> last) {
+        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
+
+        if (!interceptors.isEmpty()) {
+            for (int i = interceptors.size() - 1; i >= 0; i--) {
+                final ClusterInterceptor interceptor = interceptors.get(i);
+                final AbstractClusterInvoker<T> next = last;
+                last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
+            }
+        }
+        return last;
+    }
+
+    @Deprecated
+    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
+
+        private AbstractClusterInvoker<T> clusterInvoker;
+        private ClusterInterceptor interceptor;
+        private AbstractClusterInvoker<T> next;
+
+        public InterceptorInvokerNode(AbstractClusterInvoker<T> clusterInvoker,
+                                      ClusterInterceptor interceptor,
+                                      AbstractClusterInvoker<T> next) {
+            this.clusterInvoker = clusterInvoker;
+            this.interceptor = interceptor;
+            this.next = next;
+        }
+
+        @Override
+        public Class<T> getInterface() {
+            return clusterInvoker.getInterface();
+        }
+
+        @Override
+        public URL getUrl() {
+            return clusterInvoker.getUrl();
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return clusterInvoker.isAvailable();
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            Result asyncResult;
+            try {
+                interceptor.before(next, invocation);
+                asyncResult = interceptor.intercept(next, invocation);
+            } catch (Exception e) {
+                // onError callback
+                if (interceptor instanceof ClusterInterceptor.Listener) {
+                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
+                    listener.onError(e, clusterInvoker, invocation);
+                }
+                throw e;
+            } finally {
+                interceptor.after(next, invocation);
+            }
+            return asyncResult.whenCompleteWithContext((r, t) -> {
+                // onResponse callback
+                if (interceptor instanceof ClusterInterceptor.Listener) {
+                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
+                    if (t == null) {
+                        listener.onMessage(r, clusterInvoker, invocation);
+                    } else {
+                        listener.onError(t, clusterInvoker, invocation);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void destroy() {
+            clusterInvoker.destroy();
+        }
+
+        @Override
+        public String toString() {
+            return clusterInvoker.toString();
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+            // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
+            return null;
+        }
+    }
+
+
 }
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
new file mode 100644
index 0000000..8f70d31
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
@@ -0,0 +1,2 @@
+zone-aware=org.apache.dubbo.rpc.cluster.filter.support.ZoneAwareFilter
+consumercontext=org.apache.dubbo.rpc.cluster.filter.support.ConsumerContextFilter
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
deleted file mode 100644
index 3f3f008..0000000
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
+++ /dev/null
@@ -1,2 +0,0 @@
-context=org.apache.dubbo.rpc.cluster.interceptor.ConsumerContextClusterInterceptor
-zone-aware=org.apache.dubbo.rpc.cluster.interceptor.ZoneAwareClusterInterceptor
\ No newline at end of file
diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
index 44d9fab..b424661 100644
--- a/dubbo-common/pom.xml
+++ b/dubbo-common/pom.xml
@@ -72,6 +72,10 @@
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.collections</groupId>
+            <artifactId>eclipse-collections</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
index 619b1bb..5bd2970 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
@@ -21,8 +21,9 @@ import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
 import org.apache.dubbo.common.url.component.URLItemCache;
 
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
+
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Map;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
@@ -73,7 +74,7 @@ public final class URLStrParser {
         }
 
         TempBuf tempBuf = DECODE_TEMP_BUF.get();
-        Map<String, String> params = new HashMap<>();
+        Map<String, String> params = new UnifiedMap<>();
         int nameStart = from;
         int valueStart = -1;
         int i;
@@ -169,7 +170,7 @@ public final class URLStrParser {
         }
 
         // check cache
-        protocol = URLItemCache.checkProtocol(protocol);
+        protocol = URLItemCache.intern(protocol);
         path = URLItemCache.checkPath(path);
 
         return new ServiceConfigURL(protocol, username, password, host, port, path, parameters);
@@ -233,7 +234,7 @@ public final class URLStrParser {
         }
 
         TempBuf tempBuf = DECODE_TEMP_BUF.get();
-        Map<String, String> params = new HashMap<>();
+        Map<String, String> params = new UnifiedMap<>();
         int nameStart = from;
         int valueStart = -1;
         int i;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
index ea4b4e5..3da6cca 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
@@ -17,11 +17,13 @@
 package org.apache.dubbo.common.config;
 
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.context.FrameworkExt;
 import org.apache.dubbo.common.context.LifecycleAdapter;
 import org.apache.dubbo.common.extension.DisableInject;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ConfigUtils;
 import org.apache.dubbo.config.AbstractConfig;
 import org.apache.dubbo.config.ConfigCenterConfig;
 import org.apache.dubbo.config.context.ConfigConfigurationAdapter;
@@ -54,6 +56,7 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
     private boolean configCenterFirst = true;
 
     private DynamicConfiguration dynamicConfiguration;
+    private String localMigrationRule;
 
     public Environment() {
         this.propertiesConfiguration = new PropertiesConfiguration();
@@ -76,6 +79,19 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
 
         this.externalConfiguration.setProperties(externalConfigurationMap);
         this.appExternalConfiguration.setProperties(appExternalConfigurationMap);
+
+        loadMigrationRule();
+    }
+
+    private void loadMigrationRule() {
+        String path = System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY);
+        if (path == null || path.length() == 0) {
+            path = System.getenv(CommonConstants.DUBBO_MIGRATION_KEY);
+            if (path == null || path.length() == 0) {
+                path = CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE;
+            }
+        }
+        this.localMigrationRule = ConfigUtils.loadMigrationRule(path);
     }
 
     @DisableInject
@@ -220,6 +236,10 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
         return appExternalConfiguration;
     }
 
+    public String getLocalMigrationRule() {
+        return localMigrationRule;
+    }
+
     // For test
     public void clearExternalConfigs() {
         this.externalConfiguration.clear();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 268c313..953b11b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -45,6 +45,10 @@ public interface CommonConstants {
 
     String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties";
 
+    String DUBBO_MIGRATION_KEY = "dubbo.migration.file";
+
+    String DEFAULT_DUBBO_MIGRATION_FILE = "dubbo-migration.yaml";
+
     String ANY_VALUE = "*";
 
     /**
@@ -376,4 +380,6 @@ public interface CommonConstants {
     String CACHE_CLEAR_TASK_INTERVAL = "dubbo.application.url.cache.task.interval";
     String CACHE_CLEAR_WAITING_THRESHOLD = "dubbo.application.url.cache.clear.waiting";
 
+    String CLUSTER_INTERCEPTOR_COMPATIBLE_KEY = "dubbo.application.cluster.interceptor.compatible";
+
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
index 63d140b..a0e89f1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
@@ -252,7 +252,7 @@ public class URLAddress implements Serializable {
         }
 
         // check cache
-        protocol = URLItemCache.checkProtocol(protocol);
+        protocol = URLItemCache.intern(protocol);
         path = URLItemCache.checkPath(path);
 
         return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
index 2384493..54372c2 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
@@ -19,14 +19,13 @@ package org.apache.dubbo.common.url.component;
 import org.apache.dubbo.common.utils.LRUCache;
 
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 
 public class URLItemCache {
     // thread safe with limited size, by default 1000
     private static final Map<String, String> PARAM_KEY_CACHE = new LRUCache<>(10000);
-    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(100000);
+    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(50000);
     private static final Map<String, String> PATH_CACHE = new LRUCache<>(10000);
-    private static final Map<String, String> PROTOCOL_CACHE = new ConcurrentHashMap<>();
+    private static final Map<String, String> REVISION_CACHE = new LRUCache<>(10000);
 
     public static void putParams(Map<String, String> params, String key, String value) {
         String cachedKey = PARAM_KEY_CACHE.get(key);
@@ -43,17 +42,6 @@ public class URLItemCache {
         params.put(cachedKey, cachedValue);
     }
 
-    public static String checkProtocol(String _protocol) {
-        if (_protocol == null) {
-            return _protocol;
-        }
-        String cachedProtocol = PROTOCOL_CACHE.putIfAbsent(_protocol, _protocol);
-        if (cachedProtocol != null) {
-            return cachedProtocol;
-        }
-        return _protocol;
-    }
-
     public static String checkPath(String _path) {
         if (_path == null) {
             return _path;
@@ -64,4 +52,32 @@ public class URLItemCache {
         }
         return _path;
     }
+
+    public static String checkRevision(String _revision) {
+        if (_revision == null) {
+            return _revision;
+        }
+        String revision = REVISION_CACHE.putIfAbsent(_revision, _revision);
+        if (revision != null) {
+            return revision;
+        }
+        return _revision;
+    }
+
+    public static String intern(String _protocol) {
+        if (_protocol == null) {
+            return _protocol;
+        }
+        return _protocol.intern();
+    }
+
+    public static void putParamsIntern(Map<String, String> params, String key, String value) {
+        if (key == null || value == null) {
+            params.put(key, value);
+            return;
+        }
+        key = key.intern();
+        value = value.intern();
+        params.put(key, value);
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
index b2dca0c..cea92cb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
@@ -21,6 +21,8 @@ import org.apache.dubbo.common.URLStrParser;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
+
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashMap;
@@ -263,7 +265,7 @@ public class URLParam implements Serializable {
 
     public static URLParam parse(String rawParam) {
         String[] parts = rawParam.split("&");
-        Map<String, String> parameters = new HashMap<>((int) (parts.length/.75f) + 1);
+        Map<String, String> parameters = new UnifiedMap<>((int) (parts.length/.75f) + 1);
         for (String part : parts) {
             part = part.trim();
             if (part.length() > 0) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
index c1f4711..d0d0693 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
@@ -21,12 +21,16 @@ import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
@@ -296,6 +300,49 @@ public class ConfigUtils {
         return properties;
     }
 
+    public static String loadMigrationRule(String fileName) {
+        String rawRule = "";
+        if (checkFileNameExist(fileName)) {
+            try {
+                try (FileInputStream input = new FileInputStream(fileName)) {
+                    rawRule = readString(input);
+                }
+            } catch (Throwable e) {
+                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
+            }
+            return rawRule;
+        }
+
+        try {
+            InputStream is = ClassUtils.getClassLoader().getResourceAsStream(fileName);
+            if (is != null) {
+                rawRule = readString(is);
+            }
+        } catch (Throwable e) {
+            logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
+        }
+        return rawRule;
+    }
+
+    private static String readString(InputStream is) {
+        StringBuilder stringBuilder = new StringBuilder();
+        char[] buffer = new char[10];
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))){
+            int n;
+            while ((n = reader.read(buffer)) != -1) {
+                if (n < 10) {
+                    buffer = Arrays.copyOf(buffer, n);
+                }
+                stringBuilder.append(String.valueOf(buffer));
+                buffer = new char[10];
+            }
+        } catch (IOException e) {
+            logger.error("Read migration file error.", e);
+        }
+
+        return stringBuilder.toString();
+    }
+
     /**
      * check if the fileName can be found in filesystem
      *
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 4e7bbf4..47556c5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -36,6 +36,7 @@ import javax.annotation.PostConstruct;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -336,7 +337,7 @@ public abstract class AbstractConfig implements Serializable {
             String value = entry.getValue();
             result.put(pre + key, value);
             // For compatibility, key like "registry-type" will has a duplicate key "registry.type"
-            if (key.contains("-")) {
+            if (Arrays.binarySearch(Constants.DOT_COMPATIBLE_KEYS, key) != -1) {
                 result.put(pre + key.replace('-', '.'), value);
             }
         }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
index f8fed84..894f138 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
@@ -117,4 +117,6 @@ public interface Constants {
     String REGISTER_KEY = "register";
 
     String MULTI_SERIALIZATION_KEY = "serialize.multiple";
+
+    String[] DOT_COMPATIBLE_KEYS = new String[]{"qos-enable", "qos-port", "qos-accept-foreign-ip"};
 }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index a4116f4..cdf66dd 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -520,6 +520,7 @@ public class DubboBootstrap extends GenericEventListener {
         }
 
         ApplicationModel.initFrameworkExts();
+        
 
         startConfigCenter();
 
@@ -1162,7 +1163,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private void doRegisterServiceInstance(ServiceInstance serviceInstance) {
         // register instance only when at least one service is exported.
-        if (serviceInstance.getPort() != null && serviceInstance.getPort() != -1) {
+        if (serviceInstance.getPort() > 0) {
             publishMetadataToRemote(serviceInstance);
             logger.info("Start registering instance address to registry.");
             getServiceDiscoveries().forEach(serviceDiscovery ->
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
index 3831337..8693828 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
@@ -36,14 +36,14 @@ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomi
     @Override
     public void customize(ServiceInstance serviceInstance) {
 
-        if (serviceInstance.getPort() != null) {
+        if (serviceInstance.getPort() > 0) {
             return;
         }
 
         WritableMetadataService writableMetadataService = WritableMetadataService.getDefaultExtension();
 
         String host = null;
-        Integer port = null;
+        int port = -1;
         Set<URL> urls = writableMetadataService.getExportedServiceURLs();
         if (CollectionUtils.isNotEmpty(urls)) {
             String preferredProtocol = ApplicationModel.getApplicationConfig().getProtocol();
@@ -64,7 +64,6 @@ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomi
                 DefaultServiceInstance instance = (DefaultServiceInstance) serviceInstance;
                 instance.setHost(host);
                 instance.setPort(port);
-                instance.setId(host + ":" + port);
             }
         }
     }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
index 041cd75..04d5f72 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
@@ -116,8 +116,6 @@ public class AbstractConfigTest {
         Assertions.assertEquals("ONE,1", parameters.get("prefix.num"));
         Assertions.assertEquals("hello%2Fworld", parameters.get("prefix.naming"));
         Assertions.assertEquals("30", parameters.get("prefix.age"));
-        Assertions.assertTrue(parameters.containsKey("prefix.key-2"));
-        Assertions.assertTrue(parameters.containsKey("prefix.key.2"));
         Assertions.assertFalse(parameters.containsKey("prefix.secret"));
     }
 
@@ -807,7 +805,7 @@ public class AbstractConfigTest {
         public Map getParameters() {
             Map<String, String> map = new HashMap<String, String>();
             map.put("key.1", "one");
-            map.put("key-2", "two");
+            map.put("key.2", "two");
             return map;
         }
     }
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
new file mode 100644
index 0000000..dc2e8f2
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
@@ -0,0 +1,3 @@
+key: demo-consumer
+step: FORCE_APPLICATION
+threshold: 0.1
\ No newline at end of file
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index e6b67c1..8cc1d43 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -91,6 +91,7 @@
         <!-- Common libs -->
         <spring_version>4.3.16.RELEASE</spring_version>
         <javassist_version>3.20.0-GA</javassist_version>
+        <eclipse_collections_version>10.4.0</eclipse_collections_version>
         <netty_version>3.2.5.Final</netty_version>
         <netty4_version>4.1.56.Final</netty4_version>
         <mina_version>1.1.7</mina_version>
@@ -186,6 +187,11 @@
                 <version>${javassist_version}</version>
             </dependency>
             <dependency>
+                <groupId>org.eclipse.collections</groupId>
+                <artifactId>eclipse-collections</artifactId>
+                <version>${eclipse_collections_version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.jboss.netty</groupId>
                 <artifactId>netty</artifactId>
                 <version>${netty_version}</version>
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index 5729680..b464e41 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -312,6 +312,10 @@
             <artifactId>javassist</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.collections</groupId>
+            <artifactId>eclipse-collections</artifactId>
+        </dependency>
+        <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
         </dependency>
@@ -513,6 +517,10 @@
                                 </transformer>
                                 <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter</resource>
+                                </transformer>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                     <resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener</resource>
                                 </transformer>
                                 <transformer
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
index 024328e..4106ad2 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
@@ -42,6 +42,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPAR
 import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
 
 public class MetadataInfo implements Serializable {
+    public static final MetadataInfo EMPTY = new MetadataInfo();
+
     private String app;
     private String revision;
     private Map<String, ServiceInfo> services;
@@ -50,6 +52,8 @@ public class MetadataInfo implements Serializable {
     private transient Map<String, String> extendParams;
     private transient AtomicBoolean reported = new AtomicBoolean(false);
 
+    public MetadataInfo() {}
+
     public MetadataInfo(String app) {
         this(app, null, null);
     }
diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
index 1492e0b..47e4bc7 100644
--- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
+++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
@@ -36,7 +36,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
@@ -49,7 +48,7 @@ import static org.apache.dubbo.rpc.Constants.OUTPUT_KEY;
 /**
  * MonitorFilter. (SPI, Singleton, ThreadSafe)
  */
-@Activate(group = {PROVIDER, CONSUMER})
+@Activate(group = {PROVIDER})
 public class MonitorFilter implements Filter, Filter.Listener {
 
     private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
diff --git a/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java b/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
index cf984a5..96438c5 100644
--- a/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
+++ b/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
@@ -33,7 +33,7 @@ import org.apache.dubbo.rpc.RpcException;
  *
  * @see org.apache.dubbo.rpc.Filter
  */
-@Activate(group = CommonConstants.CONSUMER, order = -10000)
+@Activate(group = CommonConstants.CONSUMER, value = Constants.SERVICE_AUTH, order = -10000)
 public class ConsumerSignFilter implements Filter {
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
index 89e3e75..4c02cdc 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
@@ -45,4 +45,8 @@ public interface NotifyListener {
     default void addServiceListener(ServiceInstancesChangedListener instanceListener) {
     }
 
+    default URL getConsumerUrl() {
+        return null;
+    }
+
 }
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 93ba70e..1bcf284 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.client;
 import org.apache.dubbo.metadata.MetadataInfo;
 
 import com.alibaba.fastjson.JSON;
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
 
 import java.util.HashMap;
 import java.util.List;
@@ -39,24 +40,23 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private static final long serialVersionUID = 1149677083747278100L;
 
-    private String id;
-
     private String serviceName;
 
     private String host;
 
-    private Integer port;
+    private int port;
 
     private boolean enabled;
 
     private boolean healthy;
 
-    private Map<String, String> metadata = new HashMap<>();
+    private Map<String, String> metadata = new UnifiedMap<>();
 
     private transient String address;
     private transient MetadataInfo serviceMetadata;
     // used at runtime
-    private transient Map<String, String> extendParams = new HashMap<>();
+    private transient String registryCluster; // extendParams can be more flexiable, but one single property uses less space
+    private transient Map<String, String> extendParams;
     private transient List<Endpoint> endpoints;
 
     public DefaultServiceInstance() {
@@ -70,17 +70,16 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.healthy = other.healthy;
         this.metadata = other.metadata;
         this.serviceMetadata = other.serviceMetadata;
+        this.registryCluster = other.registryCluster;
         this.extendParams = other.extendParams;
         this.endpoints = other.endpoints;
         this.address = null;
-        this.id = null;
     }
 
-    public DefaultServiceInstance(String id, String serviceName, String host, Integer port) {
+    public DefaultServiceInstance(String serviceName, String host, Integer port) {
         if (port != null && port.intValue() < 1) {
             throw new IllegalArgumentException("The port must be greater than zero!");
         }
-        this.id = id;
         this.serviceName = serviceName;
         this.host = host;
         this.port = port;
@@ -88,18 +87,10 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.healthy = true;
     }
 
-    public DefaultServiceInstance(String serviceName, String host, Integer port) {
-        this(host + ":" + port, serviceName, host, port);
-    }
-
     public DefaultServiceInstance(String serviceName) {
         this.serviceName = serviceName;
     }
 
-    public void setId(String id) {
-        this.id = id;
-    }
-
     public void setServiceName(String serviceName) {
         this.serviceName = serviceName;
     }
@@ -109,11 +100,6 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
-    public String getId() {
-        return id;
-    }
-
-    @Override
     public String getServiceName() {
         return serviceName;
     }
@@ -123,12 +109,12 @@ public class DefaultServiceInstance implements ServiceInstance {
         return host;
     }
 
-    public void setPort(Integer port) {
+    public void setPort(int port) {
         this.port = port;
     }
 
     @Override
-    public Integer getPort() {
+    public int getPort() {
         return port;
     }
 
@@ -173,7 +159,19 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
+    public String getRegistryCluster() {
+        return registryCluster;
+    }
+
+    public void setRegistryCluster(String registryCluster) {
+        this.registryCluster = registryCluster;
+    }
+
+    @Override
     public Map<String, String> getExtendParams() {
+        if (extendParams == null) {
+            extendParams = new HashMap<>();
+        }
         return extendParams;
     }
 
@@ -187,16 +185,19 @@ public class DefaultServiceInstance implements ServiceInstance {
     public DefaultServiceInstance copy(Endpoint endpoint) {
         DefaultServiceInstance copyOfInstance = new DefaultServiceInstance(this);
         copyOfInstance.setPort(endpoint.getPort());
-        copyOfInstance.setId(copyOfInstance.getAddress());
         return copyOfInstance;
     }
 
     @Override
     public Map<String, String> getAllParams() {
-        Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
-        allParams.putAll(metadata);
-        allParams.putAll(extendParams);
-        return allParams;
+        if (extendParams == null) {
+            return metadata;
+        } else {
+            Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
+            allParams.putAll(metadata);
+            allParams.putAll(extendParams);
+            return allParams;
+        }
     }
 
     public void setMetadata(Map<String, String> metadata) {
@@ -249,7 +250,6 @@ public class DefaultServiceInstance implements ServiceInstance {
     @Override
     public String toString() {
         return "DefaultServiceInstance{" +
-                "id='" + id + '\'' +
                 ", serviceName='" + serviceName + '\'' +
                 ", host='" + host + '\'' +
                 ", port=" + port +
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
index ee99000..f9c8019 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
@@ -224,6 +224,21 @@ final class EventPublishingServiceDiscovery implements ServiceDiscovery {
     }
 
     @Override
+    public ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
+        return serviceDiscovery.createListener(serviceNames);
+    }
+
+    @Override
+    public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException {
+        serviceDiscovery.removeServiceInstancesChangedListener(listener);
+    }
+
+    @Override
+    public long getDelay() {
+        return serviceDiscovery.getDelay();
+    }
+
+    @Override
     public URL getUrl() {
         return serviceDiscovery.getUrl();
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
index 2a51168..1482a9d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
@@ -110,7 +110,7 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen
     }
 
     private String getServiceInstanceId(ServiceInstance serviceInstance) {
-        String id = serviceInstance.getId();
+        String id = serviceInstance.getAddress();
         if (StringUtils.isBlank(id)) {
             return serviceInstance.getHost() + "." + serviceInstance.getPort();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
index 1034420..7537090 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
@@ -207,7 +207,7 @@ public abstract class SelfHostMetaServiceDiscovery implements ServiceDiscovery {
 
     @SuppressWarnings("unchecked")
     public final void fillServiceInstance(DefaultServiceInstance serviceInstance) {
-        String hostId = serviceInstance.getId();
+        String hostId = serviceInstance.getAddress();
         if (metadataMap.containsKey(hostId)) {
             // Use cached metadata.
             // Metadata will be updated by provider callback
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
index 90ba196..aa318d4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
@@ -219,6 +219,7 @@ public interface ServiceDiscovery extends Prioritized {
 
     /**
      * unsubscribe to instances change event.
+     *
      * @param listener
      * @throws IllegalArgumentException
      */
@@ -226,6 +227,10 @@ public interface ServiceDiscovery extends Prioritized {
             throws IllegalArgumentException {
     }
 
+    default ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
+        return new ServiceInstancesChangedListener(serviceNames, this);
+    }
+
     /**
      * Dispatch the {@link ServiceInstancesChangedEvent}
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index d19c8de..31ae6f9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -291,7 +291,7 @@ public class ServiceDiscoveryRegistry implements Registry {
 
         // register ServiceInstancesChangedListener
         ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceNamesKey, k -> {
-            ServiceInstancesChangedListener serviceInstancesChangedListener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery);
+            ServiceInstancesChangedListener serviceInstancesChangedListener = serviceDiscovery.createListener(serviceNames);
             serviceInstancesChangedListener.setUrl(url);
             serviceNames.forEach(serviceName -> {
                 List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index c41e028..6232097 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -32,9 +32,10 @@ import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.cluster.RouterChain;
 
+import org.eclipse.collections.impl.map.mutable.UnifiedMap;
+
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -152,7 +153,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
      * @return invokers
      */
     private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
-        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
+        Map<String, Invoker<T>> newUrlInvokerMap = new UnifiedMap<>();
         if (urls == null || urls.isEmpty()) {
             return newUrlInvokerMap;
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index 5bb2bfe..3a55c4e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -29,13 +29,6 @@ import java.util.SortedMap;
 public interface ServiceInstance extends Serializable {
 
     /**
-     * The id of the registered service instance.
-     *
-     * @return nullable
-     */
-    String getId();
-
-    /**
      * The name of service that current instance belongs to.
      *
      * @return non-null
@@ -54,7 +47,7 @@ public interface ServiceInstance extends Serializable {
      *
      * @return the positive integer if present
      */
-    Integer getPort();
+    int getPort();
 
     String getAddress();
 
@@ -87,6 +80,10 @@ public interface ServiceInstance extends Serializable {
 
     SortedMap<String, String> getSortedMetadata();
 
+    String getRegistryCluster();
+
+    void setRegistryCluster(String registryCluster);
+
     Map<String, String> getExtendParams();
 
     Map<String, String> getAllParams();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index 22280bd..dd0a0cf 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -53,7 +53,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
@@ -67,14 +66,14 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     private static final Logger logger = LoggerFactory.getLogger(ServiceInstancesChangedListener.class);
 
-    private final Set<String> serviceNames;
-    private final ServiceDiscovery serviceDiscovery;
-    private URL url;
-    private Map<String, NotifyListener> listeners;
+    protected final Set<String> serviceNames;
+    protected final ServiceDiscovery serviceDiscovery;
+    protected URL url;
+    protected Map<String, NotifyListener> listeners;
 
     private Map<String, List<ServiceInstance>> allInstances;
 
-    private Map<String, List<URL>> serviceUrls;
+    private Map<String, Object> serviceUrls;
 
     private Map<String, MetadataInfo> revisionToMetadata;
 
@@ -112,8 +111,8 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
         Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
         Map<ServiceInfo, Set<String>> localServiceToRevisions = new HashMap<>();
-        Map<String, Map<Set<String>, List<URL>>> protocolRevisionsToUrls = new HashMap<>();
-        Map<String, List<URL>> newServiceUrls = new HashMap<>();//TODO
+        Map<String, Map<Set<String>, Object>> protocolRevisionsToUrls = new HashMap<>();
+        Map<String, Object> newServiceUrls = new HashMap<>();//TODO
         Map<String, MetadataInfo> newRevisionToMetadata = new HashMap<>();
 
         for (Map.Entry<String, List<ServiceInstance>> entry : allInstances.entrySet()) {
@@ -151,27 +150,14 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
         localServiceToRevisions.forEach((serviceInfo, revisions) -> {
             String protocol = serviceInfo.getProtocol();
-            Map<Set<String>, List<URL>> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> {
+            Map<Set<String>, Object> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> {
                 return new HashMap<>();
             });
-            List<URL> urls = revisionsToUrls.get(revisions);
+            Object urls = revisionsToUrls.get(revisions);
             if (urls != null) {
                 newServiceUrls.put(serviceInfo.getMatchKey(), urls);
             } else {
-                urls = new ArrayList<>();
-                for (String r : revisions) {
-                    for (ServiceInstance i : revisionToInstances.get(r)) {
-                        // different protocols may have ports specified in meta
-                        if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
-                            DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
-                            if (endpoint != null && !endpoint.getPort().equals(i.getPort())) {
-                                urls.add(((DefaultServiceInstance)i).copy(endpoint).toURL());
-                                break;
-                            }
-                        }
-                        urls.add(i.toURL());
-                    }
-                }
+                urls = getServiceUrlsCache(revisionToInstances, revisions, protocol);
                 revisionsToUrls.put(revisions, urls);
                 newServiceUrls.put(serviceInfo.getMatchKey(), urls);
             }
@@ -183,7 +169,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     public synchronized void addListenerAndNotify(String serviceKey, NotifyListener listener) {
         this.listeners.put(serviceKey, listener);
-        List<URL> urls = this.serviceUrls.get(serviceKey);
+        List<URL> urls = getAddresses(serviceKey);
         if (CollectionUtils.isNotEmpty(urls)) {
             listener.notify(urls);
         }
@@ -197,7 +183,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     }
 
     public List<URL> getUrls(String serviceKey) {
-        return toUrlsWithEmpty(serviceUrls.get(serviceKey));
+        return toUrlsWithEmpty(getAddresses(serviceKey));
     }
 
     /**
@@ -241,7 +227,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return serviceNames.contains(event.getServiceName());
     }
 
-    private boolean isRetryAndExpired(ServiceInstancesChangedEvent event) {
+    protected boolean isRetryAndExpired(ServiceInstancesChangedEvent event) {
         String appName = event.getServiceName();
         List<ServiceInstance> appInstances = event.getServiceInstances();
 
@@ -261,13 +247,13 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return false;
     }
 
-    private boolean hasEmptyMetadata(Map<String, MetadataInfo> revisionToMetadata) {
+    protected boolean hasEmptyMetadata(Map<String, MetadataInfo> revisionToMetadata) {
         if (revisionToMetadata == null) {
             return false;
         }
         boolean result = false;
         for (Map.Entry<String, MetadataInfo> entry : revisionToMetadata.entrySet()) {
-            if (entry.getValue() == null) {
+            if (entry.getValue() == MetadataInfo.EMPTY) {
                 result = true;
                 break;
             }
@@ -275,31 +261,31 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return result;
     }
 
-    private MetadataInfo getRemoteMetadata(ServiceInstance instance, String revision, Map<ServiceInfo, Set<String>> localServiceToRevisions, List<ServiceInstance> subInstances) {
+    protected MetadataInfo getRemoteMetadata(ServiceInstance instance, String revision, Map<ServiceInfo, Set<String>> localServiceToRevisions, List<ServiceInstance> subInstances) {
         MetadataInfo metadata = revisionToMetadata.get(revision);
-        if (metadata == null) {
-            if (failureCounter.get() < 3 || (System.currentTimeMillis() - lastFailureTime > 10000)) {
-                metadata = getMetadataInfo(instance);
-                if (metadata != null) {
-                    logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
-                    failureCounter.set(0);
-                    revisionToMetadata.putIfAbsent(revision, metadata);
-                    parseMetadata(revision, metadata, localServiceToRevisions);
-                } else {
-                    logger.error("Failed to get MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision
-                            + ", wait for retry.");
-                    lastFailureTime = System.currentTimeMillis();
-                    failureCounter.incrementAndGet();
-                }
+        if (metadata == null
+                || (metadata == MetadataInfo.EMPTY && (failureCounter.get() < 3 || (System.currentTimeMillis() - lastFailureTime > 10000)))) {
+            metadata = getMetadataInfo(instance);
+
+            if (metadata != MetadataInfo.EMPTY) {
+                logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
+                failureCounter.set(0);
+                revisionToMetadata.putIfAbsent(revision, metadata);
+                parseMetadata(revision, metadata, localServiceToRevisions);
+            } else {
+                logger.error("Failed to get MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision
+                        + ", wait for retry.");
+                lastFailureTime = System.currentTimeMillis();
+                failureCounter.incrementAndGet();
             }
-        } else if (subInstances.size() == 1) {
+        } else if (metadata != MetadataInfo.EMPTY && subInstances.size() == 1) {
             // "subInstances.size() >= 2" means metadata of this revision has been parsed, ignore
             parseMetadata(revision, metadata, localServiceToRevisions);
         }
         return metadata;
     }
 
-    private Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
+    protected Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
         Map<String, ServiceInfo> serviceInfos = metadata.getServices();
         for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
             Set<String> set = localServiceToRevisions.computeIfAbsent(entry.getValue(), k -> new TreeSet<>());
@@ -309,10 +295,12 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return localServiceToRevisions;
     }
 
-    private MetadataInfo getMetadataInfo(ServiceInstance instance) {
+    protected MetadataInfo getMetadataInfo(ServiceInstance instance) {
         String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
         // FIXME, check "REGISTRY_CLUSTER_KEY" must be set by every registry implementation.
-        instance.getExtendParams().putIfAbsent(REGISTRY_CLUSTER_KEY, RegistryClusterIdentifier.getExtension(url).consumerKey(url));
+        if (instance.getRegistryCluster() == null) {
+            instance.setRegistryCluster(RegistryClusterIdentifier.getExtension(url).consumerKey(url));
+        }
         MetadataInfo metadataInfo;
         try {
             if (logger.isDebugEnabled()) {
@@ -332,19 +320,46 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
             logger.error("Failed to load service metadata, meta type is " + metadataType, e);
             metadataInfo = null;
         }
+
+        if (metadataInfo == null) {
+            metadataInfo = MetadataInfo.EMPTY;
+        }
         return metadataInfo;
     }
 
-    private void notifyAddressChanged() {
+    protected Object getServiceUrlsCache(Map<String, List<ServiceInstance>> revisionToInstances, Set<String> revisions, String protocol) {
+        List<URL> urls;
+        urls = new ArrayList<>();
+        for (String r : revisions) {
+            for (ServiceInstance i : revisionToInstances.get(r)) {
+                // different protocols may have ports specified in meta
+                if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
+                    DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
+                    if (endpoint != null && !endpoint.getPort().equals(i.getPort())) {
+                        urls.add(((DefaultServiceInstance) i).copy(endpoint).toURL());
+                        break;
+                    }
+                }
+                urls.add(i.toURL());
+            }
+        }
+        return urls;
+    }
+
+    protected List<URL> getAddresses(String serviceProtocolKey) {
+        return (List<URL>) serviceUrls.get(serviceProtocolKey);
+    }
+
+    protected void notifyAddressChanged() {
         listeners.forEach((key, notifyListener) -> {
             //FIXME, group wildcard match
-            List<URL> urls = toUrlsWithEmpty(serviceUrls.get(key));
+            List<URL> urls = toUrlsWithEmpty(getAddresses(key));
             logger.info("Notify service " + key + " with urls " + urls.size());
             notifyListener.notify(urls);
         });
     }
 
-    private List<URL> toUrlsWithEmpty(List<URL> urls) {
+    protected List<URL> toUrlsWithEmpty(List<URL> urls) {
         if (urls == null) {
             urls = Collections.emptyList();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
index 27f66f5..42382f8 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -86,7 +86,7 @@ public class MetadataUtils {
     }
 
     public static String computeKey(ServiceInstance serviceInstance) {
-        return serviceInstance.getServiceName() + "##" + serviceInstance.getId() + "##" +
+        return serviceInstance.getServiceName() + "##" + serviceInstance.getAddress() + "##" +
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance);
     }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
index bca273b..d217e2d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
@@ -85,7 +85,7 @@ public class RemoteMetadataServiceImpl {
         SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(),
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
 
-        String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER_KEY);
+        String registryCluster = instance.getRegistryCluster();
 
         checkRemoteConfigured();
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
index 30f1ea1..a9c7b8c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
@@ -39,11 +39,11 @@ public class DefaultMigrationAddressComparator implements MigrationAddressCompar
     @Override
     public <T> boolean shouldMigrate(ClusterInvoker<T> serviceDiscoveryInvoker, ClusterInvoker<T> invoker, MigrationRule rule) {
         if (!serviceDiscoveryInvoker.hasProxyInvokers()) {
-            logger.info("No instance address available, will not migrate.");
+            logger.info("No instance address available, stop compare.");
             return false;
         }
         if (!invoker.hasProxyInvokers()) {
-            logger.info("No interface address available, will migrate.");
+            logger.info("No interface address available, stop compare.");
             return true;
         }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
index 15e2434..2e94d3f 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
@@ -27,7 +27,6 @@ import org.apache.dubbo.registry.client.migration.model.MigrationStep;
 import org.apache.dubbo.registry.integration.DynamicDirectory;
 import org.apache.dubbo.registry.integration.RegistryProtocol;
 import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
@@ -36,7 +35,6 @@ import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 
-import java.util.List;
 import java.util.Set;
 
 import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
@@ -288,6 +286,9 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
 
     private volatile boolean invokersChanged;
 
+    /**
+     * Need to know which invoker change triggered this compare.
+     */
     private synchronized void compareAddresses(ClusterInvoker<T> serviceDiscoveryInvoker, ClusterInvoker<T> invoker) {
         this.invokersChanged = true;
         if (logger.isDebugEnabled()) {
@@ -297,10 +298,10 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
         Set<MigrationAddressComparator> detectors = ExtensionLoader.getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();
         if (detectors != null && detectors.stream().allMatch(migrationDetector -> migrationDetector.shouldMigrate(serviceDiscoveryInvoker, invoker, rule))) {
             logger.info("serviceKey:" + invoker.getUrl().getServiceKey() + " switch to APP Level address");
-            discardInterfaceInvokerAddress(invoker);
+            destroyInterfaceInvoker(invoker);
         } else {
             logger.info("serviceKey:" + invoker.getUrl().getServiceKey() + " switch to Service Level address");
-            discardServiceDiscoveryInvokerAddress(serviceDiscoveryInvoker);
+            destroyServiceDiscoveryInvoker(serviceDiscoveryInvoker);
         }
     }
 
@@ -310,26 +311,28 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
         }
         if (serviceDiscoveryInvoker != null) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+            if (serviceDiscoveryInvoker.getDirectory().isNotificationReceived()) {
+                if (logger.isInfoEnabled()) {
+                    logger.info("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+                }
+                serviceDiscoveryInvoker.destroy();
             }
-            serviceDiscoveryInvoker.destroy();
         }
     }
 
-    protected synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker<T> serviceDiscoveryInvoker) {
-        if (this.invoker != null) {
-            this.currentAvailableInvoker = this.invoker;
-            updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
-        }
-        if (serviceDiscoveryInvoker != null) {
-            if (logger.isDebugEnabled()) {
-                List<Invoker<T>> invokers = serviceDiscoveryInvoker.getDirectory().getAllInvokers();
-                logger.debug("Discarding instance addresses, total size " + (invokers == null ? 0 : invokers.size()));
-            }
-//            serviceDiscoveryInvoker.getDirectory().discordAddresses();
-        }
-    }
+//    protected synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker<T> serviceDiscoveryInvoker) {
+//        if (this.invoker != null) {
+//            this.currentAvailableInvoker = this.invoker;
+//            updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
+//        }
+//        if (serviceDiscoveryInvoker != null) {
+//            if (logger.isDebugEnabled()) {
+//                List<Invoker<T>> invokers = serviceDiscoveryInvoker.getDirectory().getAllInvokers();
+//                logger.debug("Discarding instance addresses, total size " + (invokers == null ? 0 : invokers.size()));
+//            }
+////            serviceDiscoveryInvoker.getDirectory().discordAddresses();
+//        }
+//    }
 
     protected void refreshServiceDiscoveryInvoker() {
         clearListener(serviceDiscoveryInvoker);
@@ -338,8 +341,6 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
                 logger.debug("Re-subscribing instance addresses, current interface " + type.getName());
             }
             serviceDiscoveryInvoker = registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url);
-        } else {
-            ((DynamicDirectory) serviceDiscoveryInvoker.getDirectory()).markInvokersChanged();
         }
     }
 
@@ -352,8 +353,6 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             }
 
             invoker = registryProtocol.getInvoker(cluster, registry, type, url);
-        } else {
-            ((DynamicDirectory) invoker.getDirectory()).markInvokersChanged();
         }
     }
 
@@ -363,26 +362,28 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             updateConsumerModel(currentAvailableInvoker, invoker);
         }
         if (invoker != null) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName());
-            }
-            invoker.destroy();
-        }
-    }
-
-    protected synchronized void discardInterfaceInvokerAddress(ClusterInvoker<T> invoker) {
-        if (this.serviceDiscoveryInvoker != null) {
-            this.currentAvailableInvoker = this.serviceDiscoveryInvoker;
-            updateConsumerModel(currentAvailableInvoker, invoker);
-        }
-        if (invoker != null) {
-            if (logger.isDebugEnabled()) {
-                List<Invoker<T>> invokers = invoker.getDirectory().getAllInvokers();
-                logger.debug("Discarding interface addresses, total address size " + (invokers == null ? 0 : invokers.size()));
+            if (invoker.getDirectory().isNotificationReceived()) {
+                if (logger.isInfoEnabled()) {
+                    logger.info("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+                }
+                invoker.destroy();
             }
-            //invoker.getDirectory().discordAddresses();
         }
     }
+//
+//    protected synchronized void discardInterfaceInvokerAddress(ClusterInvoker<T> invoker) {
+//        if (this.serviceDiscoveryInvoker != null) {
+//            this.currentAvailableInvoker = this.serviceDiscoveryInvoker;
+//            updateConsumerModel(currentAvailableInvoker, invoker);
+//        }
+//        if (invoker != null) {
+//            if (logger.isDebugEnabled()) {
+//                List<Invoker<T>> invokers = invoker.getDirectory().getAllInvokers();
+//                logger.debug("Discarding interface addresses, total address size " + (invokers == null ? 0 : invokers.size()));
+//            }
+//            //invoker.getDirectory().discordAddresses();
+//        }
+//    }
 
     private void clearListener(ClusterInvoker<T> invoker) {
         if (invoker == null) return;
@@ -410,9 +411,9 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             if (workingInvoker != null) {
                 consumerModel.getServiceMetadata().addAttribute("currentClusterInvoker", workingInvoker);
             }
-            if (backInvoker != null) {
-                consumerModel.getServiceMetadata().addAttribute("backupClusterInvoker", backInvoker);
-            }
+//            if (backInvoker != null) {
+//                consumerModel.getServiceMetadata().addAttribute("backupClusterInvoker", backInvoker);
+//            }
         }
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
index 5972e06..b9718f4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
@@ -30,8 +30,8 @@ import java.util.Set;
 
 @Activate
 public class MigrationRuleHandler<T> {
+    public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
     private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class);
-    private static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
 
     private MigrationClusterInvoker<T> migrationInvoker;
     private MigrationStep currentStep;
@@ -119,8 +119,10 @@ public class MigrationRuleHandler<T> {
         }
 
         if (step == MigrationStep.APPLICATION_FIRST) {
+            setCurrentStepAndThreshold(step, threshold);
             migrationInvoker.refreshServiceDiscoveryInvokerOnMappingCallback(false);
         } else if (step == MigrationStep.FORCE_APPLICATION) {
+            setCurrentStepAndThreshold(step, threshold);
             migrationInvoker.refreshServiceDiscoveryInvokerOnMappingCallback(true);
         }
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
index 399a260..85c7349 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
@@ -67,20 +67,24 @@ public class MigrationRuleListener implements RegistryProtocolListener, Configur
 
     public MigrationRuleListener() {
         this.configuration = ApplicationModel.getEnvironment().getDynamicConfiguration().orElse(null);
+
+        String localRawRule = ApplicationModel.getEnvironment().getLocalMigrationRule();
+        String defaultRawRule = StringUtils.isEmpty(localRawRule) ? INIT : localRawRule;
+
         if (this.configuration != null) {
             logger.info("Listening for migration rules on dataId " + RULE_KEY + ", group " + DUBBO_SERVICEDISCOVERY_MIGRATION);
             configuration.addListener(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION, this);
 
             String rawRule = configuration.getConfig(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION);
             if (StringUtils.isEmpty(rawRule)) {
-                rawRule = INIT;
+                rawRule = defaultRawRule;
             }
             this.rawRule = rawRule;
         } else {
             if (logger.isWarnEnabled()) {
                 logger.warn("Using default configuration rule because config center is not configured!");
             }
-            rawRule = INIT;
+            rawRule = defaultRawRule;
         }
 //        process(new ConfigChangedEvent(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION, rawRule));
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
index 8b29483..c7b0daa 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -251,20 +251,18 @@ public abstract class DynamicDirectory<T> extends AbstractDirectory<T> implement
         this.invokersChangedListener = listener;
         if (invokersChangedListener != null && invokersChanged) {
             invokersChangedListener.onChange();
-            invokersChanged = false;
         }
     }
 
     protected synchronized void invokersChanged() {
         invokersChanged = true;
-        if (invokersChangedListener != null && invokersChanged) {
+        if (invokersChangedListener != null) {
             invokersChangedListener.onChange();
-            invokersChanged = false;
         }
     }
 
-    public synchronized void markInvokersChanged() {
-        this.invokersChanged = true;
+    public boolean isNotificationReceived() {
+        return invokersChanged;
     }
 
     protected abstract void destroyAllInvokers();
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
index 85b63de..e2909ce 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
@@ -19,7 +19,6 @@ package org.apache.dubbo.registry.client;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static java.lang.String.valueOf;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,7 +36,7 @@ public class DefaultServiceInstanceTest {
     public DefaultServiceInstance instance;
 
     public static DefaultServiceInstance createInstance() {
-        DefaultServiceInstance instance = new DefaultServiceInstance(valueOf(System.nanoTime()), "A", "127.0.0.1", 8080);
+        DefaultServiceInstance instance = new DefaultServiceInstance("A", "127.0.0.1", 8080);
         instance.getMetadata().put("dubbo.metadata-service.urls", "[ \"dubbo://192.168.0.102:20881/com.alibaba.cloud.dubbo.service.DubboMetadataService?anyhost=true&application=spring-cloud-alibaba-dubbo-provider&bind.ip=192.168.0.102&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=spring-cloud-alibaba-dubbo-provider&interface=com.alibaba.cloud.dubbo.service.DubboMetadataService&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedU [...]
         instance.getMetadata().put("dubbo.metadata-service.url-params", "{\"dubbo\":{\"application\":\"dubbo-provider-demo\",\"deprecated\":\"false\",\"group\":\"dubbo-provider-demo\",\"version\":\"1.0.0\",\"timestamp\":\"1564845042651\",\"dubbo\":\"2.0.2\",\"provider.host\":\"192.168.0.102\",\"provider.port\":\"20880\"}}");
         return instance;
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
index 8677381..65527df 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URLBuilder;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.dubbo.registry.client.DefaultServiceInstanceTest.createInstance;
@@ -29,6 +30,7 @@ import static org.apache.dubbo.registry.client.DefaultServiceInstanceTest.create
  *
  * @since 2.7.5
  */
+@Disabled("FileSystemServiceDiscovery implementation is not stable enough at present")
 public class FileSystemServiceDiscoveryTest {
 
     private FileSystemServiceDiscovery serviceDiscovery;
diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
index 084376b..15bf2d1 100644
--- a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
@@ -20,7 +20,6 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.utils.DefaultPage;
 import org.apache.dubbo.common.utils.Page;
-import org.apache.dubbo.event.ConditionalEventListener;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscoveryFactory;
 import org.apache.dubbo.registry.client.ServiceInstance;
@@ -33,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
 
 public class MultipleServiceDiscovery implements ServiceDiscovery {
     public static final String REGISTRY_PREFIX_KEY = "child.";
@@ -88,16 +88,23 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
 
     @Override
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
-        MultiServiceInstancesChangedListener multiListener = new MultiServiceInstancesChangedListener(listener);
+        MultiServiceInstancesChangedListener multiListener = (MultiServiceInstancesChangedListener) listener;
 
         for (String registryKey : serviceDiscoveries.keySet()) {
-            SingleServiceInstancesChangedListener singleListener = new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscoveries.get(registryKey), multiListener);
-            multiListener.putSingleListener(registryKey, singleListener);
-            serviceDiscoveries.get(registryKey).addServiceInstancesChangedListener(singleListener);
+            ServiceDiscovery serviceDiscovery = serviceDiscoveries.get(registryKey);
+            SingleServiceInstancesChangedListener singleListener = multiListener.getAndComputeIfAbsent(registryKey, k -> {
+                return new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscovery, multiListener);
+            });
+            serviceDiscovery.addServiceInstancesChangedListener(singleListener);
         }
     }
 
     @Override
+    public ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
+        return new MultiServiceInstancesChangedListener(serviceNames, this);
+    }
+
+    @Override
     public Page<ServiceInstance> getInstances(String serviceName, int offset, int pageSize, boolean healthyOnly) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
 
         List<ServiceInstance> serviceInstanceList = new ArrayList<>();
@@ -123,17 +130,12 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
         return serviceInstance;
     }
 
-    protected static class MultiServiceInstancesChangedListener implements ConditionalEventListener<ServiceInstancesChangedEvent> {
-        private final ServiceInstancesChangedListener sourceListener;
-        private final Map<String, SingleServiceInstancesChangedListener> singleListenerMap = new ConcurrentHashMap<>();
-
-        public MultiServiceInstancesChangedListener(ServiceInstancesChangedListener sourceListener) {
-            this.sourceListener = sourceListener;
-        }
+    protected static class MultiServiceInstancesChangedListener extends ServiceInstancesChangedListener {
+        private final Map<String, SingleServiceInstancesChangedListener> singleListenerMap;
 
-        @Override
-        public boolean accept(ServiceInstancesChangedEvent event) {
-            return sourceListener.getServiceNames().contains(event.getServiceName());
+        public MultiServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
+            super(serviceNames, serviceDiscovery);
+            this.singleListenerMap = new ConcurrentHashMap<>();
         }
 
         @Override
@@ -149,12 +151,16 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
                 }
             }
 
-            sourceListener.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances));
+            super.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances));
         }
 
         public void putSingleListener(String registryKey, SingleServiceInstancesChangedListener singleListener) {
             singleListenerMap.put(registryKey, singleListener);
         }
+
+        public SingleServiceInstancesChangedListener getAndComputeIfAbsent(String registryKey, Function<String, SingleServiceInstancesChangedListener> func) {
+            return singleListenerMap.computeIfAbsent(registryKey, func);
+        }
     }
 
     protected static class SingleServiceInstancesChangedListener extends ServiceInstancesChangedListener {
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
index 7542b5e..abc7467 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
@@ -57,7 +57,6 @@ public class NacosNamingServiceUtils {
      */
     public static Instance toInstance(ServiceInstance serviceInstance) {
         Instance instance = new Instance();
-        instance.setInstanceId(serviceInstance.getId());
         instance.setServiceName(serviceInstance.getServiceName());
         instance.setIp(serviceInstance.getHost());
         instance.setPort(serviceInstance.getPort());
@@ -75,8 +74,7 @@ public class NacosNamingServiceUtils {
      * @since 2.7.5
      */
     public static ServiceInstance toServiceInstance(Instance instance) {
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(instance.getInstanceId(),
-                NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort());
+        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort());
         serviceInstance.setMetadata(instance.getMetadata());
         serviceInstance.setEnabled(instance.isEnabled());
         serviceInstance.setHealthy(instance.isHealthy());
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
index dce7d20..549b803 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.Page;
 import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.rpc.RpcException;
 
@@ -40,6 +41,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
 
 import static org.apache.dubbo.common.function.ThrowableFunction.execute;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isInstanceUpdated;
@@ -199,18 +201,28 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery {
             throw new IllegalStateException("registerServiceWatcher create path=" + path + " fail.", e);
         }
 
-        CuratorWatcher watcher = watcherCaches.computeIfAbsent(path, key ->
-                new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, listener));
-        try {
-            curatorFramework.getChildren().usingWatcher(watcher).forPath(path);
-        } catch (KeeperException.NoNodeException e) {
-            // ignored
-            if (logger.isErrorEnabled()) {
-                logger.error(e.getMessage());
+        CountDownLatch latch = new CountDownLatch(1);
+        ZookeeperServiceDiscoveryChangeWatcher watcher = watcherCaches.computeIfAbsent(path, key -> {
+            ZookeeperServiceDiscoveryChangeWatcher tmpWatcher = new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, path, latch);
+            try {
+                curatorFramework.getChildren().usingWatcher(tmpWatcher).forPath(path);
+            } catch (KeeperException.NoNodeException e) {
+                // ignored
+                if (logger.isErrorEnabled()) {
+                    logger.error(e.getMessage());
+                }
+            } catch (Exception e) {
+                throw new IllegalStateException(e.getMessage(), e);
             }
-        } catch (Exception e) {
-            throw new IllegalStateException(e.getMessage(), e);
-        }
+            return tmpWatcher;
+        });
+        watcher.addListener(listener);
+        listener.onEvent(new ServiceInstancesChangedEvent(serviceName, this.getInstances(serviceName)));
+        latch.countDown();
+    }
+
+    public void reRegisterWatcher(ZookeeperServiceDiscoveryChangeWatcher watcher) throws Exception {
+        curatorFramework.getChildren().usingWatcher(watcher).forPath(watcher.getPath());
     }
 
     private String buildServicePath(String serviceName) {
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
index e34d3e4..8cffc3e 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
@@ -16,8 +16,10 @@
  */
 package org.apache.dubbo.registry.zookeeper;
 
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.registry.RegistryNotifier;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
@@ -25,6 +27,10 @@ import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
 
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
 import static org.apache.dubbo.rpc.model.ApplicationModel.getExecutorRepository;
 import static org.apache.zookeeper.Watcher.Event.EventType.NodeChildrenChanged;
 import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
@@ -37,7 +43,7 @@ import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
  * @since 2.7.5
  */
 public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
-    private ServiceInstancesChangedListener listener;
+    private Set<ServiceInstancesChangedListener> listeners = new ConcurrentHashSet<>();
 
     private final ZookeeperServiceDiscovery zookeeperServiceDiscovery;
 
@@ -47,34 +53,52 @@ public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
 
     private final String serviceName;
 
+    private final String path;
+
+    private CountDownLatch latch;
+
     public ZookeeperServiceDiscoveryChangeWatcher(ZookeeperServiceDiscovery zookeeperServiceDiscovery,
                                                   String serviceName,
-                                                  ServiceInstancesChangedListener listener) {
+                                                  String path,
+                                                  CountDownLatch latch) {
         this.zookeeperServiceDiscovery = zookeeperServiceDiscovery;
         this.serviceName = serviceName;
-        this.listener = listener;
+        this.path = path;
+        this.latch = latch;
         this.notifier = new RegistryNotifier(zookeeperServiceDiscovery.getDelay(), getExecutorRepository().getServiceDiscoveryAddressNotificationExecutor()) {
             @Override
             protected void doNotify(Object rawAddresses) {
-                listener.onEvent((ServiceInstancesChangedEvent)rawAddresses);
+                listeners.forEach(listener -> listener.onEvent((ServiceInstancesChangedEvent)rawAddresses));
             }
         };
     }
 
     @Override
     public void process(WatchedEvent event) throws Exception {
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+        }
 
         Watcher.Event.EventType eventType = event.getType();
 
         if (NodeChildrenChanged.equals(eventType) || NodeDataChanged.equals(eventType)) {
             if (shouldKeepWatching()) {
-                notifier.notify(new ServiceInstancesChangedEvent(serviceName, zookeeperServiceDiscovery.getInstances(serviceName)));
-                zookeeperServiceDiscovery.registerServiceWatcher(serviceName, listener);
-                zookeeperServiceDiscovery.dispatchServiceInstancesChangedEvent(serviceName);
+                zookeeperServiceDiscovery.reRegisterWatcher(this);
+                List<ServiceInstance> instanceList = zookeeperServiceDiscovery.getInstances(serviceName);
+                notifier.notify(new ServiceInstancesChangedEvent(serviceName, instanceList));
             }
         }
     }
 
+    public String getPath() {
+        return path;
+    }
+
+    public void addListener(ServiceInstancesChangedListener listener) {
+        listeners.add(listener);
+    }
+
     public boolean shouldKeepWatching() {
         return keepWatching;
     }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
index 278aae7..ed9be54 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
@@ -85,7 +85,7 @@ public abstract class CuratorFrameworkUtils {
         String host = instance.getAddress();
         int port = instance.getPort();
         ZookeeperInstance zookeeperInstance = instance.getPayload();
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(instance.getId(), name, host, port);
+        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(name, host, port);
         serviceInstance.setMetadata(zookeeperInstance.getMetadata());
         return serviceInstance;
     }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
index 5f3b680..359d62a 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
@@ -34,7 +34,6 @@ import java.util.Map;
 import static java.util.Arrays.asList;
 import static org.apache.dubbo.common.utils.NetUtils.getAvailablePort;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.INSTANCE_REVISION_UPDATED_KEY;
-import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.generateId;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -104,7 +103,7 @@ public class ZookeeperServiceDiscoveryTest {
     }
 
     private DefaultServiceInstance createServiceInstance(String serviceName, String host, int port) {
-        return new DefaultServiceInstance(generateId(host, port), serviceName, host, port);
+        return new DefaultServiceInstance(serviceName, host, port);
     }
 
 //    @Test
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java
new file mode 100644
index 0000000..d0b7848
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.rpc;
+
+public interface BaseFilter {
+    /**
+     * Make sure call invoker.invoke() in your implementation.
+     */
+    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
+
+    interface Listener {
+
+        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
+
+        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
index 58e8215..74acb51 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
@@ -33,6 +33,32 @@ import org.apache.dubbo.common.extension.SPI;
  *    Caching is implemented in dubbo using filter approach. If cache is configured for invocation then before
  *    remote call configured caching type's (e.g. Thread Local, JCache etc) implementation invoke method gets called.
  * </pre>
+ *
+ * Start from 3.0, the semantics of the Filter component at the consumer side has changed.
+ * Instead of intercepting a specific instance of invoker, Filter in 3.0 now intercepts ClusterInvoker. A new SPI named
+ * InstanceFilter is introduced to work as the same semantic as Filter in 2.x.
+ *
+ * The difference of Filter is as follows:
+ *
+ * 3.x Filter
+ *
+ *                                             -> InstanceFilter -> Invoker
+ *
+ * Proxy -> Filter -> Filter -> ClusterInvoker -> InstanceFilter -> Invoker
+ *
+ *                                             -> InstanceFilter -> Invoker
+ *
+ *
+ * 2.x Filter
+ *
+ *                            Filter -> Filter -> Invoker
+ *
+ * Proxy -> ClusterInvoker -> Filter -> Filter -> Invoker
+ *
+ *                            Filter -> Filter -> Invoker
+ *
+ * If you want to a Filter
+ *
  * Filter. (SPI, Singleton, ThreadSafe)
  *
  * @see org.apache.dubbo.rpc.filter.GenericFilter
@@ -41,17 +67,5 @@ import org.apache.dubbo.common.extension.SPI;
  * @see org.apache.dubbo.rpc.filter.TpsLimitFilter
  */
 @SPI
-public interface Filter {
-    /**
-     * Make sure call invoker.invoke() in your implementation.
-     */
-    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
-
-    interface Listener {
-
-        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
-
-        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
-    }
-
+public interface Filter extends BaseFilter {
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index d7e8c44..33a9b64 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -52,7 +52,7 @@ import java.util.concurrent.TimeUnit;
  */
 public abstract class AbstractInvoker<T> implements Invoker<T> {
 
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
+    protected static final Logger logger = LoggerFactory.getLogger(AbstractInvoker.class);
 
     private final Class<T> type;
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index 5255f55..7c526c2 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -3,10 +3,8 @@ generic=org.apache.dubbo.rpc.filter.GenericFilter
 genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
 token=org.apache.dubbo.rpc.filter.TokenFilter
 accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter
-activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
 classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter
 context=org.apache.dubbo.rpc.filter.ContextFilter
-consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
 exception=org.apache.dubbo.rpc.filter.ExceptionFilter
 executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter
 deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
index ee922af..ad95481 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -20,11 +20,11 @@ import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.AsyncMethodInfo;
 import org.apache.dubbo.rpc.model.ConsumerModel;
@@ -39,7 +39,7 @@ import static org.apache.dubbo.rpc.protocol.dubbo.Constants.ASYNC_METHOD_INFO;
  * EventFilter
  */
 @Activate(group = CommonConstants.CONSUMER)
-public class FutureFilter implements Filter, Filter.Listener {
+public class FutureFilter implements ClusterFilter, ClusterFilter.Listener {
 
     protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index 79a7a38..ee41594 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -1,2 +1 @@
-trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
-future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
+trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
new file mode 100644
index 0000000..4783214
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
@@ -0,0 +1 @@
+future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
index 1a1b0fd..d34f49a 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
@@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.protocol.dubbo;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.rpc.AppResponse;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService;
 
@@ -40,7 +40,7 @@ import static org.mockito.Mockito.mock;
  */
 public class FutureFilterTest {
     private static RpcInvocation invocation;
-    private Filter eventFilter = new FutureFilter();
+    private ClusterFilter eventFilter = new FutureFilter();
 
     @BeforeAll
     public static void setUp() {

[dubbo] 03/12: Revert "3.0 enhancement, do not merge (#7381)" (#7387)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 552112ed0157a0360f1499e7b04152047b350472
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Mar 16 23:18:43 2021 +0800

    Revert "3.0 enhancement, do not merge (#7381)" (#7387)
    
    This reverts commit d6c9bf69e00b468d7415a0c6db102f5bc0e2136b.
---
 .../org/apache/dubbo/rpc/cluster/Directory.java    |   4 -
 .../dubbo/rpc/cluster/filter/ClusterFilter.java    |  24 ---
 .../cluster/filter/DefaultFilterChainBuilder.java  |  10 +-
 .../rpc/cluster/filter/FilterChainBuilder.java     |  36 +---
 .../rpc/cluster/filter/ProtocolFilterWrapper.java  |   9 +-
 .../cluster/interceptor/ClusterInterceptor.java    |   1 -
 .../ConsumerContextClusterInterceptor.java         |  60 +++++++
 .../ZoneAwareClusterInterceptor.java}              |  19 +--
 .../cluster/support/wrapper/AbstractCluster.java   | 190 ++++++++++-----------
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   2 -
 ...ubbo.rpc.cluster.interceptor.ClusterInterceptor |   2 +
 dubbo-common/pom.xml                               |   4 -
 .../java/org/apache/dubbo/common/URLStrParser.java |   9 +-
 .../apache/dubbo/common/config/Environment.java    |  20 ---
 .../dubbo/common/constants/CommonConstants.java    |   6 -
 .../dubbo/common/url/component/URLAddress.java     |   2 +-
 .../dubbo/common/url/component/URLItemCache.java   |  44 ++---
 .../dubbo/common/url/component/URLParam.java       |   4 +-
 .../org/apache/dubbo/common/utils/ConfigUtils.java |  47 -----
 .../org/apache/dubbo/config/AbstractConfig.java    |   3 +-
 .../java/org/apache/dubbo/config/Constants.java    |   2 -
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   3 +-
 .../ServiceInstanceHostPortCustomizer.java         |   5 +-
 .../apache/dubbo/config/AbstractConfigTest.java    |   4 +-
 .../src/main/resources/dubbo-migration.yaml        |   3 -
 dubbo-dependencies-bom/pom.xml                     |   6 -
 dubbo-distribution/dubbo-all/pom.xml               |   8 -
 .../org/apache/dubbo/metadata/MetadataInfo.java    |   4 -
 .../dubbo/monitor/support/MonitorFilter.java       |   3 +-
 .../dubbo/auth/filter/ConsumerSignFilter.java      |   2 +-
 .../org/apache/dubbo/registry/NotifyListener.java  |   4 -
 .../registry/client/DefaultServiceInstance.java    |  58 +++----
 .../client/EventPublishingServiceDiscovery.java    |  15 --
 .../client/FileSystemServiceDiscovery.java         |   2 +-
 .../client/SelfHostMetaServiceDiscovery.java       |   2 +-
 .../dubbo/registry/client/ServiceDiscovery.java    |   5 -
 .../registry/client/ServiceDiscoveryRegistry.java  |   2 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |   5 +-
 .../dubbo/registry/client/ServiceInstance.java     |  13 +-
 .../listener/ServiceInstancesChangedListener.java  | 117 ++++++-------
 .../registry/client/metadata/MetadataUtils.java    |   2 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |   2 +-
 .../DefaultMigrationAddressComparator.java         |   4 +-
 .../client/migration/MigrationInvoker.java         |  89 +++++-----
 .../client/migration/MigrationRuleHandler.java     |   4 +-
 .../client/migration/MigrationRuleListener.java    |   8 +-
 .../registry/integration/DynamicDirectory.java     |   8 +-
 .../client/DefaultServiceInstanceTest.java         |   3 +-
 .../client/FileSystemServiceDiscoveryTest.java     |   2 -
 .../multiple/MultipleServiceDiscovery.java         |  38 ++---
 .../nacos/util/NacosNamingServiceUtils.java        |   4 +-
 .../zookeeper/ZookeeperServiceDiscovery.java       |  34 ++--
 .../ZookeeperServiceDiscoveryChangeWatcher.java    |  38 +----
 .../zookeeper/util/CuratorFrameworkUtils.java      |   2 +-
 .../zookeeper/ZookeeperServiceDiscoveryTest.java   |   3 +-
 .../main/java/org/apache/dubbo/rpc/BaseFilter.java |  31 ----
 .../src/main/java/org/apache/dubbo/rpc/Filter.java |  40 ++---
 .../dubbo/rpc/filter}/ConsumerContextFilter.java   |  26 +--
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |   2 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   2 +
 .../rpc/protocol/dubbo/filter/FutureFilter.java    |   4 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   3 +-
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   1 -
 .../dubbo/rpc/protocol/dubbo/FutureFilterTest.java |   4 +-
 64 files changed, 425 insertions(+), 688 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
index 5a92d97..9fd3ca2 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
@@ -65,8 +65,4 @@ public interface Directory<T> extends Node {
     void discordAddresses();
 
     RouterChain<T> getRouterChain();
-
-    default boolean isNotificationReceived() {
-        return false;
-    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java
deleted file mode 100644
index 7d48dc9..0000000
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.rpc.cluster.filter;
-
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.BaseFilter;
-
-@SPI
-public interface ClusterFilter extends BaseFilter {
-}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
index e2e26d2..982008b 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
@@ -27,9 +27,6 @@ import java.util.List;
 @Activate(order = 0)
 public class DefaultFilterChainBuilder implements FilterChainBuilder {
 
-    /**
-     * build consumer/provider filter chain
-     */
     @Override
     public <T> Invoker<T> buildInvokerChain(final Invoker<T> originalInvoker, String key, String group) {
         Invoker<T> last = originalInvoker;
@@ -46,17 +43,14 @@ public class DefaultFilterChainBuilder implements FilterChainBuilder {
         return last;
     }
 
-    /**
-     * build consumer cluster filter chain
-     */
     @Override
     public <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> originalInvoker, String key, String group) {
         ClusterInvoker<T> last = originalInvoker;
-        List<ClusterFilter> filters = ExtensionLoader.getExtensionLoader(ClusterFilter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
+        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
 
         if (!filters.isEmpty()) {
             for (int i = filters.size() - 1; i >= 0; i--) {
-                final ClusterFilter filter = filters.get(i);
+                final Filter filter = filters.get(i);
                 final Invoker<T> next = last;
                 last = new ClusterFilterChainNode<>(originalInvoker, next, filter);
             }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
index 949a66c..20275e2 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.rpc.cluster.filter;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.BaseFilter;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -30,27 +29,16 @@ import org.apache.dubbo.rpc.cluster.Directory;
 
 @SPI("default")
 public interface FilterChainBuilder {
-    /**
-     * build consumer/provider filter chain
-     */
     <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group);
 
-    /**
-     * build consumer cluster filter chain
-     */
     <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> invoker, String key, String group);
 
-    /**
-     * Works on provider side
-     * @param <T>
-     * @param <TYPE>
-     */
-    class FilterChainNode<T, TYPE extends Invoker<T>, FILTER extends BaseFilter> implements Invoker<T>{
+    class FilterChainNode<T, TYPE extends Invoker<T>> implements Invoker<T>{
         TYPE originalInvoker;
         Invoker<T> nextNode;
-        FILTER filter;
+        Filter filter;
 
-        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, FILTER filter) {
+        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
             this.originalInvoker = originalInvoker;
             this.nextNode = nextNode;
             this.filter = filter;
@@ -91,8 +79,8 @@ public interface FilterChainBuilder {
                     } finally {
                         listenableFilter.removeListener(invocation);
                     }
-                } else if (filter instanceof FILTER.Listener) {
-                    FILTER.Listener listener = (FILTER.Listener) filter;
+                } else if (filter instanceof Filter.Listener) {
+                    Filter.Listener listener = (Filter.Listener) filter;
                     listener.onError(e, originalInvoker, invocation);
                 }
                 throw e;
@@ -114,8 +102,8 @@ public interface FilterChainBuilder {
                     } finally {
                         listenableFilter.removeListener(invocation);
                     }
-                } else if (filter instanceof FILTER.Listener) {
-                    FILTER.Listener listener = (FILTER.Listener) filter;
+                } else if (filter instanceof Filter.Listener) {
+                    Filter.Listener listener = (Filter.Listener) filter;
                     if (t == null) {
                         listener.onResponse(r, originalInvoker, invocation);
                     } else {
@@ -136,14 +124,8 @@ public interface FilterChainBuilder {
         }
     }
 
-    /**
-     * Works on consumer side
-     * @param <T>
-     * @param <TYPE>
-     */
-    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>, FILTER extends BaseFilter>
-            extends FilterChainNode<T, TYPE, FILTER> implements ClusterInvoker<T> {
-        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, FILTER filter) {
+    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>> extends FilterChainNode<T, TYPE> implements ClusterInvoker<T> {
+        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
             super(originalInvoker, nextNode, filter);
         }
 
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
index 389b173..65e8813 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
@@ -28,9 +28,10 @@ import org.apache.dubbo.rpc.ProtocolServer;
 import org.apache.dubbo.rpc.RpcException;
 
 import java.util.List;
+import java.util.Objects;
 
-import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY;
 
 /**
  * ListenerProtocol
@@ -67,7 +68,11 @@ public class ProtocolFilterWrapper implements Protocol {
         if (UrlUtils.isRegistry(url)) {
             return protocol.refer(type, url);
         }
-        return builder.buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
+        // if it's peer-to-peer url
+        if (!Objects.isNull(url.getAttribute(PEER_KEY))) {
+            return builder.buildInvokerChain(protocol.refer(type, url), SERVICE_FILTER_KEY, CommonConstants.CONSUMER);
+        }
+        return protocol.refer(type, url);
     }
 
     @Override
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
index 821dd2e..199361f 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
@@ -26,7 +26,6 @@ import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 /**
  * Different from {@link Filter}, ClusterInterceptor works at the outmost layer, before one specific address/invoker is picked.
  */
-@Deprecated
 @SPI
 public interface ClusterInterceptor {
 
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
new file mode 100644
index 0000000..053bc87
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.rpc.cluster.interceptor;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+
+@Activate
+public class ConsumerContextClusterInterceptor implements ClusterInterceptor, ClusterInterceptor.Listener {
+
+    @Override
+    public void before(AbstractClusterInvoker<?> invoker, Invocation invocation) {
+        RpcContext context = RpcContext.getContext();
+        context.setInvocation(invocation).setLocalAddress(NetUtils.getLocalHost(), 0);
+        if (invocation instanceof RpcInvocation) {
+            ((RpcInvocation) invocation).setInvoker(invoker);
+        }
+        RpcContext.removeServerContext();
+    }
+
+    @Override
+    public void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
+        RpcContext.removeContext(true);
+    }
+
+    @Override
+    public Result intercept(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) throws RpcException {
+        return clusterInvoker.invoke(invocation);
+    }
+
+    @Override
+    public void onMessage(Result appResponse, AbstractClusterInvoker<?> invoker, Invocation invocation) {
+        RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
+    }
+
+    @Override
+    public void onError(Throwable t, AbstractClusterInvoker<?> invoker, Invocation invocation) {
+
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
similarity index 80%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
index cd0a7ab..6daec08 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ZoneAwareFilter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ZoneAwareClusterInterceptor.java
@@ -14,19 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.filter.support;
+package org.apache.dubbo.rpc.cluster.interceptor;
 
-import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.ZoneDetector;
-import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
+import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_FORCE;
@@ -36,11 +32,11 @@ import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_
  *
  * active only when url has key 'cluster=zone-aware'
  */
-@Activate(group = CommonConstants.CONSUMER, value = "cluster:zone-aware")
-public class ZoneAwareFilter implements ClusterFilter {
+@Activate(value = "cluster:zone-aware")
+public class ZoneAwareClusterInterceptor implements ClusterInterceptor {
 
     @Override
-    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+    public void before(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
         RpcContext rpcContext = RpcContext.getContext();
         String zone = (String) rpcContext.getAttachment(REGISTRY_ZONE);
         String force = (String) rpcContext.getAttachment(REGISTRY_ZONE_FORCE);
@@ -57,7 +53,10 @@ public class ZoneAwareFilter implements ClusterFilter {
         if (StringUtils.isNotEmpty(force)) {
             invocation.setAttachment(REGISTRY_ZONE_FORCE, force);
         }
+    }
+
+    @Override
+    public void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) {
 
-        return invoker.invoke(invocation);
     }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
index 1c2d047..9ce920d 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.rpc.cluster.support.wrapper;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -37,7 +36,6 @@ import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
 import java.util.List;
 
-import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_INTERCEPTOR_COMPATIBLE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.INVOCATION_INTERCEPTOR_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_INTERCEPTOR_KEY;
@@ -46,10 +44,15 @@ public abstract class AbstractCluster implements Cluster {
 
     private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
 //        AbstractClusterInvoker<T> last = clusterInvoker;
-        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new ClusterFilterInvoker<>(clusterInvoker));
+        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new FilterInvoker<>(clusterInvoker));
+        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
 
-        if (Boolean.parseBoolean(ConfigurationUtils.getProperty(CLUSTER_INTERCEPTOR_COMPATIBLE_KEY, "false"))) {
-            return build27xCompatibleClusterInterceptors(clusterInvoker, last);
+        if (!interceptors.isEmpty()) {
+            for (int i = interceptors.size() - 1; i >= 0; i--) {
+                final ClusterInterceptor interceptor = interceptors.get(i);
+                final AbstractClusterInvoker<T> next = last;
+                last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
+            }
         }
         return last;
     }
@@ -67,15 +70,90 @@ public abstract class AbstractCluster implements Cluster {
         if (CollectionUtils.isEmpty(builders)) {
             return invoker;
         }
-        return new InvocationInterceptorInvoker<>(invoker, builders);
+        return new InterceptorInvoker<>(invoker, builders);
     }
 
     protected abstract <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException;
 
-    static class ClusterFilterInvoker<T> extends AbstractClusterInvoker<T> {
+    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
+
+        private AbstractClusterInvoker<T> clusterInvoker;
+        private ClusterInterceptor interceptor;
+        private AbstractClusterInvoker<T> next;
+
+        public InterceptorInvokerNode(AbstractClusterInvoker<T> clusterInvoker,
+                                      ClusterInterceptor interceptor,
+                                      AbstractClusterInvoker<T> next) {
+            this.clusterInvoker = clusterInvoker;
+            this.interceptor = interceptor;
+            this.next = next;
+        }
+
+        @Override
+        public Class<T> getInterface() {
+            return clusterInvoker.getInterface();
+        }
+
+        @Override
+        public URL getUrl() {
+            return clusterInvoker.getUrl();
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return clusterInvoker.isAvailable();
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            Result asyncResult;
+            try {
+                interceptor.before(next, invocation);
+                asyncResult = interceptor.intercept(next, invocation);
+            } catch (Exception e) {
+                // onError callback
+                if (interceptor instanceof ClusterInterceptor.Listener) {
+                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
+                    listener.onError(e, clusterInvoker, invocation);
+                }
+                throw e;
+            } finally {
+                interceptor.after(next, invocation);
+            }
+            return asyncResult.whenCompleteWithContext((r, t) -> {
+                // onResponse callback
+                if (interceptor instanceof ClusterInterceptor.Listener) {
+                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
+                    if (t == null) {
+                        listener.onMessage(r, clusterInvoker, invocation);
+                    } else {
+                        listener.onError(t, clusterInvoker, invocation);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void destroy() {
+            clusterInvoker.destroy();
+        }
+
+        @Override
+        public String toString() {
+            return clusterInvoker.toString();
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+            // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
+            return null;
+        }
+    }
+
+    static class FilterInvoker<T> extends AbstractClusterInvoker<T> {
         private ClusterInvoker<T> filterInvoker;
 
-        public ClusterFilterInvoker(AbstractClusterInvoker<T> invoker) {
+        public FilterInvoker(AbstractClusterInvoker<T> invoker) {
             List<FilterChainBuilder> builders = ExtensionLoader.getExtensionLoader(FilterChainBuilder.class).getActivateExtensions();
             if (CollectionUtils.isEmpty(builders)) {
                 filterInvoker = invoker;
@@ -125,10 +203,10 @@ public abstract class AbstractCluster implements Cluster {
         }
     }
 
-    static class InvocationInterceptorInvoker<T> extends AbstractClusterInvoker<T> {
+    static class InterceptorInvoker<T> extends AbstractClusterInvoker<T> {
         private ClusterInvoker<T> interceptorInvoker;
 
-        public InvocationInterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
+        public InterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
             ClusterInvoker<T> tmpInvoker = invoker;
             for (InvocationInterceptorBuilder builder : builders) {
                 tmpInvoker = builder.buildClusterInterceptorChain(tmpInvoker, INVOCATION_INTERCEPTOR_KEY, CommonConstants.CONSUMER);
@@ -170,96 +248,4 @@ public abstract class AbstractCluster implements Cluster {
             return null;
         }
     }
-
-    @Deprecated
-    private <T> ClusterInvoker<T> build27xCompatibleClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, AbstractClusterInvoker<T> last) {
-        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
-
-        if (!interceptors.isEmpty()) {
-            for (int i = interceptors.size() - 1; i >= 0; i--) {
-                final ClusterInterceptor interceptor = interceptors.get(i);
-                final AbstractClusterInvoker<T> next = last;
-                last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
-            }
-        }
-        return last;
-    }
-
-    @Deprecated
-    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
-
-        private AbstractClusterInvoker<T> clusterInvoker;
-        private ClusterInterceptor interceptor;
-        private AbstractClusterInvoker<T> next;
-
-        public InterceptorInvokerNode(AbstractClusterInvoker<T> clusterInvoker,
-                                      ClusterInterceptor interceptor,
-                                      AbstractClusterInvoker<T> next) {
-            this.clusterInvoker = clusterInvoker;
-            this.interceptor = interceptor;
-            this.next = next;
-        }
-
-        @Override
-        public Class<T> getInterface() {
-            return clusterInvoker.getInterface();
-        }
-
-        @Override
-        public URL getUrl() {
-            return clusterInvoker.getUrl();
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return clusterInvoker.isAvailable();
-        }
-
-        @Override
-        public Result invoke(Invocation invocation) throws RpcException {
-            Result asyncResult;
-            try {
-                interceptor.before(next, invocation);
-                asyncResult = interceptor.intercept(next, invocation);
-            } catch (Exception e) {
-                // onError callback
-                if (interceptor instanceof ClusterInterceptor.Listener) {
-                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
-                    listener.onError(e, clusterInvoker, invocation);
-                }
-                throw e;
-            } finally {
-                interceptor.after(next, invocation);
-            }
-            return asyncResult.whenCompleteWithContext((r, t) -> {
-                // onResponse callback
-                if (interceptor instanceof ClusterInterceptor.Listener) {
-                    ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor;
-                    if (t == null) {
-                        listener.onMessage(r, clusterInvoker, invocation);
-                    } else {
-                        listener.onError(t, clusterInvoker, invocation);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void destroy() {
-            clusterInvoker.destroy();
-        }
-
-        @Override
-        public String toString() {
-            return clusterInvoker.toString();
-        }
-
-        @Override
-        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
-            // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
-            return null;
-        }
-    }
-
-
 }
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
deleted file mode 100644
index 8f70d31..0000000
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
+++ /dev/null
@@ -1,2 +0,0 @@
-zone-aware=org.apache.dubbo.rpc.cluster.filter.support.ZoneAwareFilter
-consumercontext=org.apache.dubbo.rpc.cluster.filter.support.ConsumerContextFilter
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
new file mode 100644
index 0000000..3f3f008
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor
@@ -0,0 +1,2 @@
+context=org.apache.dubbo.rpc.cluster.interceptor.ConsumerContextClusterInterceptor
+zone-aware=org.apache.dubbo.rpc.cluster.interceptor.ZoneAwareClusterInterceptor
\ No newline at end of file
diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
index b424661..44d9fab 100644
--- a/dubbo-common/pom.xml
+++ b/dubbo-common/pom.xml
@@ -72,10 +72,6 @@
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.collections</groupId>
-            <artifactId>eclipse-collections</artifactId>
-        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
index 5bd2970..619b1bb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
@@ -21,9 +21,8 @@ import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
 import org.apache.dubbo.common.url.component.URLItemCache;
 
-import org.eclipse.collections.impl.map.mutable.UnifiedMap;
-
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
@@ -74,7 +73,7 @@ public final class URLStrParser {
         }
 
         TempBuf tempBuf = DECODE_TEMP_BUF.get();
-        Map<String, String> params = new UnifiedMap<>();
+        Map<String, String> params = new HashMap<>();
         int nameStart = from;
         int valueStart = -1;
         int i;
@@ -170,7 +169,7 @@ public final class URLStrParser {
         }
 
         // check cache
-        protocol = URLItemCache.intern(protocol);
+        protocol = URLItemCache.checkProtocol(protocol);
         path = URLItemCache.checkPath(path);
 
         return new ServiceConfigURL(protocol, username, password, host, port, path, parameters);
@@ -234,7 +233,7 @@ public final class URLStrParser {
         }
 
         TempBuf tempBuf = DECODE_TEMP_BUF.get();
-        Map<String, String> params = new UnifiedMap<>();
+        Map<String, String> params = new HashMap<>();
         int nameStart = from;
         int valueStart = -1;
         int i;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
index 3da6cca..ea4b4e5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
@@ -17,13 +17,11 @@
 package org.apache.dubbo.common.config;
 
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.context.FrameworkExt;
 import org.apache.dubbo.common.context.LifecycleAdapter;
 import org.apache.dubbo.common.extension.DisableInject;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.ConfigUtils;
 import org.apache.dubbo.config.AbstractConfig;
 import org.apache.dubbo.config.ConfigCenterConfig;
 import org.apache.dubbo.config.context.ConfigConfigurationAdapter;
@@ -56,7 +54,6 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
     private boolean configCenterFirst = true;
 
     private DynamicConfiguration dynamicConfiguration;
-    private String localMigrationRule;
 
     public Environment() {
         this.propertiesConfiguration = new PropertiesConfiguration();
@@ -79,19 +76,6 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
 
         this.externalConfiguration.setProperties(externalConfigurationMap);
         this.appExternalConfiguration.setProperties(appExternalConfigurationMap);
-
-        loadMigrationRule();
-    }
-
-    private void loadMigrationRule() {
-        String path = System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY);
-        if (path == null || path.length() == 0) {
-            path = System.getenv(CommonConstants.DUBBO_MIGRATION_KEY);
-            if (path == null || path.length() == 0) {
-                path = CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE;
-            }
-        }
-        this.localMigrationRule = ConfigUtils.loadMigrationRule(path);
     }
 
     @DisableInject
@@ -236,10 +220,6 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
         return appExternalConfiguration;
     }
 
-    public String getLocalMigrationRule() {
-        return localMigrationRule;
-    }
-
     // For test
     public void clearExternalConfigs() {
         this.externalConfiguration.clear();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 953b11b..268c313 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -45,10 +45,6 @@ public interface CommonConstants {
 
     String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties";
 
-    String DUBBO_MIGRATION_KEY = "dubbo.migration.file";
-
-    String DEFAULT_DUBBO_MIGRATION_FILE = "dubbo-migration.yaml";
-
     String ANY_VALUE = "*";
 
     /**
@@ -380,6 +376,4 @@ public interface CommonConstants {
     String CACHE_CLEAR_TASK_INTERVAL = "dubbo.application.url.cache.task.interval";
     String CACHE_CLEAR_WAITING_THRESHOLD = "dubbo.application.url.cache.clear.waiting";
 
-    String CLUSTER_INTERCEPTOR_COMPATIBLE_KEY = "dubbo.application.cluster.interceptor.compatible";
-
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
index a0e89f1..63d140b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
@@ -252,7 +252,7 @@ public class URLAddress implements Serializable {
         }
 
         // check cache
-        protocol = URLItemCache.intern(protocol);
+        protocol = URLItemCache.checkProtocol(protocol);
         path = URLItemCache.checkPath(path);
 
         return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
index 54372c2..2384493 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
@@ -19,13 +19,14 @@ package org.apache.dubbo.common.url.component;
 import org.apache.dubbo.common.utils.LRUCache;
 
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 public class URLItemCache {
     // thread safe with limited size, by default 1000
     private static final Map<String, String> PARAM_KEY_CACHE = new LRUCache<>(10000);
-    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(50000);
+    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(100000);
     private static final Map<String, String> PATH_CACHE = new LRUCache<>(10000);
-    private static final Map<String, String> REVISION_CACHE = new LRUCache<>(10000);
+    private static final Map<String, String> PROTOCOL_CACHE = new ConcurrentHashMap<>();
 
     public static void putParams(Map<String, String> params, String key, String value) {
         String cachedKey = PARAM_KEY_CACHE.get(key);
@@ -42,6 +43,17 @@ public class URLItemCache {
         params.put(cachedKey, cachedValue);
     }
 
+    public static String checkProtocol(String _protocol) {
+        if (_protocol == null) {
+            return _protocol;
+        }
+        String cachedProtocol = PROTOCOL_CACHE.putIfAbsent(_protocol, _protocol);
+        if (cachedProtocol != null) {
+            return cachedProtocol;
+        }
+        return _protocol;
+    }
+
     public static String checkPath(String _path) {
         if (_path == null) {
             return _path;
@@ -52,32 +64,4 @@ public class URLItemCache {
         }
         return _path;
     }
-
-    public static String checkRevision(String _revision) {
-        if (_revision == null) {
-            return _revision;
-        }
-        String revision = REVISION_CACHE.putIfAbsent(_revision, _revision);
-        if (revision != null) {
-            return revision;
-        }
-        return _revision;
-    }
-
-    public static String intern(String _protocol) {
-        if (_protocol == null) {
-            return _protocol;
-        }
-        return _protocol.intern();
-    }
-
-    public static void putParamsIntern(Map<String, String> params, String key, String value) {
-        if (key == null || value == null) {
-            params.put(key, value);
-            return;
-        }
-        key = key.intern();
-        value = value.intern();
-        params.put(key, value);
-    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
index cea92cb..b2dca0c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
@@ -21,8 +21,6 @@ import org.apache.dubbo.common.URLStrParser;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 
-import org.eclipse.collections.impl.map.mutable.UnifiedMap;
-
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashMap;
@@ -265,7 +263,7 @@ public class URLParam implements Serializable {
 
     public static URLParam parse(String rawParam) {
         String[] parts = rawParam.split("&");
-        Map<String, String> parameters = new UnifiedMap<>((int) (parts.length/.75f) + 1);
+        Map<String, String> parameters = new HashMap<>((int) (parts.length/.75f) + 1);
         for (String part : parts) {
             part = part.trim();
             if (part.length() > 0) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
index d0d0693..c1f4711 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java
@@ -21,16 +21,12 @@ import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
@@ -300,49 +296,6 @@ public class ConfigUtils {
         return properties;
     }
 
-    public static String loadMigrationRule(String fileName) {
-        String rawRule = "";
-        if (checkFileNameExist(fileName)) {
-            try {
-                try (FileInputStream input = new FileInputStream(fileName)) {
-                    rawRule = readString(input);
-                }
-            } catch (Throwable e) {
-                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
-            }
-            return rawRule;
-        }
-
-        try {
-            InputStream is = ClassUtils.getClassLoader().getResourceAsStream(fileName);
-            if (is != null) {
-                rawRule = readString(is);
-            }
-        } catch (Throwable e) {
-            logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
-        }
-        return rawRule;
-    }
-
-    private static String readString(InputStream is) {
-        StringBuilder stringBuilder = new StringBuilder();
-        char[] buffer = new char[10];
-        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))){
-            int n;
-            while ((n = reader.read(buffer)) != -1) {
-                if (n < 10) {
-                    buffer = Arrays.copyOf(buffer, n);
-                }
-                stringBuilder.append(String.valueOf(buffer));
-                buffer = new char[10];
-            }
-        } catch (IOException e) {
-            logger.error("Read migration file error.", e);
-        }
-
-        return stringBuilder.toString();
-    }
-
     /**
      * check if the fileName can be found in filesystem
      *
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 47556c5..4e7bbf4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -36,7 +36,6 @@ import javax.annotation.PostConstruct;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -337,7 +336,7 @@ public abstract class AbstractConfig implements Serializable {
             String value = entry.getValue();
             result.put(pre + key, value);
             // For compatibility, key like "registry-type" will has a duplicate key "registry.type"
-            if (Arrays.binarySearch(Constants.DOT_COMPATIBLE_KEYS, key) != -1) {
+            if (key.contains("-")) {
                 result.put(pre + key.replace('-', '.'), value);
             }
         }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
index 894f138..f8fed84 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java
@@ -117,6 +117,4 @@ public interface Constants {
     String REGISTER_KEY = "register";
 
     String MULTI_SERIALIZATION_KEY = "serialize.multiple";
-
-    String[] DOT_COMPATIBLE_KEYS = new String[]{"qos-enable", "qos-port", "qos-accept-foreign-ip"};
 }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index cdf66dd..a4116f4 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -520,7 +520,6 @@ public class DubboBootstrap extends GenericEventListener {
         }
 
         ApplicationModel.initFrameworkExts();
-        
 
         startConfigCenter();
 
@@ -1163,7 +1162,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private void doRegisterServiceInstance(ServiceInstance serviceInstance) {
         // register instance only when at least one service is exported.
-        if (serviceInstance.getPort() > 0) {
+        if (serviceInstance.getPort() != null && serviceInstance.getPort() != -1) {
             publishMetadataToRemote(serviceInstance);
             logger.info("Start registering instance address to registry.");
             getServiceDiscoveries().forEach(serviceDiscovery ->
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
index 8693828..3831337 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
@@ -36,14 +36,14 @@ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomi
     @Override
     public void customize(ServiceInstance serviceInstance) {
 
-        if (serviceInstance.getPort() > 0) {
+        if (serviceInstance.getPort() != null) {
             return;
         }
 
         WritableMetadataService writableMetadataService = WritableMetadataService.getDefaultExtension();
 
         String host = null;
-        int port = -1;
+        Integer port = null;
         Set<URL> urls = writableMetadataService.getExportedServiceURLs();
         if (CollectionUtils.isNotEmpty(urls)) {
             String preferredProtocol = ApplicationModel.getApplicationConfig().getProtocol();
@@ -64,6 +64,7 @@ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomi
                 DefaultServiceInstance instance = (DefaultServiceInstance) serviceInstance;
                 instance.setHost(host);
                 instance.setPort(port);
+                instance.setId(host + ":" + port);
             }
         }
     }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
index 04d5f72..041cd75 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
@@ -116,6 +116,8 @@ public class AbstractConfigTest {
         Assertions.assertEquals("ONE,1", parameters.get("prefix.num"));
         Assertions.assertEquals("hello%2Fworld", parameters.get("prefix.naming"));
         Assertions.assertEquals("30", parameters.get("prefix.age"));
+        Assertions.assertTrue(parameters.containsKey("prefix.key-2"));
+        Assertions.assertTrue(parameters.containsKey("prefix.key.2"));
         Assertions.assertFalse(parameters.containsKey("prefix.secret"));
     }
 
@@ -805,7 +807,7 @@ public class AbstractConfigTest {
         public Map getParameters() {
             Map<String, String> map = new HashMap<String, String>();
             map.put("key.1", "one");
-            map.put("key.2", "two");
+            map.put("key-2", "two");
             return map;
         }
     }
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
deleted file mode 100644
index dc2e8f2..0000000
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/dubbo-migration.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-key: demo-consumer
-step: FORCE_APPLICATION
-threshold: 0.1
\ No newline at end of file
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 8cc1d43..e6b67c1 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -91,7 +91,6 @@
         <!-- Common libs -->
         <spring_version>4.3.16.RELEASE</spring_version>
         <javassist_version>3.20.0-GA</javassist_version>
-        <eclipse_collections_version>10.4.0</eclipse_collections_version>
         <netty_version>3.2.5.Final</netty_version>
         <netty4_version>4.1.56.Final</netty4_version>
         <mina_version>1.1.7</mina_version>
@@ -187,11 +186,6 @@
                 <version>${javassist_version}</version>
             </dependency>
             <dependency>
-                <groupId>org.eclipse.collections</groupId>
-                <artifactId>eclipse-collections</artifactId>
-                <version>${eclipse_collections_version}</version>
-            </dependency>
-            <dependency>
                 <groupId>org.jboss.netty</groupId>
                 <artifactId>netty</artifactId>
                 <version>${netty_version}</version>
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index b464e41..5729680 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -312,10 +312,6 @@
             <artifactId>javassist</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.eclipse.collections</groupId>
-            <artifactId>eclipse-collections</artifactId>
-        </dependency>
-        <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
         </dependency>
@@ -517,10 +513,6 @@
                                 </transformer>
                                 <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter</resource>
-                                </transformer>
-                                <transformer
-                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                     <resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener</resource>
                                 </transformer>
                                 <transformer
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
index 4106ad2..024328e 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
@@ -42,8 +42,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPAR
 import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
 
 public class MetadataInfo implements Serializable {
-    public static final MetadataInfo EMPTY = new MetadataInfo();
-
     private String app;
     private String revision;
     private Map<String, ServiceInfo> services;
@@ -52,8 +50,6 @@ public class MetadataInfo implements Serializable {
     private transient Map<String, String> extendParams;
     private transient AtomicBoolean reported = new AtomicBoolean(false);
 
-    public MetadataInfo() {}
-
     public MetadataInfo(String app) {
         this(app, null, null);
     }
diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
index 47e4bc7..1492e0b 100644
--- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
+++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java
@@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
@@ -48,7 +49,7 @@ import static org.apache.dubbo.rpc.Constants.OUTPUT_KEY;
 /**
  * MonitorFilter. (SPI, Singleton, ThreadSafe)
  */
-@Activate(group = {PROVIDER})
+@Activate(group = {PROVIDER, CONSUMER})
 public class MonitorFilter implements Filter, Filter.Listener {
 
     private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
diff --git a/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java b/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
index 96438c5..cf984a5 100644
--- a/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
+++ b/dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java
@@ -33,7 +33,7 @@ import org.apache.dubbo.rpc.RpcException;
  *
  * @see org.apache.dubbo.rpc.Filter
  */
-@Activate(group = CommonConstants.CONSUMER, value = Constants.SERVICE_AUTH, order = -10000)
+@Activate(group = CommonConstants.CONSUMER, order = -10000)
 public class ConsumerSignFilter implements Filter {
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
index 4c02cdc..89e3e75 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
@@ -45,8 +45,4 @@ public interface NotifyListener {
     default void addServiceListener(ServiceInstancesChangedListener instanceListener) {
     }
 
-    default URL getConsumerUrl() {
-        return null;
-    }
-
 }
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 1bcf284..93ba70e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -19,7 +19,6 @@ package org.apache.dubbo.registry.client;
 import org.apache.dubbo.metadata.MetadataInfo;
 
 import com.alibaba.fastjson.JSON;
-import org.eclipse.collections.impl.map.mutable.UnifiedMap;
 
 import java.util.HashMap;
 import java.util.List;
@@ -40,23 +39,24 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private static final long serialVersionUID = 1149677083747278100L;
 
+    private String id;
+
     private String serviceName;
 
     private String host;
 
-    private int port;
+    private Integer port;
 
     private boolean enabled;
 
     private boolean healthy;
 
-    private Map<String, String> metadata = new UnifiedMap<>();
+    private Map<String, String> metadata = new HashMap<>();
 
     private transient String address;
     private transient MetadataInfo serviceMetadata;
     // used at runtime
-    private transient String registryCluster; // extendParams can be more flexiable, but one single property uses less space
-    private transient Map<String, String> extendParams;
+    private transient Map<String, String> extendParams = new HashMap<>();
     private transient List<Endpoint> endpoints;
 
     public DefaultServiceInstance() {
@@ -70,16 +70,17 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.healthy = other.healthy;
         this.metadata = other.metadata;
         this.serviceMetadata = other.serviceMetadata;
-        this.registryCluster = other.registryCluster;
         this.extendParams = other.extendParams;
         this.endpoints = other.endpoints;
         this.address = null;
+        this.id = null;
     }
 
-    public DefaultServiceInstance(String serviceName, String host, Integer port) {
+    public DefaultServiceInstance(String id, String serviceName, String host, Integer port) {
         if (port != null && port.intValue() < 1) {
             throw new IllegalArgumentException("The port must be greater than zero!");
         }
+        this.id = id;
         this.serviceName = serviceName;
         this.host = host;
         this.port = port;
@@ -87,10 +88,18 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.healthy = true;
     }
 
+    public DefaultServiceInstance(String serviceName, String host, Integer port) {
+        this(host + ":" + port, serviceName, host, port);
+    }
+
     public DefaultServiceInstance(String serviceName) {
         this.serviceName = serviceName;
     }
 
+    public void setId(String id) {
+        this.id = id;
+    }
+
     public void setServiceName(String serviceName) {
         this.serviceName = serviceName;
     }
@@ -100,6 +109,11 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
     public String getServiceName() {
         return serviceName;
     }
@@ -109,12 +123,12 @@ public class DefaultServiceInstance implements ServiceInstance {
         return host;
     }
 
-    public void setPort(int port) {
+    public void setPort(Integer port) {
         this.port = port;
     }
 
     @Override
-    public int getPort() {
+    public Integer getPort() {
         return port;
     }
 
@@ -159,19 +173,7 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
-    public String getRegistryCluster() {
-        return registryCluster;
-    }
-
-    public void setRegistryCluster(String registryCluster) {
-        this.registryCluster = registryCluster;
-    }
-
-    @Override
     public Map<String, String> getExtendParams() {
-        if (extendParams == null) {
-            extendParams = new HashMap<>();
-        }
         return extendParams;
     }
 
@@ -185,19 +187,16 @@ public class DefaultServiceInstance implements ServiceInstance {
     public DefaultServiceInstance copy(Endpoint endpoint) {
         DefaultServiceInstance copyOfInstance = new DefaultServiceInstance(this);
         copyOfInstance.setPort(endpoint.getPort());
+        copyOfInstance.setId(copyOfInstance.getAddress());
         return copyOfInstance;
     }
 
     @Override
     public Map<String, String> getAllParams() {
-        if (extendParams == null) {
-            return metadata;
-        } else {
-            Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
-            allParams.putAll(metadata);
-            allParams.putAll(extendParams);
-            return allParams;
-        }
+        Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
+        allParams.putAll(metadata);
+        allParams.putAll(extendParams);
+        return allParams;
     }
 
     public void setMetadata(Map<String, String> metadata) {
@@ -250,6 +249,7 @@ public class DefaultServiceInstance implements ServiceInstance {
     @Override
     public String toString() {
         return "DefaultServiceInstance{" +
+                "id='" + id + '\'' +
                 ", serviceName='" + serviceName + '\'' +
                 ", host='" + host + '\'' +
                 ", port=" + port +
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
index f9c8019..ee99000 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
@@ -224,21 +224,6 @@ final class EventPublishingServiceDiscovery implements ServiceDiscovery {
     }
 
     @Override
-    public ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
-        return serviceDiscovery.createListener(serviceNames);
-    }
-
-    @Override
-    public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException {
-        serviceDiscovery.removeServiceInstancesChangedListener(listener);
-    }
-
-    @Override
-    public long getDelay() {
-        return serviceDiscovery.getDelay();
-    }
-
-    @Override
     public URL getUrl() {
         return serviceDiscovery.getUrl();
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
index 1482a9d..2a51168 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
@@ -110,7 +110,7 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen
     }
 
     private String getServiceInstanceId(ServiceInstance serviceInstance) {
-        String id = serviceInstance.getAddress();
+        String id = serviceInstance.getId();
         if (StringUtils.isBlank(id)) {
             return serviceInstance.getHost() + "." + serviceInstance.getPort();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
index 7537090..1034420 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
@@ -207,7 +207,7 @@ public abstract class SelfHostMetaServiceDiscovery implements ServiceDiscovery {
 
     @SuppressWarnings("unchecked")
     public final void fillServiceInstance(DefaultServiceInstance serviceInstance) {
-        String hostId = serviceInstance.getAddress();
+        String hostId = serviceInstance.getId();
         if (metadataMap.containsKey(hostId)) {
             // Use cached metadata.
             // Metadata will be updated by provider callback
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
index aa318d4..90ba196 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
@@ -219,7 +219,6 @@ public interface ServiceDiscovery extends Prioritized {
 
     /**
      * unsubscribe to instances change event.
-     *
      * @param listener
      * @throws IllegalArgumentException
      */
@@ -227,10 +226,6 @@ public interface ServiceDiscovery extends Prioritized {
             throws IllegalArgumentException {
     }
 
-    default ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
-        return new ServiceInstancesChangedListener(serviceNames, this);
-    }
-
     /**
      * Dispatch the {@link ServiceInstancesChangedEvent}
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index 31ae6f9..d19c8de 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -291,7 +291,7 @@ public class ServiceDiscoveryRegistry implements Registry {
 
         // register ServiceInstancesChangedListener
         ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceNamesKey, k -> {
-            ServiceInstancesChangedListener serviceInstancesChangedListener = serviceDiscovery.createListener(serviceNames);
+            ServiceInstancesChangedListener serviceInstancesChangedListener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery);
             serviceInstancesChangedListener.setUrl(url);
             serviceNames.forEach(serviceName -> {
                 List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index 6232097..c41e028 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -32,10 +32,9 @@ import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.cluster.RouterChain;
 
-import org.eclipse.collections.impl.map.mutable.UnifiedMap;
-
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -153,7 +152,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
      * @return invokers
      */
     private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
-        Map<String, Invoker<T>> newUrlInvokerMap = new UnifiedMap<>();
+        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
         if (urls == null || urls.isEmpty()) {
             return newUrlInvokerMap;
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index 3a55c4e..5bb2bfe 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -29,6 +29,13 @@ import java.util.SortedMap;
 public interface ServiceInstance extends Serializable {
 
     /**
+     * The id of the registered service instance.
+     *
+     * @return nullable
+     */
+    String getId();
+
+    /**
      * The name of service that current instance belongs to.
      *
      * @return non-null
@@ -47,7 +54,7 @@ public interface ServiceInstance extends Serializable {
      *
      * @return the positive integer if present
      */
-    int getPort();
+    Integer getPort();
 
     String getAddress();
 
@@ -80,10 +87,6 @@ public interface ServiceInstance extends Serializable {
 
     SortedMap<String, String> getSortedMetadata();
 
-    String getRegistryCluster();
-
-    void setRegistryCluster(String registryCluster);
-
     Map<String, String> getExtendParams();
 
     Map<String, String> getAllParams();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index dd0a0cf..22280bd 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -53,6 +53,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
@@ -66,14 +67,14 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     private static final Logger logger = LoggerFactory.getLogger(ServiceInstancesChangedListener.class);
 
-    protected final Set<String> serviceNames;
-    protected final ServiceDiscovery serviceDiscovery;
-    protected URL url;
-    protected Map<String, NotifyListener> listeners;
+    private final Set<String> serviceNames;
+    private final ServiceDiscovery serviceDiscovery;
+    private URL url;
+    private Map<String, NotifyListener> listeners;
 
     private Map<String, List<ServiceInstance>> allInstances;
 
-    private Map<String, Object> serviceUrls;
+    private Map<String, List<URL>> serviceUrls;
 
     private Map<String, MetadataInfo> revisionToMetadata;
 
@@ -111,8 +112,8 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
         Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
         Map<ServiceInfo, Set<String>> localServiceToRevisions = new HashMap<>();
-        Map<String, Map<Set<String>, Object>> protocolRevisionsToUrls = new HashMap<>();
-        Map<String, Object> newServiceUrls = new HashMap<>();//TODO
+        Map<String, Map<Set<String>, List<URL>>> protocolRevisionsToUrls = new HashMap<>();
+        Map<String, List<URL>> newServiceUrls = new HashMap<>();//TODO
         Map<String, MetadataInfo> newRevisionToMetadata = new HashMap<>();
 
         for (Map.Entry<String, List<ServiceInstance>> entry : allInstances.entrySet()) {
@@ -150,14 +151,27 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
         localServiceToRevisions.forEach((serviceInfo, revisions) -> {
             String protocol = serviceInfo.getProtocol();
-            Map<Set<String>, Object> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> {
+            Map<Set<String>, List<URL>> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> {
                 return new HashMap<>();
             });
-            Object urls = revisionsToUrls.get(revisions);
+            List<URL> urls = revisionsToUrls.get(revisions);
             if (urls != null) {
                 newServiceUrls.put(serviceInfo.getMatchKey(), urls);
             } else {
-                urls = getServiceUrlsCache(revisionToInstances, revisions, protocol);
+                urls = new ArrayList<>();
+                for (String r : revisions) {
+                    for (ServiceInstance i : revisionToInstances.get(r)) {
+                        // different protocols may have ports specified in meta
+                        if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
+                            DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
+                            if (endpoint != null && !endpoint.getPort().equals(i.getPort())) {
+                                urls.add(((DefaultServiceInstance)i).copy(endpoint).toURL());
+                                break;
+                            }
+                        }
+                        urls.add(i.toURL());
+                    }
+                }
                 revisionsToUrls.put(revisions, urls);
                 newServiceUrls.put(serviceInfo.getMatchKey(), urls);
             }
@@ -169,7 +183,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     public synchronized void addListenerAndNotify(String serviceKey, NotifyListener listener) {
         this.listeners.put(serviceKey, listener);
-        List<URL> urls = getAddresses(serviceKey);
+        List<URL> urls = this.serviceUrls.get(serviceKey);
         if (CollectionUtils.isNotEmpty(urls)) {
             listener.notify(urls);
         }
@@ -183,7 +197,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     }
 
     public List<URL> getUrls(String serviceKey) {
-        return toUrlsWithEmpty(getAddresses(serviceKey));
+        return toUrlsWithEmpty(serviceUrls.get(serviceKey));
     }
 
     /**
@@ -227,7 +241,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return serviceNames.contains(event.getServiceName());
     }
 
-    protected boolean isRetryAndExpired(ServiceInstancesChangedEvent event) {
+    private boolean isRetryAndExpired(ServiceInstancesChangedEvent event) {
         String appName = event.getServiceName();
         List<ServiceInstance> appInstances = event.getServiceInstances();
 
@@ -247,13 +261,13 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return false;
     }
 
-    protected boolean hasEmptyMetadata(Map<String, MetadataInfo> revisionToMetadata) {
+    private boolean hasEmptyMetadata(Map<String, MetadataInfo> revisionToMetadata) {
         if (revisionToMetadata == null) {
             return false;
         }
         boolean result = false;
         for (Map.Entry<String, MetadataInfo> entry : revisionToMetadata.entrySet()) {
-            if (entry.getValue() == MetadataInfo.EMPTY) {
+            if (entry.getValue() == null) {
                 result = true;
                 break;
             }
@@ -261,31 +275,31 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return result;
     }
 
-    protected MetadataInfo getRemoteMetadata(ServiceInstance instance, String revision, Map<ServiceInfo, Set<String>> localServiceToRevisions, List<ServiceInstance> subInstances) {
+    private MetadataInfo getRemoteMetadata(ServiceInstance instance, String revision, Map<ServiceInfo, Set<String>> localServiceToRevisions, List<ServiceInstance> subInstances) {
         MetadataInfo metadata = revisionToMetadata.get(revision);
-        if (metadata == null
-                || (metadata == MetadataInfo.EMPTY && (failureCounter.get() < 3 || (System.currentTimeMillis() - lastFailureTime > 10000)))) {
-            metadata = getMetadataInfo(instance);
-
-            if (metadata != MetadataInfo.EMPTY) {
-                logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
-                failureCounter.set(0);
-                revisionToMetadata.putIfAbsent(revision, metadata);
-                parseMetadata(revision, metadata, localServiceToRevisions);
-            } else {
-                logger.error("Failed to get MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision
-                        + ", wait for retry.");
-                lastFailureTime = System.currentTimeMillis();
-                failureCounter.incrementAndGet();
+        if (metadata == null) {
+            if (failureCounter.get() < 3 || (System.currentTimeMillis() - lastFailureTime > 10000)) {
+                metadata = getMetadataInfo(instance);
+                if (metadata != null) {
+                    logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
+                    failureCounter.set(0);
+                    revisionToMetadata.putIfAbsent(revision, metadata);
+                    parseMetadata(revision, metadata, localServiceToRevisions);
+                } else {
+                    logger.error("Failed to get MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision
+                            + ", wait for retry.");
+                    lastFailureTime = System.currentTimeMillis();
+                    failureCounter.incrementAndGet();
+                }
             }
-        } else if (metadata != MetadataInfo.EMPTY && subInstances.size() == 1) {
+        } else if (subInstances.size() == 1) {
             // "subInstances.size() >= 2" means metadata of this revision has been parsed, ignore
             parseMetadata(revision, metadata, localServiceToRevisions);
         }
         return metadata;
     }
 
-    protected Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
+    private Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
         Map<String, ServiceInfo> serviceInfos = metadata.getServices();
         for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
             Set<String> set = localServiceToRevisions.computeIfAbsent(entry.getValue(), k -> new TreeSet<>());
@@ -295,12 +309,10 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         return localServiceToRevisions;
     }
 
-    protected MetadataInfo getMetadataInfo(ServiceInstance instance) {
+    private MetadataInfo getMetadataInfo(ServiceInstance instance) {
         String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
         // FIXME, check "REGISTRY_CLUSTER_KEY" must be set by every registry implementation.
-        if (instance.getRegistryCluster() == null) {
-            instance.setRegistryCluster(RegistryClusterIdentifier.getExtension(url).consumerKey(url));
-        }
+        instance.getExtendParams().putIfAbsent(REGISTRY_CLUSTER_KEY, RegistryClusterIdentifier.getExtension(url).consumerKey(url));
         MetadataInfo metadataInfo;
         try {
             if (logger.isDebugEnabled()) {
@@ -320,46 +332,19 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
             logger.error("Failed to load service metadata, meta type is " + metadataType, e);
             metadataInfo = null;
         }
-
-        if (metadataInfo == null) {
-            metadataInfo = MetadataInfo.EMPTY;
-        }
         return metadataInfo;
     }
 
-    protected Object getServiceUrlsCache(Map<String, List<ServiceInstance>> revisionToInstances, Set<String> revisions, String protocol) {
-        List<URL> urls;
-        urls = new ArrayList<>();
-        for (String r : revisions) {
-            for (ServiceInstance i : revisionToInstances.get(r)) {
-                // different protocols may have ports specified in meta
-                if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
-                    DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
-                    if (endpoint != null && !endpoint.getPort().equals(i.getPort())) {
-                        urls.add(((DefaultServiceInstance) i).copy(endpoint).toURL());
-                        break;
-                    }
-                }
-                urls.add(i.toURL());
-            }
-        }
-        return urls;
-    }
-
-    protected List<URL> getAddresses(String serviceProtocolKey) {
-        return (List<URL>) serviceUrls.get(serviceProtocolKey);
-    }
-
-    protected void notifyAddressChanged() {
+    private void notifyAddressChanged() {
         listeners.forEach((key, notifyListener) -> {
             //FIXME, group wildcard match
-            List<URL> urls = toUrlsWithEmpty(getAddresses(key));
+            List<URL> urls = toUrlsWithEmpty(serviceUrls.get(key));
             logger.info("Notify service " + key + " with urls " + urls.size());
             notifyListener.notify(urls);
         });
     }
 
-    protected List<URL> toUrlsWithEmpty(List<URL> urls) {
+    private List<URL> toUrlsWithEmpty(List<URL> urls) {
         if (urls == null) {
             urls = Collections.emptyList();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
index 42382f8..27f66f5 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -86,7 +86,7 @@ public class MetadataUtils {
     }
 
     public static String computeKey(ServiceInstance serviceInstance) {
-        return serviceInstance.getServiceName() + "##" + serviceInstance.getAddress() + "##" +
+        return serviceInstance.getServiceName() + "##" + serviceInstance.getId() + "##" +
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance);
     }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
index d217e2d..bca273b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
@@ -85,7 +85,7 @@ public class RemoteMetadataServiceImpl {
         SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(),
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
 
-        String registryCluster = instance.getRegistryCluster();
+        String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER_KEY);
 
         checkRemoteConfigured();
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
index a9c7b8c..30f1ea1 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
@@ -39,11 +39,11 @@ public class DefaultMigrationAddressComparator implements MigrationAddressCompar
     @Override
     public <T> boolean shouldMigrate(ClusterInvoker<T> serviceDiscoveryInvoker, ClusterInvoker<T> invoker, MigrationRule rule) {
         if (!serviceDiscoveryInvoker.hasProxyInvokers()) {
-            logger.info("No instance address available, stop compare.");
+            logger.info("No instance address available, will not migrate.");
             return false;
         }
         if (!invoker.hasProxyInvokers()) {
-            logger.info("No interface address available, stop compare.");
+            logger.info("No interface address available, will migrate.");
             return true;
         }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
index 2e94d3f..15e2434 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
@@ -27,6 +27,7 @@ import org.apache.dubbo.registry.client.migration.model.MigrationStep;
 import org.apache.dubbo.registry.integration.DynamicDirectory;
 import org.apache.dubbo.registry.integration.RegistryProtocol;
 import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
@@ -35,6 +36,7 @@ import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 
+import java.util.List;
 import java.util.Set;
 
 import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
@@ -286,9 +288,6 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
 
     private volatile boolean invokersChanged;
 
-    /**
-     * Need to know which invoker change triggered this compare.
-     */
     private synchronized void compareAddresses(ClusterInvoker<T> serviceDiscoveryInvoker, ClusterInvoker<T> invoker) {
         this.invokersChanged = true;
         if (logger.isDebugEnabled()) {
@@ -298,10 +297,10 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
         Set<MigrationAddressComparator> detectors = ExtensionLoader.getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();
         if (detectors != null && detectors.stream().allMatch(migrationDetector -> migrationDetector.shouldMigrate(serviceDiscoveryInvoker, invoker, rule))) {
             logger.info("serviceKey:" + invoker.getUrl().getServiceKey() + " switch to APP Level address");
-            destroyInterfaceInvoker(invoker);
+            discardInterfaceInvokerAddress(invoker);
         } else {
             logger.info("serviceKey:" + invoker.getUrl().getServiceKey() + " switch to Service Level address");
-            destroyServiceDiscoveryInvoker(serviceDiscoveryInvoker);
+            discardServiceDiscoveryInvokerAddress(serviceDiscoveryInvoker);
         }
     }
 
@@ -311,28 +310,26 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
         }
         if (serviceDiscoveryInvoker != null) {
-            if (serviceDiscoveryInvoker.getDirectory().isNotificationReceived()) {
-                if (logger.isInfoEnabled()) {
-                    logger.info("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName());
-                }
-                serviceDiscoveryInvoker.destroy();
+            if (logger.isDebugEnabled()) {
+                logger.debug("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName());
             }
+            serviceDiscoveryInvoker.destroy();
         }
     }
 
-//    protected synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker<T> serviceDiscoveryInvoker) {
-//        if (this.invoker != null) {
-//            this.currentAvailableInvoker = this.invoker;
-//            updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
-//        }
-//        if (serviceDiscoveryInvoker != null) {
-//            if (logger.isDebugEnabled()) {
-//                List<Invoker<T>> invokers = serviceDiscoveryInvoker.getDirectory().getAllInvokers();
-//                logger.debug("Discarding instance addresses, total size " + (invokers == null ? 0 : invokers.size()));
-//            }
-////            serviceDiscoveryInvoker.getDirectory().discordAddresses();
-//        }
-//    }
+    protected synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker<T> serviceDiscoveryInvoker) {
+        if (this.invoker != null) {
+            this.currentAvailableInvoker = this.invoker;
+            updateConsumerModel(currentAvailableInvoker, serviceDiscoveryInvoker);
+        }
+        if (serviceDiscoveryInvoker != null) {
+            if (logger.isDebugEnabled()) {
+                List<Invoker<T>> invokers = serviceDiscoveryInvoker.getDirectory().getAllInvokers();
+                logger.debug("Discarding instance addresses, total size " + (invokers == null ? 0 : invokers.size()));
+            }
+//            serviceDiscoveryInvoker.getDirectory().discordAddresses();
+        }
+    }
 
     protected void refreshServiceDiscoveryInvoker() {
         clearListener(serviceDiscoveryInvoker);
@@ -341,6 +338,8 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
                 logger.debug("Re-subscribing instance addresses, current interface " + type.getName());
             }
             serviceDiscoveryInvoker = registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url);
+        } else {
+            ((DynamicDirectory) serviceDiscoveryInvoker.getDirectory()).markInvokersChanged();
         }
     }
 
@@ -353,6 +352,8 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             }
 
             invoker = registryProtocol.getInvoker(cluster, registry, type, url);
+        } else {
+            ((DynamicDirectory) invoker.getDirectory()).markInvokersChanged();
         }
     }
 
@@ -362,28 +363,26 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             updateConsumerModel(currentAvailableInvoker, invoker);
         }
         if (invoker != null) {
-            if (invoker.getDirectory().isNotificationReceived()) {
-                if (logger.isInfoEnabled()) {
-                    logger.info("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName());
-                }
-                invoker.destroy();
+            if (logger.isDebugEnabled()) {
+                logger.debug("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName());
+            }
+            invoker.destroy();
+        }
+    }
+
+    protected synchronized void discardInterfaceInvokerAddress(ClusterInvoker<T> invoker) {
+        if (this.serviceDiscoveryInvoker != null) {
+            this.currentAvailableInvoker = this.serviceDiscoveryInvoker;
+            updateConsumerModel(currentAvailableInvoker, invoker);
+        }
+        if (invoker != null) {
+            if (logger.isDebugEnabled()) {
+                List<Invoker<T>> invokers = invoker.getDirectory().getAllInvokers();
+                logger.debug("Discarding interface addresses, total address size " + (invokers == null ? 0 : invokers.size()));
             }
+            //invoker.getDirectory().discordAddresses();
         }
     }
-//
-//    protected synchronized void discardInterfaceInvokerAddress(ClusterInvoker<T> invoker) {
-//        if (this.serviceDiscoveryInvoker != null) {
-//            this.currentAvailableInvoker = this.serviceDiscoveryInvoker;
-//            updateConsumerModel(currentAvailableInvoker, invoker);
-//        }
-//        if (invoker != null) {
-//            if (logger.isDebugEnabled()) {
-//                List<Invoker<T>> invokers = invoker.getDirectory().getAllInvokers();
-//                logger.debug("Discarding interface addresses, total address size " + (invokers == null ? 0 : invokers.size()));
-//            }
-//            //invoker.getDirectory().discordAddresses();
-//        }
-//    }
 
     private void clearListener(ClusterInvoker<T> invoker) {
         if (invoker == null) return;
@@ -411,9 +410,9 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
             if (workingInvoker != null) {
                 consumerModel.getServiceMetadata().addAttribute("currentClusterInvoker", workingInvoker);
             }
-//            if (backInvoker != null) {
-//                consumerModel.getServiceMetadata().addAttribute("backupClusterInvoker", backInvoker);
-//            }
+            if (backInvoker != null) {
+                consumerModel.getServiceMetadata().addAttribute("backupClusterInvoker", backInvoker);
+            }
         }
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
index b9718f4..5972e06 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
@@ -30,8 +30,8 @@ import java.util.Set;
 
 @Activate
 public class MigrationRuleHandler<T> {
-    public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
     private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class);
+    private static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
 
     private MigrationClusterInvoker<T> migrationInvoker;
     private MigrationStep currentStep;
@@ -119,10 +119,8 @@ public class MigrationRuleHandler<T> {
         }
 
         if (step == MigrationStep.APPLICATION_FIRST) {
-            setCurrentStepAndThreshold(step, threshold);
             migrationInvoker.refreshServiceDiscoveryInvokerOnMappingCallback(false);
         } else if (step == MigrationStep.FORCE_APPLICATION) {
-            setCurrentStepAndThreshold(step, threshold);
             migrationInvoker.refreshServiceDiscoveryInvokerOnMappingCallback(true);
         }
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
index 85c7349..399a260 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
@@ -67,24 +67,20 @@ public class MigrationRuleListener implements RegistryProtocolListener, Configur
 
     public MigrationRuleListener() {
         this.configuration = ApplicationModel.getEnvironment().getDynamicConfiguration().orElse(null);
-
-        String localRawRule = ApplicationModel.getEnvironment().getLocalMigrationRule();
-        String defaultRawRule = StringUtils.isEmpty(localRawRule) ? INIT : localRawRule;
-
         if (this.configuration != null) {
             logger.info("Listening for migration rules on dataId " + RULE_KEY + ", group " + DUBBO_SERVICEDISCOVERY_MIGRATION);
             configuration.addListener(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION, this);
 
             String rawRule = configuration.getConfig(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION);
             if (StringUtils.isEmpty(rawRule)) {
-                rawRule = defaultRawRule;
+                rawRule = INIT;
             }
             this.rawRule = rawRule;
         } else {
             if (logger.isWarnEnabled()) {
                 logger.warn("Using default configuration rule because config center is not configured!");
             }
-            rawRule = defaultRawRule;
+            rawRule = INIT;
         }
 //        process(new ConfigChangedEvent(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION, rawRule));
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
index c7b0daa..8b29483 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -251,18 +251,20 @@ public abstract class DynamicDirectory<T> extends AbstractDirectory<T> implement
         this.invokersChangedListener = listener;
         if (invokersChangedListener != null && invokersChanged) {
             invokersChangedListener.onChange();
+            invokersChanged = false;
         }
     }
 
     protected synchronized void invokersChanged() {
         invokersChanged = true;
-        if (invokersChangedListener != null) {
+        if (invokersChangedListener != null && invokersChanged) {
             invokersChangedListener.onChange();
+            invokersChanged = false;
         }
     }
 
-    public boolean isNotificationReceived() {
-        return invokersChanged;
+    public synchronized void markInvokersChanged() {
+        this.invokersChanged = true;
     }
 
     protected abstract void destroyAllInvokers();
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
index e2909ce..85b63de 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.client;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import static java.lang.String.valueOf;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -36,7 +37,7 @@ public class DefaultServiceInstanceTest {
     public DefaultServiceInstance instance;
 
     public static DefaultServiceInstance createInstance() {
-        DefaultServiceInstance instance = new DefaultServiceInstance("A", "127.0.0.1", 8080);
+        DefaultServiceInstance instance = new DefaultServiceInstance(valueOf(System.nanoTime()), "A", "127.0.0.1", 8080);
         instance.getMetadata().put("dubbo.metadata-service.urls", "[ \"dubbo://192.168.0.102:20881/com.alibaba.cloud.dubbo.service.DubboMetadataService?anyhost=true&application=spring-cloud-alibaba-dubbo-provider&bind.ip=192.168.0.102&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=spring-cloud-alibaba-dubbo-provider&interface=com.alibaba.cloud.dubbo.service.DubboMetadataService&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedU [...]
         instance.getMetadata().put("dubbo.metadata-service.url-params", "{\"dubbo\":{\"application\":\"dubbo-provider-demo\",\"deprecated\":\"false\",\"group\":\"dubbo-provider-demo\",\"version\":\"1.0.0\",\"timestamp\":\"1564845042651\",\"dubbo\":\"2.0.2\",\"provider.host\":\"192.168.0.102\",\"provider.port\":\"20880\"}}");
         return instance;
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
index 65527df..8677381 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/FileSystemServiceDiscoveryTest.java
@@ -20,7 +20,6 @@ import org.apache.dubbo.common.URLBuilder;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.dubbo.registry.client.DefaultServiceInstanceTest.createInstance;
@@ -30,7 +29,6 @@ import static org.apache.dubbo.registry.client.DefaultServiceInstanceTest.create
  *
  * @since 2.7.5
  */
-@Disabled("FileSystemServiceDiscovery implementation is not stable enough at present")
 public class FileSystemServiceDiscoveryTest {
 
     private FileSystemServiceDiscovery serviceDiscovery;
diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
index 15bf2d1..084376b 100644
--- a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.utils.DefaultPage;
 import org.apache.dubbo.common.utils.Page;
+import org.apache.dubbo.event.ConditionalEventListener;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscoveryFactory;
 import org.apache.dubbo.registry.client.ServiceInstance;
@@ -32,7 +33,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
 
 public class MultipleServiceDiscovery implements ServiceDiscovery {
     public static final String REGISTRY_PREFIX_KEY = "child.";
@@ -88,23 +88,16 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
 
     @Override
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
-        MultiServiceInstancesChangedListener multiListener = (MultiServiceInstancesChangedListener) listener;
+        MultiServiceInstancesChangedListener multiListener = new MultiServiceInstancesChangedListener(listener);
 
         for (String registryKey : serviceDiscoveries.keySet()) {
-            ServiceDiscovery serviceDiscovery = serviceDiscoveries.get(registryKey);
-            SingleServiceInstancesChangedListener singleListener = multiListener.getAndComputeIfAbsent(registryKey, k -> {
-                return new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscovery, multiListener);
-            });
-            serviceDiscovery.addServiceInstancesChangedListener(singleListener);
+            SingleServiceInstancesChangedListener singleListener = new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscoveries.get(registryKey), multiListener);
+            multiListener.putSingleListener(registryKey, singleListener);
+            serviceDiscoveries.get(registryKey).addServiceInstancesChangedListener(singleListener);
         }
     }
 
     @Override
-    public ServiceInstancesChangedListener createListener(Set<String> serviceNames) {
-        return new MultiServiceInstancesChangedListener(serviceNames, this);
-    }
-
-    @Override
     public Page<ServiceInstance> getInstances(String serviceName, int offset, int pageSize, boolean healthyOnly) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
 
         List<ServiceInstance> serviceInstanceList = new ArrayList<>();
@@ -130,12 +123,17 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
         return serviceInstance;
     }
 
-    protected static class MultiServiceInstancesChangedListener extends ServiceInstancesChangedListener {
-        private final Map<String, SingleServiceInstancesChangedListener> singleListenerMap;
+    protected static class MultiServiceInstancesChangedListener implements ConditionalEventListener<ServiceInstancesChangedEvent> {
+        private final ServiceInstancesChangedListener sourceListener;
+        private final Map<String, SingleServiceInstancesChangedListener> singleListenerMap = new ConcurrentHashMap<>();
 
-        public MultiServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
-            super(serviceNames, serviceDiscovery);
-            this.singleListenerMap = new ConcurrentHashMap<>();
+        public MultiServiceInstancesChangedListener(ServiceInstancesChangedListener sourceListener) {
+            this.sourceListener = sourceListener;
+        }
+
+        @Override
+        public boolean accept(ServiceInstancesChangedEvent event) {
+            return sourceListener.getServiceNames().contains(event.getServiceName());
         }
 
         @Override
@@ -151,16 +149,12 @@ public class MultipleServiceDiscovery implements ServiceDiscovery {
                 }
             }
 
-            super.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances));
+            sourceListener.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances));
         }
 
         public void putSingleListener(String registryKey, SingleServiceInstancesChangedListener singleListener) {
             singleListenerMap.put(registryKey, singleListener);
         }
-
-        public SingleServiceInstancesChangedListener getAndComputeIfAbsent(String registryKey, Function<String, SingleServiceInstancesChangedListener> func) {
-            return singleListenerMap.computeIfAbsent(registryKey, func);
-        }
     }
 
     protected static class SingleServiceInstancesChangedListener extends ServiceInstancesChangedListener {
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
index abc7467..7542b5e 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
@@ -57,6 +57,7 @@ public class NacosNamingServiceUtils {
      */
     public static Instance toInstance(ServiceInstance serviceInstance) {
         Instance instance = new Instance();
+        instance.setInstanceId(serviceInstance.getId());
         instance.setServiceName(serviceInstance.getServiceName());
         instance.setIp(serviceInstance.getHost());
         instance.setPort(serviceInstance.getPort());
@@ -74,7 +75,8 @@ public class NacosNamingServiceUtils {
      * @since 2.7.5
      */
     public static ServiceInstance toServiceInstance(Instance instance) {
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort());
+        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(instance.getInstanceId(),
+                NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort());
         serviceInstance.setMetadata(instance.getMetadata());
         serviceInstance.setEnabled(instance.isEnabled());
         serviceInstance.setHealthy(instance.isHealthy());
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
index 549b803..dce7d20 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
@@ -26,7 +26,6 @@ import org.apache.dubbo.common.utils.Page;
 import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.rpc.RpcException;
 
@@ -41,7 +40,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
 
 import static org.apache.dubbo.common.function.ThrowableFunction.execute;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isInstanceUpdated;
@@ -201,28 +199,18 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery {
             throw new IllegalStateException("registerServiceWatcher create path=" + path + " fail.", e);
         }
 
-        CountDownLatch latch = new CountDownLatch(1);
-        ZookeeperServiceDiscoveryChangeWatcher watcher = watcherCaches.computeIfAbsent(path, key -> {
-            ZookeeperServiceDiscoveryChangeWatcher tmpWatcher = new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, path, latch);
-            try {
-                curatorFramework.getChildren().usingWatcher(tmpWatcher).forPath(path);
-            } catch (KeeperException.NoNodeException e) {
-                // ignored
-                if (logger.isErrorEnabled()) {
-                    logger.error(e.getMessage());
-                }
-            } catch (Exception e) {
-                throw new IllegalStateException(e.getMessage(), e);
+        CuratorWatcher watcher = watcherCaches.computeIfAbsent(path, key ->
+                new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, listener));
+        try {
+            curatorFramework.getChildren().usingWatcher(watcher).forPath(path);
+        } catch (KeeperException.NoNodeException e) {
+            // ignored
+            if (logger.isErrorEnabled()) {
+                logger.error(e.getMessage());
             }
-            return tmpWatcher;
-        });
-        watcher.addListener(listener);
-        listener.onEvent(new ServiceInstancesChangedEvent(serviceName, this.getInstances(serviceName)));
-        latch.countDown();
-    }
-
-    public void reRegisterWatcher(ZookeeperServiceDiscoveryChangeWatcher watcher) throws Exception {
-        curatorFramework.getChildren().usingWatcher(watcher).forPath(watcher.getPath());
+        } catch (Exception e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
     }
 
     private String buildServicePath(String serviceName) {
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
index 8cffc3e..e34d3e4 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
@@ -16,10 +16,8 @@
  */
 package org.apache.dubbo.registry.zookeeper;
 
-import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.registry.RegistryNotifier;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
-import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
@@ -27,10 +25,6 @@ import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
 
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-
 import static org.apache.dubbo.rpc.model.ApplicationModel.getExecutorRepository;
 import static org.apache.zookeeper.Watcher.Event.EventType.NodeChildrenChanged;
 import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
@@ -43,7 +37,7 @@ import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
  * @since 2.7.5
  */
 public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
-    private Set<ServiceInstancesChangedListener> listeners = new ConcurrentHashSet<>();
+    private ServiceInstancesChangedListener listener;
 
     private final ZookeeperServiceDiscovery zookeeperServiceDiscovery;
 
@@ -53,52 +47,34 @@ public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
 
     private final String serviceName;
 
-    private final String path;
-
-    private CountDownLatch latch;
-
     public ZookeeperServiceDiscoveryChangeWatcher(ZookeeperServiceDiscovery zookeeperServiceDiscovery,
                                                   String serviceName,
-                                                  String path,
-                                                  CountDownLatch latch) {
+                                                  ServiceInstancesChangedListener listener) {
         this.zookeeperServiceDiscovery = zookeeperServiceDiscovery;
         this.serviceName = serviceName;
-        this.path = path;
-        this.latch = latch;
+        this.listener = listener;
         this.notifier = new RegistryNotifier(zookeeperServiceDiscovery.getDelay(), getExecutorRepository().getServiceDiscoveryAddressNotificationExecutor()) {
             @Override
             protected void doNotify(Object rawAddresses) {
-                listeners.forEach(listener -> listener.onEvent((ServiceInstancesChangedEvent)rawAddresses));
+                listener.onEvent((ServiceInstancesChangedEvent)rawAddresses);
             }
         };
     }
 
     @Override
     public void process(WatchedEvent event) throws Exception {
-        try {
-            latch.await();
-        } catch (InterruptedException e) {
-        }
 
         Watcher.Event.EventType eventType = event.getType();
 
         if (NodeChildrenChanged.equals(eventType) || NodeDataChanged.equals(eventType)) {
             if (shouldKeepWatching()) {
-                zookeeperServiceDiscovery.reRegisterWatcher(this);
-                List<ServiceInstance> instanceList = zookeeperServiceDiscovery.getInstances(serviceName);
-                notifier.notify(new ServiceInstancesChangedEvent(serviceName, instanceList));
+                notifier.notify(new ServiceInstancesChangedEvent(serviceName, zookeeperServiceDiscovery.getInstances(serviceName)));
+                zookeeperServiceDiscovery.registerServiceWatcher(serviceName, listener);
+                zookeeperServiceDiscovery.dispatchServiceInstancesChangedEvent(serviceName);
             }
         }
     }
 
-    public String getPath() {
-        return path;
-    }
-
-    public void addListener(ServiceInstancesChangedListener listener) {
-        listeners.add(listener);
-    }
-
     public boolean shouldKeepWatching() {
         return keepWatching;
     }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
index ed9be54..278aae7 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
@@ -85,7 +85,7 @@ public abstract class CuratorFrameworkUtils {
         String host = instance.getAddress();
         int port = instance.getPort();
         ZookeeperInstance zookeeperInstance = instance.getPayload();
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(name, host, port);
+        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(instance.getId(), name, host, port);
         serviceInstance.setMetadata(zookeeperInstance.getMetadata());
         return serviceInstance;
     }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
index 359d62a..5f3b680 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
@@ -34,6 +34,7 @@ import java.util.Map;
 import static java.util.Arrays.asList;
 import static org.apache.dubbo.common.utils.NetUtils.getAvailablePort;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.INSTANCE_REVISION_UPDATED_KEY;
+import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.generateId;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -103,7 +104,7 @@ public class ZookeeperServiceDiscoveryTest {
     }
 
     private DefaultServiceInstance createServiceInstance(String serviceName, String host, int port) {
-        return new DefaultServiceInstance(serviceName, host, port);
+        return new DefaultServiceInstance(generateId(host, port), serviceName, host, port);
     }
 
 //    @Test
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java
deleted file mode 100644
index d0b7848..0000000
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.rpc;
-
-public interface BaseFilter {
-    /**
-     * Make sure call invoker.invoke() in your implementation.
-     */
-    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
-
-    interface Listener {
-
-        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
-
-        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
-    }
-}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
index 74acb51..58e8215 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java
@@ -33,32 +33,6 @@ import org.apache.dubbo.common.extension.SPI;
  *    Caching is implemented in dubbo using filter approach. If cache is configured for invocation then before
  *    remote call configured caching type's (e.g. Thread Local, JCache etc) implementation invoke method gets called.
  * </pre>
- *
- * Start from 3.0, the semantics of the Filter component at the consumer side has changed.
- * Instead of intercepting a specific instance of invoker, Filter in 3.0 now intercepts ClusterInvoker. A new SPI named
- * InstanceFilter is introduced to work as the same semantic as Filter in 2.x.
- *
- * The difference of Filter is as follows:
- *
- * 3.x Filter
- *
- *                                             -> InstanceFilter -> Invoker
- *
- * Proxy -> Filter -> Filter -> ClusterInvoker -> InstanceFilter -> Invoker
- *
- *                                             -> InstanceFilter -> Invoker
- *
- *
- * 2.x Filter
- *
- *                            Filter -> Filter -> Invoker
- *
- * Proxy -> ClusterInvoker -> Filter -> Filter -> Invoker
- *
- *                            Filter -> Filter -> Invoker
- *
- * If you want to a Filter
- *
  * Filter. (SPI, Singleton, ThreadSafe)
  *
  * @see org.apache.dubbo.rpc.filter.GenericFilter
@@ -67,5 +41,17 @@ import org.apache.dubbo.common.extension.SPI;
  * @see org.apache.dubbo.rpc.filter.TpsLimitFilter
  */
 @SPI
-public interface Filter extends BaseFilter {
+public interface Filter {
+    /**
+     * Make sure call invoker.invoke() in your implementation.
+     */
+    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
+
+    interface Listener {
+
+        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
+
+        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
+    }
+
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
similarity index 83%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
rename to dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
index 4a1ed3b..0fead6b 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.filter.support;
+package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -28,7 +28,6 @@ import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.TimeoutCountDown;
-import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 
 import java.util.Map;
 
@@ -40,11 +39,11 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIME_COUNTDOWN_K
  * ConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port
  * for consumer invoker.It does it to make the requires info available to execution thread's RpcContext.
  *
- * @see Filter
+ * @see org.apache.dubbo.rpc.Filter
  * @see RpcContext
  */
 @Activate(group = CONSUMER, order = -10000)
-public class ConsumerContextFilter implements ClusterFilter, ClusterFilter.Listener {
+public class ConsumerContextFilter implements Filter {
 
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@@ -77,24 +76,7 @@ public class ConsumerContextFilter implements ClusterFilter, ClusterFilter.Liste
                                 + invocation.getMethodName() + ", terminate directly."), invocation);
             }
         }
-
-        try {
-            RpcContext.removeServerContext();
-            return invoker.invoke(invocation);
-        } finally {
-            RpcContext.removeContext();
-        }
-    }
-
-    @Override
-    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
-        // pass attachments to result
-        RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
-    }
-
-    @Override
-    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
-
+        return invoker.invoke(invocation);
     }
 
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index 33a9b64..d7e8c44 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -52,7 +52,7 @@ import java.util.concurrent.TimeUnit;
  */
 public abstract class AbstractInvoker<T> implements Invoker<T> {
 
-    protected static final Logger logger = LoggerFactory.getLogger(AbstractInvoker.class);
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
 
     private final Class<T> type;
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index 7c526c2..5255f55 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -3,8 +3,10 @@ generic=org.apache.dubbo.rpc.filter.GenericFilter
 genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
 token=org.apache.dubbo.rpc.filter.TokenFilter
 accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter
+activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
 classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter
 context=org.apache.dubbo.rpc.filter.ContextFilter
+consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
 exception=org.apache.dubbo.rpc.filter.ExceptionFilter
 executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter
 deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
index ad95481..ee922af 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -20,11 +20,11 @@ import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.AsyncMethodInfo;
 import org.apache.dubbo.rpc.model.ConsumerModel;
@@ -39,7 +39,7 @@ import static org.apache.dubbo.rpc.protocol.dubbo.Constants.ASYNC_METHOD_INFO;
  * EventFilter
  */
 @Activate(group = CommonConstants.CONSUMER)
-public class FutureFilter implements ClusterFilter, ClusterFilter.Listener {
+public class FutureFilter implements Filter, Filter.Listener {
 
     protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index ee41594..79a7a38 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -1 +1,2 @@
-trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
\ No newline at end of file
+trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
+future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
deleted file mode 100644
index 4783214..0000000
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
+++ /dev/null
@@ -1 +0,0 @@
-future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
index d34f49a..1a1b0fd 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
@@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.protocol.dubbo;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService;
 
@@ -40,7 +40,7 @@ import static org.mockito.Mockito.mock;
  */
 public class FutureFilterTest {
     private static RpcInvocation invocation;
-    private ClusterFilter eventFilter = new FutureFilter();
+    private Filter eventFilter = new FutureFilter();
 
     @BeforeAll
     public static void setUp() {

[dubbo] 12/12: bump version 3.0.0.preview

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 07d5286fe859859fa92746c17af3f19fd3ded650
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Mar 19 14:44:05 2021 +0800

    bump version 3.0.0.preview
---
 dubbo-dependencies-bom/pom.xml                          | 2 +-
 dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml | 2 +-
 pom.xml                                                 | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 8cc1d43..ba437af 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -168,7 +168,7 @@
         <mortbay_jetty_version>6.1.26</mortbay_jetty_version>
         <portlet_version>2.0</portlet_version>
         <maven_flatten_version>1.1.0</maven_flatten_version>
-        <revision>3.0.0-SNAPSHOT</revision>
+        <revision>3.0.0.preview</revision>
     </properties>
 
     <dependencyManagement>
diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
index 2a9ce0c..2096371 100644
--- a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
+++ b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
@@ -32,7 +32,7 @@
     <packaging>pom</packaging>
 
     <properties>
-        <revision>3.0.0-SNAPSHOT</revision>
+        <revision>3.0.0.preview</revision>
         <maven_flatten_version>1.1.0</maven_flatten_version>
     </properties>
 
diff --git a/pom.xml b/pom.xml
index 155da8c..68eba8c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,7 +126,7 @@
         <arguments />
         <checkstyle.skip>true</checkstyle.skip>
         <rat.skip>true</rat.skip>
-        <revision>3.0.0-SNAPSHOT</revision>
+        <revision>3.0.0.preview</revision>
     </properties>
 
     <modules>

[dubbo] 11/12: fix reference cache (#7412)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c3e432c298b53ec7597bda47c744da0a8efcb82a
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Fri Mar 19 14:40:32 2021 +0800

    fix reference cache (#7412)
---
 .../config/spring/DubboConfigInitializationPostProcessor.java     | 8 --------
 .../java/org/apache/dubbo/config/spring/ReferenceBeanManager.java | 8 ++++----
 .../factory/annotation/ReferenceAnnotationBeanPostProcessor.java  | 4 ++--
 3 files changed, 6 insertions(+), 14 deletions(-)

diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
index c6578d2..d9c5181 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
@@ -75,14 +75,6 @@ public class DubboConfigInitializationPostProcessor implements BeanPostProcessor
 
     @Override
     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
-//        try {
-//            if (bean instanceof ReferenceBean) {
-//                ReferenceBean referenceBean = (ReferenceBean) bean;
-//                referenceBeanManager.addReference(referenceBean);
-//            }
-//        } catch (Exception e) {
-//            throw new BeanInitializationException("Initialization reference bean failed", e);
-//        }
         return bean;
     }
 
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
index 81afe4d..46be2e5 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
@@ -94,13 +94,13 @@ public class ReferenceBeanManager implements ApplicationContextAware {
      * @throws Exception
      */
     public void prepareReferenceBeans() throws Exception {
-        for (ReferenceBean referenceBean : getReferences()) {
-            initReferenceBean(referenceBean);
-        }
-
         // prepare all reference beans
         Map<String, ReferenceBean> referenceBeanMap = applicationContext.getBeansOfType(ReferenceBean.class, true, false);
         for (ReferenceBean referenceBean : referenceBeanMap.values()) {
+            addReference(referenceBean);
+        }
+
+        for (ReferenceBean referenceBean : getReferences()) {
             initReferenceBean(referenceBean);
         }
         initialized = true;
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index 2379f54..e6a9715 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -252,8 +252,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
                 beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
                 getBeanFactory().registerSingleton(referenceBeanName, referenceBean);
-
-                //referenceBeanManager.addReference(referenceBean);
+                //cache reference bean, avoid re-inject same element after prepare reference bean
+                referenceBeanManager.addReference(referenceBean);
             } catch (Exception e) {
                 throw new Exception("Create dubbo reference bean failed", e);
             }

[dubbo] 10/12: Improve reference bean definition and prepare logic, fix injection problems of PropertyPlaceholderConfigurer (#7405)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 68fdea818c28129b30e85332c588035c61e2a92d
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Thu Mar 18 19:27:51 2021 +0800

    Improve reference bean definition and prepare logic, fix injection problems of PropertyPlaceholderConfigurer (#7405)
---
 .../spring/DubboConfigInitializationPostProcessor.java | 17 ++++++++---------
 .../org/apache/dubbo/config/spring/ReferenceBean.java  |  4 ++--
 .../dubbo/config/spring/ReferenceBeanManager.java      | 18 +++++++-----------
 .../ReferenceAnnotationBeanPostProcessor.java          |  4 ++--
 .../spring/schema/DubboBeanDefinitionParser.java       |  7 +++++++
 5 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
index 068d700..c6578d2 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
@@ -30,7 +30,6 @@ import org.springframework.beans.BeansException;
 import org.springframework.beans.FatalBeanException;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.beans.factory.BeanFactoryAware;
-import org.springframework.beans.factory.BeanInitializationException;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -76,14 +75,14 @@ public class DubboConfigInitializationPostProcessor implements BeanPostProcessor
 
     @Override
     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
-        try {
-            if (bean instanceof ReferenceBean) {
-                ReferenceBean referenceBean = (ReferenceBean) bean;
-                referenceBeanManager.addReference(referenceBean);
-            }
-        } catch (Exception e) {
-            throw new BeanInitializationException("Initialization reference bean failed", e);
-        }
+//        try {
+//            if (bean instanceof ReferenceBean) {
+//                ReferenceBean referenceBean = (ReferenceBean) bean;
+//                referenceBeanManager.addReference(referenceBean);
+//            }
+//        } catch (Exception e) {
+//            throw new BeanInitializationException("Initialization reference bean failed", e);
+//        }
         return bean;
     }
 
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
index f8f834b..67a38e5 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
@@ -107,7 +107,7 @@ public class ReferenceBean<T> implements FactoryBean,
         if (referenceProps == null) {
             Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty");
             ConfigurableListableBeanFactory beanFactory = getBeanFactory();
-            BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(getId());
+            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId());
             propertyValues = beanDefinition.getPropertyValues();
         }
     }
@@ -187,7 +187,7 @@ public class ReferenceBean<T> implements FactoryBean,
                 String consumer = (String) referenceProps.get("consumer");
                 if (StringUtils.isBlank(generic) && consumer != null) {
                     // get generic from consumerConfig
-                    BeanDefinition consumerBeanDefinition = getBeanFactory().getMergedBeanDefinition(consumer);
+                    BeanDefinition consumerBeanDefinition = getBeanFactory().getBeanDefinition(consumer);
                     if (consumerBeanDefinition != null) {
                         generic = (String) consumerBeanDefinition.getPropertyValues().get("generic");
                     }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
index 83765f4..81afe4d 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
@@ -80,16 +80,6 @@ public class ReferenceBeanManager implements ApplicationContextAware {
         return configMap.get(id);
     }
 
-//    public ReferenceBean getOrCreateReference(Map<String, String> referenceProps) {
-//        Integer key = referenceProps.hashCode();
-//        return configMap.computeIfAbsent(key, k -> {
-//            ReferenceBean referenceBean = new ReferenceBean();
-//            referenceBean.setReferenceProps(referenceProps);
-//            //referenceBean.setId();
-//            return referenceBean;
-//        });
-//    }
-
     public Collection<ReferenceBean> getReferences() {
         return configMap.values();
     }
@@ -104,10 +94,16 @@ public class ReferenceBeanManager implements ApplicationContextAware {
      * @throws Exception
      */
     public void prepareReferenceBeans() throws Exception {
-        initialized = true;
         for (ReferenceBean referenceBean : getReferences()) {
             initReferenceBean(referenceBean);
         }
+
+        // prepare all reference beans
+        Map<String, ReferenceBean> referenceBeanMap = applicationContext.getBeansOfType(ReferenceBean.class, true, false);
+        for (ReferenceBean referenceBean : referenceBeanMap.values()) {
+            initReferenceBean(referenceBean);
+        }
+        initialized = true;
     }
 
     /**
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index 7255264..2379f54 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -114,7 +114,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
         for (String beanName : beanNames) {
             Class<?> beanType;
             if (beanFactory.isFactoryBean(beanName)){
-                BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
+                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
                 if (isReferenceBean(beanDefinition)) {
                     continue;
                 }
@@ -253,7 +253,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
                 beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
                 getBeanFactory().registerSingleton(referenceBeanName, referenceBean);
 
-                referenceBeanManager.addReference(referenceBean);
+                //referenceBeanManager.addReference(referenceBean);
             } catch (Exception e) {
                 throw new Exception("Create dubbo reference bean failed", e);
             }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
index 70f4dfe..417d28b 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -53,6 +53,7 @@ import java.lang.reflect.Modifier;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -258,6 +259,12 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
         targetDefinition.setBeanClass(interfaceClass);
         String id = (String) beanDefinition.getPropertyValues().get("id");
         beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));
+
+        //mark property value as optional
+        List<PropertyValue> propertyValues = beanDefinition.getPropertyValues().getPropertyValueList();
+        for (PropertyValue propertyValue : propertyValues) {
+            propertyValue.setOptional(true);
+        }
     }
 
     private static void getPropertyMap(Class<?> beanClass, Map<String, Class> beanPropsMap) {

[dubbo] 01/12: Brand new routing rule (#7372)

Posted by li...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 489f4884b73f113e2459810e4dbdb4f8ea543d78
Author: qinliujie <li...@alibaba-inc.com>
AuthorDate: Tue Mar 16 19:51:23 2021 +0800

    Brand new routing rule (#7372)
---
 dubbo-cluster/pom.xml                              |    4 +-
 .../org/apache/dubbo/registry/AddressListener.java |    0
 .../router/mesh/route/MeshAppRuleListener.java     |   98 ++
 .../route/MeshRuleAddressListenerInterceptor.java  |   51 +
 .../cluster/router/mesh/route/MeshRuleManager.java |   79 ++
 .../cluster/router/mesh/route/MeshRuleRouter.java  |  359 +++++
 .../router/mesh/route/MeshRuleRouterFactory.java   |   29 +-
 .../rpc/cluster/router/mesh/rule/BaseRule.java     |   60 +
 .../router/mesh/rule/VsDestinationGroup.java       |   55 +
 .../rule/destination/ConnectionPoolSettings.java   |   21 +-
 .../mesh/rule/destination/DestinationRule.java     |   36 +-
 .../mesh/rule/destination/DestinationRuleSpec.java |   60 +
 .../router/mesh/rule/destination/Subset.java       |   45 +-
 .../router/mesh/rule/destination/TCPSettings.java  |   24 +-
 .../router/mesh/rule/destination/TcpKeepalive.java |   23 +-
 .../mesh/rule/destination/TrafficPolicy.java       |   35 +-
 .../destination/loadbalance/ConsistentHashLB.java  |   21 +-
 .../loadbalance/LoadBalancerSettings.java          |   43 +-
 .../rule/destination/loadbalance/SimpleLB.java     |   25 +-
 .../rule/virtualservice/DubboMatchRequest.java     |  130 ++
 .../mesh/rule/virtualservice/DubboRoute.java       |   62 +
 .../mesh/rule/virtualservice/DubboRouteDetail.java |   62 +
 .../rule/virtualservice/VirtualServiceRule.java    |   36 +-
 .../rule/virtualservice/VirtualServiceSpec.java    |   43 +-
 .../destination/DubboDestination.java              |   59 +
 .../destination/DubboRouteDestination.java         |   35 +-
 .../mesh/rule/virtualservice/match/BoolMatch.java  |   33 +-
 .../rule/virtualservice/match/DoubleMatch.java     |   63 +
 .../virtualservice/match/DoubleRangeMatch.java     |   53 +
 .../virtualservice/match/DubboAttachmentMatch.java |   76 ++
 .../rule/virtualservice/match/DubboMethodArg.java  |   90 ++
 .../virtualservice/match/DubboMethodMatch.java     |  128 ++
 .../rule/virtualservice/match/ListBoolMatch.java   |   21 +-
 .../rule/virtualservice/match/ListDoubleMatch.java |   36 +-
 .../rule/virtualservice/match/ListStringMatch.java |   37 +-
 .../rule/virtualservice/match/StringMatch.java     |  105 ++
 .../util/VsDestinationGroupRuleDispatcher.java     |   53 +
 .../mesh/util/VsDestinationGroupRuleListener.java  |   22 +-
 .../org.apache.dubbo.registry.AddressListener      |    1 +
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |    1 +
 .../router/mesh/route/MeshAppRuleListenerTest.java |  180 +++
 .../router/mesh/route/MeshRuleManagerTest.java     |  160 +++
 .../mesh/route/MeshRuleRouterFactoryTest.java      |   27 +-
 .../router/mesh/route/MeshRuleRouterTest.java      | 1407 ++++++++++++++++++++
 .../router/mesh/rule/DestinationRuleTest.java      |  102 ++
 .../router/mesh/rule/VirtualServiceRuleTest.java   |   31 +-
 .../rule/virtualservice/DubboMatchRequestTest.java |  140 ++
 .../rule/virtualservice/match/BoolMatchTest.java   |   35 +-
 .../rule/virtualservice/match/DoubleMatchTest.java |  101 ++
 .../match/DubboAttachmentMatchTest.java            |  178 +++
 .../virtualservice/match/DubboMethodMatchTest.java |  156 +++
 .../virtualservice/match/ListDoubleMatchTest.java  |   52 +
 .../virtualservice/match/ListStringMatchTest.java  |   54 +
 .../rule/virtualservice/match/StringMatchTest.java |   81 ++
 .../util/VsDestinationGroupRuleDispatcherTest.java |   74 +
 .../src/test/resources/DestinationRuleTest.yaml    |   33 +
 .../src/test/resources/DestinationRuleTest2.yaml   |   58 +
 .../src/test/resources/VirtualServiceTest.yaml     |   41 +
 58 files changed, 4780 insertions(+), 344 deletions(-)

diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
index bd62974..bb58849 100644
--- a/dubbo-cluster/pom.xml
+++ b/dubbo-cluster/pom.xml
@@ -14,7 +14,8 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.dubbo</groupId>
@@ -39,7 +40,6 @@
             <groupId>org.yaml</groupId>
             <artifactId>snakeyaml</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.apache.curator</groupId>
             <artifactId>curator-framework</artifactId>
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/registry/AddressListener.java
similarity index 100%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/registry/AddressListener.java
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
new file mode 100644
index 0000000..3946ba4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
@@ -0,0 +1,98 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.VsDestinationGroupRuleDispatcher;
+import org.yaml.snakeyaml.Yaml;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+
+public class MeshAppRuleListener implements ConfigurationListener {
+
+    public static final Logger logger = LoggerFactory.getLogger(MeshAppRuleListener.class);
+
+    private final VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+    private String appName;
+
+    private VsDestinationGroup vsDestinationGroupHolder;
+
+    public MeshAppRuleListener(String appName) {
+        this.appName = appName;
+    }
+
+    public void receiveConfigInfo(String configInfo) {
+        logger.info(MessageFormat.format("[MeshAppRule] Received rule for app [{0}]: {1}.",
+                appName, configInfo));
+        try {
+
+            VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+            vsDestinationGroup.setAppName(appName);
+
+            Yaml yaml = new Yaml();
+            Yaml yaml2 = new Yaml();
+            Iterable objectIterable = yaml.loadAll(configInfo);
+            for (Object result : objectIterable) {
+
+                Map resultMap = (Map) result;
+                if (resultMap.get("kind").equals("DestinationRule")) {
+                    DestinationRule destinationRule = yaml2.loadAs(yaml2.dump(result), DestinationRule.class);
+                    vsDestinationGroup.getDestinationRuleList().add(destinationRule);
+
+                } else if (resultMap.get("kind").equals("VirtualService")) {
+                    VirtualServiceRule virtualServiceRule = yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class);
+                    vsDestinationGroup.getVirtualServiceRuleList().add(virtualServiceRule);
+                }
+            }
+
+            vsDestinationGroupHolder = vsDestinationGroup;
+        } catch (Exception e) {
+            logger.error("[MeshAppRule] parse failed: " + configInfo, e);
+        }
+        if (vsDestinationGroupHolder != null) {
+            vsDestinationGroupRuleDispatcher.post(vsDestinationGroupHolder);
+        }
+
+    }
+
+    public void register(MeshRuleRouter subscriber) {
+        if (vsDestinationGroupHolder != null) {
+            subscriber.onRuleChange(vsDestinationGroupHolder);
+        }
+        vsDestinationGroupRuleDispatcher.register(subscriber);
+    }
+
+    //
+    public void unregister(MeshRuleRouter sub) {
+        vsDestinationGroupRuleDispatcher.unregister(sub);
+    }
+
+    @Override
+    public void process(ConfigChangedEvent event) {
+        receiveConfigInfo(event.getContent());
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java
new file mode 100644
index 0000000..d4cf551
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.registry.AddressListener;
+import org.apache.dubbo.rpc.cluster.Directory;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Activate(order = 670)
+public class MeshRuleAddressListenerInterceptor implements AddressListener {
+
+    private static final Object mark = new Object();
+    private static ConcurrentHashMap<String, Object> appMap = new ConcurrentHashMap<String, Object>();
+
+    @Override
+    public List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory) {
+
+        if (addresses != null && !addresses.isEmpty()) {
+            for (URL serviceURL : addresses) {
+
+                String app = serviceURL.getRemoteApplication();
+                if (app != null && !app.isEmpty()) {
+                    if (appMap.putIfAbsent(app, mark) == null) {
+                        MeshRuleManager.subscribeAppRule(app);
+                    }
+                }
+            }
+        }
+
+        return addresses;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
new file mode 100644
index 0000000..aa1630f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
@@ -0,0 +1,79 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public final class MeshRuleManager {
+
+    public static final Logger logger = LoggerFactory.getLogger(MeshRuleManager.class);
+
+    private static final String MESH_RULE_DATA_ID_SUFFIX = ".MESHAPPRULE";
+    private static final String GROUP = "DEFAULT_GROUP";
+
+    private static ConcurrentHashMap<String, MeshAppRuleListener> appRuleListeners = new ConcurrentHashMap<>();
+
+    public synchronized static void subscribeAppRule(String app) {
+
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener(app);
+        String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX;
+        DynamicConfiguration configuration = ApplicationModel.getEnvironment().getDynamicConfiguration()
+                .orElse(null);
+
+        if (configuration == null) {
+            logger.warn("Doesn't support DynamicConfiguration!");
+            return;
+        }
+
+        try {
+            String rawConfig = configuration.getConfig(appRuleDataId, GROUP, 5000L);
+            if (rawConfig != null) {
+                meshAppRuleListener.receiveConfigInfo(rawConfig);
+            }
+        } catch (Throwable throwable) {
+            logger.error("get MeshRuleManager app rule failed.", throwable);
+        }
+
+        configuration.addListener(appRuleDataId, GROUP, meshAppRuleListener);
+        appRuleListeners.put(app, meshAppRuleListener);
+    }
+
+    public static void register(String app, MeshRuleRouter subscriber) {
+        MeshAppRuleListener meshAppRuleListener = appRuleListeners.get(app);
+        if (meshAppRuleListener == null) {
+            logger.warn("appRuleListener can't find when Router register");
+            return;
+        }
+        meshAppRuleListener.register(subscriber);
+    }
+
+    public static void unregister(MeshRuleRouter subscriber) {
+        Collection<MeshAppRuleListener> listeners = appRuleListeners.values();
+        for (MeshAppRuleListener listener : listeners) {
+            listener.unregister(subscriber);
+        }
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
new file mode 100644
index 0000000..34b9e7f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
@@ -0,0 +1,359 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+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.Router;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboMatchRequest;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.VsDestinationGroupRuleListener;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+
+public class MeshRuleRouter implements Router, VsDestinationGroupRuleListener {
+
+    protected int priority = -500;
+    protected boolean force = false;
+    protected URL url;
+
+    private VsDestinationGroup vsDestinationGroup;
+
+    private Map<String, String> sourcesLables = new HashMap<>();
+
+    protected List<Invoker<?>> invokerList = new ArrayList<>();
+
+    Map<String, List<Invoker<?>>> subsetMap;
+
+    private String remoteAppName;
+
+    public MeshRuleRouter(URL url) {
+        this.url = url;
+        sourcesLables.putAll(url.getParameters());
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
+
+        List<DubboRouteDestination> routeDestination = getDubboRouteDestination(invocation);
+
+        if (routeDestination == null) {
+            return invokers;
+        } else {
+            Random random = new Random();
+            int index = random.nextInt(routeDestination.size());
+            DubboRouteDestination dubboRouteDestination = routeDestination.get(index);
+
+            DubboDestination dubboDestination = dubboRouteDestination.getDestination();
+
+            String host = dubboDestination.getHost();
+            String subset = dubboDestination.getSubset();
+
+            List<Invoker<?>> result;
+
+            Map<String, List<Invoker<?>>> subsetMapCopy = this.subsetMap;
+
+            //TODO make intersection with invokers
+            if (subsetMapCopy != null) {
+
+                do {
+                    result = subsetMapCopy.get(subset);
+
+                    if (result != null && result.size() > 0) {
+                        return (List) result;
+                    }
+
+                    dubboRouteDestination = dubboDestination.getFallback();
+                    if (dubboRouteDestination == null) {
+                        break;
+                    }
+                    dubboDestination = dubboRouteDestination.getDestination();
+
+                    host = dubboDestination.getHost();
+                    subset = dubboDestination.getSubset();
+                } while (true);
+
+                return null;
+            }
+        }
+
+        return invokers;
+    }
+
+    @Override
+    public <T> void notify(List<Invoker<T>> invokers) {
+        List invokerList = invokers == null ? Collections.emptyList() : invokers;
+        this.invokerList = invokerList;
+        registerAppRule(invokerList);
+        computeSubset();
+    }
+
+
+    private void registerAppRule(List<Invoker<?>> invokers) {
+        if (StringUtils.isEmpty(remoteAppName)) {
+            synchronized (this) {
+                if (StringUtils.isEmpty(remoteAppName) && invokers != null && invokers.size() > 0) {
+                    for (Invoker invoker : invokers) {
+                        String applicationName = invoker.getUrl().getRemoteApplication();
+                        if (StringUtils.isNotEmpty(applicationName) && !"unknown".equals(applicationName)) {
+                            remoteAppName = applicationName;
+                            MeshRuleManager.register(remoteAppName, this);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    public void onRuleChange(VsDestinationGroup vsDestinationGroup) {
+        this.vsDestinationGroup = vsDestinationGroup;
+        computeSubset();
+    }
+
+    @Override
+    public boolean isRuntime() {
+        return true;
+    }
+
+    @Override
+    public boolean isForce() {
+        return force;
+    }
+
+    @Override
+    public int getPriority() {
+        return priority;
+    }
+
+    private List<DubboRouteDestination> getDubboRouteDestination(Invocation invocation) {
+
+        if (vsDestinationGroup != null) {
+
+            List<VirtualServiceRule> virtualServiceRuleList = vsDestinationGroup.getVirtualServiceRuleList();
+            if (virtualServiceRuleList.size() > 0) {
+                for (VirtualServiceRule virtualServiceRule : virtualServiceRuleList) {
+                    DubboRoute dubboRoute = getDubboRoute(virtualServiceRule, invocation);
+                    if (dubboRoute != null) {
+                        return getDubboRouteDestination(dubboRoute, invocation);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    protected DubboRoute getDubboRoute(VirtualServiceRule virtualServiceRule, Invocation invocation) {
+        String serviceName = invocation.getServiceName();
+
+        VirtualServiceSpec spec = virtualServiceRule.getSpec();
+        List<DubboRoute> dubboRouteList = spec.getDubbo();
+        if (dubboRouteList.size() > 0) {
+            for (DubboRoute dubboRoute : dubboRouteList) {
+                List<StringMatch> stringMatchList = dubboRoute.getServices();
+                if (stringMatchList == null || stringMatchList.size() == 0) {
+                    return dubboRoute;
+                }
+                for (StringMatch stringMatch : stringMatchList) {
+                    if (StringMatch.isMatch(stringMatch, serviceName)) {
+                        return dubboRoute;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+
+    protected List<DubboRouteDestination> getDubboRouteDestination(DubboRoute dubboRoute, Invocation invocation) {
+
+        List<DubboRouteDetail> dubboRouteDetailList = dubboRoute.getRoutedetail();
+        if (dubboRouteDetailList.size() > 0) {
+            DubboRouteDetail dubboRouteDetail = findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            if (dubboRouteDetail != null) {
+                return dubboRouteDetail.getRoute();
+            }
+        }
+
+        return null;
+    }
+
+    protected DubboRouteDetail findMatchDubboRouteDetail(List<DubboRouteDetail> dubboRouteDetailList, Invocation invocation) {
+
+        String methodName = invocation.getMethodName();
+        String[] parameterTypeList = invocation.getCompatibleParamSignatures();
+        Object[] parameters = invocation.getArguments();
+
+
+        for (DubboRouteDetail dubboRouteDetail : dubboRouteDetailList) {
+            List<DubboMatchRequest> matchRequestList = dubboRouteDetail.getMatch();
+            if (matchRequestList == null || matchRequestList.size() == 0) {
+                return dubboRouteDetail;
+            }
+
+            boolean match = true;
+
+            //FIXME to deal with headers
+            for (DubboMatchRequest dubboMatchRequest : matchRequestList) {
+                if (!DubboMatchRequest.isMatch(dubboMatchRequest, methodName, parameterTypeList, parameters,
+                        sourcesLables,
+                        new HashMap<>(), invocation.getAttachments(),
+                        new HashMap<>())) {
+                    match = false;
+                    break;
+                }
+            }
+
+            if (match) {
+                return dubboRouteDetail;
+            }
+        }
+        return null;
+    }
+
+
+    protected synchronized void computeSubset() {
+        if (invokerList == null || invokerList.size() == 0) {
+            this.subsetMap = null;
+            return;
+        }
+
+        if (vsDestinationGroup == null) {
+            this.subsetMap = null;
+            return;
+        }
+
+        Map<String, List<Invoker<?>>> subsetMap = computeSubsetMap(invokerList, vsDestinationGroup.getDestinationRuleList());
+
+        if (subsetMap.size() == 0) {
+            this.subsetMap = null;
+        } else {
+            this.subsetMap = subsetMap;
+        }
+    }
+
+
+    protected Map<String, List<Invoker<?>>> computeSubsetMap(List<Invoker<?>> invokers, List<DestinationRule> destinationRules) {
+        Map<String, List<Invoker<?>>> subsetMap = new HashMap<>();
+
+        for (DestinationRule destinationRule : destinationRules) {
+            DestinationRuleSpec destinationRuleSpec = destinationRule.getSpec();
+            String host = destinationRuleSpec.getHost();
+            List<Subset> subsetList = destinationRuleSpec.getSubsets();
+
+            for (Subset subset : subsetList) {
+                String subsetName = subset.getName();
+                List<Invoker<?>> subsetInvokerList = new ArrayList<>();
+                subsetMap.put(subsetName, subsetInvokerList);
+
+                Map<String, String> labels = subset.getLabels();
+
+                for (Invoker<?> invoker : invokers) {
+                    Map<String, String> parameters = invoker.getUrl().getParameters();
+                    if (containMapKeyValue(parameters, labels)) {
+                        subsetInvokerList.add(invoker);
+                    }
+                }
+            }
+        }
+
+        return subsetMap;
+    }
+
+
+    protected boolean containMapKeyValue(Map<String, String> originMap, Map<String, String> inputMap) {
+        if (inputMap == null || inputMap.size() == 0) {
+            return true;
+        }
+
+        for (Map.Entry<String, String> entry : inputMap.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+
+            String originMapValue = originMap.get(key);
+            if (!value.equals(originMapValue)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    // just for test
+    protected void setVsDestinationGroup(VsDestinationGroup vsDestinationGroup) {
+        this.vsDestinationGroup = vsDestinationGroup;
+    }
+
+    // just for test
+    protected void setSourcesLables(Map<String, String> sourcesLables) {
+        this.sourcesLables = sourcesLables;
+    }
+
+    // just for test
+    protected void setInvokerList(List<Invoker<?>> invokerList) {
+        this.invokerList = invokerList;
+    }
+
+    // just for test
+    protected void setSubsetMap(Map<String, List<Invoker<?>>> subsetMap) {
+        this.subsetMap = subsetMap;
+    }
+
+
+    public VsDestinationGroup getVsDestinationGroup() {
+        return vsDestinationGroup;
+    }
+
+    public Map<String, String> getSourcesLables() {
+        return sourcesLables;
+    }
+
+    public List<Invoker<?>> getInvokerList() {
+        return invokerList;
+    }
+
+    public Map<String, List<Invoker<?>>> getSubsetMap() {
+        return subsetMap;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java
similarity index 64%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java
index ff77800..fdbe8e9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java
@@ -14,24 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
-
-import java.util.List;
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
 
-@SPI
-public interface AddressListener {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.cluster.Router;
+import org.apache.dubbo.rpc.cluster.RouterFactory;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
 
-}
\ No newline at end of file
+@Activate(order = -50)
+public class MeshRuleRouterFactory implements RouterFactory {
+    @Override
+    public Router getRouter(URL url) {
+        return new MeshRuleRouter(url);
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
new file mode 100644
index 0000000..ffb236f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
@@ -0,0 +1,60 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule;
+
+import java.util.Map;
+
+
+public class BaseRule {
+    private String apiVersion;
+    private String kind;
+    private Map<String,String> metadata;
+
+    public String getApiVersion() {
+        return apiVersion;
+    }
+
+    public void setApiVersion(String apiVersion) {
+        this.apiVersion = apiVersion;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public Map<String, String> getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(Map<String, String> metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "BaseRule{" +
+                "apiVersion='" + apiVersion + '\'' +
+                ", kind='" + kind + '\'' +
+                ", metadata=" + metadata +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
new file mode 100644
index 0000000..1d310ee
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
@@ -0,0 +1,55 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class VsDestinationGroup {
+    private String appName;
+    private List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+    private List<DestinationRule> destinationRuleList = new ArrayList<>();
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
+
+    public List<VirtualServiceRule> getVirtualServiceRuleList() {
+        return virtualServiceRuleList;
+    }
+
+    public void setVirtualServiceRuleList(List<VirtualServiceRule> virtualServiceRuleList) {
+        this.virtualServiceRuleList = virtualServiceRuleList;
+    }
+
+    public List<DestinationRule> getDestinationRuleList() {
+        return destinationRuleList;
+    }
+
+    public void setDestinationRuleList(List<DestinationRule> destinationRuleList) {
+        this.destinationRuleList = destinationRuleList;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java
index ff77800..32b7af9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java
@@ -14,24 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class ConnectionPoolSettings {
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java
index ff77800..42bc049 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java
@@ -14,24 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class DestinationRule extends BaseRule {
+    private DestinationRuleSpec spec;
 
-}
\ No newline at end of file
+    public DestinationRuleSpec getSpec() {
+        return spec;
+    }
+
+    public void setSpec(DestinationRuleSpec spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    public String toString() {
+        return "DestinationRule{" +
+                "base=" + super.toString() +
+                ", spec=" + spec +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java
new file mode 100644
index 0000000..57d8b06
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java
@@ -0,0 +1,60 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.destination;
+
+import java.util.List;
+
+
+public class DestinationRuleSpec {
+    private String host;
+    private List<Subset> subsets;
+    private TrafficPolicy trafficPolicy;
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public List<Subset> getSubsets() {
+        return subsets;
+    }
+
+    public void setSubsets(List<Subset> subsets) {
+        this.subsets = subsets;
+    }
+
+    public TrafficPolicy getTrafficPolicy() {
+        return trafficPolicy;
+    }
+
+    public void setTrafficPolicy(TrafficPolicy trafficPolicy) {
+        this.trafficPolicy = trafficPolicy;
+    }
+
+    @Override
+    public String toString() {
+        return "DestinationRuleSpec{" +
+                "host='" + host + '\'' +
+                ", subsets=" + subsets +
+                ", trafficPolicy=" + trafficPolicy +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java
similarity index 55%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java
index ff77800..9438ec4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java
@@ -14,24 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
+import java.util.Map;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class Subset {
+    private String name;
+    private Map<String, String> labels;
 
-}
\ No newline at end of file
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Map<String, String> getLabels() {
+        return labels;
+    }
+
+    public void setLabels(Map<String, String> labels) {
+        this.labels = labels;
+    }
+
+    @Override
+    public String toString() {
+        return "Subset{" +
+                "name='" + name + '\'' +
+                ", labels=" + labels +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java
index ff77800..09dc93a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java
@@ -14,24 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class TCPSettings {
+    private int maxConnections;
+    private int connectTimeout;
+    private TcpKeepalive tcpKeepalive;
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java
index ff77800..3bc43c7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java
@@ -14,24 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class TcpKeepalive {
+    private int probes;
+    private int time;
+    private int interval;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java
similarity index 57%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java
index ff77800..7f771f0 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java
@@ -14,24 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.LoadBalancerSettings;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class TrafficPolicy {
+    private LoadBalancerSettings loadBalancer;
 
-}
\ No newline at end of file
+    public LoadBalancerSettings getLoadBalancer() {
+        return loadBalancer;
+    }
+
+    public void setLoadBalancer(LoadBalancerSettings loadBalancer) {
+        this.loadBalancer = loadBalancer;
+    }
+
+    @Override
+    public String toString() {
+        return "TrafficPolicy{" +
+                "loadBalancer=" + loadBalancer +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java
index ff77800..213dfee 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java
@@ -14,24 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class ConsistentHashLB {
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java
similarity index 51%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java
index ff77800..1f8b60e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java
@@ -14,24 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class LoadBalancerSettings {
+    private SimpleLB simple;
+    private ConsistentHashLB consistentHash;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+    public SimpleLB getSimple() {
+        return simple;
+    }
 
-}
\ No newline at end of file
+    public void setSimple(SimpleLB simple) {
+        this.simple = simple;
+    }
+
+    public ConsistentHashLB getConsistentHash() {
+        return consistentHash;
+    }
+
+    public void setConsistentHash(ConsistentHashLB consistentHash) {
+        this.consistentHash = consistentHash;
+    }
+
+    @Override
+    public String toString() {
+        return "LoadBalancerSettings{" +
+                "simple=" + simple +
+                ", consistentHash=" + consistentHash +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java
index ff77800..003f1f6 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java
@@ -14,24 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public enum SimpleLB {
+    ROUND_ROBIN,
+    LEAST_CONN,
+    RANDOM,
+    PASSTHROUGH
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
new file mode 100644
index 0000000..832bdfe
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DoubleMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+
+import java.util.Map;
+
+
+public class DubboMatchRequest {
+    private String name;
+    private DubboMethodMatch method;
+    private Map<String, String> sourceLabels;
+    private DubboAttachmentMatch attachments;
+    private Map<String, StringMatch> headers;
+    private DoubleMatch threshold;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public DubboMethodMatch getMethod() {
+        return method;
+    }
+
+    public void setMethod(DubboMethodMatch method) {
+        this.method = method;
+    }
+
+    public Map<String, String> getSourceLabels() {
+        return sourceLabels;
+    }
+
+    public void setSourceLabels(Map<String, String> sourceLabels) {
+        this.sourceLabels = sourceLabels;
+    }
+
+    public DubboAttachmentMatch getAttachments() {
+        return attachments;
+    }
+
+    public void setAttachments(DubboAttachmentMatch attachments) {
+        this.attachments = attachments;
+    }
+
+    public Map<String, StringMatch> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, StringMatch> headers) {
+        this.headers = headers;
+    }
+
+    public DoubleMatch getThreshold() {
+        return threshold;
+    }
+
+    public void setThreshold(DoubleMatch threshold) {
+        this.threshold = threshold;
+    }
+
+
+    public static boolean isMatch(DubboMatchRequest dubboMatchRequest,
+                                  String methodName, String[] parameterTypeList, Object[] parameters,
+                                  Map<String, String> sourceLabels,
+                                  Map<String, String> eagleeyeContext, Map<String, String> dubboContext,
+                                  Map<String, String> headers
+    ) {
+        if (dubboMatchRequest.getMethod() != null) {
+            if (!DubboMethodMatch.isMatch(dubboMatchRequest.getMethod(), methodName, parameterTypeList, parameters)) {
+                return false;
+            }
+        }
+
+        if (dubboMatchRequest.getSourceLabels() != null) {
+            for (Map.Entry<String, String> entry : dubboMatchRequest.getSourceLabels().entrySet()) {
+                String value = sourceLabels.get(entry.getKey());
+                if (value == null || !entry.getValue().equals(value)) {
+                    return false;
+                }
+            }
+        }
+
+        if (dubboMatchRequest.getAttachments() != null) {
+            if (!DubboAttachmentMatch.isMatch(dubboMatchRequest.getAttachments(),eagleeyeContext,dubboContext)){
+                return false;
+            }
+        }
+
+        //TODO headers
+
+
+        return true;
+
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMatchRequest{" +
+                "name='" + name + '\'' +
+                ", method=" + method +
+                ", sourceLabels=" + sourceLabels +
+                ", attachments=" + attachments +
+                ", headers=" + headers +
+                ", threshold=" + threshold +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java
new file mode 100644
index 0000000..d6bf124
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java
@@ -0,0 +1,62 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+
+import java.util.List;
+
+
+public class DubboRoute {
+    private String name;
+    private List<StringMatch> services;
+    private List<DubboRouteDetail> routedetail;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<StringMatch> getServices() {
+        return services;
+    }
+
+    public void setServices(List<StringMatch> services) {
+        this.services = services;
+    }
+
+    public List<DubboRouteDetail> getRoutedetail() {
+        return routedetail;
+    }
+
+    public void setRoutedetail(List<DubboRouteDetail> routedetail) {
+        this.routedetail = routedetail;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboRoute{" +
+                "name='" + name + '\'' +
+                ", services=" + services +
+                ", routedetail=" + routedetail +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java
new file mode 100644
index 0000000..ce42591
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java
@@ -0,0 +1,62 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+
+import java.util.List;
+
+
+public class DubboRouteDetail {
+    private String name;
+    private List<DubboMatchRequest> match;
+    private List<DubboRouteDestination> route;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<DubboMatchRequest> getMatch() {
+        return match;
+    }
+
+    public void setMatch(List<DubboMatchRequest> match) {
+        this.match = match;
+    }
+
+    public List<DubboRouteDestination> getRoute() {
+        return route;
+    }
+
+    public void setRoute(List<DubboRouteDestination> route) {
+        this.route = route;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboRouteDetail{" +
+                "name='" + name + '\'' +
+                ", match=" + match +
+                ", route=" + route +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
index ff77800..8f5497a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
@@ -14,24 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class VirtualServiceRule extends BaseRule {
+    private VirtualServiceSpec spec;
 
-}
\ No newline at end of file
+    public VirtualServiceSpec getSpec() {
+        return spec;
+    }
+
+    public void setSpec(VirtualServiceSpec spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualServiceRule{" +
+                "base=" + super.toString() +
+                ", spec=" + spec +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
similarity index 55%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
index ff77800..648fe3b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
@@ -14,24 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
 
 import java.util.List;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class VirtualServiceSpec {
+    private List<String> hosts;
+    private List<DubboRoute> dubbo;
 
-}
\ No newline at end of file
+    public List<String> getHosts() {
+        return hosts;
+    }
+
+    public void setHosts(List<String> hosts) {
+        this.hosts = hosts;
+    }
+
+    public List<DubboRoute> getDubbo() {
+        return dubbo;
+    }
+
+    public void setDubbo(List<DubboRoute> dubbo) {
+        this.dubbo = dubbo;
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualServiceSpec{" +
+                "hosts=" + hosts +
+                ", dubbo=" + dubbo +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java
new file mode 100644
index 0000000..c21a008
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java
@@ -0,0 +1,59 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.destination;
+
+
+public class DubboDestination {
+    private String host;
+    private String subset;
+    private int port;
+    private DubboRouteDestination fallback;
+
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getSubset() {
+        return subset;
+    }
+
+    public void setSubset(String subset) {
+        this.subset = subset;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public DubboRouteDestination getFallback() {
+        return fallback;
+    }
+
+    public void setFallback(DubboRouteDestination fallback) {
+        this.fallback = fallback;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
index ff77800..b9588c4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
@@ -14,24 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class DubboRouteDestination {
+    private DubboDestination destination;
+    private int weight;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+    public DubboDestination getDestination() {
+        return destination;
+    }
 
-}
\ No newline at end of file
+    public void setDestination(DubboDestination destination) {
+        this.destination = destination;
+    }
+
+    public int getWeight() {
+        return weight;
+    }
+
+    public void setWeight(int weight) {
+        this.weight = weight;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
index ff77800..6d76054 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
@@ -14,24 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class BoolMatch {
+    private Boolean exact;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+    public Boolean getExact() {
+        return exact;
+    }
 
-}
\ No newline at end of file
+    public void setExact(Boolean exact) {
+        this.exact = exact;
+    }
+
+    public static boolean isMatch(BoolMatch boolMatch,boolean input){
+        if (boolMatch.getExact() != null){
+            return input == boolMatch.getExact();
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
new file mode 100644
index 0000000..2bd094e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
@@ -0,0 +1,63 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class DoubleMatch {
+    private Double exact;
+    private DoubleRangeMatch range;
+    private Double mod;
+
+    public Double getExact() {
+        return exact;
+    }
+
+    public void setExact(Double exact) {
+        this.exact = exact;
+    }
+
+    public DoubleRangeMatch getRange() {
+        return range;
+    }
+
+    public void setRange(DoubleRangeMatch range) {
+        this.range = range;
+    }
+
+    public Double getMod() {
+        return mod;
+    }
+
+    public void setMod(Double mod) {
+        this.mod = mod;
+    }
+
+
+    public static boolean isMatch(DoubleMatch doubleMatch, Double input) {
+        if (doubleMatch.getExact() != null && doubleMatch.getMod() == null) {
+            return input.equals(doubleMatch.getExact());
+        } else if (doubleMatch.getRange() != null) {
+            return DoubleRangeMatch.isMatch(doubleMatch.getRange(), input);
+        } else if (doubleMatch.getExact() != null && doubleMatch.getMod() != null) {
+            Double result = input % doubleMatch.getMod();
+            return result.equals(doubleMatch.getExact());
+        }
+
+        return false;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
new file mode 100644
index 0000000..dd69338
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
@@ -0,0 +1,53 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class DoubleRangeMatch {
+    private Double start;
+    private Double end;
+
+    public Double getStart() {
+        return start;
+    }
+
+    public void setStart(Double start) {
+        this.start = start;
+    }
+
+    public Double getEnd() {
+        return end;
+    }
+
+    public void setEnd(Double end) {
+        this.end = end;
+    }
+
+
+    public static boolean isMatch(DoubleRangeMatch doubleRangeMatch, Double input) {
+        if (doubleRangeMatch.getStart() != null && doubleRangeMatch.getEnd() != null) {
+            return input.compareTo(doubleRangeMatch.getStart()) >= 0 && input.compareTo(doubleRangeMatch.getEnd()) < 0;
+        } else if (doubleRangeMatch.getStart() != null) {
+            return input.compareTo(doubleRangeMatch.getStart()) >= 0;
+        } else if (doubleRangeMatch.getEnd() != null) {
+            return input.compareTo(doubleRangeMatch.getEnd()) < 0;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
new file mode 100644
index 0000000..b368e5b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
@@ -0,0 +1,76 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.Map;
+
+
+public class DubboAttachmentMatch {
+    private Map<String, StringMatch> eagleeyecontext;
+    private Map<String, StringMatch> dubbocontext;
+
+    public Map<String, StringMatch> getEagleeyecontext() {
+        return eagleeyecontext;
+    }
+
+    public void setEagleeyecontext(Map<String, StringMatch> eagleeyecontext) {
+        this.eagleeyecontext = eagleeyecontext;
+    }
+
+    public Map<String, StringMatch> getDubbocontext() {
+        return dubbocontext;
+    }
+
+    public void setDubbocontext(Map<String, StringMatch> dubbocontext) {
+        this.dubbocontext = dubbocontext;
+    }
+
+    public static boolean isMatch(DubboAttachmentMatch dubboAttachmentMatch, Map<String, String> eagleeyeContext, Map<String, String> dubboContext) {
+        if (dubboAttachmentMatch.getDubbocontext() != null) {
+            for (Map.Entry<String, StringMatch> stringStringMatchEntry : dubboAttachmentMatch.getDubbocontext().entrySet()) {
+                String key = stringStringMatchEntry.getKey();
+                StringMatch stringMatch = stringStringMatchEntry.getValue();
+
+                String dubboContextValue = dubboContext.get(key);
+                if (dubboContextValue == null) {
+                    return false;
+                }
+                if (!StringMatch.isMatch(stringMatch, dubboContextValue)) {
+                    return false;
+                }
+            }
+        }
+
+        if (dubboAttachmentMatch.getEagleeyecontext() != null) {
+            for (Map.Entry<String, StringMatch> stringStringMatchEntry : dubboAttachmentMatch.getEagleeyecontext().entrySet()) {
+                String key = stringStringMatchEntry.getKey();
+                StringMatch stringMatch = stringStringMatchEntry.getValue();
+
+                String eagleeyeContextValue = eagleeyeContext.get(key);
+                if (eagleeyeContextValue == null) {
+                    return false;
+                }
+                if (!StringMatch.isMatch(stringMatch, eagleeyeContextValue)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
new file mode 100644
index 0000000..5d21572
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
@@ -0,0 +1,90 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class DubboMethodArg {
+    private int index;
+    private String type;
+    private ListStringMatch str_value;
+    private ListDoubleMatch num_value;
+    private BoolMatch bool_value;
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public ListStringMatch getStr_value() {
+        return str_value;
+    }
+
+    public void setStr_value(ListStringMatch str_value) {
+        this.str_value = str_value;
+    }
+
+    public ListDoubleMatch getNum_value() {
+        return num_value;
+    }
+
+    public void setNum_value(ListDoubleMatch num_value) {
+        this.num_value = num_value;
+    }
+
+    public BoolMatch getBool_value() {
+        return bool_value;
+    }
+
+    public void setBool_value(BoolMatch bool_value) {
+        this.bool_value = bool_value;
+    }
+
+    public static boolean isMatch(DubboMethodArg dubboMethodArg, Object input) {
+
+        if (dubboMethodArg.getStr_value() != null) {
+            return ListStringMatch.isMatch(dubboMethodArg.getStr_value(), (String) input);
+        } else if (dubboMethodArg.getNum_value() != null) {
+            return ListDoubleMatch.isMatch(dubboMethodArg.getNum_value(), Double.valueOf(input.toString()));
+        } else if (dubboMethodArg.getBool_value() != null) {
+            return BoolMatch.isMatch(dubboMethodArg.getBool_value(), (Boolean) input);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMethodArg{" +
+                "index=" + index +
+                ", type='" + type + '\'' +
+                ", str_value=" + str_value +
+                ", num_value=" + num_value +
+                ", bool_value=" + bool_value +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
new file mode 100644
index 0000000..e68ea40
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
@@ -0,0 +1,128 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.List;
+import java.util.Map;
+
+
+public class DubboMethodMatch {
+    private StringMatch name_match;
+    private Integer argc;
+    private List<DubboMethodArg> args;
+    private List<StringMatch> argp;
+    private Map<String, StringMatch> headers;
+
+    public StringMatch getName_match() {
+        return name_match;
+    }
+
+    public void setName_match(StringMatch name_match) {
+        this.name_match = name_match;
+    }
+
+    public Integer getArgc() {
+        return argc;
+    }
+
+    public void setArgc(Integer argc) {
+        this.argc = argc;
+    }
+
+    public List<DubboMethodArg> getArgs() {
+        return args;
+    }
+
+    public void setArgs(List<DubboMethodArg> args) {
+        this.args = args;
+    }
+
+    public List<StringMatch> getArgp() {
+        return argp;
+    }
+
+    public void setArgp(List<StringMatch> argp) {
+        this.argp = argp;
+    }
+
+    public Map<String, StringMatch> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, StringMatch> headers) {
+        this.headers = headers;
+    }
+
+    public static boolean isMatch(DubboMethodMatch dubboMethodMatch, String methodName, String[] parameterTypeList, Object[] parameters) {
+        StringMatch nameMatch = dubboMethodMatch.getName_match();
+        if (nameMatch != null && !StringMatch.isMatch(nameMatch, methodName)) {
+            return false;
+        }
+
+        Integer argc = dubboMethodMatch.getArgc();
+        if (argc != null &&
+                ((argc != 0 && (parameters == null || parameters.length == 0)) || (argc != parameters.length))) {
+            return false;
+        }
+        List<StringMatch> argp = dubboMethodMatch.getArgp();
+        if (argp != null) {
+            if (((parameterTypeList == null || parameterTypeList.length == 0) && argp.size() > 0)
+                    || (argp.size() != parameterTypeList.length)) {
+                return false;
+            }
+
+            for (int index = 0; index < argp.size(); index++) {
+                if (!StringMatch.isMatch(argp.get(index), parameterTypeList[index])) {
+                    return false;
+                }
+            }
+        }
+
+        List<DubboMethodArg> args = dubboMethodMatch.getArgs();
+
+        if (args != null && args.size() > 0) {
+            if (parameters == null || parameters.length == 0) {
+                return false;
+            }
+
+            for (DubboMethodArg dubboMethodArg : args) {
+                int index = dubboMethodArg.getIndex();
+                if (index >= parameters.length) {
+                    throw new IndexOutOfBoundsException("DubboMethodArg index >= parameters.length");
+                }
+                if (!DubboMethodArg.isMatch(dubboMethodArg, parameters[index])) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMethodMatch{" +
+                "name_match=" + name_match +
+                ", argc=" + argc +
+                ", args=" + args +
+                ", argp=" + argp +
+                ", headers=" + headers +
+                '}';
+    }
+}
+
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
index ff77800..9418bdf 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
@@ -14,24 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class ListBoolMatch {
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
index ff77800..c06397d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
@@ -14,24 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
 import java.util.List;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class ListDoubleMatch {
+    private List<DoubleMatch> oneof;
 
-}
\ No newline at end of file
+    public List<DoubleMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<DoubleMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+    public static boolean isMatch(ListDoubleMatch listDoubleMatch, Double input) {
+
+        for (DoubleMatch doubleMatch : listDoubleMatch.getOneof()) {
+            if (DoubleMatch.isMatch(doubleMatch, input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
index ff77800..e23d623 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
@@ -14,24 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
 import java.util.List;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class ListStringMatch {
+    private List<StringMatch> oneof;
 
-}
\ No newline at end of file
+    public List<StringMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<StringMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+
+    public static boolean isMatch(ListStringMatch listStringMatch, String input) {
+
+        for (StringMatch stringMatch : listStringMatch.getOneof()) {
+            if (StringMatch.isMatch(stringMatch, input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
new file mode 100644
index 0000000..555ee5b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
@@ -0,0 +1,105 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class StringMatch {
+    private String exact;
+    private String prefix;
+    private String regex;
+    private String noempty;
+    private String empty;
+
+
+    public String getExact() {
+        return exact;
+    }
+
+    public void setExact(String exact) {
+        this.exact = exact;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public String getRegex() {
+        return regex;
+    }
+
+    public void setRegex(String regex) {
+        this.regex = regex;
+    }
+
+    public String getNoempty() {
+        return noempty;
+    }
+
+    public void setNoempty(String noempty) {
+        this.noempty = noempty;
+    }
+
+    public String getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(String empty) {
+        this.empty = empty;
+    }
+
+
+    public static boolean isMatch(StringMatch stringMatch, String input) {
+        if (stringMatch.getExact() != null && input != null) {
+            if (input.equals(stringMatch.getExact())) {
+                return true;
+            }
+        } else if (stringMatch.getPrefix() != null && input != null) {
+            if (input.startsWith(stringMatch.getPrefix())) {
+                return true;
+            }
+        } else if (stringMatch.getRegex() != null && input != null) {
+            if (input.matches(stringMatch.getRegex())) {
+                return true;
+            }
+        } else if (stringMatch.getEmpty() != null) {
+            return input == null || "".equals(input);
+        } else if (stringMatch.getNoempty() != null) {
+            return input != null && input.length() > 0;
+        } else {
+            return false;
+        }
+
+        return false;
+    }
+
+
+    @Override
+    public String toString() {
+        return "StringMatch{" +
+                "exact='" + exact + '\'' +
+                ", prefix='" + prefix + '\'' +
+                ", regex='" + regex + '\'' +
+                ", noempty='" + noempty + '\'' +
+                ", empty='" + empty + '\'' +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java
new file mode 100644
index 0000000..4c1fae2
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java
@@ -0,0 +1,53 @@
+/*
+ * 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.rpc.cluster.router.mesh.util;
+
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+
+import java.util.Set;
+
+
+public class VsDestinationGroupRuleDispatcher {
+
+    private Set<VsDestinationGroupRuleListener> listenerSet = new ConcurrentHashSet<>();
+
+    public synchronized void post(VsDestinationGroup vsDestinationGroup) {
+        for (VsDestinationGroupRuleListener vsDestinationGroupRuleListener : listenerSet) {
+            try {
+                vsDestinationGroupRuleListener.onRuleChange(vsDestinationGroup);
+            } catch (Throwable throwable) {
+
+            }
+        }
+    }
+
+    public synchronized boolean register(VsDestinationGroupRuleListener listener) {
+        if (listener == null) {
+            return false;
+        }
+        return listenerSet.add(listener);
+    }
+
+    public synchronized void unregister(VsDestinationGroupRuleListener listener) {
+        if (listener == null) {
+            return;
+        }
+        listenerSet.remove(listener);
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java
index ff77800..9e830ef 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java
@@ -14,24 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public interface VsDestinationGroupRuleListener {
+    void onRuleChange(VsDestinationGroup vsDestinationGroup);
+}
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener
new file mode 100644
index 0000000..754e5ee
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener
@@ -0,0 +1 @@
+mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleAddressListenerInterceptor
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
index 2a807f0..1890efc 100644
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
@@ -5,3 +5,4 @@ service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactor
 app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory
 tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
 mock=org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory
+mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleRouterFactory
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
new file mode 100644
index 0000000..8b89daa
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+public class MeshAppRuleListenerTest {
+
+    @Test
+    public void receiveConfigInfo() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("qinliujie");
+
+        MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+        meshAppRuleListener.register(meshRuleRouter);
+
+        meshAppRuleListener.receiveConfigInfo("apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                "kind: DestinationRule\n" +
+                "metadata: { name: demo-route }\n" +
+                "spec:\n" +
+                "  host: demo\n" +
+                "  subsets:\n" +
+                "    - labels: { env-sign: xxx, tag1: hello }\n" +
+                "      name: isolation\n" +
+                "    - labels: { env-sign: yyy }\n" +
+                "      name: testing-trunk\n" +
+                "    - labels: { env-sign: zzz }\n" +
+                "      name: testing\n" +
+                "  trafficPolicy:\n" +
+                "    loadBalancer: { simple: ROUND_ROBIN }\n" +
+                "\n" +
+                "---\n" +
+                "\n" +
+                "apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                "kind: VirtualService\n" +
+                "metadata: {name: demo-route}\n" +
+                "spec:\n" +
+                "  dubbo:\n" +
+                "    - routedetail:\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: xxx}\n" +
+                "          name: xxx-project\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: isolation}\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                "          name: testing-trunk\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing-trunk}\n" +
+                "        - name: testing\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing}\n" +
+                "      services:\n" +
+                "        - {regex: ccc}\n" +
+                "  hosts: [demo]\n");
+
+
+        ArgumentCaptor<VsDestinationGroup> captor = ArgumentCaptor.forClass(VsDestinationGroup.class);
+        verify(meshRuleRouter, times(1)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup = captor.getValue();
+
+        assertTrue(vsDestinationGroup.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup.getDestinationRuleList().size() == 1);
+        assertTrue(vsDestinationGroup.getVirtualServiceRuleList().size() == 1);
+
+
+        meshAppRuleListener.receiveConfigInfo("");
+        verify(meshRuleRouter, times(2)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup1 = captor.getAllValues().get(captor.getAllValues().size() - 1);
+
+        assertTrue(vsDestinationGroup1.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup1.getDestinationRuleList().size() == 0);
+        assertTrue(vsDestinationGroup1.getVirtualServiceRuleList().size() == 0);
+    }
+
+    @Test
+    public void register() {
+    }
+
+    @Test
+    public void unregister() {
+    }
+
+    @Test
+    public void process() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("qinliujie");
+
+        MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+        meshAppRuleListener.register(meshRuleRouter);
+
+        ConfigChangedEvent configChangedEvent = new ConfigChangedEvent("qinliujie", "HSF", "apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                "kind: DestinationRule\n" +
+                "metadata: { name: demo-route }\n" +
+                "spec:\n" +
+                "  host: demo\n" +
+                "  subsets:\n" +
+                "    - labels: { env-sign: xxx, tag1: hello }\n" +
+                "      name: isolation\n" +
+                "    - labels: { env-sign: yyy }\n" +
+                "      name: testing-trunk\n" +
+                "    - labels: { env-sign: zzz }\n" +
+                "      name: testing\n" +
+                "  trafficPolicy:\n" +
+                "    loadBalancer: { simple: ROUND_ROBIN }\n" +
+                "\n" +
+                "---\n" +
+                "\n" +
+                "apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                "kind: VirtualService\n" +
+                "metadata: {name: demo-route}\n" +
+                "spec:\n" +
+                "  dubbo:\n" +
+                "    - routedetail:\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: xxx}\n" +
+                "          name: xxx-project\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: isolation}\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                "          name: testing-trunk\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing-trunk}\n" +
+                "        - name: testing\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing}\n" +
+                "      services:\n" +
+                "        - {regex: ccc}\n" +
+                "  hosts: [demo]\n", ConfigChangeType.MODIFIED);
+
+
+        meshAppRuleListener.process(configChangedEvent);
+
+        ArgumentCaptor<VsDestinationGroup> captor = ArgumentCaptor.forClass(VsDestinationGroup.class);
+        verify(meshRuleRouter, times(1)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup = captor.getValue();
+
+        assertTrue(vsDestinationGroup.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup.getDestinationRuleList().size() == 1);
+        assertTrue(vsDestinationGroup.getVirtualServiceRuleList().size() == 1);
+
+
+        meshAppRuleListener.receiveConfigInfo("");
+        verify(meshRuleRouter, times(2)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup1 = captor.getAllValues().get(captor.getAllValues().size() - 1);
+
+        assertTrue(vsDestinationGroup1.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup1.getDestinationRuleList().size() == 0);
+        assertTrue(vsDestinationGroup1.getVirtualServiceRuleList().size() == 0);
+
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
new file mode 100644
index 0000000..7638c85
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class MeshRuleManagerTest {
+
+    @Test
+    public void subscribeAppRule() {
+        Optional<DynamicConfiguration> before = ApplicationModel.getEnvironment().getDynamicConfiguration();
+        try {
+            DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
+
+            ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
+
+            MeshRuleManager.subscribeAppRule("test");
+
+            ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+            Mockito.verify(dynamicConfiguration).getConfig(captor.capture(), anyString(), anyLong());
+
+            String result = captor.getValue();
+
+            assertEquals("test.MESHAPPRULE", result);
+        } finally {
+            ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
+        }
+
+
+    }
+
+    @Test
+    public void register() {
+        Optional<DynamicConfiguration> before = ApplicationModel.getEnvironment().getDynamicConfiguration();
+        try {
+            DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
+
+            ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
+
+            when(dynamicConfiguration.getConfig(anyString(), anyString(), anyLong())).thenReturn("apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                    "kind: VirtualService\n" +
+                    "metadata: {name: demo-route}\n" +
+                    "spec:\n" +
+                    "  dubbo:\n" +
+                    "    - routedetail:\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: xxx}\n" +
+                    "          name: xxx-project\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: isolation}\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                    "          name: testing-trunk\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing-trunk}\n" +
+                    "        - name: testing\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing}\n" +
+                    "      services:\n" +
+                    "        - {regex: ccc}\n" +
+                    "  hosts: [demo]\n");
+
+            MeshRuleManager.subscribeAppRule("test");
+
+
+            MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+
+            MeshRuleManager.register("test", meshRuleRouter);
+
+            ArgumentCaptor<VsDestinationGroup> captor = ArgumentCaptor.forClass(VsDestinationGroup.class);
+
+
+            Mockito.verify(meshRuleRouter).onRuleChange(captor.capture());
+
+            VsDestinationGroup result = captor.getValue();
+
+            assertNotNull(result);
+            assertEquals("test", result.getAppName());
+            assertEquals(1, result.getVirtualServiceRuleList().size());
+            assertEquals(0, result.getDestinationRuleList().size());
+        } finally {
+            ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
+        }
+    }
+
+    @Test
+    public void unregister() {
+        Optional<DynamicConfiguration> before = ApplicationModel.getEnvironment().getDynamicConfiguration();
+        try {
+            DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
+
+            ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
+
+            when(dynamicConfiguration.getConfig(anyString(), anyString(), anyLong())).thenReturn("apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                    "kind: VirtualService\n" +
+                    "metadata: {name: demo-route}\n" +
+                    "spec:\n" +
+                    "  dubbo:\n" +
+                    "    - routedetail:\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: xxx}\n" +
+                    "          name: xxx-project\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: isolation}\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                    "          name: testing-trunk\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing-trunk}\n" +
+                    "        - name: testing\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing}\n" +
+                    "      services:\n" +
+                    "        - {regex: ccc}\n" +
+                    "  hosts: [demo]\n");
+
+            MeshRuleManager.subscribeAppRule("test");
+
+
+            MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+
+            MeshRuleManager.register("test", meshRuleRouter);
+
+            MeshRuleManager.unregister(meshRuleRouter);
+
+        } finally {
+            ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java
similarity index 63%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java
index ff77800..7bdc47a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java
@@ -14,24 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+import org.junit.jupiter.api.Test;
 
-import java.util.List;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class MeshRuleRouterFactoryTest {
 
+    @Test
+    public void getRouter() {
+        MeshRuleRouterFactory ruleRouterFactory = new MeshRuleRouterFactory();
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("demoService");
+        ruleRouterFactory.getRouter(url);
+    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
new file mode 100644
index 0000000..8262cd6
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
@@ -0,0 +1,1407 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboMatchRequest;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class MeshRuleRouterTest {
+
+    @Test
+    public void containMapKeyValue() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Map<String, String> originMap = new HashMap<>();
+
+        originMap.put("key1", "value1");
+        originMap.put("key2", "value2");
+        originMap.put("key3", "value3");
+
+        Map<String, String> inputMap = new HashMap<>();
+
+        inputMap.put("key1", "value1");
+        inputMap.put("key2", "value2");
+
+        assertTrue(meshRuleRouter.containMapKeyValue(originMap, inputMap));
+
+        inputMap.put("key4", "value4");
+        assertFalse(meshRuleRouter.containMapKeyValue(originMap, inputMap));
+
+
+        assertTrue(meshRuleRouter.containMapKeyValue(originMap, null));
+        assertTrue(meshRuleRouter.containMapKeyValue(originMap, new HashMap<>()));
+
+    }
+
+    @Test
+    public void computeSubsetMap() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        //--
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test4");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test4");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        //--
+
+
+        Map<String, List<Invoker<?>>> result = meshRuleRouter.computeSubsetMap(invokers, destinationRules);
+
+        assertTrue(result.size() == 3);
+        assertTrue(result.containsKey("test1"));
+        assertTrue(result.containsKey("test2"));
+        assertTrue(result.containsKey("test4"));
+
+        assertTrue(result.get("test1").size() == 1);
+        assertTrue(result.get("test2").size() == 1);
+        assertTrue(result.get("test4").size() == 0);
+
+    }
+
+    @Test
+    public void computeSubset() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+
+        meshRuleRouter.setInvokerList(null);
+        meshRuleRouter.computeSubset();
+
+        assertNull(meshRuleRouter.getSubsetMap());
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        meshRuleRouter.setInvokerList(invokers);
+
+        meshRuleRouter.computeSubset();
+
+        assertNull(meshRuleRouter.getSubsetMap());
+
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test4");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test4");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+
+        meshRuleRouter.computeSubset();
+
+        assertNotNull(meshRuleRouter.getSubsetMap());
+        assertTrue(meshRuleRouter.getSubsetMap().size() == 3);
+    }
+
+    @Test
+    public void findMatchDubboRouteDetail() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+
+        assertNull(meshRuleRouter.findMatchDubboRouteDetail(new ArrayList<>(), invocation));
+
+        //--
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+
+            DubboRouteDetail result = meshRuleRouter.findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            assertNotNull(result);
+            assertEquals("test", result.getName());
+        }
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHi");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+
+            DubboRouteDetail result = meshRuleRouter.findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            assertNull(result);
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            {
+                DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+                dubboRouteDetail.setName("test");
+
+                List<DubboMatchRequest> match = new ArrayList<>();
+
+                DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+                DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+                StringMatch stringMatch = new StringMatch();
+                stringMatch.setExact("sayHi");
+                dubboMethodMatch.setName_match(stringMatch);
+
+                dubboMatchRequest.setMethod(dubboMethodMatch);
+                match.add(dubboMatchRequest);
+
+                dubboRouteDetail.setMatch(match);
+
+                dubboRouteDetailList.add(dubboRouteDetail);
+            }
+
+
+            {
+                DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+                dubboRouteDetail.setName("test2");
+
+                List<DubboMatchRequest> match = new ArrayList<>();
+
+                DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+                DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+                StringMatch stringMatch = new StringMatch();
+                stringMatch.setExact("sayHello");
+                dubboMethodMatch.setName_match(stringMatch);
+
+                dubboMatchRequest.setMethod(dubboMethodMatch);
+                match.add(dubboMatchRequest);
+
+                dubboRouteDetail.setMatch(match);
+
+                dubboRouteDetailList.add(dubboRouteDetail);
+            }
+
+            DubboRouteDetail result = meshRuleRouter.findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            assertNotNull(result);
+            assertEquals("test2", result.getName());
+        }
+
+    }
+
+    @Test
+    public void getDubboRouteDestination() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+
+        DubboRoute dubboRoute = new DubboRoute();
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHi");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            assertNull(meshRuleRouter.getDubboRouteDestination(dubboRoute, invocation));
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            assertNotNull(meshRuleRouter.getDubboRouteDestination(dubboRoute, invocation));
+        }
+    }
+
+    @Test
+    public void getDubboRoute() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+        when(invocation.getServiceName()).thenReturn("demoService");
+
+        DubboRoute dubboRoute = new DubboRoute();
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+
+
+            dubboRoute.setServices(new ArrayList<>());
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+
+            virtualServiceRule.setSpec(spec);
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNotNull(result);
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setExact("otherService");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNull(result);
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNotNull(result);
+        }
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHi");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNotNull(result);
+        }
+
+
+    }
+
+    @Test
+    public void testNotify() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+
+        meshRuleRouter.setInvokerList(null);
+        meshRuleRouter.computeSubset();
+
+        assertNull(meshRuleRouter.getSubsetMap());
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test4");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test4");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+        meshRuleRouter.notify((List) invokers);
+
+        assertNotNull(meshRuleRouter.getSubsetMap());
+
+    }
+
+    @Test
+    public void route() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        List<Invoker<?>> inputInvokers = new ArrayList<>();
+
+        URL inputURL = mock(URL.class);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+        when(invocation.getServiceName()).thenReturn("demoService");
+
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        meshRuleRouter.setInvokerList(invokers);
+
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setExact("otherService");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+            assertEquals(inputInvokers, meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+        }
+
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            DubboRouteDestination dubboRouteDestination = new DubboRouteDestination();
+            DubboDestination destination = new DubboDestination();
+            destination.setSubset("test1");
+            dubboRouteDestination.setDestination(destination);
+            dubboRouteDestinations.add(dubboRouteDestination);
+
+
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+            assertNotEquals(inputInvokers, meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+            assertEquals(1, meshRuleRouter.route((List) inputInvokers, inputURL, invocation).size());
+
+            Map<String, String> invokerParameterMap = new HashMap<>();
+            invokerParameterMap.put("env", "test1");
+
+            assertEquals(invokerParameterMap, ((Invoker) meshRuleRouter.route((List) inputInvokers, inputURL, invocation).get(0)).getUrl().getParameters());
+        }
+    }
+
+
+    @Test
+    public void routeFallback() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        List<Invoker<?>> inputInvokers = new ArrayList<>();
+
+        URL inputURL = mock(URL.class);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+        when(invocation.getServiceName()).thenReturn("demoService");
+
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        meshRuleRouter.setInvokerList(invokers);
+
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            DubboRouteDestination dubboRouteDestination = new DubboRouteDestination();
+            DubboDestination destination = new DubboDestination();
+            destination.setSubset("test5");
+
+
+            DubboRouteDestination fallbackDubboRouteDestination = new DubboRouteDestination();
+            DubboDestination fallbackDestination = new DubboDestination();
+            fallbackDestination.setSubset("test1");
+            fallbackDubboRouteDestination.setDestination(fallbackDestination);
+
+
+            destination.setFallback(fallbackDubboRouteDestination);
+
+
+            dubboRouteDestination.setDestination(destination);
+            dubboRouteDestinations.add(dubboRouteDestination);
+
+
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+            assertNotEquals(inputInvokers, meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+            assertEquals(1, meshRuleRouter.route((List) inputInvokers, inputURL, invocation).size());
+
+            Map<String, String> invokerParameterMap = new HashMap<>();
+            invokerParameterMap.put("env", "test1");
+
+            assertEquals(invokerParameterMap, ((Invoker) meshRuleRouter.route((List) inputInvokers, inputURL, invocation).get(0)).getUrl().getParameters());
+        }
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            DubboRouteDestination dubboRouteDestination = new DubboRouteDestination();
+            DubboDestination destination = new DubboDestination();
+            destination.setSubset("test5");
+
+
+            DubboRouteDestination fallbackDubboRouteDestination = new DubboRouteDestination();
+            DubboDestination fallbackDestination = new DubboDestination();
+            fallbackDestination.setSubset("test11");
+            fallbackDubboRouteDestination.setDestination(fallbackDestination);
+
+
+            destination.setFallback(fallbackDubboRouteDestination);
+
+
+            dubboRouteDestination.setDestination(destination);
+            dubboRouteDestinations.add(dubboRouteDestination);
+
+
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+
+            assertNull(meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+
+            meshRuleRouter.setSubsetMap(null);
+            assertNotNull(meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
new file mode 100644
index 0000000..91f5ac7
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.SimpleLB;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+public class DestinationRuleTest {
+
+    @Test
+    public void parserTest() {
+        Yaml yaml = new Yaml();
+        DestinationRule destinationRule = yaml.loadAs(this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest.yaml"), DestinationRule.class);
+
+        System.out.println(destinationRule);
+
+
+//        apiVersion: service.dubbo.apache.org/v1alpha1
+//        kind: DestinationRule
+//        metadata: { name: demo-route }
+//        spec:
+//        host: demo
+//        subsets:
+//        - labels: { env-sign: xxx,tag1: hello }
+//        name: isolation
+//                - labels: { env-sign: yyy }
+//        name: testing-trunk
+//                - labels: { env-sign: zzz }
+//        name: testing
+
+
+        assertEquals("service.dubbo.apache.org/v1alpha1", destinationRule.getApiVersion());
+        assertEquals("DestinationRule", destinationRule.getKind());
+        assertEquals("demo-route", destinationRule.getMetadata().get("name"));
+        assertEquals("demo", destinationRule.getSpec().getHost());
+        assertEquals(3, destinationRule.getSpec().getSubsets().size());
+
+        assertEquals("isolation", destinationRule.getSpec().getSubsets().get(0).getName());
+        assertEquals(2, destinationRule.getSpec().getSubsets().get(0).getLabels().size());
+        assertEquals("xxx", destinationRule.getSpec().getSubsets().get(0).getLabels().get("env-sign"));
+        assertEquals("hello", destinationRule.getSpec().getSubsets().get(0).getLabels().get("tag1"));
+
+
+        assertEquals("testing-trunk", destinationRule.getSpec().getSubsets().get(1).getName());
+        assertEquals(1, destinationRule.getSpec().getSubsets().get(1).getLabels().size());
+        assertEquals("yyy", destinationRule.getSpec().getSubsets().get(1).getLabels().get("env-sign"));
+
+
+        assertEquals("testing", destinationRule.getSpec().getSubsets().get(2).getName());
+        assertEquals(1, destinationRule.getSpec().getSubsets().get(2).getLabels().size());
+        assertEquals("zzz", destinationRule.getSpec().getSubsets().get(2).getLabels().get("env-sign"));
+
+        assertEquals(SimpleLB.ROUND_ROBIN, destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getSimple());
+        assertEquals(null, destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getConsistentHash());
+    }
+
+
+    @Test
+    public void parserMultiRuleTest() {
+        Yaml yaml = new Yaml();
+        Yaml yaml2 = new Yaml();
+        Iterable objectIterable = yaml.loadAll(this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest2.yaml"));
+        for (Object result : objectIterable) {
+
+            Map resultMap = (Map) result;
+            if (resultMap.get("kind").equals("DestinationRule")) {
+                DestinationRule destinationRule = yaml2.loadAs(yaml2.dump(result), DestinationRule.class);
+                System.out.println(destinationRule);
+                assertNotNull(destinationRule);
+            } else if (resultMap.get("kind").equals("VirtualService")) {
+                VirtualServiceRule virtualServiceRule = yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class);
+                System.out.println(virtualServiceRule);
+                assertNotNull(virtualServiceRule);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
similarity index 55%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
index ff77800..ddfb127 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
@@ -14,24 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
 
-@SPI
-public interface AddressListener {
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+
+public class VirtualServiceRuleTest {
+
+    @Test
+    public void parserTest() {
+        Yaml yaml = new Yaml();
+        VirtualServiceRule virtualServiceRule = yaml.loadAs(this.getClass().getClassLoader().getResourceAsStream("VirtualServiceTest.yaml"), VirtualServiceRule.class);
+
+        System.out.println(virtualServiceRule);
+        assertNotNull(virtualServiceRule);
+    }
 
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
new file mode 100644
index 0000000..9bb55b4
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+public class DubboMatchRequestTest {
+
+    @Test
+    public void isMatch() {
+        DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+
+        // methodMatch
+        {
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch nameStringMatch = new StringMatch();
+            nameStringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(nameStringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+        }
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, new HashMap(), new HashMap(), new HashMap(), new HashMap()));
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHi", new String[]{}, new Object[]{}, new HashMap(), new HashMap(), new HashMap(), new HashMap()));
+        // sourceLabels
+        {
+            Map<String, String> sourceLabels = new HashMap<>();
+            sourceLabels.put("key1", "value1");
+            sourceLabels.put("key2", "value2");
+
+            dubboMatchRequest.setSourceLabels(sourceLabels);
+        }
+
+        Map<String, String> inputSourceLablesMap = new HashMap<>();
+        inputSourceLablesMap.put("key1", "value1");
+        inputSourceLablesMap.put("key2", "value2");
+        inputSourceLablesMap.put("key3", "value3");
+
+        Map<String, String> inputSourceLablesMap2 = new HashMap<>();
+        inputSourceLablesMap2.put("key1", "other");
+        inputSourceLablesMap2.put("key2", "value2");
+        inputSourceLablesMap2.put("key3", "value3");
+
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, new HashMap(), new HashMap(), new HashMap()));
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap2, new HashMap(), new HashMap(), new HashMap()));
+
+
+        // eagleeyeContext
+        {
+            DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+            {
+                Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+                StringMatch nameMatch = new StringMatch();
+                nameMatch.setExact("qinliujie");
+                eagleeyecontextMatchMap.put("name", nameMatch);
+                dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+            }
+
+            dubboMatchRequest.setAttachments(dubboAttachmentMatch);
+        }
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, new HashMap(), new HashMap()));
+
+
+        Map<String, String> invokeEagleEyeContextMap2 = new HashMap<>();
+        invokeEagleEyeContextMap2.put("name", "jack");
+        invokeEagleEyeContextMap2.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap2.put("other", "other");
+
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap2, new HashMap(), new HashMap()));
+
+
+        //dubbo context
+
+        {
+            DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+            {
+                Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+                StringMatch nameMatch = new StringMatch();
+                nameMatch.setExact("qinliujie");
+                eagleeyecontextMatchMap.put("name", nameMatch);
+                dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+            }
+
+
+            {
+                Map<String, StringMatch> dubboContextMatchMap = new HashMap<>();
+                StringMatch dpathMatch = new StringMatch();
+                dpathMatch.setExact("PRE");
+                dubboContextMatchMap.put("dpath", dpathMatch);
+                dubboAttachmentMatch.setDubbocontext(dubboContextMatchMap);
+            }
+
+            dubboMatchRequest.setAttachments(dubboAttachmentMatch);
+        }
+
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "PRE");
+
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, invokeDubboContextMap, new HashMap()));
+
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "other");
+
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, invokeDubboContextMap2, new HashMap()));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
similarity index 55%
rename from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
rename to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
index ff77800..72d4774 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
@@ -14,24 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+import org.junit.jupiter.api.Test;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+public class BoolMatchTest {
+
+    @Test
+    public void isMatch() {
+        BoolMatch boolMatch =  new BoolMatch();
+        boolMatch.setExact(true);
+
+
+        assertTrue(BoolMatch.isMatch(boolMatch,true));
+        assertFalse(BoolMatch.isMatch(boolMatch,false));
+
+        boolMatch.setExact(false);
+        assertFalse(BoolMatch.isMatch(boolMatch,true));
+        assertTrue(BoolMatch.isMatch(boolMatch,false));
+
+    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
new file mode 100644
index 0000000..aba44bc
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DoubleMatchTest {
+
+    @Test
+    public void exactMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+        doubleMatch.setExact(10.0);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 10.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 9.0));
+    }
+
+    @Test
+    public void rangeStartMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setStart(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 10.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 9.0));
+    }
+
+
+    @Test
+    public void rangeEndMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setEnd(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 10.0));
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 9.0));
+    }
+
+
+    @Test
+    public void rangeStartEndMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setStart(5.0);
+        doubleRangeMatch.setEnd(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 5.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 10.0));
+
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 4.9));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 10.1));
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 6.0));
+
+    }
+
+    @Test
+    public void modMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        doubleMatch.setMod(2.0);
+        doubleMatch.setExact(3.0);
+
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 3.0));
+
+        doubleMatch.setExact(1.0);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 1.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 2.0));
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 3.0));
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
new file mode 100644
index 0000000..99e704f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+public class DubboAttachmentMatchTest {
+
+    @Test
+    public void dubboContextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> dubbocontextMatchMap = new HashMap<>();
+
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        dubbocontextMatchMap.put("name", nameMatch);
+
+
+        StringMatch machineGroupMatch = new StringMatch();
+        machineGroupMatch.setExact("test_host");
+        dubbocontextMatchMap.put("machineGroup", machineGroupMatch);
+
+        dubboAttachmentMatch.setDubbocontext(dubbocontextMatchMap);
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("name", "qinliujie");
+        invokeDubboContextMap.put("machineGroup", "test_host");
+        invokeDubboContextMap.put("other", "other");
+
+        assertTrue(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, new HashMap<>(), invokeDubboContextMap));
+
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        invokeDubboContextMap2.put("name", "jack");
+        invokeDubboContextMap2.put("machineGroup", "test_host");
+        invokeDubboContextMap2.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, new HashMap<>(), invokeDubboContextMap2));
+
+
+        Map<String, String> invokeDubboContextMap3 = new HashMap<>();
+        invokeDubboContextMap3.put("name", "qinliujie");
+        invokeDubboContextMap3.put("machineGroup", "my_host");
+        invokeDubboContextMap3.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, new HashMap<>(), invokeDubboContextMap3));
+    }
+
+
+    @Test
+    public void eagleEyeContextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        eagleeyecontextMatchMap.put("name", nameMatch);
+
+
+        StringMatch machineGroupMatch = new StringMatch();
+        machineGroupMatch.setExact("test_host");
+        eagleeyecontextMatchMap.put("machineGroup", machineGroupMatch);
+
+        dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        assertTrue(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, new HashMap<>()));
+
+
+        Map<String, String> invokeEagleEyeContextMap2 = new HashMap<>();
+        invokeEagleEyeContextMap2.put("name", "jack");
+        invokeEagleEyeContextMap2.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap2.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, new HashMap<>()));
+
+
+        Map<String, String> invokeEagleEyeContextMap3 = new HashMap<>();
+        invokeEagleEyeContextMap3.put("name", "qinliujie");
+        invokeEagleEyeContextMap3.put("machineGroup", "my_host");
+        invokeEagleEyeContextMap3.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap3, new HashMap<>()));
+    }
+
+
+    @Test
+    public void contextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        {
+            Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+            StringMatch nameMatch = new StringMatch();
+            nameMatch.setExact("qinliujie");
+            eagleeyecontextMatchMap.put("name", nameMatch);
+            dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+        }
+
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        //-------
+
+        {
+            Map<String, StringMatch> dubboContextMatchMap = new HashMap<>();
+            StringMatch dpathMatch = new StringMatch();
+            dpathMatch.setExact("PRE");
+            dubboContextMatchMap.put("dpath", dpathMatch);
+            dubboAttachmentMatch.setDubbocontext(dubboContextMatchMap);
+        }
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "PRE");
+
+
+        assertTrue(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, invokeDubboContextMap));
+
+
+        Map<String, String> invokeEagleEyeContextMap1 = new HashMap<>();
+        invokeEagleEyeContextMap1.put("name", "jack");
+        invokeEagleEyeContextMap1.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap1.put("other", "other");
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap1, invokeDubboContextMap));
+
+
+        Map<String, String> invokeDubboContextMap1 = new HashMap<>();
+        invokeDubboContextMap1.put("dpath", "PRE-2");
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, invokeDubboContextMap1));
+
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap1, invokeDubboContextMap1));
+
+
+        Map<String, String> invokeEagleEyeContextMap2 = new HashMap<>();
+        invokeEagleEyeContextMap2.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap2.put("other", "other");
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, invokeDubboContextMap));
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, invokeDubboContextMap2));
+
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, invokeDubboContextMap2));
+
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
new file mode 100644
index 0000000..7c7189b
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DubboMethodMatchTest {
+
+    @Test
+    public void nameMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        StringMatch nameStringMatch = new StringMatch();
+        nameStringMatch.setExact("sayHello");
+
+        dubboMethodMatch.setName_match(nameStringMatch);
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{}));
+    }
+
+
+    @Test
+    public void argcMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+        dubboMethodMatch.setArgc(1);
+
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, null));
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{"1"}));
+    }
+
+    @Test
+    public void argpMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        List<StringMatch> argpMatch = new ArrayList<>();
+
+        StringMatch first = new StringMatch();
+        first.setExact("java.lang.Long");
+
+        StringMatch second = new StringMatch();
+        second.setRegex(".*");
+
+        argpMatch.add(first);
+        argpMatch.add(second);
+
+        dubboMethodMatch.setArgp(argpMatch);
+
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{"java.lang.Long", "java.lang.String"}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{"java.lang.Long", "java.lang.String", "java.lang.String"}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", null, new Object[]{}));
+    }
+
+    @Test
+    public void parametersMatch() {
+
+
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        List<DubboMethodArg> parametersMatch = new ArrayList<>();
+
+        //----- index 0
+        {
+            DubboMethodArg dubboMethodArg0 = new DubboMethodArg();
+            dubboMethodArg0.setIndex(0);
+
+            ListDoubleMatch listDoubleMatch = new ListDoubleMatch();
+            List<DoubleMatch> oneof = new ArrayList<>();
+
+            DoubleMatch doubleMatch1 = new DoubleMatch();
+            doubleMatch1.setExact(10.0);
+
+            oneof.add(doubleMatch1);
+
+            listDoubleMatch.setOneof(oneof);
+
+            dubboMethodArg0.setNum_value(listDoubleMatch);
+
+            parametersMatch.add(dubboMethodArg0);
+        }
+
+        //-----index 1
+
+        {
+
+            DubboMethodArg dubboMethodArg1 = new DubboMethodArg();
+            dubboMethodArg1.setIndex(1);
+
+            ListStringMatch listStringMatch = new ListStringMatch();
+
+            List<StringMatch> oneof = new ArrayList<>();
+
+            StringMatch stringMatch1 = new StringMatch();
+            stringMatch1.setExact("sayHello");
+
+            oneof.add(stringMatch1);
+
+            listStringMatch.setOneof(oneof);
+
+            dubboMethodArg1.setStr_value(listStringMatch);
+
+            parametersMatch.add(dubboMethodArg1);
+        }
+
+        dubboMethodMatch.setArgs(parametersMatch);
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String"}, new Object[]{10, "sayHello"}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String"}, new Object[]{10, "sayHi"}));
+
+
+        //-----index 2
+
+        {
+
+            DubboMethodArg dubboMethodArg2 = new DubboMethodArg();
+            dubboMethodArg2.setIndex(2);
+
+            BoolMatch boolMatch = new BoolMatch();
+            boolMatch.setExact(true);
+
+
+            dubboMethodArg2.setBool_value(boolMatch);
+
+            parametersMatch.add(dubboMethodArg2);
+        }
+
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String", "boolean"}, new Object[]{10, "sayHello", true}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String", "boolean"}, new Object[]{10, "sayHello", false}));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
new file mode 100644
index 0000000..f3b30b1
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+public class ListDoubleMatchTest {
+
+    @Test
+    public void isMatch() {
+        ListDoubleMatch listDoubleMatch = new ListDoubleMatch();
+        List<DoubleMatch> oneof = new ArrayList<>();
+
+        DoubleMatch doubleMatch1 = new DoubleMatch();
+        doubleMatch1.setExact(10.0);
+
+        DoubleMatch doubleMatch2 = new DoubleMatch();
+        doubleMatch2.setExact(11.0);
+
+        oneof.add(doubleMatch1);
+        oneof.add(doubleMatch2);
+
+        listDoubleMatch.setOneof(oneof);
+
+        assertTrue(ListDoubleMatch.isMatch(listDoubleMatch, 10.0));
+        assertTrue(ListDoubleMatch.isMatch(listDoubleMatch, 11.0));
+        assertFalse(ListDoubleMatch.isMatch(listDoubleMatch, 12.0));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
new file mode 100644
index 0000000..0561053
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ListStringMatchTest {
+
+    @Test
+    public void isMatch() {
+        ListStringMatch listStringMatch = new ListStringMatch();
+
+        List<StringMatch> oneof = new ArrayList<>();
+
+        StringMatch stringMatch1 = new StringMatch();
+        stringMatch1.setExact("1");
+
+        StringMatch stringMatch2 = new StringMatch();
+        stringMatch2.setExact("2");
+
+        oneof.add(stringMatch1);
+        oneof.add(stringMatch2);
+
+
+        listStringMatch.setOneof(oneof);
+
+        assertTrue(ListStringMatch.isMatch(listStringMatch, "1"));
+        assertTrue(ListStringMatch.isMatch(listStringMatch, "2"));
+        assertFalse(ListStringMatch.isMatch(listStringMatch, "3"));
+
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
new file mode 100644
index 0000000..13d0225
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class StringMatchTest {
+
+    @Test
+    public void exactMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setExact("qinliujie");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "qinliujie"));
+        assertFalse(StringMatch.isMatch(stringMatch, "other"));
+        assertFalse(StringMatch.isMatch(stringMatch, null));
+    }
+
+
+    @Test
+    public void prefixMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setPrefix("org.apache.dubbo.rpc.cluster.router.mesh");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "org.apache.dubbo.rpc.cluster.router.mesh.test"));
+        assertFalse(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertFalse(StringMatch.isMatch(stringMatch, null));
+    }
+
+
+    @Test
+    public void regxMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setRegex("org.apache.dubbo.rpc.cluster.router.mesh.*");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "org.apache.dubbo.rpc.cluster.router.mesh"));
+        assertTrue(StringMatch.isMatch(stringMatch, "org.apache.dubbo.rpc.cluster.router.mesh.test"));
+        assertFalse(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertFalse(StringMatch.isMatch(stringMatch, "com.taobao"));
+    }
+
+
+    @Test
+    public void emptyMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setEmpty("empty");
+
+        assertFalse(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertTrue(StringMatch.isMatch(stringMatch, ""));
+        assertTrue(StringMatch.isMatch(stringMatch, null));
+    }
+
+    @Test
+    public void noEmptyMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setNoempty("noempty");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertFalse(StringMatch.isMatch(stringMatch, ""));
+        assertFalse(StringMatch.isMatch(stringMatch, null));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java
new file mode 100644
index 0000000..5193c8d
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.rpc.cluster.router.mesh.util;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+class VsDestinationGroupRuleDispatcherTest {
+
+    @Test
+    public void post() {
+        VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+        VsDestinationGroupRuleListener vsDestinationGroupRuleListener = mock(VsDestinationGroupRuleListener.class);
+
+        vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener);
+
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+
+        verify(vsDestinationGroupRuleListener, times(2)).onRuleChange(anyObject());
+
+    }
+
+    @Test
+    public void register() {
+        VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+        VsDestinationGroupRuleListener vsDestinationGroupRuleListener = mock(VsDestinationGroupRuleListener.class);
+
+        assertFalse(vsDestinationGroupRuleDispatcher.register(null));
+        assertTrue(vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener));
+        assertFalse(vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener));
+    }
+
+    @Test
+    public void unregister() {
+
+        VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+        VsDestinationGroupRuleListener vsDestinationGroupRuleListener = mock(VsDestinationGroupRuleListener.class);
+        vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener);
+
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+
+        vsDestinationGroupRuleDispatcher.unregister(vsDestinationGroupRuleListener);
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+
+        verify(vsDestinationGroupRuleListener, times(1)).onRuleChange(anyObject());
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/DestinationRuleTest.yaml b/dubbo-cluster/src/test/resources/DestinationRuleTest.yaml
new file mode 100644
index 0000000..8f2e135
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/DestinationRuleTest.yaml
@@ -0,0 +1,33 @@
+#
+#
+#   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.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata: { name: demo-route }
+spec:
+  host: demo
+  subsets:
+    - labels: { env-sign: xxx, tag1: hello }
+      name: isolation
+    - labels: { env-sign: yyy }
+      name: testing-trunk
+    - labels: { env-sign: zzz }
+      name: testing
+  trafficPolicy:
+    loadBalancer: { simple: ROUND_ROBIN }
diff --git a/dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml b/dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml
new file mode 100644
index 0000000..178c50e
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml
@@ -0,0 +1,58 @@
+#
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata: { name: demo-route }
+spec:
+  host: demo
+  subsets:
+    - labels: { env-sign: xxx, tag1: hello }
+      name: isolation
+    - labels: { env-sign: yyy }
+      name: testing-trunk
+    - labels: { env-sign: zzz }
+      name: testing
+  trafficPolicy:
+    loadBalancer: { simple: ROUND_ROBIN }
+
+---
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata: {name: demo-route}
+spec:
+  dubbo:
+    - routedetail:
+        - match:
+            - sourceLabels: {trafficLabel: xxx}
+          name: xxx-project
+          route:
+            - destination: {host: demo, subset: isolation}
+        - match:
+            - sourceLabels: {trafficLabel: testing-trunk}
+          name: testing-trunk
+          route:
+            - destination: {host: demo, subset: testing-trunk}
+        - name: testing
+          route:
+            - destination: {host: demo, subset: testing}
+      services:
+        - {regex: ccc}
+  hosts: [demo]
diff --git a/dubbo-cluster/src/test/resources/VirtualServiceTest.yaml b/dubbo-cluster/src/test/resources/VirtualServiceTest.yaml
new file mode 100644
index 0000000..4d3454b
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/VirtualServiceTest.yaml
@@ -0,0 +1,41 @@
+#
+#
+#   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.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata: {name: demo-route}
+spec:
+  dubbo:
+    - routedetail:
+        - match:
+            - sourceLabels: {trafficLabel: xxx}
+          name: xxx-project
+          route:
+            - destination: {host: demo, subset: isolation}
+        - match:
+            - sourceLabels: {trafficLabel: testing-trunk}
+          name: testing-trunk
+          route:
+            - destination: {host: demo, subset: testing-trunk}
+        - name: testing
+          route:
+            - destination: {host: demo, subset: testing}
+      services:
+        - {regex: ccc}
+  hosts: [demo]