You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2020/04/29 01:14:49 UTC

[dubbo] branch master updated: [Enhancement] Adding the overriding rule for ExtensionLoader (#6068)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7234078  [Enhancement] Adding the overriding rule for ExtensionLoader (#6068)
7234078 is described below

commit 7234078e8cf49f4ee0231b6cd0237fb730446dd1
Author: Mercy Ma <me...@gmail.com>
AuthorDate: Wed Apr 29 09:14:25 2020 +0800

    [Enhancement] Adding the overriding rule for ExtensionLoader (#6068)
---
 dubbo-all/pom.xml                                  | 10 +++
 ...tegy.java => DubboInternalLoadingStrategy.java} | 18 +++--
 ...dingStrategy.java => DubboLoadingStrategy.java} | 25 +++++--
 .../dubbo/common/extension/ExtensionLoader.java    | 84 +++++++++++++---------
 .../dubbo/common/extension/LoadingStrategy.java    | 15 +++-
 ...gStrategy.java => ServicesLoadingStrategy.java} | 24 +++++--
 ...g.apache.dubbo.common.extension.LoadingStrategy |  3 +
 .../extension/DubboExternalLoadingStrategy.java}   | 23 ++++--
 .../common/extension/ExtensionLoaderTest.java      | 64 +++++++++++++++++
 .../convert/String2BooleanConverter.java}          | 19 +++--
 .../extension/convert/String2DoubleConverter.java} | 21 +++---
 .../convert/String2IntegerConverter.java}          | 19 +++--
 .../org.apache.dubbo.common.convert.Converter      |  2 +
 .../org.apache.dubbo.common.convert.Converter      |  3 +
 ...g.apache.dubbo.common.extension.LoadingStrategy |  1 +
 .../remoting/http/servlet/ServletHttpBinder.java   | 68 +++++++++---------
 16 files changed, 277 insertions(+), 122 deletions(-)

diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml
index 677b8ef..872fc30 100644
--- a/dubbo-all/pom.xml
+++ b/dubbo-all/pom.xml
@@ -967,6 +967,16 @@
                                     </resource>
                                 </transformer>
 
+                                <!-- @since 2.7.7 -->
+
+                                <!-- 'dubbo-common' module -->
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
+                                    </resource>
+                                </transformer>
+
                             </transformers>
                             <filters>
                                 <filter>
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboInternalLoadingStrategy.java
similarity index 73%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboInternalLoadingStrategy.java
index 2de3f2e..841679f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboInternalLoadingStrategy.java
@@ -16,14 +16,20 @@
  */
 package org.apache.dubbo.common.extension;
 
-public interface LoadingStrategy {
-    String directory();
+/**
+ * Dubbo internal {@link LoadingStrategy}
+ *
+ * @since 2.7.7
+ */
+public class DubboInternalLoadingStrategy implements LoadingStrategy {
 
-    default boolean preferExtensionClassLoader() {
-        return false;
+    @Override
+    public String directory() {
+        return "META-INF/dubbo/internal/";
     }
 
-    default String[] excludedPackages() {
-        return null;
+    @Override
+    public int getPriority() {
+        return MAX_PRIORITY;
     }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboLoadingStrategy.java
similarity index 70%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboLoadingStrategy.java
index 2de3f2e..0a74969 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboLoadingStrategy.java
@@ -16,14 +16,27 @@
  */
 package org.apache.dubbo.common.extension;
 
-public interface LoadingStrategy {
-    String directory();
+/**
+ * Dubbo {@link LoadingStrategy}
+ *
+ * @since 2.7.7
+ */
+public class DubboLoadingStrategy implements LoadingStrategy {
+
+    @Override
+    public String directory() {
+        return "META-INF/dubbo/";
+    }
 
-    default boolean preferExtensionClassLoader() {
-        return false;
+    @Override
+    public boolean overridden() {
+        return true;
     }
 
-    default String[] excludedPackages() {
-        return null;
+    @Override
+    public int getPriority() {
+        return NORMAL_PRIORITY;
     }
+
+
 }
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 844b51b..cf34202 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
@@ -45,24 +45,27 @@ import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.regex.Pattern;
 
+import static java.util.Arrays.asList;
 import static java.util.Collections.sort;
+import static java.util.ServiceLoader.load;
+import static java.util.stream.StreamSupport.stream;
 import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX;
 
 /**
- *
  * {@link org.apache.dubbo.rpc.model.ApplicationModel}, {@code DubboBootstrap} and this class are
  * at present designed to be singleton or static (by itself totally static or uses some static fields).
  * So the instances returned from them are of process or classloader scope. If you want to support
  * multiple dubbo servers in a single process, you may need to refactor these three classes.
- *
+ * <p>
  * Load dubbo extensions
  * <ul>
  * <li>auto inject dependency extension </li>
@@ -79,12 +82,6 @@ public class ExtensionLoader<T> {
 
     private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
 
-    private static final String SERVICES_DIRECTORY = "META-INF/services/";
-
-    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
-
-    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
-
     private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
 
     private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
@@ -110,14 +107,36 @@ public class ExtensionLoader<T> {
 
     private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
 
-    private static LoadingStrategy DUBBO_INTERNAL_STRATEGY =  () -> DUBBO_INTERNAL_DIRECTORY;
-    private static LoadingStrategy DUBBO_STRATEGY = () -> DUBBO_DIRECTORY;
-    private static LoadingStrategy SERVICES_STRATEGY = () -> SERVICES_DIRECTORY;
-
-    private static LoadingStrategy[] strategies = new LoadingStrategy[] { DUBBO_INTERNAL_STRATEGY, DUBBO_STRATEGY, SERVICES_STRATEGY };
+    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
 
     public static void setLoadingStrategies(LoadingStrategy... strategies) {
-        ExtensionLoader.strategies = strategies;
+        if (ArrayUtils.isNotEmpty(strategies)) {
+            ExtensionLoader.strategies = strategies;
+        }
+    }
+
+    /**
+     * Load all {@link Prioritized prioritized} {@link LoadingStrategy Loading Strategies} via {@link ServiceLoader}
+     *
+     * @return non-null
+     * @since 2.7.7
+     */
+    private static LoadingStrategy[] loadLoadingStrategies() {
+        return stream(load(LoadingStrategy.class).spliterator(), false)
+                .sorted()
+                .toArray(LoadingStrategy[]::new);
+    }
+
+    /**
+     * Get all {@link LoadingStrategy Loading Strategies}
+     *
+     * @return non-null
+     * @see LoadingStrategy
+     * @see Prioritized
+     * @since 2.7.7
+     */
+    public static List<LoadingStrategy> getLoadingStrategies() {
+        return asList(strategies);
     }
 
     private ExtensionLoader(Class<?> type) {
@@ -239,7 +258,7 @@ public class ExtensionLoader<T> {
      */
     public List<T> getActivateExtension(URL url, String[] values, String group) {
         List<T> activateExtensions = new ArrayList<>();
-        List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
+        List<String> names = values == null ? new ArrayList<>(0) : asList(values);
         if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
             getExtensionClasses();
             for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
@@ -417,7 +436,7 @@ public class ExtensionLoader<T> {
      * @return non-null
      */
     public T getOrDefaultExtension(String name) {
-        return containsExtension(name)  ? getExtension(name) : getDefaultExtension();
+        return containsExtension(name) ? getExtension(name) : getDefaultExtension();
     }
 
     /**
@@ -727,15 +746,15 @@ public class ExtensionLoader<T> {
 
     /**
      * synchronized in getExtensionClasses
-     * */
+     */
     private Map<String, Class<?>> loadExtensionClasses() {
         cacheDefaultExtensionName();
 
         Map<String, Class<?>> extensionClasses = new HashMap<>();
 
         for (LoadingStrategy strategy : strategies) {
-            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
-            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
+            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());
         }
 
         return extensionClasses;
@@ -764,11 +783,11 @@ public class ExtensionLoader<T> {
     }
 
     private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
-        loadDirectory(extensionClasses, dir, type, false);
+        loadDirectory(extensionClasses, dir, type, false, false);
     }
 
     private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
-                               boolean extensionLoaderClassLoaderFirst, String... excludedPackages) {
+                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
         String fileName = dir + type;
         try {
             Enumeration<java.net.URL> urls = null;
@@ -782,7 +801,7 @@ public class ExtensionLoader<T> {
                 }
             }
 
-            if(urls == null || !urls.hasMoreElements()) {
+            if (urls == null || !urls.hasMoreElements()) {
                 if (classLoader != null) {
                     urls = classLoader.getResources(fileName);
                 } else {
@@ -793,7 +812,7 @@ public class ExtensionLoader<T> {
             if (urls != null) {
                 while (urls.hasMoreElements()) {
                     java.net.URL resourceURL = urls.nextElement();
-                    loadResource(extensionClasses, classLoader, resourceURL, excludedPackages);
+                    loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
                 }
             }
         } catch (Throwable t) {
@@ -803,7 +822,7 @@ public class ExtensionLoader<T> {
     }
 
     private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
-                              java.net.URL resourceURL, String... excludedPackages) {
+                              java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
         try {
             try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                 String line;
@@ -822,7 +841,7 @@ public class ExtensionLoader<T> {
                                 line = line.substring(i + 1).trim();
                             }
                             if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
-                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
+                                loadClass(extensionClasses, resourceURL, Class.forName(line, 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);
@@ -848,14 +867,15 @@ public class ExtensionLoader<T> {
         return false;
     }
 
-    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
+    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
+                           boolean overridden) throws NoSuchMethodException {
         if (!type.isAssignableFrom(clazz)) {
             throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                     type + ", class line: " + clazz.getName() + "), class "
                     + clazz.getName() + " is not subtype of interface.");
         }
         if (clazz.isAnnotationPresent(Adaptive.class)) {
-            cacheAdaptiveClass(clazz);
+            cacheAdaptiveClass(clazz, overridden);
         } else if (isWrapperClass(clazz)) {
             cacheWrapperClass(clazz);
         } else {
@@ -872,7 +892,7 @@ public class ExtensionLoader<T> {
                 cacheActivateClass(clazz, names[0]);
                 for (String n : names) {
                     cacheName(clazz, n);
-                    saveInExtensionClass(extensionClasses, clazz, n);
+                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                 }
             }
         }
@@ -890,9 +910,9 @@ public class ExtensionLoader<T> {
     /**
      * put clazz in extensionClasses
      */
-    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name) {
+    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name, boolean overridden) {
         Class<?> c = extensionClasses.get(name);
-        if (c == null) {
+        if (c == null || overridden) {
             extensionClasses.put(name, clazz);
         } else if (c != clazz) {
             String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName();
@@ -922,8 +942,8 @@ public class ExtensionLoader<T> {
     /**
      * cache Adaptive class which is annotated with <code>Adaptive</code>
      */
-    private void cacheAdaptiveClass(Class<?> clazz) {
-        if (cachedAdaptiveClass == null) {
+    private void cacheAdaptiveClass(Class<?> clazz, boolean overridden) {
+        if (cachedAdaptiveClass == null || overridden) {
             cachedAdaptiveClass = clazz;
         } else if (!cachedAdaptiveClass.equals(clazz)) {
             throw new IllegalStateException("More than 1 adaptive class found: "
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
index 2de3f2e..2d4faf5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
@@ -16,7 +16,10 @@
  */
 package org.apache.dubbo.common.extension;
 
-public interface LoadingStrategy {
+import org.apache.dubbo.common.lang.Prioritized;
+
+public interface LoadingStrategy extends Prioritized {
+
     String directory();
 
     default boolean preferExtensionClassLoader() {
@@ -26,4 +29,14 @@ public interface LoadingStrategy {
     default String[] excludedPackages() {
         return null;
     }
+
+    /**
+     * Indicates current {@link LoadingStrategy} supports overriding other lower prioritized instances or not.
+     *
+     * @return if supports, return <code>true</code>, or <code>false</code>
+     * @since 2.7.7
+     */
+    default boolean overridden() {
+        return false;
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ServicesLoadingStrategy.java
similarity index 70%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/extension/ServicesLoadingStrategy.java
index 2de3f2e..779d0aa 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ServicesLoadingStrategy.java
@@ -16,14 +16,26 @@
  */
 package org.apache.dubbo.common.extension;
 
-public interface LoadingStrategy {
-    String directory();
+/**
+ * Services {@link LoadingStrategy}
+ *
+ * @since 2.7.7
+ */
+public class ServicesLoadingStrategy implements LoadingStrategy {
 
-    default boolean preferExtensionClassLoader() {
-        return false;
+    @Override
+    public String directory() {
+        return "META-INF/services/";
     }
 
-    default String[] excludedPackages() {
-        return null;
+    @Override
+    public boolean overridden() {
+        return true;
     }
+
+    @Override
+    public int getPriority() {
+        return MIN_PRIORITY;
+    }
+
 }
diff --git a/dubbo-common/src/main/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy b/dubbo-common/src/main/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
new file mode 100644
index 0000000..358696e
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
@@ -0,0 +1,3 @@
+org.apache.dubbo.common.extension.DubboInternalLoadingStrategy
+org.apache.dubbo.common.extension.DubboLoadingStrategy
+org.apache.dubbo.common.extension.ServicesLoadingStrategy
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/DubboExternalLoadingStrategy.java
similarity index 68%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/extension/DubboExternalLoadingStrategy.java
index 2de3f2e..9ae2678 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/DubboExternalLoadingStrategy.java
@@ -16,14 +16,25 @@
  */
 package org.apache.dubbo.common.extension;
 
-public interface LoadingStrategy {
-    String directory();
+/**
+ * Dubbo external {@link LoadingStrategy} for testing
+ *
+ * @since 2.7.7
+ */
+public class DubboExternalLoadingStrategy implements LoadingStrategy {
+
+    @Override
+    public String directory() {
+        return "META-INF/dubbo/external/";
+    }
 
-    default boolean preferExtensionClassLoader() {
-        return false;
+    @Override
+    public boolean overridden() {
+        return true;
     }
 
-    default String[] excludedPackages() {
-        return null;
+    @Override
+    public int getPriority() {
+        return MAX_PRIORITY + 1;
     }
 }
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 393ae3a..a1a534a 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
@@ -17,6 +17,10 @@
 package org.apache.dubbo.common.extension;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToBooleanConverter;
+import org.apache.dubbo.common.convert.StringToDoubleConverter;
+import org.apache.dubbo.common.convert.StringToIntegerConverter;
 import org.apache.dubbo.common.extension.activate.ActivateExt1;
 import org.apache.dubbo.common.extension.activate.impl.ActivateExt1Impl1;
 import org.apache.dubbo.common.extension.activate.impl.GroupActivateExtImpl;
@@ -25,6 +29,9 @@ import org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl3;
 import org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl1;
 import org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl2;
 import org.apache.dubbo.common.extension.activate.impl.ValueActivateExtImpl;
+import org.apache.dubbo.common.extension.convert.String2BooleanConverter;
+import org.apache.dubbo.common.extension.convert.String2DoubleConverter;
+import org.apache.dubbo.common.extension.convert.String2IntegerConverter;
 import org.apache.dubbo.common.extension.ext1.SimpleExt;
 import org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl1;
 import org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl2;
@@ -49,6 +56,7 @@ import org.apache.dubbo.common.extension.ext9_empty.Ext9Empty;
 import org.apache.dubbo.common.extension.ext9_empty.impl.Ext9EmptyImpl;
 import org.apache.dubbo.common.extension.injection.InjectExt;
 import org.apache.dubbo.common.extension.injection.impl.InjectExtImpl;
+import org.apache.dubbo.common.lang.Prioritized;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -60,6 +68,7 @@ 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;
 import static org.hamcrest.CoreMatchers.instanceOf;
@@ -466,4 +475,59 @@ public class ExtensionLoaderTest {
         assertEquals(1, loader.getSupportedExtensions().size());
         assertEquals(Collections.singleton("injection"), loader.getSupportedExtensions());
     }
+
+    /**
+     * @since 2.7.7
+     */
+    @Test
+    public void testOverridden() {
+        ExtensionLoader<Converter> loader = getExtensionLoader(Converter.class);
+
+        Converter converter = loader.getExtension("string-to-boolean");
+        assertEquals(String2BooleanConverter.class, converter.getClass());
+
+        converter = loader.getExtension("string-to-double");
+        assertEquals(String2DoubleConverter.class, converter.getClass());
+
+        converter = loader.getExtension("string-to-integer");
+        assertEquals(String2IntegerConverter.class, converter.getClass());
+
+        assertEquals("string-to-boolean", loader.getExtensionName(String2BooleanConverter.class));
+        assertEquals("string-to-boolean", loader.getExtensionName(StringToBooleanConverter.class));
+
+        assertEquals("string-to-double", loader.getExtensionName(String2DoubleConverter.class));
+        assertEquals("string-to-double", loader.getExtensionName(StringToDoubleConverter.class));
+
+        assertEquals("string-to-integer", loader.getExtensionName(String2IntegerConverter.class));
+        assertEquals("string-to-integer", loader.getExtensionName(StringToIntegerConverter.class));
+    }
+
+    /**
+     * @since 2.7.7
+     */
+    @Test
+    public void testGetLoadingStrategies() {
+        List<LoadingStrategy> strategies = getLoadingStrategies();
+
+        assertEquals(4, strategies.size());
+
+        int i = 0;
+
+        LoadingStrategy loadingStrategy = strategies.get(i++);
+        assertEquals(DubboInternalLoadingStrategy.class, loadingStrategy.getClass());
+        assertEquals(Prioritized.MAX_PRIORITY, loadingStrategy.getPriority());
+
+        loadingStrategy = strategies.get(i++);
+        assertEquals(DubboExternalLoadingStrategy.class, loadingStrategy.getClass());
+        assertEquals(Prioritized.MAX_PRIORITY + 1, loadingStrategy.getPriority());
+
+
+        loadingStrategy = strategies.get(i++);
+        assertEquals(DubboLoadingStrategy.class, loadingStrategy.getClass());
+        assertEquals(Prioritized.NORMAL_PRIORITY, loadingStrategy.getPriority());
+
+        loadingStrategy = strategies.get(i++);
+        assertEquals(ServicesLoadingStrategy.class, loadingStrategy.getClass());
+        assertEquals(Prioritized.MIN_PRIORITY, loadingStrategy.getPriority());
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2BooleanConverter.java
similarity index 70%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2BooleanConverter.java
index 2de3f2e..399822a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2BooleanConverter.java
@@ -14,16 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.extension;
+package org.apache.dubbo.common.extension.convert;
 
-public interface LoadingStrategy {
-    String directory();
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToBooleanConverter;
 
-    default boolean preferExtensionClassLoader() {
-        return false;
-    }
-
-    default String[] excludedPackages() {
-        return null;
-    }
+/**
+ * A {@link Converter} implementation of {@linkg String} to {@link Boolean}
+ *
+ * @since 2.7.7
+ */
+public class String2BooleanConverter extends StringToBooleanConverter {
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2DoubleConverter.java
similarity index 70%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2DoubleConverter.java
index 2de3f2e..3cb2786 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2DoubleConverter.java
@@ -14,16 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.extension;
+package org.apache.dubbo.common.extension.convert;
 
-public interface LoadingStrategy {
-    String directory();
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToDoubleConverter;
 
-    default boolean preferExtensionClassLoader() {
-        return false;
-    }
-
-    default String[] excludedPackages() {
-        return null;
-    }
-}
+/**
+ * A {@link Converter} implementation of {@linkg String} to {@link Double}
+ *
+ * @since 2.7.7
+ */
+public class String2DoubleConverter extends StringToDoubleConverter {
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2IntegerConverter.java
similarity index 70%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2IntegerConverter.java
index 2de3f2e..85f51ab 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2IntegerConverter.java
@@ -14,16 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.extension;
+package org.apache.dubbo.common.extension.convert;
 
-public interface LoadingStrategy {
-    String directory();
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToIntegerConverter;
 
-    default boolean preferExtensionClassLoader() {
-        return false;
-    }
-
-    default String[] excludedPackages() {
-        return null;
-    }
+/**
+ * A {@link Converter} implementation of {@linkg String} to {@link Integer}
+ *
+ * @since 2.7.7
+ */
+public class String2IntegerConverter extends StringToIntegerConverter {
 }
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.convert.Converter b/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.convert.Converter
new file mode 100644
index 0000000..917db37
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.convert.Converter
@@ -0,0 +1,2 @@
+# org.apache.dubbo.common.convert.Converter
+string-to-boolean=org.apache.dubbo.common.extension.convert.String2BooleanConverter
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/org.apache.dubbo.common.convert.Converter b/dubbo-common/src/test/resources/META-INF/dubbo/org.apache.dubbo.common.convert.Converter
new file mode 100644
index 0000000..4fddebc
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/org.apache.dubbo.common.convert.Converter
@@ -0,0 +1,3 @@
+# org.apache.dubbo.common.convert.Converter
+string-to-double=org.apache.dubbo.common.extension.convert.String2DoubleConverter
+string-to-integer=org.apache.dubbo.common.extension.convert.String2IntegerConverter
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy b/dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
new file mode 100644
index 0000000..ebae30a
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
@@ -0,0 +1 @@
+org.apache.dubbo.common.extension.DubboExternalLoadingStrategy
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
index cb015b7..447ea4e 100644
--- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
+++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
@@ -1,34 +1,34 @@
-/*
- * 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.remoting.http.servlet;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.remoting.http.HttpBinder;
-import org.apache.dubbo.remoting.http.HttpHandler;
-import org.apache.dubbo.remoting.http.HttpServer;
-
-/**
- * ServletHttpTransporter
- */
-public class ServletHttpBinder implements HttpBinder {
-
-    @Override
-    public HttpServer bind(URL url, HttpHandler handler) {
-        return new ServletHttpServer(url, handler);
-    }
-
-}
+/*
+ * 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.remoting.http.servlet;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+/**
+ * ServletHttpTransporter
+ */
+public class ServletHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new ServletHttpServer(url, handler);
+    }
+
+}