ExtensionLoader源码解析

ExtensionLoader源码解析

上一片文章写了dubbo基础架构和SPI的原理,这一篇将着重讲述Dubbo核心工具类ExtensionLoader的工作原理

不积跬步,无以至千里;不积小流,无以成江河。

ExtensionLoader顾名思义,扩展装载器。在dubbo框架中,每一个SPI接口都对应着自己的ExtensionLoader实例。基于这个,我们先看一下基本上使用:
假如我想获取一个SPI接口的扩展可以使用如下代码:

//1 根据接口类取获取它对应的ExtensionLoader实例对象
//2 然后再通过ExtensionLoader实例的getExtension(扩展点名称)获取对应的扩展点
BaseCommand command = ExtensionLoader.getExtensionLoader(BaseCommand.class).getExtension(commandName);

我先整理一下整个调用流程,如果看不懂可以参考这个流程去查看:
在这里插入图片描述

从以上流程中可以看到:dubbo获取对象实例或者class都是从缓存中获取,如果缓存中没有,就重新加载,然后存入缓存,然后再从缓存中获取。getExtension方法只能获取非@Adaptive和非Wrapper的扩展点实例。对于Adaptive扩展点的加载底层还是使用getExtension,而Wrapper扩展点,目前版本还没从ExtensionLoader直接获取的方法。这个在下面会详细讨论。

接下来分析一下ExtensionLoader#getExtensionLoader(Class type)的相关源码:

    /**
     * ExtensionLoader扩展类加载器缓存
     */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
    
    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!");
        }
        //接口必须标注@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
        //从ExtensionLoader缓存中查询是否已经存在对应类型的ExtensionLoader实例
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            //没有就new一个ExtensionLoader实例,并存入本地缓存
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        //返回接口对应的ExtensionLoader实例
        return loader;
    }
     /**
      * 判断给定的类是否标有@SPI注解
      */   
    private static <T> boolean withExtensionAnnotation(Class<T> type) {
        return type.isAnnotationPresent(SPI.class);
    }

再来分析一下ExtensionLoader#getExtension(String 实例名)获取扩展实例的相关源码:

/**
 * 扩展点实例缓存 key=扩展点名称,value=扩展实例的Holder实例
 */
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        //如果扩展点名称为true字符串,则返回默认的扩展类
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        /*
         *扩展容器,从缓存中获取,没有就新建一个加入缓存
         */
        final Holder<Object> holder = getOrCreateHolder(name);
        //典型的DCL 单例模式
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    //如果holder中没有响应的扩展点实例就新建一个
                    instance = createExtension(name);
                    //然后把新建的扩展点实例存入holder中,此时holder已经存入缓存,相当于把扩展点实例存入了缓存
                    holder.set(instance);
                }
            }
        }
        //返回实例对象
        return (T) instance;
    }
/**
 * Helper Class for hold a value.
 */
public class Holder<T> {

    private volatile T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

}
/**
*获取或者创建一个Holder对象
*/
private Holder<Object> getOrCreateHolder(String name) {
    // 首先通过扩展名从扩展实例缓存中获取Holder对象
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        //如果没有获取到就new一个空的Holder实例存入缓存
        cachedInstances.putIfAbsent(name, new Holder<>());
        //返回空的Holder实例
        holder = cachedInstances.get(name);
    }
    return holder;
}

 private T createExtension(String name) {
        //根据扩展名加载扩展类的class对象,
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            /*
             * 从扩展点缓存中获取对应实例对象
             */
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                //如果缓存中不存在此类的扩展点,就new一个扩展点实例,并存入缓存
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                //然后从缓存中获取对应实例
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //实现依赖注入,通过setter方法自动注入对应的属性实例
            injectExtension(instance);
            //自动注入缓存中所有的包装类。
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            //初始化实例并返回
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

以上详细分析了一下获取一个SPI接口的扩展点的过程,其中设计到两个核心方法:

  1. ExtensionLoader#getExtensionClasses()
    获取扩展点默认实现的class , 扫描SPI默认三个扩展点配置文件夹下名为:接口的全限定名 的配置文件,根据文件配置加载配置的扩展点实现类到缓存。
    配置文件的格式如下:
    key(扩展点实现类的名称)=value(扩展点实现类的class的全限定名)在这里插入图片描述
    开始撸源码:
    /**
     * 扩展点Class缓存 key=扩展名 ,value=对应的class对象
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    /**
     *  加载扩展点的类
     *  从缓存中获取所有的扩展点的类,如果没有就重新扫描一次,并缓存起来
     */
    private Map<String, Class<?>> getExtensionClasses() {
        //首先从缓存中获取已经缓存的扩展点的class
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            //如果没有缓存过,就开始根据配置扫描
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    //扫描配置文件,加载配置的扩展点的class
                    classes = loadExtensionClasses();
                    //将扫描到的class存入缓存
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    /**
     * 三个dubbo SPI默认扫描的路径
     */
    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/";
    /**
     * SPI 接口类
     */
    private final Class<?> type;
    /**
     * synchronized in getExtensionClasses
     * */
    private Map<String, Class<?>> loadExtensionClasses() {
        //设置扩展点的默认名称,值为@SPI("value")中的value
        cacheDefaultExtensionName();
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        // internal extension load from ExtensionLoader's ClassLoader first
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
        //兼容历史版本
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }
   /**
     * extract and cache default extension name if exists
     * 获取注解了SPI的接口中指定的默认的实现类的名称,
     * 存在就存入内存缓存中,
     * 接口没有标注@SPI注解或者标注了@SPI注解,但是注解中没有指定默认实现类的名称时不做任何操作
     */
    private void cacheDefaultExtensionName() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) {
                cachedDefaultName = names[0];
            }
        }
    }

要点
当在接口的@SPI(value)注解上指定value,表示指定这个SPI接口默认实现类的名称。这个值存入cachedDefaultName缓存中
举个栗子:
Dubbo中很重要的一个组件是序列化,而序列化有avro,fastjson,gson,hession2,jdk,kryo,protobuf等等,dubbo默认是的使用hessian2.
看Dubbo在序列化SPI接口的定义:
在这里插入图片描述
可以看到就是在SPI注解中指定默认实现的名称为hessian2,也就是在获取默认的序列化器时在Serialization接口对应的ExtensionLoader实例中cachedDefaultName=hessian2 ;再看一下配置文件:
在这里插入图片描述

以上讲述了加载的大致流程,这里的ExtensionLoader#loadDirectory方法设计到读取配置文件,从配置文件中解析出扩展名,扩展实现类的完整路径,然后加载实现类的过程,再看一下源码:

 private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        loadDirectory(extensionClasses, dir, type, false);
    }

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
        //配置文件的全路径:默认路径前缀+接口的全限定名
    String fileName = dir + type;
    try {
        Enumeration<java.net.URL> urls = null;
        //获取当前线程中获取类加载器
        ClassLoader classLoader = findClassLoader();
        // try to load from ExtensionLoader's ClassLoader first
        if (extensionLoaderClassLoaderFirst) {
            //获取加载ExtensionLoader.class这个类的类加载器
            ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
            if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                //如果extensionLoaderClassLoaderFirst=true时,且这两个类加载器不同,就默认使用 extensionLoaderClassLoader 
                urls = extensionLoaderClassLoader.getResources(fileName);
            }
        }
       
        if(urls == null || !urls.hasMoreElements()) {
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
        }

        if (urls != null) {
            //解析并加载配置文件中配置的实现类到extensionClasses中去
            while (urls.hasMoreElements()) {
                java.net.URL resourceURL = urls.nextElement();
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", description file: " + fileName + ").", t);
    }
}

 private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
    try {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        String name = null;
                        int i = line.indexOf('=');
                        if (i > 0) {
                            //配置的接口的实现的名称
                            name = line.substring(0, i).trim();
                            //配置的接口的实现类的全限定名
                            line = line.substring(i + 1).trim();
                        }
                        if (line.length() > 0) {
                            //加载配置的class到extensionClasses ,,使用Class.forName的方式加载class 
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                        }
                    } catch (Throwable t) {
                      ...
                    }
                }
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", class file: " + resourceURL + ") in " + resourceURL, t);
    }
}

   /**
     * 加载扩展点的实现的class ,这个类的主要作用是对解析到的class进行分类缓存
     * @param extensionClasses 装载容器
     * @param resourceURL 配置文件资源URL
     * @param clazz 扩展点实现类的class
     * @param name 扩展点实现类的名称
     * @throws NoSuchMethodException 加载异常
     */
    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        //判断配置的实现类是否是实现了type接口
        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)) {
            //如果配置中的实现类使用了@Adaptive注解,表示这个类就是一个自适应实现类,缓存到全局缓存cachedAdaptiveClass中
            cacheAdaptiveClass(clazz);
        } else if (isWrapperClass(clazz)) {
            //如果配置的实现类是一个Wrapper类,就缓存到缓存到全局缓存cachedWrapperClasses中
            cacheWrapperClass(clazz);
        } else {
            clazz.getConstructor();
            //如果配置文件中名称为空,配置文件的内容格式是  com.foo.XxxProtocol 这样,没有指定名称
            //                                      com.foo.YyyProtocol
            //这种格式的配置现在版本已经弃用,这种格式的配置,实现类需要添加注解@Extension ;由于已经弃用,就不在讨论这种方式
            if (StringUtils.isEmpty(name)) {
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            //使用逗号将name分割为字符串数组
            String[] names = NAME_SEPARATOR.split(name);
            //这个非空判断我认为可以不要
            if (ArrayUtils.isNotEmpty(names)) {
                //如果扩展点配置的实现类使用了@Activate注解,就将对应的注解信息缓存起来
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    //缓存扩展点实现类class和扩展点名称的对应关系
                    cacheName(clazz, n);
                    //最后将class存入extensionClasses
                    saveInExtensionClass(extensionClasses, clazz, n);
                }
            }
        }
    }

以上就讲述了使用ExtensionLoader加载一个SPI接口的扩展点实现的完整流程。不知道你们有没有注意到ExtensionLoader#loadClass方法,这个里面就实现了扩展点的分类缓存功能。

  1. ExtensionLoader#injectExtension(T instance)
    直接上源码,说明都在注解里面:
/**
     *  实现IOC和AOP的机制,获取到类中所有的setter方法。
     *  1 如果setter方法不是私有的和基础数据类型,
     *  并且没有注解{@link DisableInject}注解,就自动给相关属性注入默认的实现类
     *  2 如果setter方法属性是一个接口,并且次接口有多个实现类,则会更加{@link Adaptive}注解
     *    自适应加载配置的默认实现
     */
    private T injectExtension(T instance) {
        //objectFactory为空就不执行属性注入,这个对象我将会在下面详细介绍,重点!!!
        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                //如果不是setter方法就不注入,判断条件:1 public ,2 set开头 ,3 只有一个参数
                if (!isSetter(method)) {
                    continue;
                }
                /*
                 *如果扩展点的成员属性的setter方法上使用@DisableInject,在加载扩展点实例时就不会对这个属性进行依赖注入
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                //如果是Java的基本数据类型就直接跳过
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    //获取setter方法中的属性值,例如:setVersion -> version
                    String property = getSetterProperty(method);
                    //objectFactory是ExtensionFactory实例;从dubbo容器和spring容器中获取setter属性的默认扩展实例。
                    //这个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;
    }

至此使用ExtensionLoader#getExtension(String name) 获取一个SPI的普通的扩展点实例流程全部理清楚了,那么怎么获取一个自适应或者wrapper接口的扩展点呢?

下面我们来看一下获取自适应扩展点的实例;这就是ExtensionLoader#createAdaptiveExtensionClass()中的获取Compiler接口自适应实现的代码:

org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()

可以看到:

  1. 第一步还是通过Cluster.class获取它对应的ExtensionLoader,这个逻辑在上面已经分析过,就不在赘述。
  2. 第二步是通过它的ExtensionLoader#getAdaptiveExtension()方法获取它的默认的自适应的扩展点实例。既然是默认的,淡然这个方法不需要参数。

我还是先给出整个流程的流程图,不懂的可以结合这个流程图来理解:
在这里插入图片描述

使用前提: 要求这个SPI接口中至少有一个接口方法使用了@Adaptive注解 或者 SPI接口的扩展点实现类上标注了@Adaptive注解

我们再来分析ExtensionLoader#getAdaptiveExtension()的详细实现

/**
 * 获取SPI接口的自适应扩展点
 * 接口的实现类上使用了@Adaptive注解的扩展点实现类
 * @return 自适应扩展点实例
 */
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
    //首先从自适应扩展点实例缓存中获取自适应的扩展点
    Object instance = cachedAdaptiveInstance.get();
    //又是经典的DCL模式
    if (instance == null) {
        //如果没有获取到,并且缓存异常容器不为空就将这个异常抛出来
        if (createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " +createAdaptiveInstanceError.toString(),createAdaptiveInstanceError);
        }
        synchronized (cachedAdaptiveInstance) {
            instance = cachedAdaptiveInstance.get();
            if (instance == null) {
                try {
                    //创建一个自适应的扩展点实例
                    instance = createAdaptiveExtension();
                    //将新建的实例对象存入自适应扩展点实例缓存中
                    cachedAdaptiveInstance.set(instance);
                } catch (Throwable t) {
                    createAdaptiveInstanceError = t;
                    throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                }
            }
        }
    }
    //返回实例
    return (T) instance;
}

这里面设计的比较重要的一个方法是ExtensionLoader#createAdaptiveExtension(),我们再来分析一下这个方法的源码:

/**
 * 创建一个自适应扩展点实例
 */
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
        //1获取到自适应扩展点实例的class
        //2新建一个class的实例
        //3使用injectExtension方法对这个实例的属性进行依赖注入
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

/**
 * 获取自适应扩展点的class对象
 */
private Class<?> getAdaptiveExtensionClass() {
    //加载扩展点的class到cachedAdaptiveClass缓存。 缓存中没有的话就解析配置文件,这个方法上面给过详细介绍这里不在赘述
    getExtensionClasses();
    //如果自适应扩展点class缓存不为空,就直接使用缓存中的class
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //当这个SPI接口没有带有@Adaptive的实现类时。就使用字节码技术自己构建一个自适应的class
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

/**
 * 创建自适应扩展点class文件的Java代码(Java String 形式)
 * 然后使用默认的Javassist编译器来编译为对应的扩展点的class
 */
private Class<?> createAdaptiveExtensionClass() {
    //根据SPI接口和缓存中扩展点默认的名称来构建一个自适应扩展点的class的代码生产器,生产String类型的java文件
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    //ExtensionLoader#getAdaptiveExtension()加载Compiler接口默认实现类
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    //将字符串形式的java文件编译为class
    return compiler.compile(code, classLoader);
}

到此ExtensionLoader#getAdaptiveExtension()的基本流程就理清楚了。

前文说到,SPI扩展的还有个特性是自动激活,一个SPI接口可能需要同时激活多个实现,例如Filter接口。那么这个在ExtensionLoader中如何体现呢?

经过仔细阅读发现ExtensionLoader中有4个获取自动激活扩展点的方法:
在这里插入图片描述
这里使用了Java的特性多态 - 方法的重载。其实具体的实现逻辑在getActivateExtension(URL url, String[] values, String group)方法中 。

细心朋友可能已经发现getActivateExtension方法返回的结果是一个List ,这就刚好印证了一个接口,自动激活多个实现这个原理!

首先还是列举一个此方法的使用实例;这个就是获取Filter接口自动激活实现类激活的方法
源码如下:

//例如:key=reference.filter , group=consumer
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

这是实际项目中调试的界面,方便看清这些参数的详细信息。

在consumer的配置中dubbo.consumer.filter= rpcFilter,default
这是运行时的截图:
在这里插入图片描述
下面直接撸代码:

 
public List<T> getActivateExtension(URL url, String key) {
    return getActivateExtension(url, key, null);
}


public List<T> getActivateExtension(URL url, String[] values) {
    return getActivateExtension(url, values, null);
}


public List<T> getActivateExtension(URL url, String key, String group) {
    //通过key值到URL中查找对应key的值作为扩展点的名称的集合
    String value = url.getParameter(key);
    //然后将value使用逗号分割为字符串数组
    return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}

/**
 * Get activate extensions.
 * 获取所有自动激活的扩展点,从实现细节可以发现,这个方法只的主要逻辑在于自动激活规则的实现
 * 而获取扩展点实例还通过调用getExtension(String name)
 * @param url    url
 * @param values extension point names 扩展点名称 数组
 * @param group  group 
 * @return extension list which are activated
 * @see org.apache.dubbo.common.extension.Activate
 */
public List<T> getActivateExtension(URL url, String[] values, String group) {
    //exts存放系统里面自带的并且配置中没有排除的自动激活的扩展点实例
    List<T> exts = new ArrayList<>();
    /* 要激活的扩展点的名称集合,这个是获取dubbo的配置信息
       例如:Filter的配置dubbo.consumer.filter= rpcFilter,default
       此时names=["rpcFilter","default"]
    */
    List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
    //在dubbo的扩展点的配置里面,如果配置的扩展点的名称以"-"开头,就不会被激活
    if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
        //刷新缓存,如果没有装载过这个接口,就会读取配置将配置中的扩展点的信息刷到cachedActivates
        getExtensionClasses();
        //遍历缓存中的激活的扩展点信息cachedActivates中的key=扩展点名称 ,value=扩展点实现类上的@Activate注解对象
        for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
            //扩展点名称
            String name = entry.getKey();
            //扩展点上的@Activate注解对象
            Object activate = entry.getValue();
            //获取扩展点的@Activate注解对象上的参数的值
            String[] activateGroup, activateValue;

            if (activate instanceof Activate) {
                //获取注解@Activate的group参数
                activateGroup = ((Activate) activate).group();
                //获取注解@Activate的value参数
                activateValue = ((Activate) activate).value();
            } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                //这个主要是为了兼容Dubbo前期的版本,此处不做讨论
                activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
            } else {
                continue;
            }
            //匹配group ,value ,来决定是否激活这个扩展点
            if (isMatchGroup(group, activateGroup)
                    && !names.contains(name)
                    //排除单个扩展 
                    && !names.contains(REMOVE_VALUE_PREFIX + name)
                    && isActive(activateValue, url)) {
                //如果信息匹配成功,就调用getExtension(name)方法获取自动激活的扩展点实例
                exts.add(getExtension(name));
            }
        }
        //排序,通过扩展点的名称的长度来进行排序
        exts.sort(ActivateComparator.COMPARATOR);
    }
    //定义usrs存放用户自定义的扩展点实现
    List<T> usrs = new ArrayList<>();
    for (int i = 0; i < names.size(); i++) {
        String name = names.get(i);
        if (!name.startsWith(REMOVE_VALUE_PREFIX) && !names.contains(REMOVE_VALUE_PREFIX + name)) {
            if (DEFAULT_KEY.equals(name)) {
                if (!usrs.isEmpty()) {
                    exts.addAll(0, usrs);
                    usrs.clear();
                }
            } else {
                //调用getExtension(name)方法获取用户自定义配置中的扩展点实现 ,这个不需要添加@Activate注解。默认要激活
                usrs.add(getExtension(name));
            }
        }
    }
    //合并所有好激活的扩展点
    if (!usrs.isEmpty()) {
        exts.addAll(usrs);
    }
    return exts;
}

/**
 * 匹配group
 * @param group 服务的group
 * @param groups 注解@Activate中的group字符串数组
 * @return true 匹配成功,false失败
 */
private boolean isMatchGroup(String group, String[] groups) {
    //如果group是空,默认为要激活
    if (StringUtils.isEmpty(group)) {
        return true;
    }
    if (groups != null && groups.length > 0) {
        for (String g : groups) {
            if (group.equals(g)) {
                return true;
            }
        }
    }
    return false;
}

/**
 * 匹配key
 * @param keys {@link @Active}注解中配置的参数,一个或者多个
 * @param url SPI实现的核心对象URL
 * @return true 表示是自动激活扩展点,false不是
 */
private boolean isActive(String[] keys, URL url) {
    //如果keys没有指定,默认表示要激活
    if (keys.length == 0) {
        return true;
    }
    for (String key : keys) {
        // @Active(value="key1:value1, key2:value2")
        String keyValue = null;
        if (key.contains(":")) {
            String[] arr = key.split(":");
            key = arr[0];
            keyValue = arr[1];
        }
        for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
            String k = entry.getKey();
            String v = entry.getValue();
            if ((k.equals(key) || k.endsWith("." + key))
                    && ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
                return true;
            }
        }
    }
    return false;
}

至此ExtensionLoader的核心功能就已经解析完毕。

在解读ExtensionLoader时,出现了几个重要知识点:扩展工厂ExtensionFactory,字节码编译器org.apache.dubbo.common.compiler.Compiler ,还有高并发中经常使用到的DCL模式。这个将再下次进行详细介绍

发布了12 篇原创文章 · 获赞 1 · 访问量 653

猜你喜欢

转载自blog.csdn.net/zhaodongchao1992/article/details/103354138