解析SPI机制:实现灵活插件式架构( 三 )

  • 缓存所有扩展实现
  • 基于用户执行的扩展名,实例化对应的扩展实现
  • 进行扩展实例属性的 IOC 注入以及实例化扩展的包装类,实现 AOP 特性
  • 如何使用 Dubbo 扩展能力进行扩展下面以扩展协议为例进行说明如何利用 Dubbo 提供的扩展能力扩展 Triple 协议 。
    (1) 在协议的实现 jar 包内放置文本文件:META-INF/dubbo/org.apache.dubbo.remoting.api.WireProtocol
     properties
    复制代码
    tri=org.apache.dubbo.rpc.protocol.tri.TripleHttp2Protocol (2) 实现类内容
     java
    复制代码
    @Activate public class TripleHttp2Protocol extends Http2WireProtocol { // ... } 说明下:Http2WireProtocol 实现了 WireProtocol 接口
    (3) Dubbo 配置模块中,扩展点均有对应配置属性或标签,通过配置指定使用哪个扩展实现 。比如:
     xml
    复制代码
    <dubbo:protocol name="tri" /> 从上面的扩展步骤可以看出,用户基本在黑盒下就完成了扩展 。
    Dubbo 扩展的应用Dubbo 的扩展能力非常灵活,在自身功能的实现上无处不在 。
    解析SPI机制:实现灵活插件式架构

    文章插图
    Dubbo 扩展能力使得 Dubbo 项目很方便的切分成一个一个的子模块,实现热插拔特性 。用户完全可以基于自身需求,替换 Dubbo 原生实现,来满足自身业务需求 。
    Dubbo SPI 源码分析上面看了 Dubbo SPI 通过 ExtensionLoader加载扩展 。ExtensionLoadergetExtensionLoader 方法获取一个 ExtensionLoader 实例,然后再通过 ExtensionLoadergetExtension 方法获取拓展类对象 。这其中,getExtensionLoader 方法用于从缓存中获取与拓展类对应的 ExtensionLoader,若缓存未命中,则创建一个新的实例 。该方法的逻辑比较简单,本章就不进行分析了 。下面我们从 ExtensionLoadergetExtension 方法作为入口,对拓展类对象的获取过程进行详细的分析 。
    【解析SPI机制:实现灵活插件式架构】
    解析SPI机制:实现灵活插件式架构

    文章插图
    上面代码的逻辑比较简单,首先检查缓存,缓存未命中则创建拓展对象 。下面我们来看一下创建拓展对象的过程是怎样的 。
    解析SPI机制:实现灵活插件式架构

    文章插图
    createExtension 方法的逻辑稍复杂一下,包含了如下的步骤:
    1. 通过 getExtensionClasses 获取所有的拓展类
    2. 通过反射创建拓展对象
    3. 向拓展对象中注入依赖
    4. 将拓展对象包裹在相应的 WrApper 对象中
    以上步骤中,第一个步骤是加载拓展类的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现 。在接下来的章节中,将会重点分析 getExtensionClasses 方法的逻辑,以及简单介绍 Dubbo IOC 的具体实现 。
    我们在通过名称获取拓展类之前,首先需要根据配置文件解析出拓展项名称到拓展类的映射关系表(Map<名称, 拓展类>),之后再根据拓展项名称从映射关系表中取出相应的拓展类即可 。相关过程的代码分析如下:
    解析SPI机制:实现灵活插件式架构

    文章插图
    这里也是先检查缓存,若缓存未命中,则通过 synchronized 加锁 。加锁后再次检查缓存,并判空 。此时如果 classes 仍为 null,则通过 loadExtensionClasses 加载拓展类 。下面分析 loadExtensionClasses 方法的逻辑 。
    解析SPI机制:实现灵活插件式架构

    文章插图
    loadExtensionClasses 方法总共做了两件事情,一是对 SPI 注解进行解析,二是调用 loadDirectory 方法加载指定文件夹配置文件 。SPI 注解解析过程比较简单,无需多说 。下面我们来看一下 loadDirectory 做了哪些事情 。
    解析SPI机制:实现灵活插件式架构

    文章插图
    loadDirectory 方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源 。我们继续跟下去,看一下 loadResource 方法的实现 。
    解析SPI机制:实现灵活插件式架构

    文章插图
    loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作 。loadClass 方法用于主要用于操作缓存,该方法的逻辑如下:
    解析SPI机制:实现灵活插件式架构

    文章插图
    如上,loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等 。除此之外,该方法没有其他什么逻辑了 。


    推荐阅读