什么是SPI
service provider interface JDK内置的中服务发现机制

SPI的思想
- 系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制
SPI约定
- 当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。通过这个约定,就不需要把服务放在代码中了,通过模块被装配的时候就可以发现服务类了。
jdk-spi demo代码实现
- 定义一个接口:
java
public interface IMessageChannel {
/**
* 发送短信
* @param context
* @return
*/
String sendMessage(String context);
}
- 做2个相应的实现类:
``` java```
public class Demo1MessageChannel implements IMessageChannel {
@Override
public String sendMessage(String context) {
return "Demo1MessageChannel-sendMessage:"+ context;
}
}
java
public class Demo2MessageChannel implements IMessageChannel {
@Override
public String sendMessage(String context) {
return "Demo2MessageChannel-sendMessage:"+ context;
}
}
- 在默认路径下添加对应接口的指示配置文件
\resources\META-INF\services 目录下添加配置文件 **com.qindaorong.spi.api.IMessageChannel**
> 注意文件没有后缀。文件名称为接口全路径
文件内容
```xml```
com.qindaorong.spi.impl.Demo1MessageChannel
com.qindaorong.spi.impl.Demo2MessageChannel
- 调用实现
java
public class App {
public static void main(String[] args) {
ServiceLoader serviceLoader =ServiceLoader.load(IMessageChannel.class);
for(IMessageChannel messageChannel:serviceLoader){
System.out.println(messageChannel.sendMessage("hallo world!"));
}
}
}
- 实现效果
```log```
Connected to the target VM, address: '127.0.0.1:51854', transport: 'socket'
Demo1MessageChannel-sendMessage:hallo world!
Demo2MessageChannel-sendMessage:hallo world!
Disconnected from the target VM, address: '127.0.0.1:51854', transport: 'socket'
Process finished with exit code 0
#spring-spi demo代码实现
接口和实现类不再重复写,这里主要讲一下配置文件的不同
resources\META-INF\spring.factories 中添加对应接口以及实现类
xml
com.qindaorong.spi.api.IMessageChannel=
com.qindaorong.spi.impl.Demo1MessageChannel,
com.qindaorong.spi.impl.Demo2MessageChannel
- 调用实现
``` java```
public class SpringSpiTestMain {
public static void main(String[] args) {
List<IMessageChannel> messageChannel= SpringFactoriesLoader.loadFactories(IMessageChannel.class, null);
messageChannel.forEach(messageChannel-> {
messageChannel.sendMessage("SpringFactoriesLoader.loadFactories");
});
}
}
- 实现效果
logSpringFirstMessageChannel-sendMessage:SpringFactoriesLoader.loadFactories SpringSecondMessageChannel-sendMessage:SpringFactoriesLoader.loadFactories Disconnected from the target VM, address: '127.0.0.1:52632', transport: 'socket'
# 思考
springboot 对spi机制的应用方式是怎样的?
如何利用spring-spi机制完成我们自己的业务逻辑,比如按照选择加载?
我们如何拓展?
其他框架如何使用spi机制的?
# ele
#### jdk spi demo 实现
定义抽象接口
```java
/**
*
*
*
* @author zz_huns
* @version Id: ISubmitExercise.java, v 0.1 2020/5/17 11:01 PM zz_huns Exp ?
*
*/
public interface ISubmitExercise {
void submitEnglishExercise();
void submitChineseExercise();
}
实现
/**
*
*
*
* @author zz_huns
* @version Id: SubmitExercise.java, v 0.1 2020/5/17 10:58 PM zz_huns Exp ?
*
*/
public class SubmitExercise implements ISubmitExercise {
@Override
public void submitEnglishExercise() {
System.out.println("hello , summit english exercise");
}
@Override
public void submitChineseExercise() {
System.out.println("你好,提交语文作业");
}
}
配置


spring-boot spi demo 实现
1.定义一个业务类




