“Java SPI(Service Provider Interface),是JDK内置的一种动态加载扩展点的实现,是一种服务机制,SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载机器读取配置文件,加载实现类,这样就是可以在运行时动态的为接口替换实现类” ------ dubbo 官网
首先在ClassPath的META-INF/services目录下放置一个与接口同名的文本文件,文件的内容为接口的实现类,多个实现类用换行符分隔。JDK中使用java.util.ServiceLoader来加载具体的实现。
1.首先定义一个RobotCar接口
/**
* @Auther: corey
* @Date: 2020/7/3 10:05
* @Description: qq飞车
*/
public interface RobotCar {
void hello();
}
2.提供RobotCar的实现, RobotCar有两个实现。BigPenguin和OldGodmother。
/**
* @Auther: corey
* @Date: 2020/7/3 10:06
* @Description: 大企鹅
*/
public class BigPenguin implements RobotCar {
@Override
public void hello() {
System.out.println("Hello 我是大企鹅。。。");
}
}
/**
* @Auther: corey
* @Date: 2020/7/3 10:08
* @Description: 老干妈
*/
public class OldGodmother implements RobotCar {
@Override
public void hello() {
System.out.println("hello 我是老干ma。。。");
}
}
3.添加配置文件 在META-INF/services目录添加一个文件,文件名和接口全名称相同,所以文件是META-INF/services/com.service.RobotCar文件内容为:
service.impl.BigPenguin
service.impl.OldGodmother
4.通过ServiceLoader加载RobotCar实现
/**
* @Auther: corey
* @Date: 2020/7/3 10:17
* @Description:
*/
public class RobotCarTest {
public static void main(String[] args) {
ServiceLoader<RobotCar> serviceLoader = ServiceLoader.load(RobotCar.class);
System.out.println("Java SPI....");
serviceLoader.forEach(RobotCar::hello);
}
}
运行结果为:
在上面的例子中,我们定义了一个扩展点和它的两个实现。在ClassPath中添加了扩展的配置文件,最后使用ServiceLoader来加载所有的扩展点。 最终的输出结
如图所示:
Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。
优点:
使用Java SPI机制的优势是实现解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离,而不是耦合在一起。应用程序可以根据实际业务情况启用框架扩展或替换框架组件。
缺点:
虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类,的方式不够灵活
多个并发多线程使用ServiceLoader类的实例是不安全的。
数据库驱动加载接口实现类的加载
JDBC加载不同类型数据库的驱动
日志门面接口实现类加载,SLF4J加载不同提供商的日志实现类
Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
Dubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口
参考:
http://dubbo.apache.org/zh-cn/blog/introduction-to-dubbo-spi.html
https://developer.aliyun.com/article/640161
相关连接:
https://mp.weixin.qq.com/s/M0YIYOi9ucgnai1ILUVAvQ