Dubbo中的ExtensionLoader扩展(二)

dubbo中自己实现了不同于java的SPI插件化机制,使得Dubbo可以在对多个指定的目录中加载扩展实现,同时与Java SPI不同的是可以实现按需加载。

Dubbo的扩展SPI有如下特点:
1. 单例,对于某个类型扩展,只会有一个ExtensionLoader;
2. 延迟加载,可以一次只获取想要的扩展点,一次获取想要的扩展点实现;
3. 对于扩展点的Ioc和Aop,就是一个扩展可以注入到另一个扩展中,也可以对一个扩展做wrap包装实现aop的功能;
4. 对于扩展点的调用,真正调用的时候才能确认具体使用的是那个实现。

源码实现

在Dubbo加载扩展点会代码示例如下,具体实现在ExtensionLoader中。接下来会详细介绍。

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");

 getExtensionLoader方法中先从缓存中拿ExtensionLoader,如果没有就new一个,new的过程如下:

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        //也是利用扩展实现,后面会看到在注入其他扩展点或bean到当前扩展时使用
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
objectFactory就是为了获取其他扩展点而设计的,其本身也是使用ExtensionLoader来获取。具体实现看下面的TODO1

1)ExtensionLoader#getAdaptiveExtension

DCL初始化一个默认的自适应扩展实现,如果没有则创建一个createAdaptiveExtension。

2)ExtensionLoader#createAdaptiveExtension

   private T createAdaptiveExtension() {
        try {
            /**
             * 1. getAdaptiveExtensionClass:获取适配器类;
             * 2. injectExtension:为适配器类的setter方法插入其他扩展点或实现bean。
             */
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

接下来讲解一下如何获取到扩展点的,TODO1  最后再详细解释如何在扩展点上注入其他的扩展点

3)ExtensionLoader#getAdaptiveExtensionClass

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

这边会类加载所有的实现class,但是只会实例化一个cachedAdaptiveClass, TODO2 如果找不到,则dubbo通过代码生成一个自适应了扩展实现。

4)ExtensionLoader#getExtensionClasses

 初始化该类型下所有扩展点实现class,并缓存在cachedClasses中

5) ExtensionLoader # loadExtensionClasses

// synchronized in getExtensionClasses
    private Map<String, Class<?>> loadExtensionClasses() {
        // SPI注解中会指定一个默认的实现   列如  interface Cluster 上  @SPI(FailoverCluster.NAME)
        // 则cachedDefaultName = FailoverCluster.NAME
        cacheDefaultExtensionName();
        // 将类型中的所有实现存放在extensionClasses中
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());  //META-INF/dubbo/internal/
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());  // //META-INF/dubbo/
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); // //META-INF/services/
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }

6 ExtensionLoader#loadDirectory -> ExtensionLoader#loadResource -> ExtensionLoader#loadClass

主要是找到加载器,然后读取文件,忽略注释,通过class.forName来类加载具体实现。最后保存在extensionClasses。其中自适应的实现和包装类的实现class,单独赋值。

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) 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.");
        }
        //这里判断是否有自定义的适配器类,如果有,后面获取适配器的时候,就可以直接用这个创建返回,不用dubbo动态创建
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
        } else if (isWrapperClass(clazz)) {
            // 包装类会有一个参数为type的构造器
            cacheWrapperClass(clazz);
        } else { //处理不是包裹类的情况 且 又不带有Adaptive注解
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                // 计算出实现类的name  列如
                // clazz = org.apache.dubbo.common.compiler.support.JdkCompiler.class
                //  type = org.apache.dubbo.common.compiler.Compiler.class
                // 则 name = JdkCompiler - Compiler  = jdk
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }

            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                //判断下Activate注解,后面讲,这里只需要知道,会缓存Activate注解的实现
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    //缓存扩展实现
                    saveInExtensionClass(extensionClasses, clazz, name);
                }
            }
        }
    }

这边不得不强调一下,关于Adaptive注解。这个注解可以修饰具体的扩展实现类,也可以修饰具体扩展的实现类中的方法。

当修饰的是类的时候,将作为默认的扩展点,如果是修饰SPI的方法的时候,将会生成类的Methed$Adaptive中重新该方法,即上面的TODO2的内容。

另外还缓存了Activate类,关于Activate类将会后续介绍 TODO3

 

整个ExtensionLoader的流程相对是比较简单的,无非就是使用到@SPI注解的值或者是@Adaptive自定义的扩展。接下来具体讲解下上面流程中提到的TODO。

TODO1   

实现IOC,一个扩展中初始化另一个扩展, ExtensionLoader#injectExtension

/** 插入该扩展需要其他扩展或bean */
    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {  //   默认objectFactory =  AdaptiveExtensionFactory
                for (Method method : instance.getClass().getMethods()) {
                    if (isSetter(method)) {
                        /**
                         * Check {@link DisableInject} to see if we need auto injection for this property
                         */
                        if (method.getAnnotation(DisableInject.class) != null) {
                            continue;
                        }
                        Class<?> pt = method.getParameterTypes()[0];
                        // 过滤掉基础类型
                        if (ReflectUtils.isPrimitives(pt)) {
                            continue;
                        }
                        try {
                            // for instance: setVersion, return "version"
                            String property = getSetterProperty(method);
                            // 根据参数类型class, 以及方法名中获取的property,通过具体的ExtensionFactory来获取到具体的扩展点
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                // 将扩展实现作为setter方法的参数,来实现注入
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("Failed to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

其中AdaptiveExtensionFactory实际上就是加载所有的非Adaptive的扩展点,然后将其实例保存在List<ExtensionFactory> factories中,当需要获取指定name和type的Extension上的时候,实际上就是遍历所有的ExtensionFactory,直到加载到对应的扩展点。列如需要注入的扩展,即setter方法的参数类型也是注解了SPI,那么就会默认使用到SPIExtensionFactory,即自适应加载。

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        //  获取到可以支持的哪些具体实现
        for (String name : loader.getSupportedExtensions()) {
            // 对所有的ExtensionFactory扩展点  实例化,  实际上就一个SpiExtensionFactory
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

 

 

 TODO2

如果所有的SPI扩展实现都没有找到@Adaptive的扩展,那么dubbo就会生成自适应扩展的代码。根据SPI接口会重写@Adaptive注解的方法,方法参数一定是能够获取到URL的。如果在URL获取不到扩展名,则默认取SPI注解指定的值,然后用这个扩展名,通过ExtensionLoad加载对应的扩展点。最后通过编译器来编译code,加载到$Adaptive结尾的class,编译器也有多种实现,也是通过ExtensionLoad加载

    /** Dubbo生成适配类 */
    private Class<?> createAdaptiveExtensionClass() {
        //动态生成适配器代码
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

这边我们做一个简单的测试,看一下自动生成自适应代码是什么样的。

    public interface Compiler {

        /**
         * Compile java source code.
         *
         * @param code        Java source code
         * @param classLoader classloader
         * @return Compiled class
         */

        Class<?> compile(String code, ClassLoader classLoader);

        @Adaptive({"p1", "p2"})
        Validator getValidator(URL url);
    }

    public static void main(String[] args) {
        String c = new AdaptiveClassCodeGenerator(org.apache.dubbo.common.extension.ExtensionLoader.Compiler.class, "jdk").generate();
        System.out.println(c);
    }

这个测试做的事情就是加载一个org.apache.dubbo.common.extension.ExtensionLoader.Compiler.class的实现,但是由于没有找到任何的扩展点,所以就采用自动生成code,编译的方式。

其结果如下:

package org.apache.dubbo.common.extension;
import org.apache.dubbo.common.extension.ExtensionLoader;

public class Compiler$Adaptive implements org.apache.dubbo.common.extension.ExtensionLoader.Compiler {
    public java.lang.Class compile(java.lang.String arg0, java.lang.ClassLoader arg1) {
        throw new UnsupportedOperationException("The method public abstract java.lang.Class org.apache.dubbo.common.extension.ExtensionLoader$Compiler.compile(java.lang.String,java.lang.ClassLoader) of interface org.apache.dubbo.common.extension.ExtensionLoader$Compiler is not adaptive method!");
    }

    public javax.xml.validation.Validator getValidator(org.apache.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("p1", url.getParameter("p2", "jdk"));
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.ExtensionLoader$Compiler) name from url (" + url.toString() + ") use keys([p1, p2])");
        org.apache.dubbo.common.extension.ExtensionLoader$Compiler extension = (org.apache.dubbo.common.extension.ExtensionLoader$Compiler) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.ExtensionLoader$Compiler.class).getExtension(extName);
        return extension.getValidator(arg0);
    }
}

其特点如下:

1) 生成一个$Adaptive后缀的class

2)  如果没有@Adaptive注解的方法,则throw一个UnsupportedOperationException

3)对于使用的Adaptive注解的方法,其参数列表一定需要一个URL类型的参数,根据上面的配置,从URL中获取P1是值,如果获取不到则获取P2的值,如果再获取不到,则使用默认的值

4)根据第三步获取的值,从ExtensionLoader加载指定的扩展点实现。

 

TODO3 

先来看一下@Activate注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    /**
     * Group过滤条件。
     * <br />
     * 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
     * <br />
     * 如没有Group设置,则不过滤。
     */
    String[] group() default {};

    /**
     * Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
     * <p />
     * 示例:<br/>
     * 注解的值 <code>@Activate("cache,validatioin")</code>,
     * 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
     * <br/>
     * 如没有设置,则不过滤。
     */
    String[] value() default {};

    /**
     * 排序信息,可以不提供。
     */
    String[] before() default {};

    /**
     * 排序信息,可以不提供。
     */
    String[] after() default {};

    /**
     * 排序信息,可以不提供。
     */
    int order() default 0;
}

group基本表示用在服务端还是消费端,value表示激活这个扩展点的条件,before、after、order用于排序。为了加深理解,我们拿一个UT例子看一下,

getActivateExtension方法基本都会传入url作为参数,用法是获取激活条件的所有扩展点实现类。

@Test
    public void testLoadDefaultActivateExtension() throws Exception {
        // test default
        URL url = URL.valueOf("test://localhost/test?ext=order1,default");
        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
                .getActivateExtension(url, "ext", "default_group");
        Assertions.assertEquals(2, list.size());
        Assertions.assertTrue(list.get(0).getClass() == OrderActivateExtImpl1.class);
        Assertions.assertTrue(list.get(1).getClass() == ActivateExt1Impl1.class);

        url = URL.valueOf("test://localhost/test?ext=default,order1");
        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
                .getActivateExtension(url, "ext", "default_group");
        Assertions.assertEquals(2, list.size());
        Assertions.assertTrue(list.get(0).getClass() == ActivateExt1Impl1.class);
        Assertions.assertTrue(list.get(1).getClass() == OrderActivateExtImpl1.class);
    }



扩展配置如下:
group=org.apache.dubbo.common.extension.activate.impl.GroupActivateExtImpl
value=org.apache.dubbo.common.extension.activate.impl.ValueActivateExtImpl
order1=org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl1
order2=org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl2
old1=org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl2
old2=org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl3

接下里看一下ExtensionLoader#getActivateExtension

//获取满足激活条件的扩展实现
public List<T> getActivateExtension(URL url, String[] values, String group) {
    List<T> exts = new ArrayList<T>();
    List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
    if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
        //加载扩展
        getExtensionClasses();
        //上一步会缓存所有Active注解的实现类到cachedActivates
        for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
            String name = entry.getKey();
            Activate activate = entry.getValue();
            //匹配group,provider还是consumer
           //  activateGroup = activate扩展点注解activate配置的Group 
// group URL中的group
            if (isMatchGroup(group, activate.group())) {
                //获取扩展实现类
                T ext = getExtension(name);
                //isActive匹配激活条件
                if (! names.contains(name)
                        && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) 
                        && isActive(activate, url)) {
                    exts.add(ext);
                }
            }
        }
        //这里会重新排序,用到Active里面before、after和order
        Collections.sort(exts, ActivateComparator.COMPARATOR);
    }
    ....
    ....
    return exts;
}

 就是说根据URL,会把需要的扩展都会收集起来,并排好序。

posted @ 2021-12-14 14:38  gaojy  阅读(345)  评论(0编辑  收藏  举报