推荐两本书:《架构修炼之道》和《软件框架设计的艺术》
SPI是一种回调的思想,回调是指我们在使用api时,我们可以向api传入一个类或者方法,api在合适的时间调用类或者方法。SPI是在一些通用的标准中,为标准的实现产商提供的扩展点。标准在上层提供API,API内部使用了SPI,当API被客户使用时,会动态得从当前运行的classpath中寻找该SPI的实现,然后使用该SPI的实现来完成API的功能。
“SPI”,全程为:service provider interface,“SPI”的规则如下:
概念上更依赖调用方。
组织上位于调用方所在的包中。
实现位于独立的包中。
常见的例子是:插件模式的插件。
“API”的规则如下:
概念上更接近实现方。
组织上位于实现方所在的包中。
实现和接口在一个包中。
二、SPI的机制和原理
2.1 SPI的机制
Service provider提供Interface的具体实现后,在目录META-INF/services下的文件(以Interface全路径命名)中添加具体实现类的全路径名;
接口实现类的jar包存放在所使用的类加载器能找到的地方。
应用程序使用ServiceLoader动态加载实现类(根据目录META-INF/services下的配置文件找到实现类的全限定名并调用classloader来加载实现类到JVM);
SPI的实现类必须具有无参数的构造方法。
SPI加载的核心就是ClassLoader的getResource系列方法,jdk提供了一个工具类,就是上面说的ServiceLoader。
2.2 SPI原理
(1)应用程序调用 ServiceLoader.load 方法,ServiceLoader.load 方法内先创建一个新的ServiceLoader,并实例化该类中的成员变数,包括:ClassLoader loader(类载入器)
AccessControlContext acc(访问控制器)
LinkedHashMap<String, S> providers(用于缓存载入成功的类)
LazyIterator lookupIterator(实现迭代器功能)
(2) 应用程序通过迭代器获取对象实例ServiceLoader 先判断成员变量 providers 对象中否有缓存实例对象,如果有缓存,直接返回。
如果没有缓存,执行类的装载:读取 META-INF/services/ 下的配置文件,获得所有能被实例化的类的名称,通过反射方法 Class.forName() 载入类对象,并用 instance() 方法将类实例化。把实例化后的类缓存到providers 对象中然后返回实例对象。