dubbo SPI源码解读: ExtensionLoader 加载过程

上一篇博客中我们介绍了dubbo中SPI功能的使用规则,这里我们看一下它的加载过程:

这里它的加载过程就两步:

  1.  获取ExtensionLoader的实例对象 
  2. 通过ExtensionLoader的实例对象获取到我们要的扩展点实例

1、获取ExtensionLoader实例对象

getExtensionLoader 方法做了这么几件事: 

1、 判断class对象是否为空、是否为接口、类上是否有@SPI注解

2、查询缓存获取ExtensionLoader实例, 如果没有则创建一个实例对象,并放入缓存中

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 interface!");
        }
        // 如果没有SPI注解,报错 
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        // 先查询缓存,缓存中没有则创建
        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;
    }

我们可以看一下它的构造方法: 

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        /**
         * type如果是ExtensionFactory类型,那么objectFactory是null,否则是ExtensionFactory类型的适配器类型
         */
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

私有构造,说明它无法在外部进行实例化; ExtensionFactory  是通过扩展点类型和名称来获取扩展点的。

@SPI
public interface ExtensionFactory {

    /**
     * Get extension.
     *
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

ExtensionFactory  的扩展点有三个:

2、获取扩展点实例:

getAdaptiveExtension方法获取扩展点实例, 它也是先从缓存中获取扩展点实例,如果没有则创建, 并放入缓存中:createAdaptiveExtension方法

    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        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;
    }

1、createAdaptiveExtension: 调用getAdaptiveExtensionClass

  1. 获取到适配器类的Class。
  2. 利用反射创建适配器类的实例。
  3. injectExtension是dubbo的DI,依赖注入,如果适配器类有属性的set方法,会自动注入。
   private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

2、getAdaptiveExtensionClass: 

  1.   SPI扫描: 包括@SPI、@Adaptive注解的类和方法、 SPI 配置文件(这一步是SPI的关键)
  2.   如果上一步获取到了cachedAdaptiveClass 则返回
  3.   创建cachedAdaptiveClass 动态代理类
    private Class<?> getAdaptiveExtensionClass() {
        /**
         * 触发SPI流程的扫描
         */
        getExtensionClasses();
        /**
         * 如果通过上面的步骤可以获取到cachedAdaptiveClass直接返回
         */
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        /**
         * 利用动态代理创建一个扩展类
         */
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

2.1、 getExtensionClasses : 

这里先是查询一次缓存,如果缓存中没有则开始加载扩展点: loadExtensionClasses

    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    /**
                     * 开始加载
                     */
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

2.1.1、 loadExtensionClasses 加载扩展点

它的主要职能有两个: 

  1. 加载当前SPI默认实现
  2. 加载这个类的所有扩展点实现,且按照name和class对象的形式存储
    /**
     * 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.overridden(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        }

        return extensionClasses;
    }   

2.1.1.1: cacheDefaultExtensionName 加载并缓存当前SPI默认实现

    private void cacheDefaultExtensionName() {
        // 获取当前类是够包含SPI注解, 一般走道这里就是拥有该注解
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }
        // 获取SPI注解的value值
        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];
            }
        }
    }

2.1.1.2:loadDirectory 加载扩展点实现

这里的主要工作是: 

  1. 拼接文件名
  2. 先从缓存中尝试获取需要的扩展点
  3. 如果URL为空,即没有文件或文件中没有任何配置,尝试从其他线程获取扩展点
  4. 如果URL存在,逐个遍历文件资源,并加载资源信息到ExtensionClasses中
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
        // 拼接文件名: 路径/包名.接口名
        String fileName = dir + type;
        try {
            // 寻找classloader和url列表
            Enumeration<java.net.URL> urls = null;
            ClassLoader classLoader = findClassLoader();

            // 如果需要,先从当前类的ClassLoader中获取
            if (extensionLoaderClassLoaderFirst) {
                ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                    urls = extensionLoaderClassLoader.getResources(fileName);
                }
            }
            // 如果找不到任何URL列表,继续尝试去其他当前线程的ClassLoader中寻找
            if (urls == null || !urls.hasMoreElements()) {
                if (classLoader != null) {
                    urls = classLoader.getResources(fileName);
                } else {
                    urls = ClassLoader.getSystemResources(fileName);
                }
            }
            // 如果URL列表存在, 逐个遍历资源文件,并加载资源信息到extensionClasses
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

这里需要注意两个方法: loadResource 和 loadClass。

  • loadResource主要用于读取文件操作,并将读取到的信息交给loadClass加载
  • loadClass 用于完成类的映射,执行完该方法后,会对几个字段进行更新
    • cachedAdaptiveClass: 当前Extension类型对应的AdaptiveExtension类型,只有一个
    • cachedWrapperClass: 当前Extension类型对应的所有Wrapper实现类型,无顺序
    • chchedActivated: 当前Extension实现自动激活实现缓存( map 类型)
    • cachedNames:扩展点实现类对应的名称, 如果配置多个名称则值为第一个

2.2 createAdaptiveExtensionClass 创建扩展点的代理类对象

  1. 实例化一个新的Adaptive代码生成器, 并生成代码
  2. 获取类加载器
  3. 通过扩展点,寻找编译器, 目前有java自带的编译器和javassis编译器
  4. 编译生成class对象
    private Class<?> createAdaptiveExtensionClass() {
        // 实例化一个新的Adaptive代码生成器, 并生成代码
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        // 获取类加载器
        ClassLoader classLoader = findClassLoader();
        // 通过扩展点,寻找编译器, 目前有java自带的编译器和javassis编译器
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        // 编译生成class
        return compiler.compile(code, classLoader);
    }

2.2.1: 通过 AdaptiveClassCodeGenerator.generate 方法来生成代码

    /**
     * generate and return class code
     */
    public String generate() {
        // 如果没有任何方法有Adaptive注解,直接略过
        if (!hasAdaptiveMethod()) {
            throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
        }
        // 用于拼接代码
        StringBuilder code = new StringBuilder();
        code.append(generatePackageInfo()); // 生成包信息
        code.append(generateImports());     // 生成引用信息
        code.append(generateClassDeclaration());  // 生成类声明

        Method[] methods = type.getMethods();  // 生成每个方法
        for (Method method : methods) {
            code.append(generateMethod(method));
        }
        code.append("}"); // 输出最后一个“}”

        if (logger.isDebugEnabled()) {
            logger.debug(code.toString());
        }
        return code.toString();
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值