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/19 12:32:07 UTC

[dubbo] 01/02: Add ExtensionDirector and Models

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

commit 8183aafb9173d003f3662d2c6abf9acf8dd5348c
Author: gongdewei <ky...@qq.com>
AuthorDate: Thu Aug 19 20:27:05 2021 +0800

    Add ExtensionDirector and Models
---
 .../dubbo/common/extension/ExtensionDirector.java  | 125 ++++++++++++++++++
 .../dubbo/common/extension/ExtensionLoader.java    | 136 ++++++++++++--------
 .../common/extension/ExtensionPostProcessor.java   |  32 +++++
 .../dubbo/common/extension/ExtensionScope.java     |  39 ++++++
 .../org/apache/dubbo/common/extension/SPI.java     |   4 +
 .../apache/dubbo/rpc/model/ApplicationModel.java   | 120 +++++++++---------
 .../org/apache/dubbo/rpc/model/FrameworkModel.java |  50 ++++++++
 .../org/apache/dubbo/rpc/model/ModelAware.java     |  33 +++++
 .../dubbo/rpc/model/ModelAwarePostProcessor.java   |  57 +++++++++
 .../org/apache/dubbo/rpc/model/ModuleModel.java    |  44 +++++++
 .../common/extension/ExtensionDirectorTest.java    | 140 +++++++++++++++++++++
 .../common/extension/ExtensionLoaderTest.java      |  11 +-
 .../extension/director/ApplicationService.java     |  25 ++++
 .../extension/director/FrameworkService.java       |  25 ++++
 .../common/extension/director/ModuleService.java   |  25 ++++
 .../extension/director/impl/BaseTestService.java   |  55 ++++++++
 .../director/impl/TestApplicationService.java      |  22 ++++
 .../director/impl/TestFrameworkService.java        |  23 ++++
 .../extension/director/impl/TestModuleService.java |  22 ++++
 ...bo.common.extension.director.ApplicationService |   1 +
 ...ubbo.common.extension.director.FrameworkService |   1 +
 ...e.dubbo.common.extension.director.ModuleService |   1 +
 22 files changed, 878 insertions(+), 113 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java
new file mode 100644
index 0000000..ae686fd
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java
@@ -0,0 +1,125 @@
+/*
+ * 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.common.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * ExtensionDirector is a scoped extension loader manager.
+ *
+ * <p></p>
+ * <p>ExtensionDirector supports multiple levels, and the child can inherit the parent's extension instances. </p>
+ * <p>The way to find and create an extension instance is similar to Java classloader.</p>
+ */
+public class ExtensionDirector {
+
+    private final ConcurrentMap<Class<?>, ExtensionLoader<?>> extensionLoadersMap = new ConcurrentHashMap<>(64);
+    private ExtensionDirector parent;
+    private final ExtensionScope scope;
+    private List<ExtensionPostProcessor> extensionPostProcessors = new ArrayList<>();
+
+    public ExtensionDirector(ExtensionDirector parent, ExtensionScope scope) {
+        this.parent = parent;
+        this.scope = scope;
+    }
+
+    public void addExtensionPostProcessor(ExtensionPostProcessor processor) {
+        if (!this.extensionPostProcessors.contains(processor)) {
+            this.extensionPostProcessors.add(processor);
+        }
+    }
+
+    public List<ExtensionPostProcessor> getExtensionPostProcessors() {
+        return extensionPostProcessors;
+    }
+
+    public <T> T getExtension(Class<T> type, String name) {
+        ExtensionLoader<T> extensionLoader = getExtensionLoader(type);
+        return extensionLoader != null ? extensionLoader.getExtension(name) : null;
+    }
+
+    public <T> T getAdaptiveExtension(Class<T> type) {
+        ExtensionLoader<T> extensionLoader = getExtensionLoader(type);
+        return extensionLoader != null ? extensionLoader.getAdaptiveExtension() : null;
+    }
+
+    public <T> T getDefaultExtension(Class<T> type) {
+        ExtensionLoader<T> extensionLoader = getExtensionLoader(type);
+        return extensionLoader != null ? extensionLoader.getDefaultExtension() : null;
+    }
+
+    public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
+        if (type == null) {
+            throw new IllegalArgumentException("Extension type == null");
+        }
+        if (!type.isInterface()) {
+            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
+        }
+        if (!withExtensionAnnotation(type)) {
+            throw new IllegalArgumentException("Extension type (" + type +
+                ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
+        }
+
+        // 1. find in local cache
+        ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
+
+        // 2. find in parent
+        if (loader == null) {
+            if (this.parent != null) {
+                loader = this.parent.getExtensionLoader(type);
+            }
+        }
+
+        // 3. create it
+        if (loader == null) {
+            loader = createExtensionLoader(type);
+        }
+
+        return loader;
+    }
+
+    private <T> ExtensionLoader<T> createExtensionLoader(Class<T> type) {
+        ExtensionLoader<T> loader = null;
+        if (isScopeMatched(type)) {
+            // if scope is matched, just create it
+            extensionLoadersMap.putIfAbsent(type, new ExtensionLoader<T>(type, this));
+            loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
+        } else {
+            // if scope is not matched, redirect to parent director
+            if (this.parent != null) {
+                loader = this.parent.createExtensionLoader(type);
+            }
+        }
+        return loader;
+    }
+
+    private boolean isScopeMatched(Class<?> type) {
+        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
+        return defaultAnnotation.scope().equals(scope);
+    }
+
+    private static boolean withExtensionAnnotation(Class<?> type) {
+        return type.isAnnotationPresent(SPI.class);
+    }
+
+    protected ExtensionDirector getParent() {
+        return parent;
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
index d2beedc..4d30c22 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
@@ -17,6 +17,8 @@
 package org.apache.dubbo.common.extension;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.Environment;
+import org.apache.dubbo.common.context.FrameworkExt;
 import org.apache.dubbo.common.context.Lifecycle;
 import org.apache.dubbo.common.extension.support.ActivateComparator;
 import org.apache.dubbo.common.extension.support.WrapperComparator;
@@ -32,6 +34,8 @@ import org.apache.dubbo.common.utils.Holder;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
 
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
@@ -90,9 +94,7 @@ public class ExtensionLoader<T> {
 
     private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
 
-    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
-
-    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
+    private final ConcurrentMap<Class<?>, Object> extensionInstances = new ConcurrentHashMap<>(64);
 
     private final Class<?> type;
 
@@ -121,6 +123,9 @@ public class ExtensionLoader<T> {
      * Record all unacceptable exceptions when using SPI
      */
     private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
+    private ExtensionDirector extensionDirector;
+    private List<ExtensionPostProcessor> extensionPostProcessors;
+    private Environment environment;
 
     public static void setLoadingStrategies(LoadingStrategy... strategies) {
         if (ArrayUtils.isNotEmpty(strategies)) {
@@ -152,52 +157,43 @@ public class ExtensionLoader<T> {
         return asList(strategies);
     }
 
-    private ExtensionLoader(Class<?> type) {
+    ExtensionLoader(Class<?> type, ExtensionDirector extensionDirector) {
+        this.extensionDirector = extensionDirector;
+        this.extensionPostProcessors = extensionDirector.getExtensionPostProcessors();
         this.type = type;
-        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
-    }
-
-    private static <T> boolean withExtensionAnnotation(Class<T> type) {
-        return type.isAnnotationPresent(SPI.class);
+        this.objectFactory = (type == ExtensionFactory.class ? null : extensionDirector.getExtensionLoader(ExtensionFactory.class)
+            .getAdaptiveExtension());
     }
 
-    @SuppressWarnings("unchecked")
+    /**
+     * @deprecated get extension loader from extension director of some module.
+     *
+     * @see ApplicationModel#getExtensionDirector()
+     * @see FrameworkModel#getExtensionDirector()
+     * @see ModuleModel#getExtensionDirector()
+     * @see ExtensionDirector#getExtensionLoader(java.lang.Class)
+     */
+    @Deprecated
     public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
-        if (type == null) {
-            throw new IllegalArgumentException("Extension type == null");
-        }
-        if (!type.isInterface()) {
-            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
-        }
-        if (!withExtensionAnnotation(type)) {
-            throw new IllegalArgumentException("Extension type (" + type +
-                ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
-        }
-
-        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
-        if (loader == null) {
-            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
-            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
-        }
-        return loader;
+        return ApplicationModel.defaultModel().getExtensionDirector().getExtensionLoader(type);
     }
 
     // For testing purposes only
     public static void resetExtensionLoader(Class type) {
-        ExtensionLoader loader = EXTENSION_LOADERS.get(type);
-        if (loader != null) {
-            // Remove all instances associated with this loader as well
-            Map<String, Class<?>> classes = loader.getExtensionClasses();
-            for (Map.Entry<String, Class<?>> entry : classes.entrySet()) {
-                EXTENSION_INSTANCES.remove(entry.getValue());
-            }
-            classes.clear();
-            EXTENSION_LOADERS.remove(type);
-        }
+//        ExtensionLoader loader = EXTENSION_LOADERS.get(type);
+//        if (loader != null) {
+//            // Remove all instances associated with this loader as well
+//            Map<String, Class<?>> classes = loader.getExtensionClasses();
+//            for (Map.Entry<String, Class<?>> entry : classes.entrySet()) {
+//                EXTENSION_INSTANCES.remove(entry.getValue());
+//            }
+//            classes.clear();
+//            EXTENSION_LOADERS.remove(type);
+//        }
     }
 
-    public static void destroyAll() {
-        EXTENSION_INSTANCES.forEach((_type, instance) -> {
+    public void destroy() {
+        extensionInstances.forEach((_type, instance) -> {
             if (instance instanceof Lifecycle) {
                 Lifecycle lifecycle = (Lifecycle) instance;
                 try {
@@ -207,13 +203,9 @@ public class ExtensionLoader<T> {
                 }
             }
         });
+        extensionInstances.clear();
 
-        // TODO Improve extension loader, clear static refer extension instance.
-        // Some extension instances may be referenced by static fields, if clear EXTENSION_INSTANCES may cause inconsistent.
-        // e.g. org.apache.dubbo.registry.client.metadata.MetadataUtils.localMetadataService
-        // EXTENSION_INSTANCES.clear();
-
-        EXTENSION_LOADERS.clear();
+        // TODO destroy extension loader, release resources.
     }
 
     private static ClassLoader findClassLoader() {
@@ -695,11 +687,12 @@ public class ExtensionLoader<T> {
             throw findException(name);
         }
         try {
-            T instance = (T) EXTENSION_INSTANCES.get(clazz);
+            T instance = (T) extensionInstances.get(clazz);
             if (instance == null) {
-                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
-                instance = (T) EXTENSION_INSTANCES.get(clazz);
+                extensionInstances.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
+                instance = (T) extensionInstances.get(clazz);
             }
+            instance = postProcessBeforeInitialization(instance, name);
             injectExtension(instance);
 
 
@@ -723,6 +716,7 @@ public class ExtensionLoader<T> {
                 }
             }
 
+            instance = postProcessAfterInitialization(instance, name);
             initExtension(instance);
             return instance;
         } catch (Throwable t) {
@@ -731,6 +725,24 @@ public class ExtensionLoader<T> {
         }
     }
 
+    private T postProcessBeforeInitialization(T instance, String name) throws Exception {
+        if (extensionPostProcessors != null) {
+            for (ExtensionPostProcessor processor : extensionPostProcessors) {
+                instance = (T) processor.postProcessBeforeInitialization(instance, name);
+            }
+        }
+        return instance;
+    }
+
+    private T postProcessAfterInitialization(T instance, String name) throws Exception {
+        if (extensionPostProcessors != null) {
+            for (ExtensionPostProcessor processor : extensionPostProcessors) {
+                instance = (T) processor.postProcessAfterInitialization(instance, name);
+            }
+        }
+        return instance;
+    }
+
     private boolean containsExtension(String name) {
         return getExtensionClasses().containsKey(name);
     }
@@ -839,8 +851,11 @@ public class ExtensionLoader<T> {
         Map<String, Class<?>> extensionClasses = new HashMap<>();
 
         for (LoadingStrategy strategy : strategies) {
-            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
-            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
+            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(),
+                strategy.overridden(), strategy.excludedPackages());
+            String oldType = this.type.getName().replace("org.apache", "com.alibaba");
+            loadDirectory(extensionClasses, strategy.directory(), oldType, strategy.preferExtensionClassLoader(),
+                strategy.overridden(), strategy.excludedPackages());
         }
 
         return extensionClasses;
@@ -933,7 +948,8 @@ public class ExtensionLoader<T> {
                                 loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
                             }
                         } catch (Throwable t) {
-                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
+                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type +
+                                ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                             exceptions.put(line, e);
                         }
                     }
@@ -1086,7 +1102,11 @@ public class ExtensionLoader<T> {
     @SuppressWarnings("unchecked")
     private T createAdaptiveExtension() {
         try {
-            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
+            T instance = (T) getAdaptiveExtensionClass().newInstance();
+            instance = postProcessBeforeInitialization(instance, null);
+            instance = injectExtension(instance);
+            instance = postProcessAfterInitialization(instance, null);
+            return instance;
         } catch (Exception e) {
             throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
         }
@@ -1103,17 +1123,25 @@ public class ExtensionLoader<T> {
     private Class<?> createAdaptiveExtensionClass() {
         ClassLoader classLoader = findClassLoader();
         try {
-            if (ApplicationModel.getEnvironment().getConfiguration().getBoolean(NATIVE, false)) {
+            if (getEnvironment().getConfiguration().getBoolean(NATIVE, false)) {
                 return classLoader.loadClass(type.getName() + "$Adaptive");
             }
         } catch (Throwable ignore) {
 
         }
         String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
-        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
+        org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector.getExtensionLoader(
+            org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
         return compiler.compile(code, classLoader);
     }
 
+    private Environment getEnvironment() {
+        if (environment == null) {
+            environment = (Environment) extensionDirector.getExtensionLoader(FrameworkExt.class).getExtension(Environment.NAME);
+        }
+        return environment;
+    }
+
     @Override
     public String toString() {
         return this.getClass().getName() + "[" + type.getName() + "]";
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionPostProcessor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionPostProcessor.java
new file mode 100644
index 0000000..fcc4822
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionPostProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.common.extension;
+
+/**
+ * A Post-processor called before or after extension initialization.
+ */
+public interface ExtensionPostProcessor {
+
+    default Object postProcessBeforeInitialization(Object instance, String name) throws Exception {
+        return instance;
+    }
+
+    default Object postProcessAfterInitialization(Object instance, String name) throws Exception {
+        return instance;
+    }
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionScope.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionScope.java
new file mode 100644
index 0000000..4391e52
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionScope.java
@@ -0,0 +1,39 @@
+/*
+ * 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.common.extension;
+
+/**
+ * Extension SPI Scope
+ */
+public enum ExtensionScope {
+
+    /**
+     * The extension instance is used within framework, shared with all applications and modules.
+     */
+    FRAMEWORK,
+
+    /**
+     * The extension instance is used within one application, shared with all modules of the application,
+     * and different applications create different extension instances.
+     */
+    APPLICATION,
+
+    /**
+     * The extension instance is used within one module, and different modules create different extension instances.
+     */
+    MODULE
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/SPI.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/SPI.java
index 70aa5b7..76e9fd5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/SPI.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/SPI.java
@@ -61,4 +61,8 @@ public @interface SPI {
      */
     String value() default "";
 
+    /**
+     * scope of SPI
+     */
+    ExtensionScope scope() default ExtensionScope.FRAMEWORK;
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
index 9e0521b..0d47697 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
@@ -18,7 +18,9 @@ package org.apache.dubbo.rpc.model;
 
 import org.apache.dubbo.common.config.Environment;
 import org.apache.dubbo.common.context.FrameworkExt;
+import org.apache.dubbo.common.extension.ExtensionDirector;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.extension.ExtensionScope;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
@@ -46,14 +48,43 @@ import java.util.concurrent.atomic.AtomicBoolean;
 public class ApplicationModel {
     protected static final Logger LOGGER = LoggerFactory.getLogger(ApplicationModel.class);
     public static final String NAME = "application";
+    private static volatile ApplicationModel defaultInstance;
 
-    private static AtomicBoolean INIT_FLAG = new AtomicBoolean(false);
-    private static Environment environment;
-    private static ConfigManager configManager;
-    private static ServiceRepository serviceRepository;
+    private AtomicBoolean initFlag = new AtomicBoolean(false);
+    private Environment environment;
+    private ConfigManager configManager;
+    private ServiceRepository serviceRepository;
 
-    public static void init() {
-        if (INIT_FLAG.compareAndSet(false, true)) {
+    private FrameworkModel frameworkModel;
+    private ExtensionDirector extensionDirector;
+
+    public ApplicationModel(FrameworkModel frameworkModel) {
+        this.frameworkModel = frameworkModel;
+        extensionDirector = new ExtensionDirector(frameworkModel.getExtensionDirector(), ExtensionScope.APPLICATION);
+        extensionDirector.addExtensionPostProcessor(new ModelAwarePostProcessor(this));
+    }
+
+    public static ApplicationModel defaultModel() {
+        if (defaultInstance == null) {
+            synchronized (ApplicationModel.class) {
+                if (defaultInstance == null) {
+                    defaultInstance = new ApplicationModel(FrameworkModel.defaultModel());
+                }
+            }
+        }
+        return defaultInstance;
+    }
+
+    public FrameworkModel getFrameworkModel() {
+        return frameworkModel;
+    }
+
+    public ExtensionDirector getExtensionDirector() {
+        return extensionDirector;
+    }
+
+    public void init() {
+        if (initFlag.compareAndSet(false, true)) {
             ExtensionLoader<ApplicationInitListener> extensionLoader = ExtensionLoader.getExtensionLoader(ApplicationInitListener.class);
             Set<String> listenerNames = extensionLoader.getSupportedExtensions();
             for (String listenerName : listenerNames) {
@@ -62,99 +93,76 @@ public class ApplicationModel {
         }
     }
 
-    public static Collection<ConsumerModel> allConsumerModels() {
+    public void destroy() {
+        // TODO destroy application resources
+    }
+
+    public Collection<ConsumerModel> allConsumerModels() {
         return getServiceRepository().getReferredServices();
     }
 
-    public static Collection<ProviderModel> allProviderModels() {
+    public Collection<ProviderModel> allProviderModels() {
         return getServiceRepository().getExportedServices();
     }
 
-    public static ProviderModel getProviderModel(String serviceKey) {
+    public ProviderModel getProviderModel(String serviceKey) {
         return getServiceRepository().lookupExportedService(serviceKey);
     }
 
-    public static ConsumerModel getConsumerModel(String serviceKey) {
+    public ConsumerModel getConsumerModel(String serviceKey) {
         return getServiceRepository().lookupReferredService(serviceKey);
     }
 
-    private static ExtensionLoader<FrameworkExt> LOADER = ExtensionLoader.getExtensionLoader(FrameworkExt.class);
-
-    public static void initFrameworkExts() {
-        Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
+    public void initFrameworkExts() {
+        Set<FrameworkExt> exts = extensionDirector.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
         for (FrameworkExt ext : exts) {
             ext.initialize();
         }
     }
 
-    public static Environment getEnvironment() {
+    public Environment getEnvironment() {
         if (environment == null) {
-            environment = (Environment) LOADER.getExtension(Environment.NAME);
+            environment = (Environment) extensionDirector.getExtensionLoader(FrameworkExt.class)
+                .getExtension(Environment.NAME);
         }
         return environment;
     }
 
-    public static ConfigManager getConfigManager() {
+    public ConfigManager getConfigManager() {
         if (configManager == null) {
-            configManager = (ConfigManager) LOADER.getExtension(ConfigManager.NAME);
+            configManager = (ConfigManager) extensionDirector.getExtensionLoader(FrameworkExt.class)
+                .getExtension(ConfigManager.NAME);
         }
         return configManager;
     }
 
-    public static ServiceRepository getServiceRepository() {
+    public ServiceRepository getServiceRepository() {
         if (serviceRepository == null) {
-            serviceRepository = (ServiceRepository) LOADER.getExtension(ServiceRepository.NAME);
+            serviceRepository = (ServiceRepository) extensionDirector.getExtensionLoader(FrameworkExt.class)
+                .getExtension(ServiceRepository.NAME);
         }
         return serviceRepository;
     }
 
-    public static ExecutorRepository getExecutorRepository() {
-        return ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+    public ExecutorRepository getExecutorRepository() {
+        return extensionDirector.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
     }
 
-    public static ApplicationConfig getApplicationConfig() {
+    public ApplicationConfig getApplicationConfig() {
         return getConfigManager().getApplicationOrElseThrow();
     }
 
-    public static String getName() {
+    public String getName() {
         return getApplicationConfig().getName();
     }
 
-    @Deprecated
-    //It will be remove at next version
-    private static String application;
-
-    /**
-     *
-     * @deprecated Use {@link #getName()} instead. It will be remove at next version.
-     */
-    @Deprecated
-    public static String getApplication() {
-        return application == null ? getName() : application;
-    }
-
-    // Currently used by UT, it will be remove at next version.
-    @Deprecated
-    public static void setApplication(String application) {
-        ApplicationModel.application = application;
-    }
-
     // only for unit test
+    @Deprecated
     public static void reset() {
-        if (serviceRepository!=null){
-            serviceRepository.destroy();
-            serviceRepository = null;
-        }
-        if (configManager != null) {
-            configManager.destroy();
-            configManager = null;
-        }
-        if (environment != null) {
-            environment.destroy();
-            environment = null;
+        if (defaultInstance != null) {
+            defaultInstance.destroy();
+            defaultInstance = null;
         }
-        ExtensionLoader.resetExtensionLoader(FrameworkExt.class);
-        LOADER = ExtensionLoader.getExtensionLoader(FrameworkExt.class);
     }
 
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkModel.java
new file mode 100644
index 0000000..859f5e8
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkModel.java
@@ -0,0 +1,50 @@
+/*
+ * 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.model;
+
+import org.apache.dubbo.common.extension.ExtensionDirector;
+import org.apache.dubbo.common.extension.ExtensionScope;
+
+/**
+ * Model of dubbo framework, it can be shared with multiple applications.
+ */
+public class FrameworkModel {
+
+    private volatile static FrameworkModel defaultInstance;
+
+    private final ExtensionDirector extensionDirector;
+
+    public FrameworkModel() {
+        extensionDirector = new ExtensionDirector(null, ExtensionScope.FRAMEWORK);
+        extensionDirector.addExtensionPostProcessor(new ModelAwarePostProcessor(this));
+    }
+
+    public ExtensionDirector getExtensionDirector() {
+        return extensionDirector;
+    }
+
+    public static FrameworkModel defaultModel() {
+        if (defaultInstance == null) {
+            synchronized (FrameworkModel.class) {
+                if (defaultInstance == null) {
+                    defaultInstance = new FrameworkModel();
+                }
+            }
+        }
+        return defaultInstance;
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelAware.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelAware.java
new file mode 100644
index 0000000..dafa769
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelAware.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.model;
+
+/**
+ * Interface to inject FrameworkModel/ApplicationModel/ModuleModel for SPI extension.
+ */
+public interface ModelAware {
+
+    default void setFrameworkModel(FrameworkModel frameworkModel) {
+    }
+
+    default void setApplicationModel(ApplicationModel applicationModel) {
+    }
+
+    default void setModuleModel(ModuleModel moduleModel) {
+    }
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelAwarePostProcessor.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelAwarePostProcessor.java
new file mode 100644
index 0000000..2e93318
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelAwarePostProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * 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.model;
+
+import org.apache.dubbo.common.extension.ExtensionPostProcessor;
+
+public class ModelAwarePostProcessor implements ExtensionPostProcessor {
+    private Object model;
+    private FrameworkModel frameworkModel;
+    private ApplicationModel applicationModel;
+    private ModuleModel moduleModel;
+
+    public ModelAwarePostProcessor(Object model) {
+        this.model = model;
+        if (model instanceof FrameworkModel) {
+            frameworkModel = (FrameworkModel) model;
+        } else if (model instanceof ApplicationModel) {
+            applicationModel = (ApplicationModel) model;
+            frameworkModel = applicationModel.getFrameworkModel();
+        } else if (model instanceof ModuleModel) {
+            moduleModel = (ModuleModel) model;
+            applicationModel = moduleModel.getApplicationModel();
+            frameworkModel = applicationModel.getFrameworkModel();
+        }
+    }
+
+    @Override
+    public Object postProcessAfterInitialization(Object instance, String name) throws Exception {
+        if (instance instanceof ModelAware) {
+            ModelAware modelAware = (ModelAware) instance;
+            if (this.applicationModel != null) {
+                modelAware.setApplicationModel(this.applicationModel);
+            }
+            if (this.moduleModel != null) {
+                modelAware.setModuleModel(this.moduleModel);
+            }
+            if (this.frameworkModel != null) {
+                modelAware.setFrameworkModel(this.frameworkModel);
+            }
+        }
+        return instance;
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
new file mode 100644
index 0000000..1334b80
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
@@ -0,0 +1,44 @@
+/*
+ * 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.model;
+
+import org.apache.dubbo.common.extension.ExtensionDirector;
+import org.apache.dubbo.common.extension.ExtensionScope;
+
+/**
+ * Model of a service module
+ */
+public class ModuleModel {
+
+    private final ApplicationModel applicationModel;
+    private final ExtensionDirector extensionDirector;
+
+    public ModuleModel(ApplicationModel applicationModel) {
+        this.applicationModel = applicationModel;
+        extensionDirector = new ExtensionDirector(applicationModel.getExtensionDirector(), ExtensionScope.MODULE);
+        extensionDirector.addExtensionPostProcessor(new ModelAwarePostProcessor(this));
+    }
+
+    public ApplicationModel getApplicationModel() {
+        return applicationModel;
+    }
+
+    public ExtensionDirector getExtensionDirector() {
+        return extensionDirector;
+    }
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionDirectorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionDirectorTest.java
new file mode 100644
index 0000000..1c09a53
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionDirectorTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.extension;
+
+import org.apache.dubbo.common.extension.ExtensionDirector;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.director.ApplicationService;
+import org.apache.dubbo.common.extension.director.FrameworkService;
+import org.apache.dubbo.common.extension.director.ModuleService;
+import org.apache.dubbo.common.extension.director.impl.TestApplicationService;
+import org.apache.dubbo.common.extension.director.impl.TestFrameworkService;
+import org.apache.dubbo.common.extension.director.impl.TestModuleService;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ExtensionDirectorTest {
+
+    String testFwSrvName = "testFwSrv";
+    String testAppSrvName = "testAppSrv";
+    String testMdSrvName = "testMdSrv";
+
+    @Test
+    public void testInheritanceAndScope() {
+
+        // expecting:
+        // 1. SPI extension only be created in ExtensionDirector which matched scope
+        // 2. Child ExtensionDirector can get extension instance from parent
+        // 3. Parent ExtensionDirector can't get extension instance from child
+
+        ExtensionDirector fwExtensionDirector = new ExtensionDirector(null, ExtensionScope.FRAMEWORK);
+        ExtensionDirector appExtensionDirector = new ExtensionDirector(fwExtensionDirector, ExtensionScope.APPLICATION);
+        ExtensionDirector moduleExtensionDirector = new ExtensionDirector(appExtensionDirector, ExtensionScope.MODULE);
+
+        // test module extension loader
+        FrameworkService testFwSrvFromModule = moduleExtensionDirector.getExtension(FrameworkService.class, testFwSrvName);
+        ApplicationService testAppSrvFromModule = moduleExtensionDirector.getExtension(ApplicationService.class, testAppSrvName);
+        ModuleService testMdSrvFromModule = moduleExtensionDirector.getExtension(ModuleService.class, testMdSrvName);
+
+        Assertions.assertNotNull(testFwSrvFromModule);
+        Assertions.assertNotNull(testAppSrvFromModule);
+        Assertions.assertNotNull(testMdSrvFromModule);
+
+        // test app extension loader
+        FrameworkService testFwSrvFromApp = appExtensionDirector.getExtension(FrameworkService.class, testFwSrvName);
+        ApplicationService testAppSrvFromApp = appExtensionDirector.getExtension(ApplicationService.class, testAppSrvName);
+        ModuleService testMdSrvFromApp = appExtensionDirector.getExtension(ModuleService.class, testMdSrvName);
+
+        Assertions.assertSame(testFwSrvFromApp, testFwSrvFromModule);
+        Assertions.assertSame(testAppSrvFromApp, testAppSrvFromModule);
+        Assertions.assertNull(testMdSrvFromApp);
+
+        // test framework extension loader
+        FrameworkService testFwSrvFromFw = fwExtensionDirector.getExtension(FrameworkService.class, testFwSrvName);
+        ApplicationService testAppSrvFromFw = fwExtensionDirector.getExtension(ApplicationService.class, testAppSrvName);
+        ModuleService testMdSrvFromFw = fwExtensionDirector.getExtension(ModuleService.class, testMdSrvName);
+
+        Assertions.assertSame(testFwSrvFromFw, testFwSrvFromApp);
+        Assertions.assertNull(testAppSrvFromFw);
+        Assertions.assertNull(testMdSrvFromFw);
+    }
+
+    @Test
+    public void testPostProcessor() {
+
+    }
+
+    @Test
+    public void testModelAware() {
+        // Expecting:
+        // 1. Module scope SPI can be injected ModuleModel, ApplicationModel, FrameworkModel
+        // 2. Application scope SPI can be injected ApplicationModel, FrameworkModel, but not ModuleModel
+        // 3. Framework scope SPI can be injected FrameworkModel, but not ModuleModel, ApplicationModel
+
+        FrameworkModel frameworkModel = new FrameworkModel();
+        ApplicationModel applicationModel = new ApplicationModel(frameworkModel);
+        ModuleModel moduleModel = new ModuleModel(applicationModel);
+
+        ExtensionDirector moduleExtensionDirector = moduleModel.getExtensionDirector();
+        ExtensionDirector appExtensionDirector = applicationModel.getExtensionDirector();
+        ExtensionDirector fwExtensionDirector = frameworkModel.getExtensionDirector();
+
+        // check extension director inheritance
+        Assertions.assertSame(appExtensionDirector, moduleExtensionDirector.getParent());
+        Assertions.assertSame(fwExtensionDirector, appExtensionDirector.getParent());
+        Assertions.assertSame(null, fwExtensionDirector.getParent());
+
+        // check module extension aware
+        TestFrameworkService testFwSrvFromModule = (TestFrameworkService) moduleExtensionDirector.getExtension(FrameworkService.class, testFwSrvName);
+        TestApplicationService testAppSrvFromModule = (TestApplicationService) moduleExtensionDirector.getExtension(ApplicationService.class, testAppSrvName);
+        TestModuleService testMdSrvFromModule = (TestModuleService) moduleExtensionDirector.getExtension(ModuleService.class, testMdSrvName);
+
+        Assertions.assertSame(frameworkModel, testFwSrvFromModule.getFrameworkModel());
+        Assertions.assertSame(null, testFwSrvFromModule.getApplicationModel());
+        Assertions.assertSame(null, testFwSrvFromModule.getModuleModel());
+
+        Assertions.assertSame(frameworkModel, testAppSrvFromModule.getFrameworkModel());
+        Assertions.assertSame(applicationModel, testAppSrvFromModule.getApplicationModel());
+        Assertions.assertSame(null, testAppSrvFromModule.getModuleModel());
+
+        Assertions.assertSame(frameworkModel, testMdSrvFromModule.getFrameworkModel());
+        Assertions.assertSame(applicationModel, testMdSrvFromModule.getApplicationModel());
+        Assertions.assertSame(moduleModel, testMdSrvFromModule.getModuleModel());
+
+        // check app extension aware
+        TestFrameworkService testFwSrvFromApp = (TestFrameworkService) appExtensionDirector.getExtension(FrameworkService.class, testFwSrvName);
+        TestApplicationService testAppSrvFromApp = (TestApplicationService) appExtensionDirector.getExtension(ApplicationService.class, testAppSrvName);
+        TestModuleService testMdSrvFromApp = (TestModuleService) appExtensionDirector.getExtension(ModuleService.class, testMdSrvName);
+
+        Assertions.assertSame(testFwSrvFromApp, testFwSrvFromModule);
+        Assertions.assertSame(testAppSrvFromApp, testAppSrvFromModule);
+        Assertions.assertNull(testMdSrvFromApp);
+
+        // check framework extension aware
+        FrameworkService testFwSrvFromFw = fwExtensionDirector.getExtension(FrameworkService.class, testFwSrvName);
+        ApplicationService testAppSrvFromFw = fwExtensionDirector.getExtension(ApplicationService.class, testAppSrvName);
+        ModuleService testMdSrvFromFw = fwExtensionDirector.getExtension(ModuleService.class, testMdSrvName);
+
+        Assertions.assertSame(testFwSrvFromFw, testFwSrvFromApp);
+        Assertions.assertNull(testAppSrvFromFw);
+        Assertions.assertNull(testMdSrvFromFw);
+
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java
index 57736b8..b9b80b2 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java
@@ -61,6 +61,7 @@ import org.apache.dubbo.common.extension.injection.impl.InjectExtImpl;
 import org.apache.dubbo.common.lang.Prioritized;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
 
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -70,7 +71,6 @@ import java.util.List;
 import java.util.Set;
 
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 import static org.apache.dubbo.common.extension.ExtensionLoader.getLoadingStrategies;
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.anyOf;
@@ -84,6 +84,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
 public class ExtensionLoaderTest {
+
+    private <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
+        return ApplicationModel.defaultModel().getExtensionDirector().getExtensionLoader(type);
+    }
+
     @Test
     public void test_getExtensionLoader_Null() throws Exception {
         try {
@@ -559,7 +564,7 @@ public class ExtensionLoaderTest {
         List<LoadingStrategy> loadingStrategies = ExtensionLoader.getLoadingStrategies();
         ExtensionLoader.setLoadingStrategies(new DubboExternalLoadingStrategyTest(false),
                 new DubboInternalLoadingStrategyTest(false));
-        ExtensionLoader<DuplicatedWithoutOverriddenExt> extensionLoader = ExtensionLoader.getExtensionLoader(DuplicatedWithoutOverriddenExt.class);
+        ExtensionLoader<DuplicatedWithoutOverriddenExt> extensionLoader = getExtensionLoader(DuplicatedWithoutOverriddenExt.class);
         try {
             extensionLoader.getExtension("duplicated");
             fail();
@@ -577,7 +582,7 @@ public class ExtensionLoaderTest {
         List<LoadingStrategy> loadingStrategies = ExtensionLoader.getLoadingStrategies();
         ExtensionLoader.setLoadingStrategies(new DubboExternalLoadingStrategyTest(true),
                 new DubboInternalLoadingStrategyTest(true));
-        ExtensionLoader<DuplicatedOverriddenExt> extensionLoader = ExtensionLoader.getExtensionLoader(DuplicatedOverriddenExt.class);
+        ExtensionLoader<DuplicatedOverriddenExt> extensionLoader = getExtensionLoader(DuplicatedOverriddenExt.class);
         DuplicatedOverriddenExt duplicatedOverriddenExt = extensionLoader.getExtension("duplicated");
         assertEquals("DuplicatedOverriddenExt1", duplicatedOverriddenExt.echo());
         //recover the loading strategies
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/ApplicationService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/ApplicationService.java
new file mode 100644
index 0000000..c0dff97
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/ApplicationService.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.extension.director;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+@SPI(scope = ExtensionScope.APPLICATION)
+public interface ApplicationService {
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FrameworkService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FrameworkService.java
new file mode 100644
index 0000000..9703689
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FrameworkService.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.extension.director;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface FrameworkService {
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/ModuleService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/ModuleService.java
new file mode 100644
index 0000000..a9b940f
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/ModuleService.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.extension.director;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+@SPI(scope = ExtensionScope.MODULE)
+public interface ModuleService {
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/BaseTestService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/BaseTestService.java
new file mode 100644
index 0000000..c3399b8
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/BaseTestService.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.extension.director.impl;
+
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModelAware;
+import org.apache.dubbo.rpc.model.ModuleModel;
+
+public class BaseTestService implements ModelAware {
+    private FrameworkModel frameworkModel;
+    private ApplicationModel applicationModel;
+    private ModuleModel moduleModel;
+
+    @Override
+    public void setFrameworkModel(FrameworkModel frameworkModel) {
+        this.frameworkModel = frameworkModel;
+    }
+
+    @Override
+    public void setApplicationModel(ApplicationModel applicationModel) {
+        this.applicationModel = applicationModel;
+    }
+
+    @Override
+    public void setModuleModel(ModuleModel moduleModel) {
+        this.moduleModel = moduleModel;
+    }
+
+    public FrameworkModel getFrameworkModel() {
+        return frameworkModel;
+    }
+
+    public ApplicationModel getApplicationModel() {
+        return applicationModel;
+    }
+
+    public ModuleModel getModuleModel() {
+        return moduleModel;
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestApplicationService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestApplicationService.java
new file mode 100644
index 0000000..9a0e1ef
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestApplicationService.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.extension.director.impl;
+
+import org.apache.dubbo.common.extension.director.ApplicationService;
+
+public class TestApplicationService extends BaseTestService implements ApplicationService {
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestFrameworkService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestFrameworkService.java
new file mode 100644
index 0000000..416ba0d
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestFrameworkService.java
@@ -0,0 +1,23 @@
+/*
+ * 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.common.extension.director.impl;
+
+import org.apache.dubbo.common.extension.director.FrameworkService;
+
+public class TestFrameworkService extends BaseTestService implements FrameworkService {
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestModuleService.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestModuleService.java
new file mode 100644
index 0000000..afdbecb
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestModuleService.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.extension.director.impl;
+
+import org.apache.dubbo.common.extension.director.ModuleService;
+
+public class TestModuleService extends BaseTestService implements ModuleService {
+}
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.ApplicationService b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.ApplicationService
new file mode 100644
index 0000000..10d253b
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.ApplicationService
@@ -0,0 +1 @@
+testAppSrv=org.apache.dubbo.common.extension.director.impl.TestApplicationService
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FrameworkService b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FrameworkService
new file mode 100644
index 0000000..f695488
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FrameworkService
@@ -0,0 +1 @@
+testFwSrv=org.apache.dubbo.common.extension.director.impl.TestFrameworkService
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.ModuleService b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.ModuleService
new file mode 100644
index 0000000..9d3ef32
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.ModuleService
@@ -0,0 +1 @@
+testMdSrv=org.apache.dubbo.common.extension.director.impl.TestModuleService