SPI机制详解
什么是SPI机制?
SPI:Service Provider Interface,中文直译:服务提供者接口,它通过在ClassPath路径下的META-INF/service文件夹中查找文件,并自动加载文件里所定义的类
在面向对象的设计原则中,一般推荐模块之间基于接口编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。一旦代码里面涉及具体实现类,就违反了开闭原则。如果需要替换一种实现,就需要修改代码。
为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。Java SPI 就是提供了这样一个机制:为某个接口寻找服务实现的机制。这有点类似 IoC 的思想,将装配的控制权移交到了程序之外。
SPI机制的出现是为了解决什么问题?
举个经典的栗子,在后端开发中,不可避免的就是通过JDBC去连接数据库,但是不同的数据库有不同的驱动,MySQL有MySQL的驱动,Oracle有Oracle的驱动,但是它们都实现了JDBC接口
假如现在有个需求,项目原来使用的是MySQL作为数据库,但是现在要替换成Oracle数据库,怎么实现?
去项目中手动去改变Driver的实现类?
Driver driver = new OracleDriverImpl();
太不优雅了,太不体面了🤣
有没有一种方法能动态替换实现类,如果我现在导入的Jar包是MySQL的驱动我就将Driver的实现类定义为MySQL的驱动包,导入的Jar包是Oracle的驱动我就将Driver的实现类定义为Oracle的驱动包?
诶,Java开发者定义了这样一个规则,在项目的ClassPath目录中创建META-INF/Services目录,在这里面创建以接口名为文件名,内容为实现类的全限定名的一个文件;
再通过IO的方式获取到所有的全限定名,如果存在MySQL数据库的驱动,那么calssNameList = [“com.mysql.cj.jdbc.Driver”]
以下代码为伪代码,在java.util.ServiceLoader#load(Class clazz) 完成了spi 的实现,具体的思想跟下面的流程类似,感兴趣的可以去看一看
SPI这种方式可以很好解决不同框架之间的拓展问题,可以同时兼容同一个接口的多个实现类;
SPI机制的变种
学过SpringBoot的童鞋们都知道自动装配绝对是SpringBoot的大招,它可以将外部框架的配置类动态的加载到我们IOC的容器中;其实它的思想与SPI机制非常类似;
以mybatis-plus为例:
问题:因为mybatis-plus的ClassPath与我们的项目路径不同,肯定是不能通过扫描注解的方式注入到IOC中
那么SpringBoot定义了一个机制,如果这些框架想将自己的配置类注入到使用者的IOC中,你可以在框架包的ClassPath下创建一个META-INF目录,在这个目录中创建一个spring.factories文件
在SpringBoot项目启动时,会加载所有Jar包下META-INF/spring.factories文件,根据org.springframework.boot.autoconfigure.EnableAutoConfiguration后面的全限定类名,通过反射将这个类加载到IOC的容器中
这样就达到了项目与框架之间的解耦,在项目不需要手动去将这些框架的配置类加载到IOC容器中;
除了SpringBoot的自动装配体现了SPI的思想,还有例如Dubbo、Slf4j等等
总结
- SPI机制能够使接口与具体的实现类解耦,可以根据实际的业务情况启用或替换具体组件
- SPI机制为很多框架的拓展提供了可能
- SPI机制更多的是一种思想
如果觉得本篇文章对于您有帮助,可否点个小赞😺;篇幅较长建议收藏🫠;关注一手等待后续更新更多干货🚀
参考视频:https://www.bilibili.com/video/BV1E44y1N7Nk/?spm_id_from=333.337.search-card.all.click&vd_source=835177d483a47b8fcf9934ddad59626a
参考文章:https://javaguide.cn/java/basis/spi.html