怎么去实现动态扩展机制SPI

一、前言

关于动态扩展机制SPI,在之前我有发过该类的文章,感兴趣的可以去看一下,或者可以自己去百度了解一下,在本篇文章中就不做过多的赘述了。

本篇主要是着重于怎么去实现SPI,参考于Dubbo SPI,感兴趣的可以去了解一下。

二、实现

Dubbo SPI的核心类在于ExtensionLoader,通过加载本地目录的配置文件以实现动态植入扩展类。

1、配置类定义

我们这里仿照Dubbo里面对配置文件的定义,讲扩展接口的配置定义在资源路径下的 META-INF/extensions/ 目录下,配置文件以对应扩展接口的类路径命名

image-20230618104934725

配置文件的内部以 Key-Value 键值对形式定义,key 由自己定义,value为对应实现类的类路径

image-20230618104934725

具体的结构如下图所示:

image-20230618104934725

基于上面的定义,给出获取实现类最简单的方式:

image-20230618104934725

程序运行情况:

image-20230618104934725

2、模块化实现

基于上面的思路,已经可以简单的实现了,现在我们把它给完善一下。

  1. 核心类属性

    image-20230618104934725

  2. 内部方法 getExtensionLoader

    首先传入扩展接口的类型 ,以获取对应泛型的 ExtensionLoader 对象,在这里还可以做一些判别。此处引入了自定义的 @SPI 注解以判断该接口是否是扩展接口。

    t7

  3. 内部方法 getExtension

    getExtension 方法传入的是配置文件中的 Key 值,获取其对应的扩展实例,这里引入了泛型,其返回的是上一步中我们创建 ExtensionLoader 类时绑定的接口类型。

    t7

  4. 内部方法 createExtension

    当扩展实例还未被创建时,通过 double check 进行创建,然后调用 getExtensionsClasses 方法获取缓存,从缓存中获取对应的实现类。

    t7

  5. 内部方法 getExtensionsClasses

    一样用到了 double check ,通过调用 loadDirectory 方法去本地目录中加载扩展实现类,在这里传入了一个 Map,用于保存从配置文件中加载到的扩展实例对象。

    t7

  6. 内部方法 loadDirectory

    在本方法中组装我们真正要访问的某个配置文件,通过类加载器读取配置文件,调用 loadResource 解析配置文件,然后将加载到的类缓存起来。

    t7

  7. 内部方法 loadResource

    通过 BufferedReader 缓冲字符流按行读取配置文件,文件中以 Key-Value 的形式保存扩展信息,通过类加载器的 loadClass 方法加载对应的类,然后存入我们最初传入的缓存 Map 中。

    t7

3、使用方式

t7