spring-boot - 核心思想spi

1,434 阅读3分钟

什么是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");
        });
    }
}
  • 实现效果 log SpringFirstMessageChannel-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.定义一个业务类

2.定义自定义的 Configuration
3.配置文件,创建在META-INF/spring.factories 配置文件
4.将此工程打为jar包,在另一工程内引入
5.启动,查看启动日志,打印成功