SPI
2020-04-14 本文已影响0人
愤怒的老照
背景
最近看到了SPI这个东西,搜了几篇文章有了大概的认识,在这里记录一下。
简介
SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制
,可以用来替换不同的实现,Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。
使用场景
其实SPI已经接触过很多次了,只是自己之前没有注意过。JDBC就是使用了SPI机制,JDBC提供连接数据库的接口,由不同的数据库厂商提供各自的连接实现(应该是不提供的话就会失去Java这个大市场吧),我们需要哪个数据库只需要加载对应的服务实现。
Java的SPI机制
Java中已经提供了SPI的约定,当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件,该文件里就是实现该服务接口的具体实现类。当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。也可以使用其它方式。
尝试
实现SPI的方式有很多,正好项目里出现了starter包的使用,所以打算使用starter测试一下
![](https://img.haomeiwen.com/i14249104/96e0670fb677145b.png)
1、创建maven多模块,包含了api模块、spiA模块、spiB模块和starter模块
- api模块,这个模块是提供给第三方接口的模块,并没有任何实现。
public interface IPersonWork {
public String work();
}
- spiA模块和spiB模块是api模块接口的两个实现
public class PersonAWork implements IPersonWork {
@Override
public String work() {
return "我是A,我来工作了";
}
}
public class PersonBWork implements IPersonWork {
@Override
public String work() {
return "我是PersonB,我来工作了";
}
}
- 提供starter模块,可以让第三方直接引用
@Configuration
@ConditionalOnClass(IPersonWork.class)
public class AutoConfig {
@ConditionalOnMissingClass({"personAWork"})
@Bean
public IPersonWork personBWork() {
return new PersonBWork();
}
@Bean
public IPersonWork personAWork() {
return new PersonAWork();
}
}
2、第三方使用
- 第三方引入starter包
<dependency>
<artifactId>com.suhongyan.www.starter</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
- 注入直接使用就可以了
@SpringBootTest
public class Test {
@Resource
private IPersonWork personBWork;
@Autowired
@org.junit.jupiter.api.Test
void contextLoads() {
System.out.println(personBWork.work());
}
}
- 输出
我是PersonB,我来工作了
spi的实现没有限制,可以使用starter、使用java的ServiceLoader,也可以在调用方直接做判断。