目录
1. 源码的注释描述
ServiceLoader,一种服务提供者加载工具,体现了java原生SPI扩展机制。也可以参考其逻辑实现自己的服务加载器。
服务指的是接口或抽象类,服务提供者指的是服务的实现。服务提供者以扩展的方式被加载和实例化,例如把jar包放到规定的扩展路径中、放到应用的classpath中或平台特定的方式。工具会加载并实例化提供者实现类,强制要求提供者实现类必须有无参数的构造函数。
服务提供者需要在项目的resources下创建资源路径(默认为META-INFO/services),提供者新增一个文件,文件名为服务的全路径类名,按行添加文件内容,一个服务实现类的全路径类名作为一行,使用#添加注释,且文件必须utf-8编码。
举一个例子
第一步,定义扩展点和扩展实现。如下的目录结构,MYSPIInterface作为扩展点,spiimpl下为两个扩展实现类:
第二步,添加文件配置。注意文件路径和文件名。
第三步,使用SPI获取扩展点实现类实例
ServiceLoader<MySPIInterface> load = ServiceLoader.load(MySPIInterface.class);
for (MySPIInterface mySPIInterface : load) {
System.out.println(mySPIInterface.who());
}
明确的提供者实现类出现在多个配置文件中或同一文件中列举多次,重复列举的部分会被忽略。
服务提供者的加载和实例化都是延迟进行的。使用linkedHashMap维护已加载且实例化的提供者。iterator()方法首先遍历这个cache,然后定位并解析配置文件、加载和实例化提供者,实例放入cache。reload()方法清空cache。
非线程安全
2. 源码逻辑
ServiceLoader
LazyIterator
private class LazyIterator implements Iterator<S>
load(xxx)
public static ServiceLoader load(xxx) 返回一个serviceLoader实例
iterator()
public Iterator<S> iterator() 遍历获取提供者实例
3. SPI扩展的场景
优点
模块之间解耦,开关原则,对扩展开放,对修改关闭。
场景
- 对某些中间件进行SPI扩展
中间件A支持SPI扩展,项目B依赖A的jar,可以在B中提供服务提供者,即实现A中允许SPI扩展的接口,按照A制定的规则编辑扩展配置。如dubbo支持协议、调用拦截扩展等
- 自己的系统支持SPI扩展