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

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


The following commit(s) were added to refs/heads/3.0 by this push:
     new 23e2f4d  Verify remote refer for registry (#8645)
23e2f4d is described below

commit 23e2f4d46f41297c9a0fd7c6dbf757cf41ff7423
Author: huazhongming <[email protected]>
AuthorDate: Tue Aug 31 19:21:59 2021 +0800

    Verify remote refer for registry (#8645)
    
    * add unit test:remote refer for registry
    
    * fix
    
    * fix
    
    * add license
---
 .../org/apache/dubbo/rpc/cluster/RouterChain.java  |  14 +-
 .../cluster/support/wrapper/AbstractCluster.java   |   4 +
 .../support/wrapper/MockClusterWrapper.java        |   5 +-
 .../apache/dubbo/rpc/cluster/RouterChainTest.java  |  52 ++
 .../support/wrapper/AbstractClusterTest.java       | 106 ++++
 .../support/wrapper/DemoClusterFilter.java}        |  27 +-
 ...g.apache.dubbo.rpc.cluster.filter.ClusterFilter |   1 +
 .../java/org/apache/dubbo/common/URLBuilder.java   |  17 +
 .../url/component/DubboServiceAddressURL.java      |   1 +
 .../common/url/component/ServiceConfigURL.java     |   4 +
 .../org/apache/dubbo/config/ReferenceConfig.java   |   5 +-
 .../apache/dubbo/config/ReferenceConfigTest.java   | 173 ++++++-
 .../dubbo/registry/client/InstanceAddressURL.java  |   2 +
 .../registry/client/ServiceDiscoveryRegistry.java  |   6 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |  17 +-
 .../client/migration/MigrationInvoker.java         |   8 +
 .../registry/integration/DynamicDirectory.java     |  75 ++-
 .../InterfaceCompatibleRegistryProtocol.java       |   1 +
 .../registry/integration/RegistryDirectory.java    |  21 +-
 .../registry/integration/RegistryProtocol.java     |  18 +-
 ...est.java => CacheableFallbackRegistryTest.java} |  31 +-
 .../registry/ListenerRegistryWrapperTest.java      |  84 ++++
 .../dubbo/registry/RegistryFactoryWrapperTest.java |   2 +-
 .../dubbo/registry/RegistryServiceListener1.java   |   2 +-
 .../dubbo/registry/RegistryServiceListener2.java   |   2 +-
 .../store/RemoteMetadataServiceImplTest.java       |  21 -
 .../client/migration/MigrationRuleHandlerTest.java |   2 +-
 .../CountRegistryProtocolListener.java}            |  31 +-
 .../DemoService.java}                              |   4 +-
 .../registry/integration/DynamicDirectoryTest.java | 173 +++++++
 .../registry/integration/RegistryProtocolTest.java | 550 +++++++++++++++++++++
 ...o.registry.integration.RegistryProtocolListener |   1 +
 32 files changed, 1309 insertions(+), 151 deletions(-)

diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
index 1b6b192..5385ea1 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
@@ -53,14 +53,20 @@ import static 
org.apache.dubbo.rpc.cluster.Constants.STATE_ROUTER_KEY;
 public class RouterChain<T> {
     private static final Logger logger = 
LoggerFactory.getLogger(RouterChain.class);
 
-    // full list of addresses from registry, classified by method name.
+    /**
+     * full list of addresses from registry, classified by method name.
+     */
     private volatile List<Invoker<T>> invokers = Collections.emptyList();
 
-    // containing all routers, reconstruct every time 'route://' urls change.
+    /**
+     * containing all routers, reconstruct every time 'route://' urls change.
+     */
     private volatile List<Router> routers = Collections.emptyList();
 
-    // Fixed router instances: ConfigConditionRouter, TagRouter, e.g., the 
rule for each instance may change but the
-    // instance will never delete or recreate.
+    /**
+     * Fixed router instances: ConfigConditionRouter, TagRouter, e.g.,
+     * the rule for each instance may change but the instance will never 
delete or recreate.
+     */
     private List<Router> builtinRouters = Collections.emptyList();
 
     private List<StateRouter> builtinStateRouters = Collections.emptyList();
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 684f372..4ede912 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
@@ -123,6 +123,10 @@ public abstract class AbstractCluster implements Cluster {
         protected Result doInvoke(Invocation invocation, List<Invoker<T>> 
invokers, LoadBalance loadbalance) throws RpcException {
            return null;
         }
+
+        public ClusterInvoker<T> getFilterInvoker() {
+            return filterInvoker;
+        }
     }
 
     static class InvocationInterceptorInvoker<T> extends 
AbstractClusterInvoker<T> {
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
index 8aa2306..686474f 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
@@ -27,7 +27,7 @@ import org.apache.dubbo.rpc.cluster.Directory;
  */
 public class MockClusterWrapper implements Cluster {
 
-    private Cluster cluster;
+    private final Cluster cluster;
 
     public MockClusterWrapper(Cluster cluster) {
         this.cluster = cluster;
@@ -39,4 +39,7 @@ public class MockClusterWrapper implements Cluster {
                 this.cluster.join(directory));
     }
 
+    public Cluster getCluster() {
+        return cluster;
+    }
 }
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.java 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.java
new file mode 100644
index 0000000..4d431b6
--- /dev/null
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.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;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.rpc.cluster.filter.DemoService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+
+public class RouterChainTest {
+
+    /**
+     * verify the router and state router loaded by default
+     */
+    @Test
+    public void testBuildRouterChain() {
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        URL url = new ServiceConfigURL("dubbo",
+            "127.0.0.1",
+            20881,
+            DemoService.class.getName(),
+            parameters);
+
+        RouterChain<DemoService> routerChain = RouterChain.buildChain(url);
+        Assertions.assertEquals(5, routerChain.getRouters().size());
+        Assertions.assertEquals(2, routerChain.getStateRouters().size());
+    }
+}
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractClusterTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractClusterTest.java
new file mode 100644
index 0000000..e98d2ba
--- /dev/null
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractClusterTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.support.wrapper;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+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.Directory;
+import org.apache.dubbo.rpc.cluster.LoadBalance;
+import org.apache.dubbo.rpc.cluster.filter.DemoService;
+import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder;
+import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static 
org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AbstractClusterTest {
+
+    @Test
+    public void testBuildClusterInvokerChain() {
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put(REFERENCE_FILTER_KEY, "demo");
+        ServiceConfigURL url = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+
+        URL consumerUrl = new ServiceConfigURL("dubbo",
+            "127.0.0.1",
+            20881,
+            DemoService.class.getName(),
+            parameters);
+
+        Directory<?> directory = mock(Directory.class);
+        when(directory.getUrl()).thenReturn(url);
+        when(directory.getConsumerUrl()).thenReturn(consumerUrl);
+        DemoCluster demoCluster = new DemoCluster();
+        Invoker<?> invoker = demoCluster.join(directory);
+        Assertions.assertTrue(invoker instanceof 
AbstractCluster.ClusterFilterInvoker);
+        Assertions.assertTrue(((AbstractCluster.ClusterFilterInvoker<?>) 
invoker).getFilterInvoker()
+            instanceof FilterChainBuilder.ClusterFilterChainNode);
+
+
+    }
+
+    static class DemoCluster extends AbstractCluster {
+        @Override
+        public <T> Invoker<T> join(Directory<T> directory) throws RpcException 
{
+            return super.join(directory);
+        }
+
+        @Override
+        protected <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) 
throws RpcException {
+            return new DemoAbstractClusterInvoker<>(directory, 
directory.getUrl());
+        }
+    }
+
+    static class DemoAbstractClusterInvoker<T> extends 
AbstractClusterInvoker<T> {
+
+        @Override
+        public URL getUrl() {
+            return super.getUrl();
+        }
+
+        public DemoAbstractClusterInvoker(Directory<T> directory, URL url) {
+            super(directory, url);
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, List list, 
LoadBalance loadbalance) throws RpcException {
+            return null;
+        }
+    }
+
+
+}
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/DemoClusterFilter.java
similarity index 66%
copy from 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
copy to 
dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/DemoClusterFilter.java
index 8aa2306..2cea0f4 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/DemoClusterFilter.java
@@ -16,27 +16,20 @@
  */
 package org.apache.dubbo.rpc.cluster.support.wrapper;
 
+
+import org.apache.dubbo.common.extension.Activate;
+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;
-import org.apache.dubbo.rpc.cluster.Directory;
-
-/**
- * mock impl
- *
- */
-public class MockClusterWrapper implements Cluster {
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 
-    private Cluster cluster;
-
-    public MockClusterWrapper(Cluster cluster) {
-        this.cluster = cluster;
-    }
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 
+@Activate(value = "demo",group = {CONSUMER})
+public class DemoClusterFilter implements ClusterFilter {
     @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        return new MockClusterInvoker<T>(directory,
-                this.cluster.join(directory));
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws 
RpcException {
+        return invoker.invoke(invocation);
     }
-
 }
diff --git 
a/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
 
b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
new file mode 100644
index 0000000..ba1a331
--- /dev/null
+++ 
b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter
@@ -0,0 +1 @@
+demo=org.apache.dubbo.rpc.cluster.support.wrapper.DemoClusterFilter
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java 
b/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
index ce6e8e7..83a2dab 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
@@ -161,6 +161,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder setUsername(String username) {
         this.username = username;
         return this;
@@ -171,16 +172,19 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder setHost(String host) {
         this.host = host;
         return this;
     }
 
+    @Override
     public URLBuilder setPort(int port) {
         this.port = port;
         return this;
     }
 
+    @Override
     public URLBuilder setAddress(String address) {
         int i = address.lastIndexOf(':');
         String host;
@@ -196,11 +200,13 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder setPath(String path) {
         this.path = path;
         return this;
     }
 
+    @Override
     public URLBuilder addParameterAndEncoded(String key, String value) {
         if (StringUtils.isEmpty(value)) {
             return this;
@@ -278,6 +284,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder addParameterIfAbsent(String key, String value) {
         if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
             return this;
@@ -300,6 +307,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder addParameters(Map<String, String> parameters) {
         if (CollectionUtils.isEmptyMap(parameters)) {
             return this;
@@ -332,6 +340,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder addParametersIfAbsent(Map<String, String> parameters) {
         if (CollectionUtils.isEmptyMap(parameters)) {
             return this;
@@ -342,6 +351,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder addParameters(String... pairs) {
         if (pairs == null || pairs.length == 0) {
             return this;
@@ -357,6 +367,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return addParameters(map);
     }
 
+    @Override
     public URLBuilder addParameterString(String query) {
         if (StringUtils.isEmpty(query)) {
             return this;
@@ -364,6 +375,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return addParameters(StringUtils.parseQueryString(query));
     }
 
+    @Override
     public URLBuilder removeParameter(String key) {
         if (StringUtils.isEmpty(key)) {
             return this;
@@ -371,6 +383,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return removeParameters(key);
     }
 
+    @Override
     public URLBuilder removeParameters(Collection<String> keys) {
         if (CollectionUtils.isEmpty(keys)) {
             return this;
@@ -378,6 +391,7 @@ public final class URLBuilder extends ServiceConfigURL {
         return removeParameters(keys.toArray(new String[0]));
     }
 
+    @Override
     public URLBuilder removeParameters(String... keys) {
         if (keys == null || keys.length == 0) {
             return this;
@@ -388,16 +402,19 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
     public URLBuilder clearParameters() {
         parameters.clear();
         return this;
     }
 
+    @Override
     public boolean hasParameter(String key) {
         String value = getParameter(key);
         return StringUtils.isNotEmpty(value);
     }
 
+    @Override
     public boolean hasMethodParameter(String method, String key) {
         if (method == null) {
             String suffix = "." + key;
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java
index 3155fe0..d0eccff 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java
@@ -42,6 +42,7 @@ public class DubboServiceAddressURL extends ServiceAddressURL 
{
         this.overrideURL = overrideURL;
     }
 
+    @Override
     protected <T extends URL> T newURL(URLAddress urlAddress, URLParam 
urlParam) {
         return (T) new DubboServiceAddressURL(urlAddress, urlParam, 
this.consumerURL, this.overrideURL);
     }
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
index 87018a9..4771297 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
@@ -93,14 +93,17 @@ public class ServiceConfigURL extends URL {
         this(new PathURLAddress(protocol, username, password, path, host, 
port), URLParam.parse(parameters), attributes);
     }
 
+    @Override
     protected <T extends URL> T newURL(URLAddress urlAddress, URLParam 
urlParam) {
         return (T) new ServiceConfigURL(urlAddress, urlParam, attributes);
     }
 
+    @Override
     public Map<String, Object> getAttributes() {
         return attributes;
     }
 
+    @Override
     public Object getAttribute(String key) {
         return attributes.get(key);
     }
@@ -114,6 +117,7 @@ public class ServiceConfigURL extends URL {
         return new ServiceConfigURL(getUrlAddress(), getUrlParam(), 
newAttributes);
     }
 
+    @Override
     public ServiceConfigURL putAttribute(String key, Object obj) {
         Map<String, Object> newAttributes = new HashMap<>(attributes);
         newAttributes.put(key, obj);
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index 04511e2..aa5bef5 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
@@ -596,7 +596,10 @@ public class ReferenceConfig<T> extends 
ReferenceConfigBase<T> {
         configPostProcessors.forEach(component -> 
component.postProcessReferConfig(this));
     }
 
-    // just for test
+    /**
+     * just for test
+     * @return
+     */
     @Deprecated
     public Invoker<?> getInvoker() {
         return invoker;
diff --git 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java
 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java
index 45841ad..9f13ce3 100644
--- 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java
+++ 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java
@@ -35,6 +35,7 @@ import org.apache.dubbo.config.provider.impl.DemoServiceImpl;
 
 import org.apache.dubbo.metadata.report.MetadataReport;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
+import org.apache.dubbo.registry.client.migration.MigrationInvoker;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.listener.ListenerInvokerWrapper;
@@ -113,6 +114,7 @@ import static org.mockito.Mockito.when;
 
 public class ReferenceConfigTest {
     private TestingServer zkServer;
+    private String zkUrl;
     private String registryUrl;
 
     @BeforeEach
@@ -121,7 +123,8 @@ public class ReferenceConfigTest {
         int zkServerPort = NetUtils.getAvailablePort(NetUtils.getRandomPort());
         this.zkServer = new TestingServer(zkServerPort, true);
         this.zkServer.start();
-        this.registryUrl = "zookeeper://localhost:" + zkServerPort;
+        this.zkUrl = "zookeeper://localhost:" + zkServerPort;
+        this.registryUrl = "registry://localhost:" + 
zkServerPort+"?registry=zookeeper";
         ApplicationModel.getConfigManager();
         DubboBootstrap.getInstance();
     }
@@ -266,7 +269,7 @@ public class ReferenceConfigTest {
         referenceConfig.getInterfaceClass();
         referenceConfig.setCheck(false);
         RegistryConfig registry = new RegistryConfig();
-        registry.setAddress(registryUrl);
+        registry.setAddress(zkUrl);
         applicationConfig.setRegistries(Collections.singletonList(registry));
         applicationConfig.setRegistryIds(registry.getId());
         moduleConfig.setRegistries(Collections.singletonList(registry));
@@ -548,6 +551,146 @@ public class ReferenceConfigTest {
         metadataReportInstanceMockedStatic.closeOnDemand();
     }
 
+
+    /**
+     * Verify the configuration of the registry protocol for remote reference
+     */
+    @Test
+    public  void testCreateInvokerForRemoteRefer(){
+
+        ReferenceConfig<DemoService> referenceConfig = new ReferenceConfig<>();
+        referenceConfig.setGeneric(Boolean.FALSE.toString());
+        referenceConfig.setProtocol("dubbo");
+        referenceConfig.setInit(true);
+        referenceConfig.setLazy(false);
+        referenceConfig.setInjvm(false);
+
+        DubboBootstrap.getInstance()
+            .application("application1")
+            .initialize();
+        referenceConfig.setBootstrap(DubboBootstrap.getInstance());
+
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("key1", "value1");
+        parameters.put("key2", "value2");
+        applicationConfig.setParameters(parameters);
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        Environment environment = mock(Environment.class);
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+        ServiceRepository serviceRepository = mock(ServiceRepository.class);
+        ConsumerModel consumerModel = mock(ConsumerModel.class);
+
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+        
applicationModelMockedStatic.when(ApplicationModel::getServiceRepository).thenReturn(serviceRepository);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        MockedStatic<MetadataReportInstance> 
metadataReportInstanceMockedStatic =
+            Mockito.mockStatic(MetadataReportInstance.class);
+
+        MetadataReport metadataReport = mock(MetadataReport.class);
+        metadataReportInstanceMockedStatic.when(() -> 
MetadataReportInstance.getMetadataReport("default"))
+            .thenReturn(metadataReport);
+
+
+        
when(serviceRepository.lookupReferredService("org.apache.dubbo.config.api.DemoService"))
+            .thenReturn(consumerModel);
+
+        referenceConfig.refreshed.set(true);
+        referenceConfig.setInterface(DemoService.class);
+        referenceConfig.getInterfaceClass();
+        referenceConfig.setCheck(false);
+        RegistryConfig registry = new RegistryConfig();
+        registry.setAddress(zkUrl);
+        applicationConfig.setRegistries(Collections.singletonList(registry));
+        applicationConfig.setRegistryIds(registry.getId());
+
+        referenceConfig.setRegistry(registry);
+
+        referenceConfig.init();
+        Assertions.assertTrue(referenceConfig.getInvoker() instanceof 
MigrationInvoker);
+
+        applicationModelMockedStatic.closeOnDemand();
+        metadataReportInstanceMockedStatic.closeOnDemand();
+    }
+
+    /**
+     * Verify that the registry url is directly configured for remote reference
+     */
+    @Test
+    public  void testCreateInvokerWithRegistryUrlForRemoteRefer(){
+
+        ReferenceConfig<DemoService> referenceConfig = new ReferenceConfig<>();
+        referenceConfig.setGeneric(Boolean.FALSE.toString());
+        referenceConfig.setProtocol("dubbo");
+        referenceConfig.setInit(true);
+        referenceConfig.setLazy(false);
+        referenceConfig.setInjvm(false);
+
+        DubboBootstrap.getInstance()
+            .application("application1")
+            .initialize();
+        referenceConfig.setBootstrap(DubboBootstrap.getInstance());
+
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("key1", "value1");
+        parameters.put("key2", "value2");
+        applicationConfig.setParameters(parameters);
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        Environment environment = mock(Environment.class);
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+        ServiceRepository serviceRepository = mock(ServiceRepository.class);
+        ConsumerModel consumerModel = mock(ConsumerModel.class);
+
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+        
applicationModelMockedStatic.when(ApplicationModel::getServiceRepository).thenReturn(serviceRepository);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        MockedStatic<MetadataReportInstance> 
metadataReportInstanceMockedStatic =
+            Mockito.mockStatic(MetadataReportInstance.class);
+
+        MetadataReport metadataReport = mock(MetadataReport.class);
+        metadataReportInstanceMockedStatic.when(() -> 
MetadataReportInstance.getMetadataReport("default"))
+            .thenReturn(metadataReport);
+
+
+        
when(serviceRepository.lookupReferredService("org.apache.dubbo.config.api.DemoService"))
+            .thenReturn(consumerModel);
+
+        referenceConfig.refreshed.set(true);
+        referenceConfig.setInterface(DemoService.class);
+        referenceConfig.getInterfaceClass();
+        referenceConfig.setCheck(false);
+
+        referenceConfig.setUrl(registryUrl);
+        referenceConfig.init();
+        Assertions.assertTrue(referenceConfig.getInvoker() instanceof 
MigrationInvoker);
+
+        applicationModelMockedStatic.closeOnDemand();
+        metadataReportInstanceMockedStatic.closeOnDemand();
+    }
+
     @Test
     @Disabled("Disabled due to Github Actions environment")
     public void testInjvm() throws Exception {
@@ -557,7 +700,7 @@ public class ReferenceConfigTest {
         ApplicationModel.getConfigManager().setApplication(application);
 
         RegistryConfig registry = new RegistryConfig();
-        registry.setAddress(registryUrl);
+        registry.setAddress(zkUrl);
 
         ProtocolConfig protocol = new ProtocolConfig();
         protocol.setName("dubbo");
@@ -601,7 +744,7 @@ public class ReferenceConfigTest {
         ApplicationModel.getConfigManager().setApplication(application);
 
         RegistryConfig registry = new RegistryConfig();
-        registry.setAddress(registryUrl);
+        registry.setAddress(zkUrl);
         ProtocolConfig protocol = new ProtocolConfig();
         protocol.setName("injvm");
 
@@ -762,17 +905,17 @@ public class ReferenceConfigTest {
         Reference reference = 
getClass().getDeclaredField("innerTest").getAnnotation(Reference.class);
         ReferenceConfig referenceConfig = new ReferenceConfig(reference);
         Assertions.assertEquals(1, referenceConfig.getMethods().size());
-        Assertions.assertEquals(((MethodConfig) 
referenceConfig.getMethods().get(0)).getName(), "sayHello");
-        Assertions.assertEquals(1300, (int) ((MethodConfig) 
referenceConfig.getMethods().get(0)).getTimeout());
-        Assertions.assertEquals(4, (int) ((MethodConfig) 
referenceConfig.getMethods().get(0)).getRetries());
-        Assertions.assertEquals(((MethodConfig) 
referenceConfig.getMethods().get(0)).getLoadbalance(), "random");
-        Assertions.assertEquals(3, (int) ((MethodConfig) 
referenceConfig.getMethods().get(0)).getActives());
-        Assertions.assertEquals(5, (int) ((MethodConfig) 
referenceConfig.getMethods().get(0)).getExecutes());
-        Assertions.assertTrue(((MethodConfig) 
referenceConfig.getMethods().get(0)).isAsync());
-        Assertions.assertEquals(((MethodConfig) 
referenceConfig.getMethods().get(0)).getOninvokeMethod(), "i");
-        Assertions.assertEquals(((MethodConfig) 
referenceConfig.getMethods().get(0)).getOnreturnMethod(), "r");
-        Assertions.assertEquals(((MethodConfig) 
referenceConfig.getMethods().get(0)).getOnthrowMethod(), "t");
-        Assertions.assertEquals(((MethodConfig) 
referenceConfig.getMethods().get(0)).getCache(), "c");
+        
Assertions.assertEquals((referenceConfig.getMethods().get(0)).getName(), 
"sayHello");
+        Assertions.assertEquals(1300, (int) 
(referenceConfig.getMethods().get(0)).getTimeout());
+        Assertions.assertEquals(4, (int) 
(referenceConfig.getMethods().get(0)).getRetries());
+        Assertions.assertEquals(( 
referenceConfig.getMethods().get(0)).getLoadbalance(), "random");
+        Assertions.assertEquals(3, (int) 
(referenceConfig.getMethods().get(0)).getActives());
+        Assertions.assertEquals(5, (int) 
(referenceConfig.getMethods().get(0)).getExecutes());
+        Assertions.assertTrue((referenceConfig.getMethods().get(0)).isAsync());
+        Assertions.assertEquals(( 
referenceConfig.getMethods().get(0)).getOninvokeMethod(), "i");
+        
Assertions.assertEquals((referenceConfig.getMethods().get(0)).getOnreturnMethod(),
 "r");
+        Assertions.assertEquals(( 
referenceConfig.getMethods().get(0)).getOnthrowMethod(), "t");
+        
Assertions.assertEquals((referenceConfig.getMethods().get(0)).getCache(), "c");
     }
 
 
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index fade48d..0898fe7 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -65,10 +65,12 @@ public class InstanceAddressURL extends URL {
         return RpcContext.getServiceContext().getInterfaceName();
     }
 
+    @Override
     public String getGroup() {
         return RpcContext.getServiceContext().getGroup();
     }
 
+    @Override
     public String getVersion() {
         return RpcContext.getServiceContext().getVersion();
     }
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 8aca204..5d3f3c8 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
@@ -109,10 +109,8 @@ public class ServiceDiscoveryRegistry implements Registry {
      */
     protected ServiceDiscovery createServiceDiscovery(URL registryURL) {
         ServiceDiscovery serviceDiscovery = getServiceDiscovery(registryURL);
-        execute(() -> {
-            
serviceDiscovery.initialize(registryURL.addParameter(INTERFACE_KEY, 
ServiceDiscovery.class.getName())
-                    .removeParameter(REGISTRY_TYPE_KEY));
-        });
+        execute(() -> 
serviceDiscovery.initialize(registryURL.addParameter(INTERFACE_KEY, 
ServiceDiscovery.class.getName())
+                .removeParameter(REGISTRY_TYPE_KEY)));
         return serviceDiscovery;
     }
 
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 bf982dc..5792693 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
@@ -54,12 +54,15 @@ import static 
org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
 public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> {
     private static final Logger logger = 
LoggerFactory.getLogger(ServiceDiscoveryRegistryDirectory.class);
 
-    // instance address to invoker mapping.
-    private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial 
value is null and the midway may be assigned to null, please use the local 
variable reference
+    /**
+     * instance address to invoker mapping.
+     * The initial value is null and the midway may be assigned to null, 
please use the local variable reference
+     */
+    private volatile Map<String, Invoker<T>> urlInvokerMap;
     private final static ConsumerConfigurationListener 
CONSUMER_CONFIGURATION_LISTENER = new ConsumerConfigurationListener();
     private volatile ReferenceConfigurationListener 
referenceConfigurationListener;
     private volatile boolean enableConfigurationListen = true;
-    private volatile List<URL> originalUrls = null; // initial for null
+    private volatile List<URL> originalUrls = null;
     private volatile Map<String, String> overrideQueryMap;
     private volatile Map<String, String> consumerFirstQueryMap;
 
@@ -132,9 +135,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends 
DynamicDirectory<T> {
         // Set the context of the address notification thread.
         RpcServiceContext.setRpcContext(getConsumerUrl());
 
-        /**
-         * 3.x added for extend URL address
-         */
+        //  3.x added for extend URL address
         ExtensionLoader<AddressListener> addressListenerExtensionLoader = 
ExtensionLoader.getExtensionLoader(AddressListener.class);
         List<AddressListener> supportedListeners = 
addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
         if (supportedListeners != null && !supportedListeners.isEmpty()) {
@@ -179,7 +180,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends 
DynamicDirectory<T> {
     }
 
     private InstanceAddressURL overrideWithConfigurator(InstanceAddressURL 
providerUrl) {
-        // override url with configurator from configurator from 
"app-name.configurators"
+        // override url with configurator from "app-name.configurators"
         providerUrl = 
overrideWithConfigurators(CONSUMER_CONFIGURATION_LISTENER.getConfigurators(), 
providerUrl);
 
         // override url with configurator from configurators from 
"service-name.configurators"
@@ -233,7 +234,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends 
DynamicDirectory<T> {
             routerChain.setInvokers(this.invokers);
             destroyAllInvokers(); // Close all invokers
         } else {
-            this.forbidden = false; // Allow to access
+            this.forbidden = false; // Allow accessing
             Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // 
local reference
             if (CollectionUtils.isEmpty(invokerUrls)) {
                 return;
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 39612cc..42ea7b7 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
@@ -518,4 +518,12 @@ public class MigrationInvoker<T> implements 
MigrationClusterInvoker<T> {
     protected void setMigrationRuleListener(MigrationRuleListener 
migrationRuleListener) {
         this.migrationRuleListener = migrationRuleListener;
     }
+
+    public Cluster getCluster() {
+        return cluster;
+    }
+
+    public URL getConsumerUrl() {
+        return consumerUrl;
+    }
 }
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 fb38944..ec23771 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
@@ -59,32 +59,54 @@ public abstract class DynamicDirectory<T> extends 
AbstractDirectory<T> implement
     protected static final Cluster CLUSTER = 
ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
 
     protected static final RouterFactory ROUTER_FACTORY = 
ExtensionLoader.getExtensionLoader(RouterFactory.class)
-            .getAdaptiveExtension();
+        .getAdaptiveExtension();
 
-    protected final String serviceKey; // Initialization at construction time, 
assertion not null
-    protected final Class<T> serviceType; // Initialization at construction 
time, assertion not null
-    protected final URL directoryUrl; // Initialization at construction time, 
assertion not null, and always assign non null value
+    /**
+     * Initialization at construction time, assertion not null
+     */
+    protected final String serviceKey;
+
+    /**
+     * Initialization at construction time, assertion not null
+     */
+    protected final Class<T> serviceType;
+
+    /**
+     * Initialization at construction time, assertion not null, and always 
assign non null value
+     */
+    protected final URL directoryUrl;
     protected final boolean multiGroup;
-    protected Protocol protocol; // Initialization at the time of injection, 
the assertion is not null
-    protected Registry registry; // Initialization at the time of injection, 
the assertion is not null
+
+    /**
+     * Initialization at the time of injection, the assertion is not null
+     */
+    protected Protocol protocol;
+
+    /**
+     * Initialization at the time of injection, the assertion is not null
+     */
+    protected Registry registry;
     protected volatile boolean forbidden = false;
     protected boolean shouldRegister;
     protected boolean shouldSimplified;
 
-    protected volatile URL overrideDirectoryUrl; // Initialization at 
construction time, assertion not null, and always assign non null value
+    /**
+     * Initialization at construction time, assertion not null, and always 
assign not null value
+     */
+    protected volatile URL overrideDirectoryUrl;
     protected volatile URL subscribeUrl;
     protected volatile URL registeredConsumerUrl;
 
     /**
+     * The initial value is null and the midway may be assigned to null, 
please use the local variable reference
      * override rules
      * Priority: override>-D>consumer>provider
      * Rule one: for a certain provider <ip:port,timeout=100>
      * Rule two: for all providers <* ,timeout=5000>
      */
-    protected volatile List<Configurator> configurators; // The initial value 
is null and the midway may be assigned to null, please use the local variable 
reference
+    protected volatile List<Configurator> configurators;
 
     protected volatile List<Invoker<T>> invokers;
-    // Set<invokerUrls> cache invokeUrls to invokers mapping.
 
     protected ServiceInstancesChangedListener serviceListener;
 
@@ -146,9 +168,9 @@ public abstract class DynamicDirectory<T> extends 
AbstractDirectory<T> implement
         if (forbidden) {
             // 1. No service provider 2. Service providers are disabled
             throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No 
provider available from registry " +
-                    getUrl().getAddress() + " for service " + 
getConsumerUrl().getServiceKey() + " on consumer " +
-                    NetUtils.getLocalHost() + " use dubbo version " + 
Version.getVersion() +
-                    ", please check status of providers(disabled, not 
registered or in blacklist).");
+                getUrl().getAddress() + " for service " + 
getConsumerUrl().getServiceKey() + " on consumer " +
+                NetUtils.getLocalHost() + " use dubbo version " + 
Version.getVersion() +
+                ", please check status of providers(disabled, not registered 
or in blacklist).");
         }
 
         if (multiGroup) {
@@ -176,23 +198,39 @@ public abstract class DynamicDirectory<T> extends 
AbstractDirectory<T> implement
         return this.invokers == null ? Collections.emptyList() : this.invokers;
     }
 
-    // The currently effective consumer url
+    /**
+     * The currently effective consumer url
+     *
+     * @return URL
+     */
     @Override
     public URL getConsumerUrl() {
         return this.overrideDirectoryUrl;
     }
 
-    // The original consumer url
+    /**
+     * The original consumer url
+     *
+     * @return URL
+     */
     public URL getOriginalConsumerUrl() {
         return this.overrideDirectoryUrl;
     }
 
-    // The url registered to registry or metadata center
+    /**
+     * The url registered to registry or metadata center
+     *
+     * @return URL
+     */
     public URL getRegisteredConsumerUrl() {
         return registeredConsumerUrl;
     }
 
-    // The url used to subscribe from registry
+    /**
+     * The url used to subscribe from registry
+     *
+     * @return URL
+     */
     public URL getSubscribeUrl() {
         return subscribeUrl;
     }
@@ -204,10 +242,10 @@ public abstract class DynamicDirectory<T> extends 
AbstractDirectory<T> implement
     public void setRegisteredConsumerUrl(URL url) {
         if (!shouldSimplified) {
             this.registeredConsumerUrl = url.addParameters(CATEGORY_KEY, 
CONSUMERS_CATEGORY, CHECK_KEY,
-                    String.valueOf(false));
+                String.valueOf(false));
         } else {
             this.registeredConsumerUrl = URL.valueOf(url, 
DEFAULT_REGISTER_CONSUMER_KEYS, null).addParameters(
-                    CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, 
String.valueOf(false));
+                CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, 
String.valueOf(false));
         }
     }
 
@@ -291,6 +329,7 @@ public abstract class DynamicDirectory<T> extends 
AbstractDirectory<T> implement
         }
     }
 
+    @Override
     public boolean isNotificationReceived() {
         return invokersChanged;
     }
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
index b7374a3..2643f33 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
@@ -65,6 +65,7 @@ public class InterfaceCompatibleRegistryProtocol extends 
RegistryProtocol {
         return doCreateInvoker(directory, cluster, registry, type);
     }
 
+    @Override
     protected <T> ClusterInvoker<T> getMigrationInvoker(RegistryProtocol 
registryProtocol, Cluster cluster, Registry registry, Class<T> type, URL url, 
URL consumerUrl) {
 //        ClusterInvoker<T> invoker = getInvoker(cluster, registry, type, url);
         return new MigrationInvoker<T>(registryProtocol, cluster, registry, 
type, url, consumerUrl);
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
index e0996ba..82058af 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
@@ -89,10 +89,15 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
     private static final ConsumerConfigurationListener 
CONSUMER_CONFIGURATION_LISTENER = new ConsumerConfigurationListener();
     private ReferenceConfigurationListener referenceConfigurationListener;
 
-    // Map<url, Invoker> cache service url to invoker mapping.
-    // The initial value is null and the midway may be assigned to null, 
please use the local variable reference
+    /**
+     * Map<url, Invoker> cache service url to invoker mapping.
+     * The initial value is null and the midway may be assigned to null, 
please use the local variable reference
+     */
     protected volatile Map<URL, Invoker<T>> urlInvokerMap;
-    // The initial value is null and the midway may be assigned to null, 
please use the local variable reference
+
+    /**
+     * The initial value is null and the midway may be assigned to null, 
please use the local variable reference
+     */
     protected volatile Set<URL> cachedInvokerUrls;
 
     public RegistryDirectory(Class<T> serviceType, URL url) {
@@ -135,9 +140,8 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
 
         // providers
         List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, 
Collections.emptyList());
-        /**
-         * 3.x added for extend URL address
-         */
+
+        // 3.x added for extend URL address
         ExtensionLoader<AddressListener> addressListenerExtensionLoader = 
ExtensionLoader.getExtensionLoader(AddressListener.class);
         List<AddressListener> supportedListeners = 
addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
         if (supportedListeners != null && !supportedListeners.isEmpty()) {
@@ -407,7 +411,7 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
         // override url with configurator from "override://" URL for dubbo 2.6 
and before
         providerUrl = overrideWithConfigurators(this.configurators, 
providerUrl);
 
-        // override url with configurator from configurator from 
"app-name.configurators"
+        // override url with configurator from "app-name.configurators"
         providerUrl = 
overrideWithConfigurators(CONSUMER_CONFIGURATION_LISTENER.getConfigurators(), 
providerUrl);
 
         // override url with configurator from configurators from 
"service-name.configurators"
@@ -535,10 +539,12 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
         return this.overrideDirectoryUrl;
     }
 
+    @Override
     public URL getRegisteredConsumerUrl() {
         return registeredConsumerUrl;
     }
 
+    @Override
     public void setRegisteredConsumerUrl(URL url) {
         if (!shouldSimplified) {
             this.registeredConsumerUrl = url.addParameters(CATEGORY_KEY, 
CONSUMERS_CATEGORY, CHECK_KEY,
@@ -566,6 +572,7 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
         return urlInvokerMap;
     }
 
+    @Override
     public List<Invoker<T>> getInvokers() {
         return invokers;
     }
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
index a6997b0..a4af51b 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
@@ -516,7 +516,7 @@ public class RegistryProtocol implements Protocol {
         directory.setRegistry(registry);
         directory.setProtocol(protocol);
         // all attributes of REFER_KEY
-        Map<String, String> parameters = new HashMap<String, 
String>(directory.getConsumerUrl().getParameters());
+        Map<String, String> parameters = new 
HashMap<>(directory.getConsumerUrl().getParameters());
         URL urlToRegistry = new ServiceConfigURL(
             parameters.get(PROTOCOL_KEY) == null ? DUBBO : 
parameters.get(PROTOCOL_KEY),
             parameters.remove(REGISTER_IP_KEY), 0, getPath(parameters, type), 
parameters);
@@ -568,7 +568,7 @@ public class RegistryProtocol implements Protocol {
             }
         }
 
-        List<Exporter<?>> exporters = new 
ArrayList<Exporter<?>>(bounds.values());
+        List<Exporter<?>> exporters = new ArrayList<>(bounds.values());
         for (Exporter<?> exporter : exporters) {
             exporter.unexport();
         }
@@ -586,7 +586,7 @@ public class RegistryProtocol implements Protocol {
     }
 
     //Merge the urls of configurators
-    private static URL getConfigedInvokerUrl(List<Configurator> configurators, 
URL url) {
+    private static URL getConfiguredInvokerUrl(List<Configurator> 
configurators, URL url) {
         if (configurators != null && configurators.size() > 0) {
             for (Configurator configurator : configurators) {
                 url = configurator.configure(url);
@@ -694,9 +694,9 @@ public class RegistryProtocol implements Protocol {
             //The current, may have been merged many times
             URL currentUrl = exporter.getInvoker().getUrl();
             //Merged with this configuration
-            URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
-            newUrl = 
getConfigedInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl);
-            newUrl = 
getConfigedInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey())
+            URL newUrl = getConfiguredInvokerUrl(configurators, originUrl);
+            newUrl = 
getConfiguredInvokerUrl(providerConfigurationListener.getConfigurators(), 
newUrl);
+            newUrl = 
getConfiguredInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey())
                 .getConfigurators(), newUrl);
             if (!currentUrl.equals(newUrl)) {
                 if (newUrl.getParameter(Constants.NEED_REEXPORT, true)) {
@@ -708,7 +708,7 @@ public class RegistryProtocol implements Protocol {
         }
 
         private List<URL> getMatchedUrls(List<URL> configuratorUrls, URL 
currentSubscribe) {
-            List<URL> result = new ArrayList<URL>();
+            List<URL> result = new ArrayList<>();
             for (URL url : configuratorUrls) {
                 URL overrideUrl = url;
                 // Compatible with the old version
@@ -738,7 +738,7 @@ public class RegistryProtocol implements Protocol {
         }
 
         private <T> URL overrideUrl(URL providerUrl) {
-            return RegistryProtocol.getConfigedInvokerUrl(configurators, 
providerUrl);
+            return RegistryProtocol.getConfiguredInvokerUrl(configurators, 
providerUrl);
         }
 
         @Override
@@ -763,7 +763,7 @@ public class RegistryProtocol implements Protocol {
          * @return
          */
         private <T> URL overrideUrl(URL providerUrl) {
-            return RegistryProtocol.getConfigedInvokerUrl(configurators, 
providerUrl);
+            return RegistryProtocol.getConfiguredInvokerUrl(configurators, 
providerUrl);
         }
 
         @Override
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFallbackRegistryTest.java
similarity index 88%
rename from 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java
rename to 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFallbackRegistryTest.java
index 25dceb6..f508ef6 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFallbackRegistryTest.java
@@ -24,12 +24,11 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-public class CacheableFailbackRegistryTest {
+public class CacheableFallbackRegistryTest {
 
     static String service;
     static URL serviceUrl;
@@ -64,12 +63,7 @@ public class CacheableFailbackRegistryTest {
         registry = new MockCacheableRegistryImpl(registryUrl);
         URL url = URLStrParser.parseEncodedStr(urlStr);
 
-        NotifyListener listener = new NotifyListener() {
-            @Override
-            public void notify(List<URL> urls) {
-                resCount.set(urls.size());
-            }
-        };
+        NotifyListener listener = urls -> resCount.set(urls.size());
 
         registry.addChildren(url);
         registry.subscribe(serviceUrl, listener);
@@ -100,12 +94,7 @@ public class CacheableFailbackRegistryTest {
         registry = new MockCacheableRegistryImpl(registryUrl);
         URL url = URLStrParser.parseEncodedStr(urlStr);
 
-        NotifyListener listener = new NotifyListener() {
-            @Override
-            public void notify(List<URL> urls) {
-                resCount.set(urls.size());
-            }
-        };
+        NotifyListener listener = urls -> resCount.set(urls.size());
 
         registry.addChildren(url);
         registry.subscribe(serviceUrl, listener);
@@ -131,12 +120,7 @@ public class CacheableFailbackRegistryTest {
         registry = new MockCacheableRegistryImpl(registryUrl);
         URL url = URLStrParser.parseEncodedStr(urlStr);
 
-        NotifyListener listener = new NotifyListener() {
-            @Override
-            public void notify(List<URL> urls) {
-                resCount.set(urls.size());
-            }
-        };
+        NotifyListener listener = urls -> resCount.set(urls.size());
 
         registry.addChildren(url);
         registry.subscribe(serviceUrl, listener);
@@ -162,12 +146,7 @@ public class CacheableFailbackRegistryTest {
         registry = new MockCacheableRegistryImpl(registryUrl);
         URL url = URLStrParser.parseEncodedStr(urlStr);
 
-        NotifyListener listener = new NotifyListener() {
-            @Override
-            public void notify(List<URL> urls) {
-                resCount.set(urls.size());
-            }
-        };
+        NotifyListener listener = urls -> resCount.set(urls.size());
 
         registry.addChildren(url);
         registry.subscribe(serviceUrl, listener);
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ListenerRegistryWrapperTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ListenerRegistryWrapperTest.java
new file mode 100644
index 0000000..ee313ef
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ListenerRegistryWrapperTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.registry;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.registry.integration.DemoService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+
+public class ListenerRegistryWrapperTest {
+
+    @Test
+    public void testSubscribe() {
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "true");
+        parameters.put(REGISTER_IP_KEY, "172.23.236.180");
+        parameters.put("registry.listeners", "listener-one");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        attributes.put("key1", "value1");
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryFactory registryFactory = mock(RegistryFactory.class);
+        Registry registry = mock(Registry.class);
+        NotifyListener notifyListener = mock(NotifyListener.class);
+        when(registryFactory.getRegistry(url)).thenReturn(registry);
+
+        RegistryFactoryWrapper registryFactoryWrapper = new 
RegistryFactoryWrapper(registryFactory);
+        Registry registryWrapper = registryFactoryWrapper.getRegistry(url);
+
+        Assertions.assertTrue(registryWrapper instanceof 
ListenerRegistryWrapper);
+
+        URL subscribeUrl = new ServiceConfigURL("dubbo",
+            "127.0.0.1",
+            20881,
+            DemoService.class.getName(),
+            parameters);
+
+        RegistryServiceListener listener = 
Mockito.mock(RegistryServiceListener.class);
+        RegistryServiceListener1.delegate = listener;
+
+        registryWrapper.subscribe(subscribeUrl, notifyListener);
+        verify(listener, times(1)).onSubscribe(subscribeUrl, registry);
+    }
+
+}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryFactoryWrapperTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryFactoryWrapperTest.java
index fa5627b..cc79743 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryFactoryWrapperTest.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryFactoryWrapperTest.java
@@ -33,7 +33,7 @@ public class RegistryFactoryWrapperTest {
         RegistryServiceListener listener2 = 
Mockito.mock(RegistryServiceListener.class);
         RegistryServiceListener2.delegate = listener2;
 
-        Registry registry = 
registryFactory.getRegistry(URL.valueOf("simple://localhost:8080/registry-service"));
+        Registry registry = 
registryFactory.getRegistry(URL.valueOf("simple://localhost:8080/registry-service?registry.listeners=listener-one,listener-two"));
         URL url = URL.valueOf("dubbo://localhost:8081/simple.service");
         registry.register(url);
 
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener1.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener1.java
index 3132ade..391e0e1 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener1.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener1.java
@@ -20,7 +20,7 @@ package org.apache.dubbo.registry;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Activate;
 
-@Activate(order = 1)
+@Activate(order = 1, value = "listener-one")
 public class RegistryServiceListener1 implements RegistryServiceListener {
     static RegistryServiceListener delegate;
 
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
index 8f1f318..3cf932e 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
@@ -20,7 +20,7 @@ package org.apache.dubbo.registry;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Activate;
 
-@Activate(order = 2)
+@Activate(order = 2, value = "listener-two")
 public class RegistryServiceListener2 implements RegistryServiceListener {
     static RegistryServiceListener delegate;
 
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImplTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImplTest.java
deleted file mode 100644
index c81f6d8..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImplTest.java
+++ /dev/null
@@ -1,21 +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.registry.client.metadata.store;
-
-
-public class RemoteMetadataServiceImplTest {
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandlerTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandlerTest.java
index ccbf23a..e60a088 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandlerTest.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandlerTest.java
@@ -30,7 +30,7 @@ public class MigrationRuleHandlerTest {
         MigrationClusterInvoker invoker = 
Mockito.mock(MigrationClusterInvoker.class);
         URL url = Mockito.mock(URL.class);
         Mockito.when(url.getDisplayServiceKey()).thenReturn("test");
-        Mockito.when(url.getParameter((String) Mockito.any(), (String) 
Mockito.any())).thenAnswer(i->i.getArgument(1));
+        Mockito.when(url.getParameter(Mockito.any(), (String) 
Mockito.any())).thenAnswer(i->i.getArgument(1));
         MigrationRuleHandler handler = new MigrationRuleHandler(invoker, url);
 
         
Mockito.when(invoker.migrateToForceApplicationInvoker(Mockito.any())).thenReturn(true);
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/CountRegistryProtocolListener.java
similarity index 56%
copy from 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
copy to 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/CountRegistryProtocolListener.java
index 8f1f318..1de1e39 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/CountRegistryProtocolListener.java
@@ -14,33 +14,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.dubbo.registry.integration;
 
-package org.apache.dubbo.registry;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
 
-@Activate(order = 2)
-public class RegistryServiceListener2 implements RegistryServiceListener {
-    static RegistryServiceListener delegate;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+public class CountRegistryProtocolListener implements RegistryProtocolListener 
{
+
+    private static final AtomicInteger referCounter = new AtomicInteger(0);
 
     @Override
-    public void onRegister(URL url, Registry registry) {
-        delegate.onRegister(url, registry);
+    public void onExport(RegistryProtocol registryProtocol, Exporter<?> 
exporter) {
+
     }
 
     @Override
-    public void onUnregister(URL url, Registry registry) {
-        delegate.onUnregister(url, registry);
+    public void onRefer(RegistryProtocol registryProtocol, ClusterInvoker<?> 
invoker, URL url, URL registryURL) {
+        referCounter.incrementAndGet();
     }
 
     @Override
-    public void onSubscribe(URL url, Registry registry) {
-        delegate.onSubscribe(url, registry);
+    public void onDestroy() {
+
     }
 
-    @Override
-    public void onUnsubscribe(URL url, Registry registry) {
-        delegate.onUnsubscribe(url, registry);
+    public static AtomicInteger getReferCounter() {
+        return referCounter;
     }
 }
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/DelayedRegistryNotifierTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DemoService.java
similarity index 91%
rename from 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/DelayedRegistryNotifierTest.java
rename to 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DemoService.java
index c158fdc..a48a5c9 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/DelayedRegistryNotifierTest.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DemoService.java
@@ -14,8 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
+package org.apache.dubbo.registry.integration;
 
-public class DelayedRegistryNotifierTest {
 
+public interface DemoService {
 }
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DynamicDirectoryTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DynamicDirectoryTest.java
new file mode 100644
index 0000000..a1ca082
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DynamicDirectoryTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.registry.integration;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.registry.Registry;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
+import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
+import static 
org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
+import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+public class DynamicDirectoryTest {
+
+    /**
+     * verify simplified consumer url information that needs to be registered
+     */
+    @Test
+    public void testSimplifiedUrl() {
+
+        // verify that the consumer url information that needs to be 
registered is not simplified by default
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "true");
+        parameters.put(REGISTER_IP_KEY, "172.23.236.180");
+
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURLWithoutSimplified = new 
ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        attributes.put("key1", "value1");
+        URL urlWithoutSimplified = 
serviceConfigURLWithoutSimplified.addAttributes(attributes);
+
+        DemoDynamicDirectory<DemoService> dynamicDirectoryWithoutSimplified =
+            new DemoDynamicDirectory<>(DemoService.class, 
urlWithoutSimplified);
+
+        URL registeredConsumerUrlWithoutSimplified = new 
ServiceConfigURL("dubbo",
+            "127.0.0.1",
+            2181,
+            DemoService.class.getName(),
+            parameters);
+
+        
dynamicDirectoryWithoutSimplified.setRegisteredConsumerUrl(registeredConsumerUrlWithoutSimplified);
+
+        URL urlForNotSimplified = registeredConsumerUrlWithoutSimplified
+            .addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, 
String.valueOf(false));
+
+        Assertions.assertEquals(urlForNotSimplified, 
dynamicDirectoryWithoutSimplified.getRegisteredConsumerUrl());
+
+        // verify simplified consumer url information that needs to be 
registered
+        parameters.put(SIMPLIFIED_KEY, "true");
+        ServiceConfigURL serviceConfigURLWithSimplified = new 
ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        URL urlWithSimplified = 
serviceConfigURLWithSimplified.addAttributes(attributes);
+        DemoDynamicDirectory<DemoService> dynamicDirectoryWithSimplified = new 
DemoDynamicDirectory<>(DemoService.class, urlWithSimplified);
+
+        URL registeredConsumerUrlWithSimplified = new ServiceConfigURL("dubbo",
+            "127.0.0.1",
+            2181,
+            DemoService.class.getName(),
+            parameters);
+
+        
dynamicDirectoryWithSimplified.setRegisteredConsumerUrl(registeredConsumerUrlWithSimplified);
+
+        URL urlForSimplified = URL.valueOf(
+            registeredConsumerUrlWithSimplified,
+            DEFAULT_REGISTER_CONSUMER_KEYS,
+            null).addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, 
String.valueOf(false));
+
+        Assertions.assertEquals(urlForSimplified, 
dynamicDirectoryWithSimplified.getRegisteredConsumerUrl());
+
+    }
+
+
+    @Test
+    public void testSubscribe() {
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "true");
+        parameters.put(REGISTER_IP_KEY, "172.23.236.180");
+
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigUrl = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        attributes.put("key1", "value1");
+        URL url = serviceConfigUrl.addAttributes(attributes);
+
+        DemoDynamicDirectory<DemoService> demoDynamicDirectory = new 
DemoDynamicDirectory<>(DemoService.class, url);
+
+        URL subscribeUrl = new ServiceConfigURL("dubbo",
+            "127.0.0.1",
+            20881,
+            DemoService.class.getName(),
+            parameters);
+
+        Registry registry = mock(Registry.class);
+        demoDynamicDirectory.setRegistry(registry);
+
+        demoDynamicDirectory.subscribe(subscribeUrl);
+
+        verify(registry, times(1)).subscribe(subscribeUrl, 
demoDynamicDirectory);
+        Assertions.assertEquals(subscribeUrl, 
demoDynamicDirectory.getSubscribeUrl());
+    }
+
+
+    static class DemoDynamicDirectory<T> extends DynamicDirectory<T> {
+
+        public DemoDynamicDirectory(Class<T> serviceType, URL url) {
+            super(serviceType, url);
+        }
+
+        @Override
+        protected void destroyAllInvokers() {
+
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        @Override
+        public void notify(List<URL> urls) {
+
+        }
+    }
+}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java
new file mode 100644
index 0000000..9f1e635
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java
@@ -0,0 +1,550 @@
+/*
+ * 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.registry.integration;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.CompositeConfiguration;
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.config.Environment;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.RegistryFactory;
+import org.apache.dubbo.registry.client.migration.MigrationInvoker;
+import org.apache.dubbo.registry.client.migration.MigrationRuleListener;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.Cluster;
+import org.apache.dubbo.rpc.cluster.support.FailoverCluster;
+import org.apache.dubbo.rpc.cluster.support.MergeableCluster;
+import org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN;
+import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
+import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.CONSUMER_URL_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.mockStatic;
+
+public class RegistryProtocolTest {
+
+    @AfterEach
+    public void tearDown() throws IOException {
+        Mockito.framework().clearInlineMocks();
+    }
+
+    /**
+     * verify the generated consumer url information
+     */
+    @Test
+    public void testConsumerUrlWithoutProtocol() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "false");
+        parameters.put(REGISTER_IP_KEY, "172.23.236.180");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        attributes.put("key1", "value1");
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryFactory registryFactory = mock(RegistryFactory.class);
+        Registry registry = mock(Registry.class);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        registryProtocol.setRegistryFactory(registryFactory);
+
+        
when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry);
+
+        Cluster cluster = mock(Cluster.class);
+
+        Invoker<?> invoker = registryProtocol.doRefer(cluster, registry, 
DemoService.class, url, parameters);
+
+        Assertions.assertTrue(invoker instanceof MigrationInvoker);
+
+        URL consumerUrl = ((MigrationInvoker<?>) invoker).getConsumerUrl();
+        Assertions.assertTrue((consumerUrl != null));
+
+        // verify that the default is dubbo protocol
+        Assertions.assertEquals("dubbo", consumerUrl.getProtocol());
+        Assertions.assertEquals(parameters.get(REGISTER_IP_KEY), 
consumerUrl.getHost());
+        
Assertions.assertFalse(consumerUrl.getAttributes().containsKey(REFER_KEY));
+        Assertions.assertEquals("value1", consumerUrl.getAttribute("key1"));
+
+        applicationModelMockedStatic.closeOnDemand();
+    }
+
+    /**
+     * verify that when the protocol is configured, the protocol of consumer 
url is the configured protocol
+     */
+    @Test
+    public void testConsumerUrlWithProtocol() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "false");
+        parameters.put(REGISTER_IP_KEY, "172.23.236.180");
+        parameters.put(PROTOCOL_KEY, "tri");
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        attributes.put("key1", "value1");
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryFactory registryFactory = mock(RegistryFactory.class);
+        Registry registry = mock(Registry.class);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        registryProtocol.setRegistryFactory(registryFactory);
+
+        
when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry);
+
+        Cluster cluster = mock(Cluster.class);
+
+        Invoker<?> invoker = registryProtocol.doRefer(cluster, registry, 
DemoService.class, url, parameters);
+
+        Assertions.assertTrue(invoker instanceof MigrationInvoker);
+
+        URL consumerUrl = ((MigrationInvoker<?>) invoker).getConsumerUrl();
+        Assertions.assertTrue((consumerUrl != null));
+
+        // verify that the protocol of consumer url
+        Assertions.assertEquals("tri", consumerUrl.getProtocol());
+        Assertions.assertEquals(parameters.get(REGISTER_IP_KEY), 
consumerUrl.getHost());
+        
Assertions.assertFalse(consumerUrl.getAttributes().containsKey(REFER_KEY));
+        Assertions.assertEquals("value1", consumerUrl.getAttribute("key1"));
+
+        applicationModelMockedStatic.closeOnDemand();
+    }
+
+    /**
+     * verify that if multiple groups are not configured, the service 
reference of the registration center
+     * the default is FailoverCluster
+     *
+     * @see FailoverCluster
+     */
+    @Test
+    public void testReferWithoutGroup() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "false");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryFactory registryFactory = mock(RegistryFactory.class);
+        Registry registry = mock(Registry.class);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        registryProtocol.setRegistryFactory(registryFactory);
+
+        
when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry);
+
+        Invoker<?> invoker = registryProtocol.refer(DemoService.class, url);
+
+        Assertions.assertTrue(invoker instanceof MigrationInvoker);
+        Assertions.assertTrue(((MigrationInvoker<?>) invoker).getCluster() 
instanceof MockClusterWrapper);
+        Assertions.assertTrue(
+            ((MockClusterWrapper) ((MigrationInvoker<?>) 
invoker).getCluster()).getCluster() instanceof FailoverCluster);
+
+        applicationModelMockedStatic.closeOnDemand();
+    }
+
+    /**
+     * verify that if multiple groups are configured, the service reference of 
the registration center
+     *
+     * @see MergeableCluster
+     */
+    @Test
+    public void testReferWithGroup() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "false");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL(
+            "registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        refer.put(GROUP_KEY, "group1,group2");
+        attributes.put(REFER_KEY, refer);
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryFactory registryFactory = mock(RegistryFactory.class);
+        Registry registry = mock(Registry.class);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        registryProtocol.setRegistryFactory(registryFactory);
+
+        
when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry);
+
+        Invoker<?> invoker = registryProtocol.refer(DemoService.class, url);
+
+        Assertions.assertTrue(invoker instanceof MigrationInvoker);
+
+        Assertions.assertTrue(((MigrationInvoker<?>) invoker).getCluster() 
instanceof MockClusterWrapper);
+
+        Assertions.assertTrue(
+            ((MockClusterWrapper) ((MigrationInvoker<?>) 
invoker).getCluster()).getCluster() instanceof MergeableCluster);
+
+        applicationModelMockedStatic.closeOnDemand();
+    }
+
+    /**
+     * verify that the default RegistryProtocolListener will be executed
+     *
+     * @see MigrationRuleListener
+     */
+    @Test
+    public void testInterceptInvokerForMigrationRuleListener() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "false");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL(
+            "registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        refer.put(GROUP_KEY, "group1,group2");
+        attributes.put(REFER_KEY, refer);
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        MigrationInvoker<?> clusterInvoker = mock(MigrationInvoker.class);
+
+        Map<String, Object> consumerAttribute = new 
HashMap<>(url.getAttributes());
+        consumerAttribute.remove(REFER_KEY);
+        URL consumerUrl = new ServiceConfigURL(parameters.get(PROTOCOL_KEY) == 
null ? DUBBO : parameters.get(PROTOCOL_KEY),
+            null,
+            null,
+            parameters.get(REGISTER_IP_KEY),
+            0, url.getPath(),
+            parameters,
+            consumerAttribute);
+        url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
+        MigrationRuleListener migrationRuleListener = 
mock(MigrationRuleListener.class);
+        List<RegistryProtocolListener> registryProtocolListeners = new 
ArrayList<>();
+        registryProtocolListeners.add(migrationRuleListener);
+        MockedStatic<ExtensionLoader> extensionLoaderMockedStatic = 
mockStatic(ExtensionLoader.class);
+        ExtensionLoader extensionLoaderForRegistryProtocolListener = 
mock(ExtensionLoader.class);
+        
when(ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class))
+            .thenReturn(extensionLoaderForRegistryProtocolListener);
+        
when(extensionLoaderForRegistryProtocolListener.getActivateExtension(url, 
"registry.protocol.listener"))
+            .thenReturn(registryProtocolListeners);
+
+        registryProtocol.interceptInvoker(clusterInvoker, url, consumerUrl, 
url);
+        verify(migrationRuleListener, times(1)).onRefer(registryProtocol, 
clusterInvoker, consumerUrl, url);
+
+        extensionLoaderMockedStatic.closeOnDemand();
+        applicationModelMockedStatic.closeOnDemand();
+
+    }
+
+
+    /**
+     * Verify that if registry.protocol.listener is configured,
+     * whether the corresponding RegistryProtocolListener will be executed 
normally
+     *
+     * @see CountRegistryProtocolListener
+     */
+    @Test
+    public void testInterceptInvokerForCustomRegistryProtocolListener() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "false");
+        parameters.put("registry.protocol.listener", "count");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL(
+            "registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        refer.put(GROUP_KEY, "group1,group2");
+        attributes.put(REFER_KEY, refer);
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        MigrationInvoker<?> clusterInvoker = mock(MigrationInvoker.class);
+
+        Map<String, Object> consumerAttribute = new 
HashMap<>(url.getAttributes());
+        consumerAttribute.remove(REFER_KEY);
+        URL consumerUrl = new ServiceConfigURL(parameters.get(PROTOCOL_KEY) == 
null ? DUBBO : parameters.get(PROTOCOL_KEY),
+            null,
+            null,
+            parameters.get(REGISTER_IP_KEY),
+            0, url.getPath(),
+            parameters,
+            consumerAttribute);
+        url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
+
+        registryProtocol.interceptInvoker(clusterInvoker, url, consumerUrl, 
url);
+
+        Assertions.assertEquals(1, 
CountRegistryProtocolListener.getReferCounter().get());
+        applicationModelMockedStatic.closeOnDemand();
+    }
+
+    /**
+     * verify the registered consumer url
+     */
+    @Test
+    public void testRegisterConsumerUrl() {
+        ApplicationConfig applicationConfig = new ApplicationConfig();
+        applicationConfig.setName("application1");
+
+        ConfigManager configManager = mock(ConfigManager.class);
+        
when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig);
+
+        CompositeConfiguration compositeConfiguration = 
mock(CompositeConfiguration.class);
+        when(compositeConfiguration.convert(Boolean.class, 
ENABLE_CONFIGURATION_LISTEN, true))
+            .thenReturn(true);
+
+        Configuration dynamicGlobalConfiguration = mock(Configuration.class);
+
+        Environment environment = mock(Environment.class);
+        
when(environment.getConfiguration()).thenReturn(compositeConfiguration);
+        
when(environment.getDynamicGlobalConfiguration()).thenReturn(dynamicGlobalConfiguration);
+
+        MockedStatic<ApplicationModel> applicationModelMockedStatic = 
Mockito.mockStatic(ApplicationModel.class);
+        
applicationModelMockedStatic.when(ApplicationModel::getConfigManager).thenReturn(configManager);
+        
applicationModelMockedStatic.when(ApplicationModel::getEnvironment).thenReturn(environment);
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put("registry", "zookeeper");
+        parameters.put("register", "true");
+        parameters.put(REGISTER_IP_KEY, "172.23.236.180");
+
+        Map<String, Object> attributes = new HashMap<>();
+        ServiceConfigURL serviceConfigURL = new ServiceConfigURL("registry",
+            "127.0.0.1",
+            2181,
+            "org.apache.dubbo.registry.RegistryService",
+            parameters);
+        Map<String, String> refer = new HashMap<>();
+        attributes.put(REFER_KEY, refer);
+        attributes.put("key1", "value1");
+        URL url = serviceConfigURL.addAttributes(attributes);
+
+        RegistryFactory registryFactory = mock(RegistryFactory.class);
+        Registry registry = mock(Registry.class);
+
+        RegistryProtocol registryProtocol = new RegistryProtocol();
+        registryProtocol.setRegistryFactory(registryFactory);
+
+        
when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry);
+
+        Cluster cluster = mock(Cluster.class);
+
+        Invoker<?> invoker = registryProtocol.doRefer(cluster, registry, 
DemoService.class, url, parameters);
+
+        Assertions.assertTrue(invoker instanceof MigrationInvoker);
+
+        URL consumerUrl = ((MigrationInvoker<?>) invoker).getConsumerUrl();
+        Assertions.assertTrue((consumerUrl != null));
+
+        Map<String, String> urlParameters = consumerUrl.getParameters();
+        URL urlToRegistry = new ServiceConfigURL(
+            urlParameters.get(PROTOCOL_KEY) == null ? DUBBO : 
urlParameters.get(PROTOCOL_KEY),
+            urlParameters.remove(REGISTER_IP_KEY), 0, consumerUrl.getPath(), 
urlParameters);
+
+        URL registeredConsumerUrl = urlToRegistry.addParameters(CATEGORY_KEY, 
CONSUMERS_CATEGORY, CHECK_KEY,
+            String.valueOf(false));
+
+        verify(registry,times(1)).register(registeredConsumerUrl);
+
+        applicationModelMockedStatic.closeOnDemand();
+    }
+
+}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.integration.RegistryProtocolListener
 
b/dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.integration.RegistryProtocolListener
new file mode 100644
index 0000000..326fc38
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.integration.RegistryProtocolListener
@@ -0,0 +1 @@
+count=org.apache.dubbo.registry.integration.CountRegistryProtocolListener

Reply via email to