目录
1、ServiceLoader.load(Class class)
一、什么是SPI
SPI是Java提供的服务发现接口,它的全称是Service-Provide-Interface,由用户提供一个抽象接口,实现的子类可以动态扩展,而不需要改动到之前的逻辑,有点像策略模式。
SPI实现接口编程+策略模式+配置文件的动态加载机制。
二、SPI的作用
在模块化设计中,模块之间实现接口编程,将装配的控制权放在程序之外,模块间彻底解藕。
三、如何使用
首先创建一个接口和多个实现类,在main文件夹下创建resource文件夹,resource和java文件夹并列,然后在resource文件夹创建META-INF.service文件夹,在该文件夹下创建接口的全限定名的文件,最后在文件内写入每个子类的全限定类名。
代码非常简单
public interface SPIService {
void execute();
}
public class SpiImpl1 implements SPIService {
@Override
public void execute() {
Log.d("spi", "SpiImpl1 execute");
}
}
ServiceLoader<SPIService> serviceLoader = ServiceLoader.load(SPIService.class);
Iterator<SPIService> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
SPIService next = iterator.next();
next.execute();
}
四、流程图
五、源码学习
1、ServiceLoader.load(Class<T> class)
ServiceLoader.load()方法创建对象,ServiceLoader对象的构造方法是私有的, 不允许外部new创建对象,这样可以保证必要的参数,防止空指针问题。同时还有loadInstalled(Class<S> service)方法,支持加载系统接口类对象。
2、serviceLoader.iterator()
ServiceLoader实现iterator方法,这里是个迭代器设计模式。迭代器模式的使用场景是顺序遍历某个对象的内部元素,无需暴露内部的实现,实现解藕的目的。
3、iterator.hasNext()
iterator()返回一个iterator匿名内部类对象,hasNext()先判断LinkedHashMap集合中是否有创建好的实例,有就直接返回,没有就调用LazyIterator的hasNext方法,懒加载迭代器在使用时加载,因为会使用到反射和IO操作,对性能有点影响。
4、iterator.next()
同样next()会先判断LinkedHashMap集合中有没有数据,有就直接返回,没有就从LazyIterator.next()中读取。
知识点回顾:
1.迭代器模式,有遍历数据需求,迭代器模式将数据隔离到内部,解藕,提升拓展性和维护性。
2.内存-磁盘缓存模型,内存数据结构和和磁盘接口保持一致,先取内存,再取磁盘,保存到内存中,提升性能。
六、拓展
适用于调用方根据实际需求启用、扩展、替换服务的策略实现。
许多开源框架中都使用了 Java 的 SPI 机制,Android中美团WMRouter路由框架,java中javac注解处理器框架、JDBC 的 SPI 加载模式、日志框架 SLF4J 加载不同提供商的日志实现、Spring 中也大量适用了 SPI、Dubbo 的扩张机制、ServiceComb Java Chassis (CSE) 的 Filter、异常处理等扩展机制。
参考文献: