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

  • 使用 ClassLoader 加载这些实现类的类对象 。
  • 使用反射实例化这些类,得到服务提供者的实例 。
  • 服务使用者: 服务使用者是通过服务提供者接口来获取服务实例的组件 。它可以从已注册的服务提供者中选择一个合适的实现,然后使用该实现的功能 。
  • 在运行时,SPI机制允许系统自动加载并实例化服务提供者的实现类,从而实现插拔式的架构 。这样,您可以在不修改核心代码的情况下,通过添加新的实现类来扩展系统功能,实现更好的可扩展性和灵活性 。
    下面我们看下JDK中ServiceLoader<S>方法的具体实现:
    解析SPI机制:实现灵活插件式架构

    文章插图
    首先,ServiceLoader实现了Iterable接口,所以它有迭代器的属性,这里主要都是实现了迭代器的hasNextnext方法 。这里主要都是调用的lookupIterator的相应hasNextnext方法,lookupIterator是懒加载迭代器 。
    其次,LazyIterator中的hasNext方法,静态变量PREFIX就是”META-INF/services/”目录,这也就是为什么需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件 。
    最后,通过反射方法Class.forName()加载类对象,并用newInstance方法将类实例化,并把实例化后的类缓存到providers对象中,(LinkedHashMap<String,S>类型)然后返回实例对象 。
    所以我们可以看到ServiceLoader不是实例化以后,就去读取配置文件中的具体实现,并进行实例化 。而是等到使用迭代器去遍历的时候,才会加载对应的配置文件去解析,调用hasNext方法的时候会去加载配置文件进行解析,调用next方法的时候进行实例化并缓存 。
    所有的配置文件只会加载一次,服务提供者也只会被实例化一次,重新加载配置文件可使用reload方法
    SPI机制的缺陷通过上面的解析,可以发现,我们使用SPI机制的缺陷:
    • 不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现 。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费 。
    • 如果扩展点加载失败,连扩展点的名称都拿不到了 。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因 。
    • 多个并发多线程使用 ServiceLoader 类的实例是不安全的 。
    Dubbo SPI机制Dubbo 就是通过 SPI 机制加载所有的组件 。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求 。在 Dubbo 中,SPI 是一个非常重要的模块 。基于 SPI,我们可以很容易的对 Dubbo 进行拓展 。Dubbo 中,SPI 主要有两种用法,一种是加载固定的扩展类,另一种是加载自适应扩展类 。这两种方式会在下面详细的介绍 。需要特别注意的是: 在 Dubbo 中,基于 SPI 扩展加载的类是单例的 。
    Dubbo SPI 优势
    1. 按需加载,Dubbo SPI配置文件采用KV格式存储,key 被称为扩展名,当我们在为一个接口查找具体实现类时,可以指定扩展名来选择相应的扩展实现,只实例化这一个扩展实现即可,无须实例化 SPI 配置文件中的其他扩展实现类,避免资源浪费,此外通过 KV 格式的 SPI 配置文件,当我们使用的一个扩展实现类所在的 jar 包没有引入到项目中时,Dubbo SPI 在抛出异常的时候,会携带该扩展名信息,而不是简单地提示扩展实现类无法加载 。这些更加准确的异常信息降低了排查问题的难度,提高了排查问题的效率 。;
    2. 增加扩展类的 IOC 能力,Dubbo 的扩展能力并不仅仅只是发现扩展服务实现类,而是在此基础上更进一步,如果该扩展类的属性依赖其他对象,则 Dubbo 会自动的完成该依赖对象的注入功能;
    3. 增加扩展类的 AOP 能力,Dubbo 扩展能力会自动的发现扩展类的包装类,完成包装类的构造,增强扩展类的功能;
    Dubbo SPI原理Dubbo 扩展加载流程Dubbo 加载扩展的整个流程如下:
    解析SPI机制:实现灵活插件式架构

    文章插图
    主要步骤为 4 个: