Dubbo 源码分析 —【5】SPI 扩展机制 下

本贴最后更新于 2193 天前,其中的信息可能已经时移世易

承上

上篇分析到了 injectExtension,其中 objectFactory 需要详细分析

Object object = objectFactory.getExtension(pt, property);

objectFactory 是在用私有构造方法初始化的 ExtensionFactory 的自适应实现类。

// 如果是扩展工厂类型 则返回 null 
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

即最重要的是这一句:ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension() 和简单使用方法对比一下,我们会发现它调用的是 getAdaptiveExtension 方法

第一层 getAdaptiveExtension 分析

getAdaptiveExtension 主要的作用是 获取

// 获得扩展实例
public T getAdaptiveExtension() {
    // 先从本地缓存中查找
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                // 解决多线程情况下 指令重排的问题 参数使用 volatile 修饰
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 创建适应的实例
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException ...
                    }
                }
            }
        } else {
            throw new IllegalStateException ...
        }
    }

    return (T) instance;
}

第二层 createAdaptiveExtension 分析

createAdaptiveExtension 和上文分析的 createExtension 作用相似,但是实现有很大区别,这个方法标示着 Dubbo 自己扩展的 SPI 机制增加的对扩展点自适应的支持。

第三层 getAdaptiveExtensionClass 分析

getAdaptiveExtensionClass 的主要作用的是先从缓存中加载,如果从未加载过则再调用方法加载

第四层 createAdaptiveExtensionClass 分析

创建一个扩展点的代理,将扩展的选择从 Dubbo 启动时,延迟到 RPC 调用时。非常有意思的是实现动态编译的 Compiler 也使用的插件的方式。

private Class<?> createAdaptiveExtensionClass() {
    // 生成 SPI 的自适应实现类的 .java 代码
    // 先生成Java源代码,然后编译,加载到jvm中。通过这种方式,可以更好的控制生成的Java类。而且这样也不用care各个字节码生成框架的api
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    // 选择一个编译框架
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 编译 Java 源码
    return compiler.compile(code, classLoader);
}

第五层 createAdaptiveExtensionClassCode 分析

这个主要时生成 SPI 自适应实现类的 .java 代码,最重要也是相对比较复杂的,耐心看完这一点就离成功不远了,刚把得!

Compiler 解析

Dubbo 会自动为我们创建一个,默认使用 Javaassist

@SPI("javassist")
public interface Compiler {
    // 你会发现 这个方法上没有 @Adaptive 注解,根据上文分析应该会报错吧?
    // 不用担心不会,它的dubbo源码中唯二 实现类上有 @Adaptive 的,走不到那段生成代码的逻辑
    // 另一个是 ExtensionFactory 
    Class<?> compile(String code, ClassLoader classLoader);
}

自适应实现

总结

getActivateExtension 就不再分析了。另外实现 AOP 功能的 Wrapper 类也不再详细分析了。

理解了 SPI 的实现能够帮助我们分析 Dubbo 源码的实现。不就是各个插件组装嘛,对吧 :)

  • 扩展点自动包装
    • wrapperClass.getConstructor(type).newInstance(instance) 实现
    • 自动包装扩展点的 Wrapper 类
    • 根据代码分析 只要扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。
    • 新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。
  • 扩展点自动装配
    • injectExtension 实现
    • ExtensionLoader 通过扫描扩展点实现类的所有 setter 方法来判定其成员。
    • 自动注入依赖的扩展点,实现 IoC
  • 扩展点自适应
    • getAdaptiveExtension 实现
    • ExtensionLoader 注入的依赖扩展点是一个 Adaptive 实例,直到扩展点方法执行时才决定调用是一个扩展点实现。
    • Dubbo 使用 URL 对象(包含了 Key-Value)传递配置信息。
  • 扩展点自动激活
    • getActivateExtension 实现
    • 同时加载多个实现,可以用自动激活来简化配置
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3453 回帖 • 200 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 598 关注
  • SPI

    Service Provider Interface

    12 引用 • 2 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...