SPI(Service Provider Interface)机制及SPI在springMVC的应用
什么是SPI
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类, 整体机制如下图:
Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制,
服务的接口标准的定义,然后服务接口的实现通过SPI机制在META-INF/services暴露自己的接口的实现。
使用介绍
要使用Java SPI,需要遵循如下约定:
- 1、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
- 2、接口实现类所在的jar包放在主程序的classpath中;
- 3、主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
- 4、SPI的实现类必须携带一个不带参数的构造方法
具体demo
标准服务接口提供方:
- 先新建工程,新建模块spi-db-interface
定义标准接口
/** * @author fangYaJun * @date 2021/7/29 10:29 */
public interface DbInterface {
/** * 保存数据接口 * @param data */
public void saveData(String data);
}
服务提供方1(对接口标准的具体实现)
- 新建模块spi-db-mysql-impl,如下截图
对标准接口的实现
package com.spi.mysql;
import com.spi.db.myinterface.DbInterface;
/** * @author fangYaJun * @date 2021/7/29 10:36 */
public class MySqlSpiTest implements DbInterface {
@Override
public void saveData(String data) {
System.out.println("MySqlSpiTest保存了数据:" + data);
}
}
- 在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
服务提供方2
- 新建模块spi-db-redis-impl,如下截图
实现标准服务接口
/** * @author fangYaJun * @date 2021/7/29 10:48 */
public class RedisSpiImpl implements DbInterface {
@Override
public void saveData(String data) {
System.out.println("RedisSpiImpl 保存了" + data);
}
}
- 在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
测试工程
- 新建测试模块
pom引入接口模块,具体实现模块的依赖
<dependencies>
<dependency>
<groupId>com.spi.db</groupId>
<artifactId>spi-db-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.spi.db</groupId>
<artifactId>spi-db-mysql-impl</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.spi.db</groupId>
<artifactId>spi-db-redis-impl</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
测试类
/** * @author fangYaJun * @date 2021/7/29 11:04 * * 1、 ServiceLoader:load()指定一个接口, * 他就会加载当前系统里面所有的这个接口的【指定实现】 * 2、SPI(Service Provider Interface) * 接口工程---提供接口 * ---- 实现工程1 : 实现接口 【META-INF/services 创建文件 接口名作为文件名 实现类全路径作为文件内容】 * ---- 实现工程2 : 实现接口 * 客户端----引用 工程1、或者 工程2 */
public class SpiAppTest {
public static void main(String[] args) {
ServiceLoader<DbInterface> load = ServiceLoader.load(DbInterface.class);
for (DbInterface dbInterface : load) {
dbInterface.saveData("我是数据");
}
}
}
运行结果:
从结果中我们看出了,通过spi机制,测试类只需要通过ServiceLoader.load(DbInterface.class)
加载对应的类名,我们就可以获取到全部实现了DbInterface接口的对象,调用DbInterface接口定义的方法,我们就可以运行具体实现类实现了DbInterface接口的方法。
SPI在springMVC的应用
我们来看spring-web项目
从截图我们可以看出spring-web项目META-INF有SPI机制
对javax.servlet.ServletContainerInitializer
的规范实现了
org.springframework.web.SpringServletContainerInitializer
我们来看看 ServletContainerInitializer,这是java官方定义的servlet 规范:
而SpringServletContainerInitializer 是spring官方对ServletContainerInitializer规范的实:
综上所诉,tomcat在启动的时候,会加载ServletContainerInitializer类,通过SPI机制,进而调用SpringServletContainerInitializer 的 onStartup方法来加载springweb项目
还没有评论,来说两句吧...