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

[dubbo] branch 3.0 updated: Verify local reference for the creation of a local invoker process (#8592)

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

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


The following commit(s) were added to refs/heads/3.0 by this push:
     new 7537786  Verify local reference for the creation of a local invoker process (#8592)
7537786 is described below

commit 7537786b8e7d70cb7700d3297b852f0a101c30a4
Author: huazhongming <cr...@gmail.com>
AuthorDate: Fri Aug 27 10:13:45 2021 +0800

    Verify local reference for the creation of a local invoker process (#8592)
    
    * verify create local invoker
    
    * fix
    
    * fix
    
    * fix injvm protocol
---
 .../filter/DefaultFilterChainBuilderTest.java      |  66 +++++++++
 .../dubbo/rpc/cluster/filter/DemoServiceImpl.java  |  29 +---
 .../apache/dubbo/rpc/cluster/filter/LogFilter.java |  36 ++---
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   1 +
 .../org/apache/dubbo/config/ReferenceConfig.java   |   2 +-
 .../apache/dubbo/config/ReferenceConfigTest.java   | 162 +++++++++++++++++++--
 .../dubbo/rpc/listener/ListenerInvokerWrapper.java |   7 +
 .../dubbo/rpc/protocol/AbstractProtocol.java       |   8 +-
 .../dubbo/rpc/protocol/CountInvokerListener.java}  |  31 ++--
 .../rpc/protocol/ProtocolListenerWrapperTest.java  |  81 +++++++++++
 .../internal/org.apache.dubbo.rpc.InvokerListener  |   1 +
 .../dubbo/rpc/protocol/injvm/InjvmExporter.java    |   2 +-
 .../dubbo/rpc/protocol/injvm/InjvmInvoker.java     |   2 +-
 .../rpc/protocol/injvm/InjvmProtocolTest.java      |  19 +--
 14 files changed, 359 insertions(+), 88 deletions(-)

diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java
new file mode 100644
index 0000000..655f6d9
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.filter;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.protocol.AbstractInvoker;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+
+public class DefaultFilterChainBuilderTest {
+
+    @Test
+    public void testBuildInvokerChainForLocalReference() {
+        DefaultFilterChainBuilder defaultFilterChainBuilder = new DefaultFilterChainBuilder();
+
+        // verify that no filter is built by default
+        URL urlWithoutFilter = URL.valueOf("injvm://127.0.0.1/DemoService")
+            .addParameter(INTERFACE_KEY, DemoService.class.getName());
+        AbstractInvoker<DemoService> invokerWithoutFilter = new AbstractInvoker<DemoService>(DemoService.class, urlWithoutFilter) {
+            @Override
+            protected Result doInvoke(Invocation invocation) throws Throwable {
+                return null;
+            }
+        };
+
+        Invoker<?> invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithoutFilter, REFERENCE_FILTER_KEY, CONSUMER);
+        Assertions.assertTrue(invokerAfterBuild instanceof AbstractInvoker);
+
+        // verify that if LogFilter is configured, LogFilter should exist in the filter chain
+        URL urlWithFilter = URL.valueOf("injvm://127.0.0.1/DemoService")
+            .addParameter(INTERFACE_KEY, DemoService.class.getName())
+            .addParameter(REFERENCE_FILTER_KEY, "log");
+        AbstractInvoker<DemoService> invokerWithFilter = new AbstractInvoker<DemoService>(DemoService.class, urlWithFilter) {
+            @Override
+            protected Result doInvoke(Invocation invocation) throws Throwable {
+                return null;
+            }
+        };
+        invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithFilter, REFERENCE_FILTER_KEY, CONSUMER);
+        Assertions.assertTrue(invokerAfterBuild instanceof FilterChainBuilder.FilterChainNode);
+        Assertions.assertTrue(((FilterChainBuilder.FilterChainNode<?, ?, ?>) invokerAfterBuild).filter instanceof LogFilter);
+
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceImpl.java
similarity index 56%
copy from dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
copy to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceImpl.java
index cd362a3..9ccdd1b 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceImpl.java
@@ -14,33 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.protocol.injvm;
+package org.apache.dubbo.rpc.cluster.filter;
 
-import org.apache.dubbo.rpc.Exporter;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.protocol.AbstractExporter;
 
-import java.util.Map;
+public class DemoServiceImpl implements DemoService{
 
-/**
- * InjvmExporter
- */
-class InjvmExporter<T> extends AbstractExporter<T> {
-
-    private final String key;
-
-    private final Map<String, Exporter<?>> exporterMap;
-
-    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
-        super(invoker);
-        this.key = key;
-        this.exporterMap = exporterMap;
-        exporterMap.put(key, this);
+    @Override
+    public String sayHello(String name) {
+        return name;
     }
 
     @Override
-    public void afterUnExport() {
-        exporterMap.remove(key);
+    public int plus(int a, int b) {
+        return 0;
     }
-
 }
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/LogFilter.java
similarity index 53%
copy from dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
copy to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/LogFilter.java
index cd362a3..d34c1b7 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/LogFilter.java
@@ -14,33 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.protocol.injvm;
+package org.apache.dubbo.rpc.cluster.filter;
 
-import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.protocol.AbstractExporter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.RpcException;
 
-import java.util.Map;
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 
-/**
- * InjvmExporter
- */
-class InjvmExporter<T> extends AbstractExporter<T> {
-
-    private final String key;
 
-    private final Map<String, Exporter<?>> exporterMap;
+@Activate(group = CONSUMER, value = "log")
+public class LogFilter implements Filter, Filter.Listener {
 
-    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
-        super(invoker);
-        this.key = key;
-        this.exporterMap = exporterMap;
-        exporterMap.put(key, this);
+    @Override
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+        return invoker.invoke(invocation);
     }
 
     @Override
-    public void afterUnExport() {
-        exporterMap.remove(key);
+    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
+
     }
 
+    @Override
+    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
+
+    }
 }
diff --git a/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
new file mode 100644
index 0000000..44e3921
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -0,0 +1 @@
+log=org.apache.dubbo.rpc.cluster.filter.LogFilter
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 41266c0..04511e2 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
@@ -570,7 +570,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
         boolean isJvmRefer;
         if (isInjvm() == null) {
             // if an url is specified, don't do local reference
-            if (url != null && url.length() > 0) {
+            if (StringUtils.isNotEmpty(url)) {
                 isJvmRefer = false;
             } else {
                 // by default, reference local service if there is
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 71e5878..45841ad 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
@@ -16,10 +16,12 @@
  */
 package org.apache.dubbo.config;
 
+import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.Version;
 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.utils.ConfigUtils;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
@@ -33,11 +35,16 @@ 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.rpc.Exporter;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.listener.ListenerInvokerWrapper;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.curator.test.TestingServer;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 import org.apache.dubbo.rpc.model.ServiceMetadata;
 import org.apache.dubbo.rpc.model.ServiceRepository;
+import org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker;
+import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -60,6 +67,8 @@ import java.util.stream.Collectors;
 
 
 import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.BROADCAST_CLUSTER;
+import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
@@ -84,8 +93,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.REFER_BACKGROUND
 import static org.apache.dubbo.common.constants.CommonConstants.REFER_ASYNC_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
-
-
+import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_KEY;
 import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
 import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
 import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT;
@@ -97,6 +105,9 @@ import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
 import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE;
 import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL;
 import static org.apache.dubbo.rpc.Constants.DEFAULT_STUB_EVENT;
+import static org.apache.dubbo.rpc.Constants.LOCAL_KEY;
+import static org.apache.dubbo.rpc.Constants.SCOPE_KEY;
+import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -412,6 +423,132 @@ public class ReferenceConfigTest {
     }
 
     @Test
+    public void testShouldJvmRefer() {
+
+        Map<String, String> parameters = new HashMap<>();
+
+        ReferenceConfig<DemoService> referenceConfig = new ReferenceConfig<>();
+
+        // verify that if injvm is configured as true, local references should be made
+        referenceConfig.setInjvm(true);
+        Assertions.assertTrue(referenceConfig.shouldJvmRefer(parameters));
+
+        // verify that if injvm is configured as false, local references should not be made
+        referenceConfig.setInjvm(false);
+        Assertions.assertFalse(referenceConfig.shouldJvmRefer(parameters));
+
+        // verify that if url is configured, local reference should not be made
+        referenceConfig.setInjvm(null);
+        referenceConfig.setUrl("dubbo://127.0.0.1:20880/DemoService");
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        Assertions.assertFalse(referenceConfig.shouldJvmRefer(parameters));
+        parameters.clear();
+
+        // verify that if scope is configured as local, local references should be made
+        referenceConfig.setInjvm(null);
+        referenceConfig.setUrl(null);
+        parameters.put(SCOPE_KEY, SCOPE_LOCAL);
+        Assertions.assertTrue(referenceConfig.shouldJvmRefer(parameters));
+        parameters.clear();
+
+        // verify that if url protocol is configured as injvm, local references should be made
+        referenceConfig.setInjvm(null);
+        referenceConfig.setUrl(null);
+        parameters.put(LOCAL_PROTOCOL, "true");
+        Assertions.assertTrue(referenceConfig.shouldJvmRefer(parameters));
+        parameters.clear();
+
+        // verify that if generic is configured as true, local references should not be made
+        referenceConfig.setInjvm(null);
+        referenceConfig.setUrl(null);
+        parameters.put(GENERIC_KEY, "true");
+        Assertions.assertFalse(referenceConfig.shouldJvmRefer(parameters));
+        parameters.clear();
+
+        // verify that if the service has been exposed, and the cluster is not configured with broadcast, local reference should be made
+        referenceConfig.setInjvm(null);
+        referenceConfig.setUrl(null);
+        ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+        DemoService service = new DemoServiceImpl();
+        URL url = URL.valueOf("dubbo://127.0.0.1/DemoService")
+            .addParameter(INTERFACE_KEY, DemoService.class.getName());
+        parameters.put(INTERFACE_KEY, DemoService.class.getName());
+        Exporter<?> exporter = InjvmProtocol.getInjvmProtocol().export(proxy.getInvoker(service, DemoService.class, url));
+        InjvmProtocol.getInjvmProtocol().getExporterMap().put(DemoService.class.getName(), exporter);
+        Assertions.assertTrue(referenceConfig.shouldJvmRefer(parameters));
+
+        // verify that if the service has been exposed, and the cluster is configured with broadcast, local reference should not be made
+        parameters.put(CLUSTER_KEY, BROADCAST_CLUSTER);
+        Assertions.assertFalse(referenceConfig.shouldJvmRefer(parameters));
+        parameters.clear();
+        InjvmProtocol.getInjvmProtocol().destroy();
+    }
+
+    @Test
+    public void testCreateInvokerForLocalRefer() {
+
+        ReferenceConfig<DemoService> referenceConfig = new ReferenceConfig<>();
+        referenceConfig.setScope(LOCAL_KEY);
+
+        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.init();
+        Assertions.assertTrue(referenceConfig.getInvoker() instanceof ListenerInvokerWrapper);
+        Assertions.assertTrue(((ListenerInvokerWrapper<?>) referenceConfig.getInvoker()).getInvoker() instanceof InjvmInvoker);
+        URL url = ((ListenerInvokerWrapper<?>) referenceConfig.getInvoker()).getInvoker().getUrl();
+        Assertions.assertEquals("application1", url.getParameter("application"));
+        Assertions.assertEquals("value1", url.getParameter("key1"));
+        Assertions.assertEquals("value2", url.getParameter("key2"));
+
+        applicationModelMockedStatic.closeOnDemand();
+        metadataReportInstanceMockedStatic.closeOnDemand();
+    }
+
+    @Test
     @Disabled("Disabled due to Github Actions environment")
     public void testInjvm() throws Exception {
         ApplicationConfig application = new ApplicationConfig();
@@ -426,13 +563,13 @@ public class ReferenceConfigTest {
         protocol.setName("dubbo");
 
         ServiceConfig<DemoService> demoService;
-        demoService = new ServiceConfig<DemoService>();
+        demoService = new ServiceConfig<>();
         demoService.setInterface(DemoService.class);
         demoService.setRef(new DemoServiceImpl());
         demoService.setRegistry(registry);
         demoService.setProtocol(protocol);
 
-        ReferenceConfig<DemoService> rc = new ReferenceConfig<DemoService>();
+        ReferenceConfig<DemoService> rc = new ReferenceConfig<>();
         rc.setRegistry(registry);
         rc.setInterface(DemoService.class.getName());
         rc.setScope(SCOPE_REMOTE);
@@ -466,7 +603,7 @@ public class ReferenceConfigTest {
         RegistryConfig registry = new RegistryConfig();
         registry.setAddress(registryUrl);
         ProtocolConfig protocol = new ProtocolConfig();
-        protocol.setName("mockprotocol");
+        protocol.setName("injvm");
 
         ReferenceConfig<DemoService> rc = new ReferenceConfig<>();
         rc.setRegistry(registry);
@@ -483,23 +620,22 @@ public class ReferenceConfigTest {
         Assertions.assertFalse(success);
         Assertions.assertNull(demoService);
 
-        ServiceConfig<DemoService> sc = new ServiceConfig<>();
-        sc.setInterface(DemoService.class);
-        sc.setRef(new DemoServiceImpl());
-        sc.setRegistry(registry);
-        sc.setProtocol(protocol);
-
         try {
             System.setProperty("java.net.preferIPv4Stack", "true");
-            sc.export();
+            ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+            DemoService service = new DemoServiceImpl();
+            URL url = URL.valueOf("dubbo://127.0.0.1/DemoService")
+                .addParameter(INTERFACE_KEY, DemoService.class.getName());
+            InjvmProtocol.getInjvmProtocol().export(proxy.getInvoker(service, DemoService.class, url));
             demoService = rc.get();
             success = true;
         } catch (Exception e) {
             // ignore
         } finally {
             rc.destroy();
-            sc.unexport();
+            InjvmProtocol.getInjvmProtocol().destroy();
             System.clearProperty("java.net.preferIPv4Stack");
+
         }
         Assertions.assertTrue(success);
         Assertions.assertNotNull(demoService);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java
index a0fea10..14254c7 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java
@@ -102,4 +102,11 @@ public class ListenerInvokerWrapper<T> implements Invoker<T> {
         }
     }
 
+    public Invoker<T> getInvoker() {
+        return invoker;
+    }
+
+    public List<InvokerListener> getListeners() {
+        return listeners;
+    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java
index 26db25b..64bc064 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java
@@ -43,15 +43,15 @@ public abstract class AbstractProtocol implements Protocol {
 
     protected final Logger logger = LoggerFactory.getLogger(getClass());
 
-    protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
+    protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<>();
 
     /**
      * <host:port, ProtocolServer>
      */
     protected final Map<String, ProtocolServer> serverMap = new ConcurrentHashMap<>();
 
-    //TODO SoftReference
-    protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
+    // TODO SoftReference
+    protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<>();
 
     protected static String serviceKey(URL url) {
         int port = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
@@ -82,7 +82,7 @@ public abstract class AbstractProtocol implements Protocol {
                 }
             }
         }
-        for (String key : new ArrayList<String>(exporterMap.keySet())) {
+        for (String key : new ArrayList<>(exporterMap.keySet())) {
             Exporter<?> exporter = exporterMap.remove(key);
             if (exporter != null) {
                 try {
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/CountInvokerListener.java
similarity index 59%
copy from dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
copy to dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/CountInvokerListener.java
index cd362a3..4883380 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/CountInvokerListener.java
@@ -14,33 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.protocol.injvm;
+package org.apache.dubbo.rpc.protocol;
 
-import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.protocol.AbstractExporter;
+import org.apache.dubbo.rpc.InvokerListener;
+import org.apache.dubbo.rpc.RpcException;
 
-import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
-/**
- * InjvmExporter
- */
-class InjvmExporter<T> extends AbstractExporter<T> {
 
-    private final String key;
+public class CountInvokerListener implements InvokerListener {
 
-    private final Map<String, Exporter<?>> exporterMap;
+    private final static AtomicInteger counter = new AtomicInteger(0);
 
-    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
-        super(invoker);
-        this.key = key;
-        this.exporterMap = exporterMap;
-        exporterMap.put(key, this);
+    @Override
+    public void referred(Invoker<?> invoker) throws RpcException {
+        counter.incrementAndGet();
     }
 
     @Override
-    public void afterUnExport() {
-        exporterMap.remove(key);
+    public void destroyed(Invoker<?> invoker) {
+
     }
 
+    public static int getCounter() {
+        return counter.get();
+    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapperTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapperTest.java
new file mode 100644
index 0000000..5f0bbe4
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapperTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.listener.ListenerInvokerWrapper;
+import org.apache.dubbo.rpc.proxy.DemoService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.INVOKER_LISTENER_KEY;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProtocolListenerWrapperTest {
+
+
+    @Test
+    public void testLoadingListenerForLocalReference() {
+        // verify that no listener is loaded by default
+        URL urlWithoutListener = URL.valueOf("injvm://127.0.0.1/DemoService")
+            .addParameter(INTERFACE_KEY, DemoService.class.getName());
+        AbstractInvoker<DemoService> invokerWithoutListener = new AbstractInvoker<DemoService>(DemoService.class, urlWithoutListener) {
+            @Override
+            protected Result doInvoke(Invocation invocation) throws Throwable {
+                return null;
+            }
+        };
+
+        Protocol protocolWithoutListener = mock(Protocol.class);
+        when(protocolWithoutListener.refer(DemoService.class, urlWithoutListener)).thenReturn(invokerWithoutListener);
+
+        ProtocolListenerWrapper protocolListenerWrapperWithoutListener = new ProtocolListenerWrapper(protocolWithoutListener);
+
+        Invoker<?> invoker = protocolListenerWrapperWithoutListener.refer(DemoService.class, urlWithoutListener);
+        Assertions.assertTrue(invoker instanceof ListenerInvokerWrapper);
+        Assertions.assertEquals(0, ((ListenerInvokerWrapper<?>) invoker).getListeners().size());
+
+        // verify that if the invoker.listener is configured, then load the specified listener
+        URL urlWithListener = URL.valueOf("injvm://127.0.0.1/DemoService")
+            .addParameter(INTERFACE_KEY, DemoService.class.getName())
+            .addParameter(INVOKER_LISTENER_KEY, "count");
+        AbstractInvoker<DemoService> invokerWithListener = new AbstractInvoker<DemoService>(DemoService.class, urlWithListener) {
+            @Override
+            protected Result doInvoke(Invocation invocation) throws Throwable {
+                return null;
+            }
+        };
+
+        Protocol protocol = mock(Protocol.class);
+        when(protocol.refer(DemoService.class, urlWithListener)).thenReturn(invokerWithListener);
+
+        ProtocolListenerWrapper protocolListenerWrapper = new ProtocolListenerWrapper(protocol);
+
+        invoker = protocolListenerWrapper.refer(DemoService.class, urlWithListener);
+        Assertions.assertTrue(invoker instanceof ListenerInvokerWrapper);
+        Assertions.assertEquals(1, CountInvokerListener.getCounter());
+    }
+
+
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener b/dubbo-rpc/dubbo-rpc-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener
new file mode 100644
index 0000000..e7baaec
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener
@@ -0,0 +1 @@
+count=org.apache.dubbo.rpc.protocol.CountInvokerListener
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
index cd362a3..bc38844 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
@@ -25,7 +25,7 @@ import java.util.Map;
 /**
  * InjvmExporter
  */
-class InjvmExporter<T> extends AbstractExporter<T> {
+public class InjvmExporter<T> extends AbstractExporter<T> {
 
     private final String key;
 
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
index 04afe85..5aaaed2 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
@@ -42,7 +42,7 @@ import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
 /**
  * InjvmInvoker
  */
-class InjvmInvoker<T> extends AbstractInvoker<T> {
+public class InjvmInvoker<T> extends AbstractInvoker<T> {
 
     private final String key;
 
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
index 2228aed..a35e0b3 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
@@ -51,13 +51,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class InjvmProtocolTest {
 
-    static {
-        InjvmProtocol injvm = InjvmProtocol.getInjvmProtocol();
-    }
 
-    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
-    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
-    private List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
+    private final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+    private final ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    private final List<Exporter<?>> exporters = new ArrayList<>();
 
     @AfterEach
     public void after() throws Exception {
@@ -78,7 +75,7 @@ public class InjvmProtocolTest {
         assertEquals(service.getSize(new String[]{"", "", ""}), 3);
         service.invoke("injvm://127.0.0.1/TestService", "invoke");
 
-        InjvmInvoker injvmInvoker = new InjvmInvoker(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService"), null, new HashMap<String, Exporter<?>>());
+        InjvmInvoker<?> injvmInvoker = new InjvmInvoker<>(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService"), null, new HashMap<>());
         assertFalse(injvmInvoker.isAvailable());
 
     }
@@ -98,7 +95,7 @@ public class InjvmProtocolTest {
     public void testIsInjvmRefer() throws Exception {
         DemoService service = new DemoServiceImpl();
         URL url = URL.valueOf("injvm://127.0.0.1/TestService")
-                .addParameter(INTERFACE_KEY, DemoService.class.getName());
+            .addParameter(INTERFACE_KEY, DemoService.class.getName());
         Exporter<?> exporter = protocol.export(proxy.getInvoker(service, DemoService.class, url));
         exporters.add(exporter);
 
@@ -106,7 +103,7 @@ public class InjvmProtocolTest {
         assertTrue(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
 
         url = url.addParameter(GROUP_KEY, "*")
-                .addParameter(VERSION_KEY, "*");
+            .addParameter(VERSION_KEY, "*");
         assertTrue(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
 
         url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(SCOPE_KEY, SCOPE_LOCAL);
@@ -129,8 +126,8 @@ public class InjvmProtocolTest {
     public void testLocalProtocolAsync() throws Exception {
         DemoService service = new DemoServiceImpl();
         URL url = URL.valueOf("injvm://127.0.0.1/TestService")
-                .addParameter(ASYNC_KEY, true)
-                .addParameter(INTERFACE_KEY, DemoService.class.getName()).addParameter("application", "consumer");
+            .addParameter(ASYNC_KEY, true)
+            .addParameter(INTERFACE_KEY, DemoService.class.getName()).addParameter("application", "consumer");
         Invoker<?> invoker = proxy.getInvoker(service, DemoService.class, url);
         assertTrue(invoker.isAvailable());
         Exporter<?> exporter = protocol.export(invoker);