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

[dubbo] branch 3.0-multi-instances updated: Pass scope model through URL

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

kylixs pushed a commit to branch 3.0-multi-instances
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0-multi-instances by this push:
     new f9ea1fa  Pass scope model through URL
f9ea1fa is described below

commit f9ea1fa670fe41cc2fd0111571691168b516baad
Author: gongdewei <ky...@qq.com>
AuthorDate: Fri Aug 27 19:14:42 2021 +0800

    Pass scope model through URL
---
 .../src/main/java/org/apache/dubbo/common/URL.java | 35 +++++++++--
 .../java/org/apache/dubbo/common/URLBuilder.java   |  9 +++
 .../common/beans/factory/ScopeBeanFactory.java     | 53 +++++++++++-----
 .../dubbo/common/constants/CommonConstants.java    |  1 +
 .../common/url/component/ServiceConfigURL.java     | 22 +------
 .../dubbo/config/AbstractInterfaceConfig.java      |  1 +
 .../apache/dubbo/config/ConfigCenterConfig.java    |  6 +-
 .../apache/dubbo/config/MetadataReportConfig.java  |  6 +-
 .../org/apache/dubbo/config/RegistryConfig.java    |  2 +-
 .../org/apache/dubbo/config/ReferenceConfig.java   |  1 +
 .../org/apache/dubbo/config/ServiceConfig.java     |  7 ++-
 .../dubbo/config/bootstrap/DubboBootstrap.java     | 15 +++--
 .../ConfigurableMetadataServiceExporter.java       | 26 +++++---
 .../dubbo/config/utils/ConfigValidationUtils.java  |  1 +
 .../bootstrap/DubboBootstrapMultiInstanceTest.java | 72 ++++++++++++++++------
 .../metadata/MetadataServiceExporterTest.java      |  3 +-
 .../dubbo/metadata/MetadataServiceExporter.java    |  4 +-
 .../metadata/report/MetadataReportInstance.java    |  1 +
 .../org/apache/dubbo/registry/RegistryFactory.java |  4 +-
 .../registry/client/DefaultServiceInstance.java    | 19 ++++++
 .../dubbo/registry/client/ServiceInstance.java     |  8 +++
 .../listener/ServiceInstancesChangedListener.java  |  2 +-
 .../registry/client/metadata/MetadataUtils.java    | 18 ++----
 .../metadata/ServiceInstanceMetadataUtils.java     |  2 +-
 .../InterfaceCompatibleRegistryProtocol.java       |  2 +-
 .../registry/integration/RegistryProtocol.java     | 27 +++-----
 .../java/org/apache/dubbo/rpc/ProxyFactory.java    |  3 +-
 27 files changed, 229 insertions(+), 121 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index 65ec401..1c4867f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.common;
 
 import org.apache.dubbo.common.config.Configuration;
 import org.apache.dubbo.common.config.InmemoryConfiguration;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.constants.RemotingConstants;
 import org.apache.dubbo.common.url.component.PathURLAddress;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
@@ -29,6 +30,7 @@ import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.LRUCache;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.model.ScopeModel;
 
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
@@ -122,15 +124,22 @@ class URL implements Serializable {
 
     private transient String serviceKey;
     private transient String protocolServiceKey;
+    protected final Map<String, Object> attributes;
 
     protected URL() {
         this.urlAddress = null;
         this.urlParam = null;
+        this.attributes = new HashMap<>();
     }
 
     public URL(URLAddress urlAddress, URLParam urlParam) {
+        this(urlAddress, urlParam, null);
+    }
+
+    public URL(URLAddress urlAddress, URLParam urlParam, Map<String, Object> attributes) {
         this.urlAddress = urlAddress;
         this.urlParam = urlParam;
+        this.attributes = (attributes != null ? attributes : new HashMap<>());
     }
 
     public URL(String protocol, String host, int port) {
@@ -179,6 +188,7 @@ class URL implements Serializable {
 
         this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
         this.urlParam = URLParam.parse(parameters);
+        this.attributes = new HashMap<>();
     }
 
     protected URL(String protocol,
@@ -196,6 +206,7 @@ class URL implements Serializable {
 
         this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
         this.urlParam = URLParam.parse(parameters);
+        this.attributes = new HashMap<>();
     }
 
     public static URL cacheableValueOf(String url) {
@@ -218,6 +229,10 @@ class URL implements Serializable {
         return valueOf(url, false);
     }
 
+    public static URL valueOf(String url, ScopeModel scopeModel) {
+        return valueOf(url).setScopeModel(scopeModel);
+    }
+
     /**
      * parse normal or encoded url string into strutted URL:
      * - dubbo://host:port/path?param=value
@@ -532,6 +547,15 @@ class URL implements Serializable {
         return result;
     }
 
+    public URL setScopeModel(ScopeModel scopeModel) {
+        this.attributes.put(CommonConstants.SCOPE_MODEL, scopeModel);
+        return this;
+    }
+
+    public ScopeModel getScopeModel() {
+        return (ScopeModel) this.attributes.get(CommonConstants.SCOPE_MODEL);
+    }
+
     protected Map<String, Number> getNumbers() {
         // concurrent initialization is tolerant
         if (numbers == null) {
@@ -1449,7 +1473,7 @@ class URL implements Serializable {
     }
 
     protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
-        return (T) new ServiceConfigURL(urlAddress, urlParam, null);
+        return (T) new ServiceConfigURL(urlAddress, urlParam, attributes);
     }
 
     /* methods introduced for CompositeURL, CompositeURL must override to make the implementations meaningful */
@@ -1517,27 +1541,30 @@ class URL implements Serializable {
 
     /* Service Config URL, START*/
     public Map<String, Object> getAttributes() {
-        return new HashMap<>();
+        return attributes;
     }
 
     public URL addAttributes(Map<String, Object> attributes) {
+        attributes.putAll(attributes);
         return this;
     }
 
     public Object getAttribute(String key) {
-        return null;
+        return attributes.get(key);
     }
 
     public URL putAttribute(String key, Object obj) {
+        attributes.put(key, obj);
         return this;
     }
 
     public URL removeAttribute(String key) {
+        attributes.remove(key);
         return this;
     }
 
     public boolean hasAttribute(String key) {
-        return true;
+        return attributes.containsKey(key);
     }
 
     /* Service Config URL, END*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
index ce6e8e7..24ea293 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
@@ -19,12 +19,15 @@ package org.apache.dubbo.common;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.model.ScopeModel;
 
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
+import static org.apache.dubbo.common.constants.CommonConstants.SCOPE_MODEL;
+
 public final class URLBuilder extends ServiceConfigURL {
     private String protocol;
 
@@ -201,6 +204,12 @@ public final class URLBuilder extends ServiceConfigURL {
         return this;
     }
 
+    @Override
+    public URLBuilder setScopeModel(ScopeModel scopeModel) {
+        this.attributes.put(SCOPE_MODEL, scopeModel);
+        return this;
+    }
+
     public URLBuilder addParameterAndEncoded(String key, String value) {
         if (StringUtils.isEmpty(value)) {
             return this;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java
index 85f2d63..1e56488 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.common.extension.ExtensionPostProcessor;
 import org.apache.dubbo.common.utils.StringUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -38,7 +39,7 @@ public class ScopeBeanFactory {
     private ExtensionAccessor extensionAccessor;
     private List<ExtensionPostProcessor> extensionPostProcessors;
     private Map<Class, AtomicInteger> beanNameIdCounterMap = new ConcurrentHashMap<>();
-    private Map<Class, List<BeanInfo>> beanMap = new ConcurrentHashMap<>();
+    private List<BeanInfo> registeredBeanInfos = Collections.synchronizedList(new ArrayList<>());
 
     public ScopeBeanFactory(ScopeBeanFactory parent, ExtensionAccessor extensionAccessor) {
         this.parent = parent;
@@ -69,6 +70,11 @@ public class ScopeBeanFactory {
     }
 
     public void registerBean(String name, Object bean) {
+        // avoid duplicated register same bean
+        if (containsBean(name, bean)) {
+            return;
+        }
+
         Class<?> beanClass = bean.getClass();
         try {
             if (bean instanceof ExtensionAccessorAware) {
@@ -84,8 +90,17 @@ public class ScopeBeanFactory {
         if (name == null) {
             name = beanClass.getName() + "#" + getNextId(beanClass);
         }
-        List<BeanInfo> beanInfos = beanMap.computeIfAbsent(beanClass, key -> new ArrayList<>());
-        beanInfos.add(new BeanInfo(name, bean));
+        registeredBeanInfos.add(new BeanInfo(name, bean));
+    }
+
+    private boolean containsBean(String name, Object bean) {
+        for (BeanInfo beanInfo : registeredBeanInfos) {
+            if (beanInfo.instance == bean &&
+                (name == null || StringUtils.isEquals(name, beanInfo.name))) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private int getNextId(Class<?> beanClass) {
@@ -105,24 +120,28 @@ public class ScopeBeanFactory {
     }
 
     private <T> T getBeanInternal(String name, Class<T> type) {
-        List<BeanInfo> beanInfos = beanMap.get(type);
-        if (beanInfos == null || beanInfos.isEmpty()) {
-            return null;
-        }
-
-        for (BeanInfo beanInfo : beanInfos) {
-            if (name == null || StringUtils.isEquals(beanInfo.name, name)) {
-                return (T) beanInfo.instance;
+        List<BeanInfo> candidates = null;
+        for (BeanInfo beanInfo : registeredBeanInfos) {
+            // if required bean type is same class/superclass/interface of the registered bean
+            if (type.isAssignableFrom(beanInfo.instance.getClass())) {
+                if (StringUtils.isEquals(beanInfo.name, name)) {
+                    return (T) beanInfo.instance;
+                } else {
+                    if (candidates == null) {
+                        candidates = new ArrayList<>();
+                    }
+                    candidates.add(beanInfo);
+                }
             }
         }
 
         // if bean name not matched and only single candidate
-        if (name != null) {
-            if (beanInfos.size() == 1) {
-                return (T) beanInfos.get(0);
-            } else if (beanInfos.size() > 1) {
-                List<String> candidateBeanNames = beanInfos.stream().map(beanInfo -> beanInfo.name).collect(Collectors.toList());
-                throw new ScopeBeanException("expected single matching bean but found " + beanInfos.size() + " candidates for type [" + type.getName() + "]: " + candidateBeanNames);
+        if (candidates != null) {
+            if (candidates.size() == 1) {
+                return (T) candidates.get(0).instance;
+            } else if (candidates.size() > 1) {
+                List<String> candidateBeanNames = candidates.stream().map(beanInfo -> beanInfo.name).collect(Collectors.toList());
+                throw new ScopeBeanException("expected single matching bean but found " + candidates.size() + " candidates for type [" + type.getName() + "]: " + candidateBeanNames);
             }
         }
         return null;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 87bfe54..e40550c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -448,4 +448,5 @@ public interface CommonConstants {
 
     String SERVICE_NAME_MAPPING_KEY = "service-name-mapping";
 
+    String SCOPE_MODEL = "scopeModel";
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
index 87018a9..e24547c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
@@ -23,7 +23,6 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class ServiceConfigURL extends URL {
-    private final Map<String, Object> attributes;
 
     private volatile transient String full;
     private volatile transient String string;
@@ -31,12 +30,11 @@ public class ServiceConfigURL extends URL {
     private volatile transient String parameter;
 
     public ServiceConfigURL() {
-        this.attributes = null;
+        super();
     }
 
     public ServiceConfigURL(URLAddress urlAddress, URLParam urlParam, Map<String, Object> attributes) {
-        super(urlAddress, urlParam);
-        this.attributes = (attributes != null ? attributes : new HashMap<>());
+        super(urlAddress, urlParam, attributes);
     }
 
 
@@ -97,27 +95,17 @@ public class ServiceConfigURL extends URL {
         return (T) new ServiceConfigURL(urlAddress, urlParam, attributes);
     }
 
-    public Map<String, Object> getAttributes() {
-        return attributes;
-    }
-
-    public Object getAttribute(String key) {
-        return attributes.get(key);
-    }
-
     @Override
     public URL addAttributes(Map<String, Object> attributes) {
         Map<String, Object> newAttributes = new HashMap<>();
         newAttributes.putAll(this.attributes);
         newAttributes.putAll(attributes);
-
         return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes);
     }
 
     public ServiceConfigURL putAttribute(String key, Object obj) {
         Map<String, Object> newAttributes = new HashMap<>(attributes);
         newAttributes.put(key, obj);
-
         return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes);
     }
 
@@ -125,16 +113,10 @@ public class ServiceConfigURL extends URL {
     public URL removeAttribute(String key) {
         Map<String, Object> newAttributes = new HashMap<>(attributes);
         newAttributes.remove(key);
-
         return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes);
     }
 
     @Override
-    public boolean hasAttribute(String key) {
-        return getAttribute(key) != null;
-    }
-
-    @Override
     public String toString() {
         if (string != null) {
             return string;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
index eb88a57..bc1d400 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
@@ -289,6 +289,7 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
                 Class<?> finalInterfaceClass = interfaceClass;
                 List<MethodConfig> validMethodConfigs = methodConfigs.stream().filter(methodConfig -> {
                     methodConfig.setParentPrefix(preferredPrefix);
+                    methodConfig.setScopeModel(getScopeModel());
                     methodConfig.refresh();
                     // verify method config
                     return verifyMethodConfig(methodConfig, finalInterfaceClass, ignoreInvalidMethodConfig);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java
index 8735af3..788f549 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java
@@ -131,7 +131,9 @@ public class ConfigCenterConfig extends AbstractConfig {
         if (StringUtils.isEmpty(map.get(PROTOCOL_KEY))) {
             map.put(PROTOCOL_KEY, ZOOKEEPER_PROTOCOL);
         }
-        return UrlUtils.parseURL(address, map);
+        URL url = UrlUtils.parseURL(address, map);
+        url.setScopeModel(getScopeModel());
+        return url;
     }
 
     public boolean checkOrUpdateInited() {
@@ -171,7 +173,7 @@ public class ConfigCenterConfig extends AbstractConfig {
         this.address = address;
         if (address != null) {
             try {
-                URL url = URL.valueOf(address);
+                URL url = URL.valueOf(address, getScopeModel());
                 updatePropertyIfAbsent(this::getUsername, this::setUsername, url.getUsername());
                 updatePropertyIfAbsent(this::getPassword, this::setPassword, url.getPassword());
                 updatePropertyIfAbsent(this::getProtocol, this::setProtocol, url.getProtocol());
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java
index 6704ff5..3cc9004 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java
@@ -107,7 +107,7 @@ public class MetadataReportConfig extends AbstractConfig {
             throw new IllegalArgumentException("The address of metadata report is invalid.");
         }
         Map<String, String> map = new HashMap<String, String>();
-        URL url = URL.valueOf(address);
+        URL url = URL.valueOf(address, getScopeModel());
         // Issue : https://github.com/apache/dubbo/issues/6491
         // Append the parameters from address
         map.putAll(url.getParameters());
@@ -118,7 +118,7 @@ public class MetadataReportConfig extends AbstractConfig {
         // put the protocol of URL as the "metadata"
         map.put("metadata", url.getProtocol());
         return new ServiceConfigURL("metadata", url.getUsername(), url.getPassword(), url.getHost(),
-                url.getPort(), url.getPath(), map);
+                url.getPort(), url.getPath(), map).setScopeModel(getScopeModel());
 
     }
 
@@ -139,7 +139,7 @@ public class MetadataReportConfig extends AbstractConfig {
         this.address = address;
         if (address != null) {
             try {
-                URL url = URL.valueOf(address);
+                URL url = URL.valueOf(address, getScopeModel());
 
                 // Refactor since 2.7.8
                 updatePropertyIfAbsent(this::getUsername, this::setUsername, url.getUsername());
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
index f007b54..80c9996 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
@@ -220,7 +220,7 @@ public class RegistryConfig extends AbstractConfig {
         this.address = address;
         if (address != null) {
             try {
-                URL url = URL.valueOf(address);
+                URL url = URL.valueOf(address, getScopeModel());
 
                 // Refactor since 2.7.8
                 updatePropertyIfAbsent(this::getUsername, this::setUsername, url.getUsername());
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 d06574c..5c912c1 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
@@ -390,6 +390,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
 
         URL consumerUrl = new ServiceConfigURL(CONSUMER_PROTOCOL, referenceParameters.get(REGISTER_IP_KEY), 0,
             referenceParameters.get(INTERFACE_KEY), referenceParameters);
+        consumerUrl.setScopeModel(getScopeModel());
         MetadataUtils.publishServiceDefinition(consumerUrl);
 
         // create service proxy
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index f94d0aa..4b94872 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -268,7 +268,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
 
         // init some null configuration.
         List<ConfigInitializer> configInitializers = this.getExtensionLoader(ConfigInitializer.class)
-                .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
+                .getActivateExtension(URL.valueOf("configInitializer://", getScopeModel()), (String[]) null);
         configInitializers.forEach(e -> e.initServiceConfig(this));
 
         // if protocol is not injvm checkRegistry
@@ -528,6 +528,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
         String host = findConfigedHosts(protocolConfig, registryURLs, params);
         Integer port = findConfigedPorts(protocolConfig, name, params);
         URL url = new ServiceConfigURL(name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), params);
+        url.setScopeModel(getScopeModel());
 
         // You can customize Configurator to append extra parameters
         if (this.getExtensionLoader(ConfiguratorFactory.class)
@@ -535,7 +536,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
             url = this.getExtensionLoader(ConfiguratorFactory.class)
                     .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
         }
-
+        url.setScopeModel(getScopeModel());
         return url;
     }
 
@@ -813,7 +814,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
 
     private void postProcessConfig() {
         List<ConfigPostProcessor> configPostProcessors = this.getExtensionLoader(ConfigPostProcessor.class)
-                .getActivateExtension(URL.valueOf("configPostProcessor://"), (String[]) null);
+                .getActivateExtension(URL.valueOf("configPostProcessor://", getScopeModel()), (String[]) null);
         configPostProcessors.forEach(component -> component.postProcessServiceConfig(this));
     }
 
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index 8921dda..f1edbd2 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -869,7 +869,7 @@ public class DubboBootstrap {
     private ConfigCenterConfig registryAsConfigCenter(RegistryConfig registryConfig) {
         String protocol = registryConfig.getProtocol();
         Integer port = registryConfig.getPort();
-        URL url = URL.valueOf(registryConfig.getAddress());
+        URL url = URL.valueOf(registryConfig.getAddress(), registryConfig.getScopeModel());
         String id = "config-center-" + protocol + "-" + url.getHost() + "-" + port;
         ConfigCenterConfig cc = new ConfigCenterConfig();
         cc.setId(id);
@@ -979,7 +979,7 @@ public class DubboBootstrap {
 
     private MetadataReportConfig registryAsMetadataCenter(RegistryConfig registryConfig) {
         String protocol = registryConfig.getProtocol();
-        URL url = URL.valueOf(registryConfig.getAddress());
+        URL url = URL.valueOf(registryConfig.getAddress(), registryConfig.getScopeModel());
         String id = "metadata-center-" + protocol + "-" + url.getHost() + "-" + url.getPort();
         MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
         metadataReportConfig.setId(id);
@@ -1009,7 +1009,7 @@ public class DubboBootstrap {
         // since 2.7.8
         // Issue : https://github.com/apache/dubbo/issues/6476
         StringBuilder metadataAddressBuilder = new StringBuilder();
-        URL url = URL.valueOf(address);
+        URL url = URL.valueOf(address, registryConfig.getScopeModel());
         String protocolFromAddress = url.getProtocol();
         if (isEmpty(protocolFromAddress)) {
             // If the protocol from address is missing, is like :
@@ -1150,8 +1150,12 @@ public class DubboBootstrap {
      */
     private void initMetadataService() {
 //        startMetadataCenter();
-        this.metadataService = getDefaultExtension();
-        this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
+        this.metadataService = getExtensionLoader(WritableMetadataService.class).getDefaultExtension();
+        // support injection by super type MetadataService
+        applicationModel.getBeanFactory().registerBean(this.metadataService);
+
+        //this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
+        this.metadataServiceExporter = getExtensionLoader(MetadataServiceExporter.class).getDefaultExtension();
     }
 
     /**
@@ -1621,6 +1625,7 @@ public class DubboBootstrap {
 
     private ServiceInstance createServiceInstance(String serviceName) {
         this.serviceInstance = new DefaultServiceInstance(serviceName);
+        serviceInstance.setScopeModel(applicationModel);
         setMetadataStorageType(serviceInstance, getMetadataType());
         ServiceInstanceMetadataUtils.customizeInstance(this.serviceInstance);
         return this.serviceInstance;
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index 5a5ed15..05d48fc 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -30,6 +30,7 @@ import org.apache.dubbo.config.context.ConfigManager;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelAware;
 
 import java.util.Collections;
 import java.util.List;
@@ -52,15 +53,24 @@ import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
  * @see ConfigManager
  * @since 2.7.5
  */
-public class ConfigurableMetadataServiceExporter implements MetadataServiceExporter {
+public class ConfigurableMetadataServiceExporter implements MetadataServiceExporter, ScopeModelAware {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
-    private final MetadataService metadataService;
+    private MetadataService metadataService;
 
     private volatile ServiceConfig<MetadataService> serviceConfig;
+    private ApplicationModel applicationModel;
 
-    public ConfigurableMetadataServiceExporter(MetadataService metadataService) {
+    public ConfigurableMetadataServiceExporter() {
+    }
+
+    @Override
+    public void setApplicationModel(ApplicationModel applicationModel) {
+        this.applicationModel = applicationModel;
+    }
+
+    public void setMetadataService(MetadataService metadataService) {
         this.metadataService = metadataService;
     }
 
@@ -69,14 +79,16 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
 
         if (!isExported()) {
 
+            ApplicationConfig applicationConfig = getApplicationConfig();
             ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
-            serviceConfig.setApplication(getApplicationConfig());
+            serviceConfig.setScopeModel(applicationModel.getDefaultModule());
+            serviceConfig.setApplication(applicationConfig);
             serviceConfig.setRegistry(new RegistryConfig("N/A"));
             serviceConfig.setProtocol(generateMetadataProtocol());
             serviceConfig.setInterface(MetadataService.class);
             serviceConfig.setDelay(0);
             serviceConfig.setRef(metadataService);
-            serviceConfig.setGroup(getApplicationConfig().getName());
+            serviceConfig.setGroup(applicationConfig.getName());
             serviceConfig.setVersion(metadataService.version());
             serviceConfig.setMethods(generateMethodConfig());
 
@@ -138,7 +150,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
     }
 
     private ApplicationConfig getApplicationConfig() {
-        return ApplicationModel.defaultModel().getConfigManager().getApplication().get();
+        return applicationModel.getConfigManager().getApplication().get();
     }
 
     private ProtocolConfig generateMetadataProtocol() {
@@ -149,7 +161,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
             if (logger.isInfoEnabled()) {
                 logger.info("Metadata Service Port hasn't been set will use default protocol defined in protocols.");
             }
-            List<ProtocolConfig> defaultProtocols = ApplicationModel.defaultModel().getConfigManager().getDefaultProtocols();
+            List<ProtocolConfig> defaultProtocols = applicationModel.getConfigManager().getDefaultProtocols();
 
             ProtocolConfig dubboProtocol = findDubboProtocol(defaultProtocols);
             if (dubboProtocol != null) {
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
index c0b90cb..7306dd5 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
@@ -213,6 +213,7 @@ public class ConfigValidationUtils {
                         url = URLBuilder.from(url)
                             .addParameter(REGISTRY_KEY, url.getProtocol())
                             .setProtocol(extractRegistryType(url))
+                            .setScopeModel(interfaceConfig.getScopeModel())
                             .build();
                         if ((provider && url.getParameter(REGISTER_KEY, true))
                             || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java
index 9485505..e6ebe39 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java
@@ -16,19 +16,27 @@
  */
 package org.apache.dubbo.config.bootstrap;
 
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.ReferenceConfig;
 import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.ServiceConfig;
+import org.apache.dubbo.config.SysProps;
 import org.apache.dubbo.config.api.DemoService;
 import org.apache.dubbo.config.provider.impl.DemoServiceImpl;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 public class DubboBootstrapMultiInstanceTest {
 
+    @AfterEach
+    protected void afterEach() {
+        SysProps.clear();
+    }
+
     @Test
     public void testIsolatedApplications() {
 
@@ -41,21 +49,39 @@ public class DubboBootstrapMultiInstanceTest {
         Assertions.assertNotSame(dubboBootstrap1.getConfigManager(), dubboBootstrap2.getConfigManager());
 
         // bootstrap1: provider app
-        testProviderApp(dubboBootstrap1);
+        configProviderApp(dubboBootstrap1).start();
 
         // bootstrap2: consumer app
-        testConsumerApp(dubboBootstrap2);
-
-        DemoService demoServiceRefer = dubboBootstrap2.getCache().get(DemoService.class);
-        String result = demoServiceRefer.sayName("dubbo");
-        System.out.println("result: " + result);
+        configConsumerApp(dubboBootstrap2).start();
+        testConsumer(dubboBootstrap2);
     }
 
     @Test
-    public void testDefaultApplication() {
+    public void testDefaultProviderApplication() {
+        configProviderApp(DubboBootstrap.getInstance()).start();
+    }
 
-        testProviderApp(DubboBootstrap.getInstance());
+    @Test
+    public void testDefaultConsumerApplication() {
+        SysProps.setProperty("dubbo.consumer.check", "false");
+        try {
+            DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance();
+            configConsumerApp(dubboBootstrap).start();
+            testConsumer(dubboBootstrap);
+        } catch (Exception e) {
+            Assertions.assertTrue(e.toString().contains("No provider available from registry"), StringUtils.toString(e));
+        }
+    }
 
+    @Test
+    public void testDefaultMixedApplication() {
+        DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance();
+        dubboBootstrap.application("mixed-app");
+
+        configProviderApp(dubboBootstrap);
+        configConsumerApp(dubboBootstrap);
+        dubboBootstrap.start();
+        testConsumer(dubboBootstrap);
     }
 
     @Test
@@ -72,17 +98,25 @@ public class DubboBootstrapMultiInstanceTest {
 
     }
 
-    private void testConsumerApp(DubboBootstrap dubboBootstrap2) {
+    private DubboBootstrap configConsumerApp(DubboBootstrap dubboBootstrap) {
         ReferenceConfig<DemoService> referenceConfig = new ReferenceConfig<>();
         referenceConfig.setInterface(DemoService.class);
 
-        dubboBootstrap2.application("consumer-app")
-            .registry(new RegistryConfig("zookeeper://localhost:2181"))
-            .reference(referenceConfig)
-            .start();
+        if (!dubboBootstrap.getConfigManager().getApplication().isPresent()) {
+            dubboBootstrap.application("consumer-app");
+        }
+        dubboBootstrap.registry(new RegistryConfig("zookeeper://localhost:2181"))
+            .reference(referenceConfig);
+        return dubboBootstrap;
+    }
+
+    private void testConsumer(DubboBootstrap dubboBootstrap) {
+        DemoService demoService = dubboBootstrap.getCache().get(DemoService.class);
+        String result = demoService.sayName("dubbo");
+        Assertions.assertEquals("say:dubbo", result);
     }
 
-    private void testProviderApp(DubboBootstrap dubboBootstrap1) {
+    private DubboBootstrap configProviderApp(DubboBootstrap dubboBootstrap) {
         RegistryConfig registry1 = new RegistryConfig();
         registry1.setAddress("zookeeper://localhost:2181");
 
@@ -94,11 +128,13 @@ public class DubboBootstrapMultiInstanceTest {
         serviceConfig.setInterface(DemoService.class);
         serviceConfig.setRef(new DemoServiceImpl());
 
-        dubboBootstrap1.application("provider-app")
-            .registry(registry1)
+        if (!dubboBootstrap.getConfigManager().getApplication().isPresent()) {
+            dubboBootstrap.application("provider-app");
+        }
+        dubboBootstrap.registry(registry1)
             .protocol(protocol1)
-            .service(serviceConfig)
-            .start();
+            .service(serviceConfig);
+        return dubboBootstrap;
     }
 
 
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
index 9175bf2..807de6d 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
@@ -56,7 +56,8 @@ public class MetadataServiceExporterTest {
     @Test
     public void test() {
         MetadataService metadataService = Mockito.mock(MetadataService.class);
-        MetadataServiceExporter exporter = new ConfigurableMetadataServiceExporter(metadataService);
+        ConfigurableMetadataServiceExporter exporter = new ConfigurableMetadataServiceExporter();
+        exporter.setMetadataService(metadataService);
 
         exporter.export();
         assertTrue(exporter.isExported());
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceExporter.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceExporter.java
index 34d3b54..4a3d6c5 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceExporter.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceExporter.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.metadata;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionScope;
 import org.apache.dubbo.common.extension.SPI;
 import org.apache.dubbo.common.lang.Prioritized;
 
@@ -24,6 +25,7 @@ import java.util.List;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION;
 
 /**
  * The exporter of {@link MetadataService}
@@ -33,7 +35,7 @@ import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoad
  * @see #unexport()
  * @since 2.7.5
  */
-@SPI(DEFAULT_METADATA_STORAGE_TYPE)
+@SPI(value = DEFAULT_METADATA_STORAGE_TYPE, scope = APPLICATION)
 public interface MetadataServiceExporter extends Prioritized {
 
     /**
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
index 9bc3cb0..8222da6 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
@@ -52,6 +52,7 @@ public class MetadataReportInstance {
             String protocol = url.getParameter(METADATA_REPORT_KEY, DEFAULT_DIRECTORY);
             url = URLBuilder.from(url)
                     .setProtocol(protocol)
+                    .setScopeModel(config.getScopeModel())
                     .removeParameter(METADATA_REPORT_KEY)
                     .build();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryFactory.java
index 2401139..5707b62 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryFactory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryFactory.java
@@ -20,12 +20,14 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Adaptive;
 import org.apache.dubbo.common.extension.SPI;
 
+import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION;
+
 /**
  * RegistryFactory. (SPI, Singleton, ThreadSafe)
  *
  * @see org.apache.dubbo.registry.support.AbstractRegistryFactory
  */
-@SPI("dubbo")
+@SPI(value = "dubbo", scope = APPLICATION)
 public interface RegistryFactory {
 
     /**
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index c8a6ffe..8c4ac5c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.client;
 import org.apache.dubbo.metadata.MetadataInfo;
 
 import com.alibaba.fastjson.JSON;
+import org.apache.dubbo.rpc.model.ScopeModel;
 
 import java.util.HashMap;
 import java.util.List;
@@ -28,6 +29,7 @@ import java.util.SortedMap;
 import java.util.TreeMap;
 
 import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.SCOPE_MODEL;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.ENDPOINTS;
 
 /**
@@ -59,6 +61,7 @@ public class DefaultServiceInstance implements ServiceInstance {
     private transient String registryCluster; // extendParams can be more flexiable, but one single property uses less space
     private transient Map<String, String> extendParams;
     private transient List<Endpoint> endpoints;
+    private transient Map<String, Object> attributes = new HashMap<>();
 
     public DefaultServiceInstance() {
     }
@@ -75,6 +78,7 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.extendParams = other.extendParams;
         this.endpoints = other.endpoints;
         this.address = null;
+        this.attributes = other.attributes;
     }
 
     public DefaultServiceInstance(String serviceName, String host, Integer port) {
@@ -203,6 +207,21 @@ public class DefaultServiceInstance implements ServiceInstance {
         }
     }
 
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public void setScopeModel(ScopeModel scopeModel) {
+        this.attributes.put(SCOPE_MODEL,scopeModel);
+    }
+
+    @Override
+    public ScopeModel getScopeModel() {
+        return (ScopeModel) this.attributes.get(SCOPE_MODEL);
+    }
+
     public void setMetadata(Map<String, String> metadata) {
         this.metadata = metadata;
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index 3a55c4e..5192b17 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.registry.client;
 
+import org.apache.dubbo.rpc.model.ScopeModel;
+
 import java.io.Serializable;
 import java.util.Map;
 import java.util.SortedMap;
@@ -88,6 +90,12 @@ public interface ServiceInstance extends Serializable {
 
     Map<String, String> getAllParams();
 
+    Map<String, Object> getAttributes();
+
+    void setScopeModel(ScopeModel scopeModel);
+
+    ScopeModel getScopeModel();
+
     /**
      * Get the value of metadata by the specified name
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index bae3450..b83915d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -330,7 +330,7 @@ public class ServiceInstancesChangedListener {
                 logger.debug("Instance " + instance.getAddress() + " is using metadata type " + metadataType);
             }
             if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
-                RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
+                RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService(instance.getScopeModel());
                 metadataInfo = remoteMetadataService.getMetadata(instance);
             } else {
                 // change the instance used to communicate to avoid all requests route to the same instance
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
index 9200289..6468efa 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -27,6 +27,7 @@ import org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ScopeModel;
 
 import java.util.List;
 import java.util.Map;
@@ -41,8 +42,6 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
 
 public class MetadataUtils {
 
-    private static final Object REMOTE_LOCK = new Object();
-
     public static ConcurrentMap<String, MetadataService> metadataServiceProxies = new ConcurrentHashMap<>();
 
     public static ConcurrentMap<String, Invoker<?>> metadataServiceInvokers = new ConcurrentHashMap<>();
@@ -53,17 +52,8 @@ public class MetadataUtils {
 
     private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
 
-    public static RemoteMetadataServiceImpl remoteMetadataService;
-
-    public static RemoteMetadataServiceImpl getRemoteMetadataService() {
-        if (remoteMetadataService == null) {
-            synchronized (REMOTE_LOCK) {
-                if (remoteMetadataService == null) {
-                    remoteMetadataService = new RemoteMetadataServiceImpl();
-                }
-            }
-        }
-        return remoteMetadataService;
+    public static RemoteMetadataServiceImpl getRemoteMetadataService(ScopeModel scopeModel) {
+        return scopeModel.getBeanFactory().getBean(RemoteMetadataServiceImpl.class);
     }
 
     public static void publishServiceDefinition(URL url) {
@@ -71,7 +61,7 @@ public class MetadataUtils {
         WritableMetadataService.getDefaultExtension().publishServiceDefinition(url);
         // send to remote
         if (REMOTE_METADATA_STORAGE_TYPE.equalsIgnoreCase(url.getParameter(METADATA_KEY))) {
-            getRemoteMetadataService().publishServiceDefinition(url);
+            getRemoteMetadataService(url.getScopeModel()).publishServiceDefinition(url);
         }
     }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
index 26e2814..a23f15b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
@@ -258,7 +258,7 @@ public class ServiceInstanceMetadataUtils {
     }
 
     public static void refreshMetadataAndInstance(ServiceInstance serviceInstance) {
-        RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
+        RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService(serviceInstance.getScopeModel());
         remoteMetadataService.publishMetadata(ApplicationModel.defaultModel().getName());
 
         AbstractRegistryFactory.getServiceDiscoveries().forEach(serviceDiscovery -> {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
index b7374a3..e2064ad 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
@@ -60,7 +60,7 @@ public class InterfaceCompatibleRegistryProtocol extends RegistryProtocol {
 
     @Override
     public <T> ClusterInvoker<T> getServiceDiscoveryInvoker(Cluster cluster, Registry registry, Class<T> type, URL url) {
-        registry = registryFactory.getRegistry(super.getRegistryUrl(url));
+        registry = getRegistry(super.getRegistryUrl(url));
         DynamicDirectory<T> directory = new ServiceDiscoveryRegistryDirectory<>(type, url);
         return doCreateInvoker(directory, cluster, registry, type);
     }
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 ffe01a4..0f7abe6 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
@@ -141,8 +141,8 @@ public class RegistryProtocol implements Protocol {
     //provider url <--> exporter
     private final ConcurrentMap<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<>();
     protected Protocol protocol;
-    protected RegistryFactory registryFactory;
     protected ProxyFactory proxyFactory;
+    //protected RegistryFactory registryFactory;
 
     private ConcurrentMap<URL, ReExportTask> reExportFailedTasks = new ConcurrentHashMap<>();
     private HashedWheelTimer retryTimer = new HashedWheelTimer(new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, 128);
@@ -163,9 +163,10 @@ public class RegistryProtocol implements Protocol {
         this.protocol = protocol;
     }
 
-    public void setRegistryFactory(RegistryFactory registryFactory) {
-        this.registryFactory = registryFactory;
-    }
+    // Cannot inject registryFactory (application scope) into protocol (framework scope)
+//    public void setRegistryFactory(RegistryFactory registryFactory) {
+//        this.registryFactory = registryFactory;
+//    }
 
     public void setProxyFactory(ProxyFactory proxyFactory) {
         this.proxyFactory = proxyFactory;
@@ -356,6 +357,7 @@ public class RegistryProtocol implements Protocol {
      * @return
      */
     protected Registry getRegistry(final URL registryUrl) {
+        RegistryFactory registryFactory = registryUrl.getScopeModel().getAdaptiveExtension(RegistryFactory.class);
         return registryFactory.getRegistry(registryUrl);
     }
 
@@ -435,7 +437,7 @@ public class RegistryProtocol implements Protocol {
     @SuppressWarnings("unchecked")
     public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
         url = getRegistryUrl(url);
-        Registry registry = registryFactory.getRegistry(url);
+        Registry registry = getRegistry(url);
         if (RegistryService.class.equals(type)) {
             return proxyFactory.getInvoker((T) registry, type, url);
         }
@@ -843,19 +845,4 @@ public class RegistryProtocol implements Protocol {
         }
     }
 
-    // for unit test
-    private static RegistryProtocol INSTANCE;
-
-    // for unit test
-    public RegistryProtocol() {
-        INSTANCE = this;
-    }
-
-    // for unit test
-    public static RegistryProtocol getRegistryProtocol() {
-        if (INSTANCE == null) {
-            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(REGISTRY_PROTOCOL); // load
-        }
-        return INSTANCE;
-    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ProxyFactory.java
index f7e607f..dcd51ac 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ProxyFactory.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ProxyFactory.java
@@ -20,12 +20,13 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Adaptive;
 import org.apache.dubbo.common.extension.SPI;
 
+import static org.apache.dubbo.common.extension.ExtensionScope.FRAMEWORK;
 import static org.apache.dubbo.rpc.Constants.PROXY_KEY;
 
 /**
  * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
  */
-@SPI("javassist")
+@SPI(value = "javassist", scope = FRAMEWORK)
 public interface ProxyFactory {
 
     /**