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/09/28 02:59:07 UTC

[dubbo] branch 3.0 updated: Refactor Injvm invoke (#8926)

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 31c5ee4  Refactor Injvm invoke (#8926)
31c5ee4 is described below

commit 31c5ee4086d7714850124f4b79f292c3443e5784
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Tue Sep 28 10:58:51 2021 +0800

    Refactor Injvm invoke (#8926)
    
    * Refactor Injvm invoke
    
    * Add ut
    
    * Fix import
    
    * Remove deprecated method & fix ut
    
    * Fix Cluster join
---
 .../java/org/apache/dubbo/rpc/cluster/Cluster.java |   2 +-
 .../rpc/cluster/directory/UrlStaticDirectory.java  | 114 ---------
 .../rpc/cluster/support/AvailableCluster.java      |   2 +-
 .../cluster/support/wrapper/AbstractCluster.java   |   8 +-
 .../support/wrapper/MockClusterWrapper.java        |   4 +-
 .../support/wrapper/AbstractClusterTest.java       |   7 +-
 .../com/alibaba/dubbo/rpc/cluster/Cluster.java     |   2 +-
 .../org/apache/dubbo/config/ReferenceConfig.java   |  17 +-
 .../apache/dubbo/config/ReferenceConfigTest.java   |  35 +--
 .../org/apache/dubbo/config/mock/MockCluster.java  |   2 +-
 dubbo-distribution/dubbo-all/pom.xml               |   6 +
 .../apache/dubbo/rpc/cluster/Cluster$Adaptive.java |   5 +-
 .../registry/integration/RegistryDirectory.java    |   2 +-
 .../registry/integration/RegistryProtocol.java     |   2 +-
 .../rpc/protocol/dubbo/DubboProtocolTest.java      |   3 +-
 dubbo-rpc/dubbo-rpc-injvm/pom.xml                  |   8 +-
 .../protocol/injvm/DefaultParamDeepCopyUtil.java   |  65 ++++++
 .../dubbo/rpc/protocol/injvm/InjvmInvoker.java     | 107 ++++++++-
 .../dubbo/rpc/protocol/injvm/InjvmProtocol.java    |  14 +-
 .../rpc/protocol/injvm/ParamDeepCopyUtil.java      |  20 +-
 ...ache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil |   1 +
 .../test/java/demo/MultiClassLoaderService.java    |  15 +-
 .../java/demo/MultiClassLoaderServiceImpl.java     |  32 +--
 .../java/demo/MultiClassLoaderServiceRequest.java  |  15 +-
 .../java/demo/MultiClassLoaderServiceResult.java   |  15 +-
 .../rpc/protocol/injvm/InjvmClassLoaderTest.java   | 254 +++++++++++++++++++++
 .../rpc/protocol/injvm/InjvmDeepCopyTest.java      | 111 +++++++++
 .../rpc/protocol/injvm/InjvmProtocolTest.java      |  15 +-
 .../dubbo/rpc/protocol/injvm/ProtocolTest.java     |   3 +-
 .../dubbo/AbstractHessian2FactoryInitializer.java  |  13 ++
 30 files changed, 655 insertions(+), 244 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java
index d6d4d5e..909693c 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java
@@ -45,7 +45,7 @@ public interface Cluster {
      * @throws RpcException
      */
     @Adaptive
-    <T> Invoker<T> join(Directory<T> directory) throws RpcException;
+    <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException;
 
     static Cluster getCluster(ScopeModel scopeModel, String name) {
         return getCluster(scopeModel, name, true);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/UrlStaticDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/UrlStaticDirectory.java
deleted file mode 100644
index 37bf1eb..0000000
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/UrlStaticDirectory.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.rpc.cluster.directory;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.RouterChain;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * copy StaticDirectory for rpc protocol url direct
- */
-public class UrlStaticDirectory<T> extends AbstractDirectory<T> {
-    private static final Logger logger = LoggerFactory.getLogger(UrlStaticDirectory.class);
-
-    private final List<Invoker<T>> invokers;
-
-    public UrlStaticDirectory(List<Invoker<T>> invokers) {
-        this(null, invokers, null);
-    }
-
-    public UrlStaticDirectory(List<Invoker<T>> invokers, RouterChain<T> routerChain) {
-        this(null, invokers, routerChain);
-    }
-
-    public UrlStaticDirectory(URL url, List<Invoker<T>> invokers) {
-        this(url, invokers, null);
-    }
-
-    public UrlStaticDirectory(URL url, List<Invoker<T>> invokers, RouterChain<T> routerChain) {
-        super(url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain, false);
-        if (CollectionUtils.isEmpty(invokers)) {
-            throw new IllegalArgumentException("invokers == null");
-        }
-        this.invokers = invokers;
-    }
-
-    @Override
-    public Class<T> getInterface() {
-        return invokers.get(0).getInterface();
-    }
-
-    @Override
-    public List<Invoker<T>> getAllInvokers() {
-        return invokers;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        if (isDestroyed()) {
-            return false;
-        }
-        for (Invoker<T> invoker : invokers) {
-            if (invoker.isAvailable()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void destroy() {
-        if (isDestroyed()) {
-            return;
-        }
-        super.destroy();
-        for (Invoker<T> invoker : invokers) {
-            invoker.destroy();
-        }
-        invokers.clear();
-    }
-
-    public void buildRouterChain() {
-        RouterChain<T> routerChain = RouterChain.buildChain(getUrl());
-        routerChain.setInvokers(invokers);
-        routerChain.loop(true);
-        this.setRouterChain(routerChain);
-    }
-
-    @Override
-    protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
-        List<Invoker<T>> finalInvokers = invokers;
-        if (routerChain != null) {
-            try {
-                finalInvokers = routerChain.route(getConsumerUrl(), invocation);
-            } catch (Throwable t) {
-                logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
-            }
-        }
-        return finalInvokers == null ? Collections.emptyList() : finalInvokers;
-    }
-
-}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableCluster.java
index 6fcc03c..d6c0163 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableCluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableCluster.java
@@ -30,7 +30,7 @@ public class AvailableCluster implements Cluster {
     public static final String NAME = "available";
 
     @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+    public <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException {
         return new AvailableClusterInvoker<>(directory);
     }
 
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 923ca4e..968ef3e 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
@@ -28,7 +28,6 @@ import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.ClusterInvoker;
 import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
-import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder;
 import org.apache.dubbo.rpc.cluster.filter.InvocationInterceptorBuilder;
 import org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor;
@@ -54,11 +53,12 @@ public abstract class AbstractCluster implements Cluster {
     }
 
     @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        if (directory instanceof StaticDirectory) {
+    public <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException {
+        if (buildFilterChain) {
+            return buildClusterInterceptors(doJoin(directory));
+        } else {
             return doJoin(directory);
         }
-        return buildClusterInterceptors(doJoin(directory));
     }
 
     private <T> AbstractClusterInvoker<T> buildInterceptorInvoker(AbstractClusterInvoker<T> invoker) {
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 686474f..4f8be0a 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
@@ -34,9 +34,9 @@ public class MockClusterWrapper implements Cluster {
     }
 
     @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+    public <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException {
         return new MockClusterInvoker<T>(directory,
-                this.cluster.join(directory));
+                this.cluster.join(directory, buildFilterChain));
     }
 
     public Cluster getCluster() {
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
index e98d2ba..9eccf0a 100644
--- 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
@@ -28,6 +28,7 @@ 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;
 
@@ -65,7 +66,7 @@ public class AbstractClusterTest {
         when(directory.getUrl()).thenReturn(url);
         when(directory.getConsumerUrl()).thenReturn(consumerUrl);
         DemoCluster demoCluster = new DemoCluster();
-        Invoker<?> invoker = demoCluster.join(directory);
+        Invoker<?> invoker = demoCluster.join(directory, true);
         Assertions.assertTrue(invoker instanceof AbstractCluster.ClusterFilterInvoker);
         Assertions.assertTrue(((AbstractCluster.ClusterFilterInvoker<?>) invoker).getFilterInvoker()
             instanceof FilterChainBuilder.ClusterFilterChainNode);
@@ -75,8 +76,8 @@ public class AbstractClusterTest {
 
     static class DemoCluster extends AbstractCluster {
         @Override
-        public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-            return super.join(directory);
+        public <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException {
+            return super.join(directory, buildFilterChain);
         }
 
         @Override
diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
index e17ebc1..13c9629 100644
--- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
+++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
@@ -27,7 +27,7 @@ public interface Cluster extends org.apache.dubbo.rpc.cluster.Cluster {
             com.alibaba.dubbo.rpc.RpcException;
 
     @Override
-    default <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+    default <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException {
         return null;
     }
 }
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 d005e38..43ab5f5 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
@@ -37,7 +37,6 @@ import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
-import org.apache.dubbo.rpc.cluster.directory.UrlStaticDirectory;
 import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
 import org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster;
 import org.apache.dubbo.rpc.model.ApplicationModel;
@@ -52,6 +51,7 @@ import org.apache.dubbo.rpc.support.ProtocolUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -403,7 +403,12 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
         URL url = new ServiceConfigURL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(referenceParameters);
         url = url.setScopeModel(getScopeModel());
         url = url.setServiceModel(consumerModel);
-        invoker = protocolSPI.refer(interfaceClass, url);
+        Invoker<?> withFilter = protocolSPI.refer(interfaceClass, url);
+
+        // Local Invoke ( Support Cluster Filter / Filter )
+        StaticDirectory<?> directory = new StaticDirectory<>(url, Collections.singletonList(withFilter));
+        invoker = Cluster.getCluster(url.getScopeModel(), Cluster.DEFAULT).join(directory, true);
+
         if (logger.isInfoEnabled()) {
             logger.info("Using in jvm service " + interfaceClass.getName());
         }
@@ -470,7 +475,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
             if (!UrlUtils.isRegistry(curUrl)){
                 List<Invoker<?>> invokers = new ArrayList<>();
                 invokers.add(invoker);
-                invoker = Cluster.getCluster(scopeModel, Cluster.DEFAULT).join(new UrlStaticDirectory(curUrl,invokers));
+                invoker = Cluster.getCluster(scopeModel, Cluster.DEFAULT).join(new StaticDirectory(curUrl, invokers), true);
             }
         } else {
             List<Invoker<?>> invokers = new ArrayList<>();
@@ -492,7 +497,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
                 String cluster = registryUrl.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
                 // The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker
                 // (RegistryDirectory, routing happens here) -> Invoker
-                invoker = Cluster.getCluster(registryUrl.getScopeModel(), cluster, false).join(new StaticDirectory(registryUrl, invokers));
+                invoker = Cluster.getCluster(registryUrl.getScopeModel(), cluster, false).join(new StaticDirectory(registryUrl, invokers), false);
             } else {
                 // not a registry url, must be direct invoke.
                 if (CollectionUtils.isEmpty(invokers)) {
@@ -500,7 +505,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
                 }
                 URL curUrl = invokers.get(0).getUrl();
                 String cluster = curUrl.getParameter(CLUSTER_KEY, Cluster.DEFAULT);
-                invoker = Cluster.getCluster(scopeModel, cluster).join(new UrlStaticDirectory(curUrl, invokers));
+                invoker = Cluster.getCluster(scopeModel, cluster).join(new StaticDirectory(curUrl, invokers), true);
             }
         }
     }
@@ -589,7 +594,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
                 isJvmRefer = false;
             } else {
                 // by default, reference local service if there is
-                isJvmRefer = InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl);
+                isJvmRefer = InjvmProtocol.getInjvmProtocol(getScopeModel()).isInjvmRefer(tmpUrl);
             }
         } else {
             isJvmRefer = isInjvm();
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 5f094b0..5d7b42c 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,13 +16,6 @@
  */
 package org.apache.dubbo.config;
 
-import demo.MultiClassLoaderService;
-import demo.MultiClassLoaderServiceImpl;
-import demo.MultiClassLoaderServiceRequest;
-import demo.MultiClassLoaderServiceResult;
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.NotFoundException;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.compiler.support.CtClassBuilder;
@@ -42,8 +35,10 @@ import org.apache.dubbo.registry.client.migration.MigrationInvoker;
 import org.apache.dubbo.registrycenter.RegistryCenter;
 import org.apache.dubbo.registrycenter.ZookeeperSingleRegistryCenter;
 import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareClusterInvoker;
+import org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker;
 import org.apache.dubbo.rpc.listener.ListenerInvokerWrapper;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -51,6 +46,14 @@ import org.apache.dubbo.rpc.model.ModuleModel;
 import org.apache.dubbo.rpc.model.ServiceMetadata;
 import org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker;
 import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol;
+
+import demo.MultiClassLoaderService;
+import demo.MultiClassLoaderServiceImpl;
+import demo.MultiClassLoaderServiceRequest;
+import demo.MultiClassLoaderServiceResult;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.NotFoundException;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
@@ -456,15 +459,15 @@ public class ReferenceConfigTest {
         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);
+        Exporter<?> exporter = InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).export(proxy.getInvoker(service, DemoService.class, url));
+        InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).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();
+        InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).destroy();
     }
 
     @Test
@@ -490,9 +493,11 @@ public class ReferenceConfigTest {
             .initialize();
 
         referenceConfig.init();
-        Assertions.assertTrue(referenceConfig.getInvoker() instanceof ListenerInvokerWrapper);
-        Assertions.assertTrue(((ListenerInvokerWrapper<?>) referenceConfig.getInvoker()).getInvoker() instanceof InjvmInvoker);
-        URL url = ((ListenerInvokerWrapper<?>) referenceConfig.getInvoker()).getInvoker().getUrl();
+        Assertions.assertTrue(referenceConfig.getInvoker() instanceof MockClusterInvoker);
+        Invoker<?> withFilter = ((MockClusterInvoker<?>) referenceConfig.getInvoker()).getDirectory().getAllInvokers().get(0);
+        Assertions.assertTrue(withFilter instanceof ListenerInvokerWrapper);
+        Assertions.assertTrue(((ListenerInvokerWrapper<?>) withFilter).getInvoker() instanceof InjvmInvoker);
+        URL url = withFilter.getUrl();
         Assertions.assertEquals("application1", url.getParameter("application"));
         Assertions.assertEquals("value1", url.getParameter("key1"));
         Assertions.assertEquals("value2", url.getParameter("key2"));
@@ -714,14 +719,14 @@ public class ReferenceConfigTest {
             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));
+            InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).export(proxy.getInvoker(service, DemoService.class, url));
             demoService = rc.get();
             success = true;
         } catch (Exception e) {
             // ignore
         } finally {
             rc.destroy();
-            InjvmProtocol.getInjvmProtocol().destroy();
+            InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).destroy();
             System.clearProperty("java.net.preferIPv4Stack");
 
         }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
index 39bf974..3b5b2cc 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
@@ -23,7 +23,7 @@ import org.apache.dubbo.rpc.cluster.Directory;
 
 public class MockCluster implements Cluster {
     @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+    public <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException {
         return null;
     }
 }
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index 164115b..b51d77b 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -795,6 +795,12 @@
                                         META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer
                                     </resource>
                                 </transformer>
+                                <transformer
+                                    implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil
+                                    </resource>
+                                </transformer>
                             </transformers>
                             <filters>
                                 <filter>
diff --git a/dubbo-native/src/main/java/org/apache/dubbo/rpc/cluster/Cluster$Adaptive.java b/dubbo-native/src/main/java/org/apache/dubbo/rpc/cluster/Cluster$Adaptive.java
index a266c82..7048752 100644
--- a/dubbo-native/src/main/java/org/apache/dubbo/rpc/cluster/Cluster$Adaptive.java
+++ b/dubbo-native/src/main/java/org/apache/dubbo/rpc/cluster/Cluster$Adaptive.java
@@ -18,7 +18,8 @@ package org.apache.dubbo.rpc.cluster;
 import org.apache.dubbo.rpc.model.ScopeModel;
 import org.apache.dubbo.rpc.model.ScopeModelUtil;
 public class Cluster$Adaptive implements org.apache.dubbo.rpc.cluster.Cluster {
-public org.apache.dubbo.rpc.Invoker join(org.apache.dubbo.rpc.cluster.Directory arg0) throws org.apache.dubbo.rpc.RpcException {
+@Override
+public org.apache.dubbo.rpc.Invoker join(org.apache.dubbo.rpc.cluster.Directory arg0, boolean buildFilterChain) throws org.apache.dubbo.rpc.RpcException {
 if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.cluster.Directory argument == null");
 if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.cluster.Directory argument getUrl() == null");
 org.apache.dubbo.common.URL url = arg0.getUrl();
@@ -26,7 +27,7 @@ String extName = url.getParameter("cluster", "failover");
 if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.cluster.Cluster) name from url (" + url.toString() + ") use keys([cluster])");
 ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.cluster.Cluster.class);
 org.apache.dubbo.rpc.cluster.Cluster extension = (org.apache.dubbo.rpc.cluster.Cluster)scopeModel.getExtensionLoader(org.apache.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
-return extension.join(arg0);
+return extension.join(arg0, buildFilterChain);
 }
 public org.apache.dubbo.rpc.cluster.Cluster getCluster(java.lang.String arg0)  {
 throw new UnsupportedOperationException("The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(java.lang.String) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!");
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 d1badc5..2257245 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
@@ -281,7 +281,7 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
             for (List<Invoker<T>> groupList : groupMap.values()) {
                 StaticDirectory<T> staticDirectory = new StaticDirectory<>(groupList);
                 staticDirectory.buildRouterChain();
-                mergedInvokers.add(cluster.join(staticDirectory));
+                mergedInvokers.add(cluster.join(staticDirectory, false));
             }
         } else {
             mergedInvokers = 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 404c679..d9bb2f9 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
@@ -556,7 +556,7 @@ public class RegistryProtocol implements Protocol, ScopeModelAware {
         directory.buildRouterChain(urlToRegistry);
         directory.subscribe(toSubscribeUrl(urlToRegistry));
 
-        return (ClusterInvoker<T>) cluster.join(directory);
+        return (ClusterInvoker<T>) cluster.join(directory, true);
     }
 
     public <T> void reRefer(ClusterInvoker<?> invoker, URL newSubscribeUrl) {
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
index 99a8da2..d16b209 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
@@ -38,6 +38,7 @@ import org.apache.dubbo.rpc.protocol.dubbo.support.RemoteService;
 import org.apache.dubbo.rpc.protocol.dubbo.support.RemoteServiceImpl;
 import org.apache.dubbo.rpc.protocol.dubbo.support.Type;
 import org.apache.dubbo.rpc.service.EchoService;
+
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
@@ -242,7 +243,7 @@ public class DubboProtocolTest {
         Mockito.when(dic.getConsumerUrl()).thenReturn(url);
 
         FailfastCluster cluster = new FailfastCluster();
-        Invoker<DemoService> clusterInvoker = cluster.join(dic);
+        Invoker<DemoService> clusterInvoker = cluster.join(dic, true);
         Result result = clusterInvoker.invoke(invocation);
         Thread.sleep(10);
         assertEquals(result.getValue(), "consumer");
diff --git a/dubbo-rpc/dubbo-rpc-injvm/pom.xml b/dubbo-rpc/dubbo-rpc-injvm/pom.xml
index b9f8661..3346ed0 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-injvm/pom.xml
@@ -40,5 +40,11 @@
             <artifactId>dubbo-cluster</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-serialization-hessian2</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java
new file mode 100644
index 0000000..c2af281
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java
@@ -0,0 +1,65 @@
+/*
+ * 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.injvm;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.remoting.Constants;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class DefaultParamDeepCopyUtil implements ParamDeepCopyUtil {
+    private static final Logger logger = LoggerFactory.getLogger(DefaultParamDeepCopyUtil.class);
+
+    public final static String NAME = "default";
+
+    @Override
+    @SuppressWarnings({"unchecked"})
+    public <T> T copy(URL url, Object src, Class<T> targetClass) {
+        Serialization serialization = url.getOrDefaultFrameworkModel().getExtensionLoader(Serialization.class).getExtension(
+            url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
+
+        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+            ObjectOutput objectOutput = serialization.serialize(url, outputStream);
+            objectOutput.writeObject(src);
+            objectOutput.flushBuffer();
+
+            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray())) {
+                ObjectInput objectInput = serialization.deserialize(url, inputStream);
+                return objectInput.readObject(targetClass);
+            } catch (ClassNotFoundException | IOException e) {
+                logger.error("Unable to deep copy parameter to target class.", e);
+            }
+
+        } catch (IOException e) {
+            logger.error("Unable to deep copy parameter to target class.", e);
+        }
+
+
+        if (src.getClass().equals(targetClass)) {
+            return (T) src;
+        } else {
+            return null;
+        }
+    }
+}
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 33a0775..42dbfdb 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
@@ -18,6 +18,8 @@ package org.apache.dubbo.rpc.protocol.injvm;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
+import org.apache.dubbo.common.utils.ArrayUtils;
+import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Constants;
@@ -25,12 +27,17 @@ import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.FutureContext;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.InvokeMode;
+import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.model.MethodDescriptor;
+import org.apache.dubbo.rpc.model.ServiceModel;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
 
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
@@ -49,11 +56,15 @@ public class InjvmInvoker<T> extends AbstractInvoker<T> {
 
     private final ExecutorRepository executorRepository;
 
+    private final ParamDeepCopyUtil paramDeepCopyUtil;
+
     InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) {
         super(type, url);
         this.key = key;
         this.exporterMap = exporterMap;
         this.executorRepository = url.getOrDefaultApplicationModel().getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+        this.paramDeepCopyUtil = url.getOrDefaultFrameworkModel().getExtensionLoader(ParamDeepCopyUtil.class)
+            .getExtension(url.getParameter("injvm-copy-util", DefaultParamDeepCopyUtil.NAME));
     }
 
     @Override
@@ -74,30 +85,114 @@ public class InjvmInvoker<T> extends AbstractInvoker<T> {
         }
         RpcContext.getServiceContext().setRemoteAddress(LOCALHOST_VALUE, 0);
         // Solve local exposure, the server opens the token, and the client call fails.
-        URL serverURL = exporter.getInvoker().getUrl();
+        Invoker<?> invoker = exporter.getInvoker();
+        URL serverURL = invoker.getUrl();
         boolean serverHasToken = serverURL.hasParameter(Constants.TOKEN_KEY);
         if (serverHasToken) {
             invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY));
         }
-        if (isAsync(exporter.getInvoker().getUrl(), getUrl())) {
-            ((RpcInvocation) invocation).setInvokeMode(InvokeMode.ASYNC);
+
+        String desc = ReflectUtils.getDesc(invocation.getParameterTypes());
+
+        // recreate invocation ---> deep copy parameters
+        Invocation copiedInvocation = recreateInvocation(invocation, invoker, desc);
+
+        if (isAsync(invoker.getUrl(), getUrl())) {
+            ((RpcInvocation) copiedInvocation).setInvokeMode(InvokeMode.ASYNC);
             // use consumer executor
             ExecutorService executor = executorRepository.createExecutorIfAbsent(getUrl());
             CompletableFuture<AppResponse> appResponseFuture = CompletableFuture.supplyAsync(() -> {
-                Result result = exporter.getInvoker().invoke(invocation);
+                Result result = invoker.invoke(copiedInvocation);
                 if (result.hasException()) {
                     return new AppResponse(result.getException());
                 } else {
+                    rebuildValue(invocation, desc, result);
                     return new AppResponse(result.getValue());
                 }
             }, executor);
             // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
             FutureContext.getContext().setCompatibleFuture(appResponseFuture);
-            AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, invocation);
+            AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, copiedInvocation);
             result.setExecutor(executor);
             return result;
         } else {
-            return exporter.getInvoker().invoke(invocation);
+            Result result = invoker.invoke(copiedInvocation);
+            if (result.hasException()) {
+                return result;
+            } else {
+                rebuildValue(invocation, desc, result);
+                return result;
+            }
+        }
+    }
+
+    private Class<?> getReturnType(ServiceModel consumerServiceModel, String methodName, String desc) {
+        MethodDescriptor consumerMethod = consumerServiceModel.getServiceModel().getMethod(methodName, desc);
+        if (consumerMethod != null) {
+            Type[] returnTypes = consumerMethod.getReturnTypes();
+            if (ArrayUtils.isNotEmpty(returnTypes)) {
+                return (Class<?>) returnTypes[0];
+            }
+        }
+        return null;
+    }
+
+    private Invocation recreateInvocation(Invocation invocation, Invoker<?> invoker, String desc) {
+        ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader();
+
+        ServiceModel providerServiceModel = invoker.getUrl().getServiceModel();
+
+        if (providerServiceModel == null) {
+            return invocation;
+        }
+        MethodDescriptor providerMethod = providerServiceModel.getServiceModel().getMethod(invocation.getMethodName(), desc);
+        Object[] realArgument = null;
+        if (providerMethod != null) {
+            Class<?>[] pts = providerMethod.getParameterClasses();
+            Object[] args = invocation.getArguments();
+
+            // switch ClassLoader
+            Thread.currentThread().setContextClassLoader(providerServiceModel.getClassLoader());
+
+            try {
+                // copy parameters
+                if (pts != null && args != null && pts.length == args.length) {
+                    realArgument = new Object[pts.length];
+                    for (int i = 0; i < pts.length; i++) {
+                        realArgument[i] = paramDeepCopyUtil.copy(getUrl(), args[i], pts[i]);
+                    }
+                }
+                if (realArgument == null) {
+                    realArgument = args;
+                }
+
+                return new RpcInvocation(invocation.getServiceModel(), invocation.getMethodName(), invocation.getServiceName(), invocation.getProtocolServiceKey(),
+                    pts, realArgument, new LinkedHashMap<>(invocation.getObjectAttachments()),
+                    invocation.getInvoker(), invocation.getAttributes());
+            } finally {
+                Thread.currentThread().setContextClassLoader(originClassLoader);
+            }
+        } else {
+            return invocation;
+        }
+    }
+
+    private void rebuildValue(Invocation invocation, String desc, Result result) {
+        Object originValue = result.getValue();
+        Object value = originValue;
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        try {
+            ServiceModel consumerServiceModel = getUrl().getServiceModel();
+            if (consumerServiceModel != null) {
+                Class<?> returnType = getReturnType(consumerServiceModel, invocation.getMethodName(), desc);
+                if (returnType != null) {
+                    Thread.currentThread().setContextClassLoader(consumerServiceModel.getClassLoader());
+                    value = paramDeepCopyUtil.copy(getUrl(), originValue, returnType);
+                }
+            }
+            result.setValue(value);
+        } finally {
+            Thread.currentThread().setContextClassLoader(cl);
         }
     }
 
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
index a61220d..02db886 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
@@ -17,13 +17,13 @@
 package org.apache.dubbo.rpc.protocol.injvm;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.ScopeModel;
 import org.apache.dubbo.rpc.protocol.AbstractProtocol;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
 
@@ -45,17 +45,9 @@ public class InjvmProtocol extends AbstractProtocol implements Protocol {
     public static final String NAME = LOCAL_PROTOCOL;
 
     public static final int DEFAULT_PORT = 0;
-    private static InjvmProtocol INSTANCE;
 
-    public InjvmProtocol() {
-        INSTANCE = this;
-    }
-
-    public static InjvmProtocol getInjvmProtocol() {
-        if (INSTANCE == null) {
-            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(InjvmProtocol.NAME); // load
-        }
-        return INSTANCE;
+    public static InjvmProtocol getInjvmProtocol(ScopeModel scopeModel) {
+        return (InjvmProtocol) scopeModel.getExtensionLoader(Protocol.class).getExtension(InjvmProtocol.NAME, false);
     }
 
     static Exporter<?> getExporter(Map<String, Exporter<?>> map, URL key) {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/ParamDeepCopyUtil.java
similarity index 64%
copy from dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
copy to dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/ParamDeepCopyUtil.java
index 39bf974..c76a5a1 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/ParamDeepCopyUtil.java
@@ -6,7 +6,7 @@
  * (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
+ *     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,
@@ -14,16 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.mock;
+package org.apache.dubbo.rpc.protocol.injvm;
 
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.Directory;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
 
-public class MockCluster implements Cluster {
-    @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        return null;
-    }
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface ParamDeepCopyUtil {
+
+    <T> T copy(URL url, Object src, Class<T> targetClass);
 }
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil b/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil
new file mode 100644
index 0000000..59e42b8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil
@@ -0,0 +1 @@
+default=org.apache.dubbo.rpc.protocol.injvm.DefaultParamDeepCopyUtil
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderService.java
similarity index 64%
copy from dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
copy to dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderService.java
index 39bf974..5f77227 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderService.java
@@ -6,7 +6,7 @@
  * (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
+ *     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,
@@ -14,16 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.mock;
+package demo;
 
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.Directory;
+public interface MultiClassLoaderService {
 
-public class MockCluster implements Cluster {
-    @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        return null;
-    }
+    Object call(MultiClassLoaderServiceRequest innerRequest);
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceImpl.java
similarity index 51%
copy from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
copy to dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceImpl.java
index 686474f..b315aa2 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceImpl.java
@@ -14,32 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.support.wrapper;
+package demo;
 
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.Directory;
+import java.util.concurrent.atomic.AtomicReference;
 
-/**
- * mock impl
- *
- */
-public class MockClusterWrapper implements Cluster {
-
-    private final Cluster cluster;
+public class MultiClassLoaderServiceImpl implements MultiClassLoaderService {
+    private AtomicReference<MultiClassLoaderServiceRequest> innerRequestReference;
+    private AtomicReference<MultiClassLoaderServiceResult> innerResultReference;
 
-    public MockClusterWrapper(Cluster cluster) {
-        this.cluster = cluster;
+    public MultiClassLoaderServiceImpl(AtomicReference<MultiClassLoaderServiceRequest> innerRequestReference, AtomicReference<MultiClassLoaderServiceResult> innerResultReference) {
+        this.innerRequestReference = innerRequestReference;
+        this.innerResultReference = innerResultReference;
     }
 
     @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        return new MockClusterInvoker<T>(directory,
-                this.cluster.join(directory));
-    }
-
-    public Cluster getCluster() {
-        return cluster;
+    public MultiClassLoaderServiceResult call(MultiClassLoaderServiceRequest innerRequest) {
+        innerRequestReference.set(innerRequest);
+        return innerResultReference.get();
     }
 }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceRequest.java
similarity index 64%
copy from dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
copy to dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceRequest.java
index 39bf974..9dd67ca 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceRequest.java
@@ -6,7 +6,7 @@
  * (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
+ *     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,
@@ -14,16 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.mock;
+package demo;
 
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.Directory;
+import java.io.Serializable;
 
-public class MockCluster implements Cluster {
-    @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        return null;
-    }
+public class MultiClassLoaderServiceRequest implements Serializable {
 }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceResult.java
similarity index 64%
copy from dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
copy to dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceResult.java
index 39bf974..c4ed2aa 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceResult.java
@@ -6,7 +6,7 @@
  * (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
+ *     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,
@@ -14,16 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.mock;
+package demo;
 
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.Directory;
+import java.io.Serializable;
 
-public class MockCluster implements Cluster {
-    @Override
-    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
-        return null;
-    }
+public class MultiClassLoaderServiceResult implements Serializable {
 }
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java
new file mode 100644
index 0000000..04e5b3b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java
@@ -0,0 +1,254 @@
+/*
+ * 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.injvm;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.compiler.support.CtClassBuilder;
+import org.apache.dubbo.common.compiler.support.JavassistCompiler;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ConsumerModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+
+import demo.MultiClassLoaderService;
+import demo.MultiClassLoaderServiceImpl;
+import demo.MultiClassLoaderServiceRequest;
+import demo.MultiClassLoaderServiceResult;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class InjvmClassLoaderTest {
+    @Test
+    public void testDifferentClassLoaderRequest() throws Exception {
+        String basePath = DemoService.class.getProtectionDomain().getCodeSource().getLocation().getFile();
+        basePath = java.net.URLDecoder.decode(basePath, "UTF-8");
+        TestClassLoader1 classLoader1 = new TestClassLoader1(basePath);
+        TestClassLoader1 classLoader2 = new TestClassLoader1(basePath);
+        TestClassLoader2 classLoader3 = new TestClassLoader2(classLoader2, basePath);
+
+        ApplicationConfig applicationConfig = new ApplicationConfig("TestApp");
+        ApplicationModel applicationModel = new ApplicationModel(FrameworkModel.defaultModel());
+        applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
+        ModuleModel moduleModel = new ModuleModel(applicationModel);
+
+        Class clazz1 = classLoader1.loadClass(MultiClassLoaderService.class.getName(), false);
+        Class<?> clazz1impl = classLoader1.loadClass(MultiClassLoaderServiceImpl.class.getName(), false);
+        Class<?> requestClazzCustom1 = compileCustomRequest(classLoader1);
+        Class<?> resultClazzCustom1 = compileCustomResult(classLoader1);
+        classLoader1.loadedClass.put(requestClazzCustom1.getName(), requestClazzCustom1);
+        classLoader1.loadedClass.put(resultClazzCustom1.getName(), resultClazzCustom1);
+
+        // AtomicReference to cache request/response of provider
+        AtomicReference innerRequestReference = new AtomicReference();
+        AtomicReference innerResultReference = new AtomicReference();
+        innerResultReference.set(resultClazzCustom1.newInstance());
+        Constructor<?> declaredConstructor = clazz1impl.getDeclaredConstructor(AtomicReference.class, AtomicReference.class);
+
+
+        // export provider
+        ProxyFactory proxyFactory = moduleModel.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
+        Protocol protocol = moduleModel.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+        Object providerInstance = declaredConstructor.newInstance(innerRequestReference, innerResultReference);
+
+        URL url = URL.valueOf("injvm://localhost:0/" + MultiClassLoaderServiceImpl.class.getName() + "?interface=" + MultiClassLoaderServiceImpl.class.getName());
+        ServiceDescriptor providerServiceDescriptor = moduleModel.getServiceRepository().registerService(clazz1);
+        ProviderModel providerModel = new ProviderModel(
+            url.getServiceKey(),
+            providerInstance,
+            providerServiceDescriptor,
+            null,
+            null);
+        providerModel.setClassLoader(classLoader1);
+
+        URL providerUrl = url.setScopeModel(moduleModel).setServiceModel(providerModel);
+        Invoker invoker = proxyFactory.getInvoker(providerInstance, clazz1, providerUrl);
+        Exporter<?> exporter = protocol.export(invoker);
+
+        Class<?> clazz2 = classLoader2.loadClass(MultiClassLoaderService.class.getName(), false);
+        Class<?> requestClazzOrigin = classLoader2.loadClass(MultiClassLoaderServiceRequest.class.getName(), false);
+        Class<?> requestClazzCustom2 = compileCustomRequest(classLoader2);
+        Class<?> resultClazzCustom3 = compileCustomResult(classLoader3);
+        classLoader2.loadedClass.put(requestClazzCustom2.getName(), requestClazzCustom2);
+        classLoader3.loadedClass.put(resultClazzCustom3.getName(), resultClazzCustom3);
+
+        // refer consumer
+        ServiceDescriptor consumerServiceDescriptor = moduleModel.getServiceRepository().registerService(clazz2);
+        ConsumerModel consumerModel = new ConsumerModel(clazz2.getName(), null, consumerServiceDescriptor, null,
+            ApplicationModel.defaultModel().getDefaultModule(), null, null);
+        consumerModel.setClassLoader(classLoader3);
+        URL consumerUrl = url.setScopeModel(moduleModel).setServiceModel(consumerModel);
+
+        Object object1 = proxyFactory.getProxy(protocol.refer(clazz2, consumerUrl));
+
+        java.lang.reflect.Method callBean1 = object1.getClass().getDeclaredMethod("call", requestClazzOrigin);
+        callBean1.setAccessible(true);
+        Object result1 = callBean1.invoke(object1, requestClazzCustom2.newInstance());
+
+        // invoke result should load from classLoader3 ( sub classLoader of classLoader2 --> consumer side classLoader)
+        Assertions.assertEquals(resultClazzCustom3, result1.getClass());
+        Assertions.assertNotEquals(classLoader2, result1.getClass().getClassLoader());
+
+        // invoke reqeust param should load from classLoader1 ( provider side classLoader )
+        Assertions.assertEquals(classLoader1, innerRequestReference.get().getClass().getClassLoader());
+
+        exporter.unexport();
+        applicationModel.destroy();
+    }
+
+    private Class<?> compileCustomRequest(ClassLoader classLoader) throws NotFoundException, CannotCompileException {
+        CtClassBuilder builder = new CtClassBuilder();
+        builder.setClassName(MultiClassLoaderServiceRequest.class.getName() + "A");
+        builder.setSuperClassName(MultiClassLoaderServiceRequest.class.getName());
+        CtClass cls = builder.build(classLoader);
+        return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
+    }
+
+    private Class<?> compileCustomResult(ClassLoader classLoader) throws NotFoundException, CannotCompileException {
+        CtClassBuilder builder = new CtClassBuilder();
+        builder.setClassName(MultiClassLoaderServiceResult.class.getName() + "A");
+        builder.setSuperClassName(MultiClassLoaderServiceResult.class.getName());
+        CtClass cls = builder.build(classLoader);
+        return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
+    }
+
+    private static class TestClassLoader1 extends ClassLoader {
+        private String basePath;
+
+        public TestClassLoader1(String basePath) {
+            this.basePath = basePath;
+        }
+
+        Map<String, Class<?>> loadedClass = new ConcurrentHashMap<>();
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            try {
+                byte[] bytes = loadClassData(name);
+                return defineClass(name, bytes, 0, bytes.length);
+            } catch (Exception e) {
+                throw new ClassNotFoundException();
+            }
+        }
+
+        @Override
+        public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (loadedClass.containsKey(name)) {
+                return loadedClass.get(name);
+            }
+            if (name.startsWith("demo")) {
+                Class<?> aClass = this.findClass(name);
+                this.loadedClass.put(name, aClass);
+                if (resolve) {
+                    this.resolveClass(aClass);
+                }
+                return aClass;
+            } else {
+                Class<?> loadedClass = this.findLoadedClass(name);
+                if (loadedClass != null) {
+                    return loadedClass;
+                } else {
+                    return super.loadClass(name, resolve);
+                }
+            }
+        }
+
+
+        public byte[] loadClassData(String className) throws IOException {
+            className = className.replaceAll("\\.", "/");
+            String path = basePath + File.separator + className + ".class";
+            FileInputStream fileInputStream;
+            byte[] classBytes;
+            fileInputStream = new FileInputStream(path);
+            int length = fileInputStream.available();
+            classBytes = new byte[length];
+            fileInputStream.read(classBytes);
+            fileInputStream.close();
+            return classBytes;
+        }
+    }
+
+    private static class TestClassLoader2 extends ClassLoader {
+        private String basePath;
+        private TestClassLoader1 testClassLoader;
+
+        Map<String, Class<?>> loadedClass = new ConcurrentHashMap<>();
+
+        public TestClassLoader2(TestClassLoader1 testClassLoader, String basePath) {
+            this.testClassLoader = testClassLoader;
+            this.basePath = basePath;
+        }
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            try {
+                byte[] bytes = loadClassData(name);
+                return defineClass(name, bytes, 0, bytes.length);
+            } catch (Exception e) {
+                throw new ClassNotFoundException();
+            }
+        }
+
+        @Override
+        public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (loadedClass.containsKey(name)) {
+                return loadedClass.get(name);
+            }
+            if (name.startsWith("demo.MultiClassLoaderServiceRe")) {
+                Class<?> aClass = this.findClass(name);
+                this.loadedClass.put(name, aClass);
+                if (resolve) {
+                    this.resolveClass(aClass);
+                }
+                return aClass;
+            } else {
+                return testClassLoader.loadClass(name, resolve);
+            }
+        }
+
+
+        public byte[] loadClassData(String className) throws IOException {
+            className = className.replaceAll("\\.", "/");
+            String path = basePath + File.separator + className + ".class";
+            FileInputStream fileInputStream;
+            byte[] classBytes;
+            fileInputStream = new FileInputStream(path);
+            int length = fileInputStream.available();
+            classBytes = new byte[length];
+            fileInputStream.read(classBytes);
+            fileInputStream.close();
+            return classBytes;
+        }
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmDeepCopyTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmDeepCopyTest.java
new file mode 100644
index 0000000..8521ebf
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmDeepCopyTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.injvm;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ConsumerModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class InjvmDeepCopyTest {
+
+    @Test
+    public void testDeepCopy() {
+        ApplicationModel applicationModel = new ApplicationModel(FrameworkModel.defaultModel());
+        applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("TestInjvm"));
+
+        ModuleModel moduleModel = applicationModel.newModule();
+
+        AtomicReference<Data> requestReference = new AtomicReference<>();
+        AtomicReference<Data> responseReference = new AtomicReference<>();
+        Demo demo = new Demo(requestReference, responseReference);
+
+        // export provider
+        ProxyFactory proxyFactory = moduleModel.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
+        Protocol protocol = moduleModel.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+        URL url = URL.valueOf("injvm://localhost:0/" + DemoInterface.class.getName() + "?interface=" + DemoInterface.class.getName());
+        ServiceDescriptor providerServiceDescriptor = moduleModel.getServiceRepository().registerService(DemoInterface.class);
+        ProviderModel providerModel = new ProviderModel(
+            url.getServiceKey(),
+            demo,
+            providerServiceDescriptor,
+            null,
+            null);
+
+        URL providerUrl = url.setScopeModel(moduleModel).setServiceModel(providerModel);
+        Invoker invoker = proxyFactory.getInvoker(demo, DemoInterface.class, providerUrl);
+        Exporter<?> exporter = protocol.export(invoker);
+
+        // refer consumer
+        ServiceDescriptor consumerServiceDescriptor = moduleModel.getServiceRepository().registerService(DemoInterface.class);
+        ConsumerModel consumerModel = new ConsumerModel(DemoInterface.class.getName(), null, consumerServiceDescriptor, null,
+            ApplicationModel.defaultModel().getDefaultModule(), null, null);
+        URL consumerUrl = url.setScopeModel(moduleModel).setServiceModel(consumerModel);
+
+        DemoInterface stub = proxyFactory.getProxy(protocol.refer(DemoInterface.class, consumerUrl));
+
+        Data request = new Data();
+        Data response = stub.call(request);
+
+        Assertions.assertNotEquals(requestReference.get(), request);
+        Assertions.assertNotEquals(responseReference.get(), response);
+
+        exporter.unexport();
+        applicationModel.destroy();
+    }
+
+    private interface DemoInterface {
+        Data call(Data obj);
+    }
+
+    private static class Demo implements DemoInterface {
+        private AtomicReference<Data> requestReference;
+        private AtomicReference<Data> responseReference;
+
+        public Demo(AtomicReference<Data> requestReference, AtomicReference<Data> responseReference) {
+            this.requestReference = requestReference;
+            this.responseReference = responseReference;
+        }
+
+        @Override
+        public Data call(Data obj) {
+            requestReference.set(obj);
+            Data result = new Data();
+            responseReference.set(result);
+            return result;
+        }
+    }
+
+    private static class Data implements Serializable {
+
+    }
+}
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 a35e0b3..4e57d05 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
@@ -23,6 +23,7 @@ import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -100,26 +101,26 @@ public class InjvmProtocolTest {
         exporters.add(exporter);
 
         url = url.setProtocol("dubbo");
-        assertTrue(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertTrue(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
 
         url = url.addParameter(GROUP_KEY, "*")
             .addParameter(VERSION_KEY, "*");
-        assertTrue(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertTrue(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
 
         url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(SCOPE_KEY, SCOPE_LOCAL);
-        assertTrue(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertTrue(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
 
         url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(LOCAL_PROTOCOL, true);
-        assertTrue(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertTrue(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
 
         url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(SCOPE_KEY, SCOPE_REMOTE);
-        assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertFalse(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
 
         url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(GENERIC_KEY, true);
-        assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertFalse(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
 
         url = URL.valueOf("fake://127.0.0.1/TestService").addParameter("cluster", "broadcast");
-        assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
+        assertFalse(InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).isInjvmRefer(url));
     }
 
     @Test
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/ProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/ProtocolTest.java
index 12de553..8a53d87 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/ProtocolTest.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/ProtocolTest.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 
 import org.junit.jupiter.api.Test;
 
@@ -37,7 +38,7 @@ public class ProtocolTest {
     };
 
     static {
-        InjvmProtocol injvm = InjvmProtocol.getInjvmProtocol();
+        InjvmProtocol injvm = InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel());
     }
 
     ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java
index 9d4ccf7..6bd18d2 100644
--- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java
+++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java
@@ -23,10 +23,23 @@ import java.util.concurrent.ConcurrentHashMap;
 
 public abstract class AbstractHessian2FactoryInitializer implements Hessian2FactoryInitializer {
     private static final Map<ClassLoader, SerializerFactory> CL_2_SERIALIZER_FACTORY = new ConcurrentHashMap<>();
+    private static volatile SerializerFactory SYSTEM_SERIALIZER_FACTORY;
 
     @Override
     public SerializerFactory getSerializerFactory() {
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader == null) {
+            // system classloader
+            if (SYSTEM_SERIALIZER_FACTORY == null) {
+                synchronized (AbstractHessian2FactoryInitializer.class) {
+                    if (SYSTEM_SERIALIZER_FACTORY == null) {
+                        SYSTEM_SERIALIZER_FACTORY = createSerializerFactory();
+                    }
+                }
+            }
+            return SYSTEM_SERIALIZER_FACTORY;
+        }
+
         if (!CL_2_SERIALIZER_FACTORY.containsKey(classLoader)) {
             synchronized (AbstractHessian2FactoryInitializer.class) {
                 if (!CL_2_SERIALIZER_FACTORY.containsKey(classLoader)) {