SPI(Service Provider Interface)机制及SPI在springMVC的应用

桃扇骨 2022-09-02 00:56 257阅读 0赞

什么是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

标准服务接口提供方

  1. 先新建工程,新建模块spi-db-interface
    在这里插入图片描述
  2. 定义标准接口

    1. /** * @author fangYaJun * @date 2021/7/29 10:29 */
    2. public interface DbInterface {
    3. /** * 保存数据接口 * @param data */
    4. public void saveData(String data);
    5. }

服务提供方1(对接口标准的具体实现)

  1. 新建模块spi-db-mysql-impl,如下截图
    在这里插入图片描述
  2. 对标准接口的实现

    1. package com.spi.mysql;
    2. import com.spi.db.myinterface.DbInterface;
    3. /** * @author fangYaJun * @date 2021/7/29 10:36 */
    4. public class MySqlSpiTest implements DbInterface {
    5. @Override
    6. public void saveData(String data) {
    7. System.out.println("MySqlSpiTest保存了数据:" + data);
    8. }
    9. }
  3. 在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
    在这里插入图片描述

服务提供方2

  1. 新建模块spi-db-redis-impl,如下截图
    在这里插入图片描述
  2. 实现标准服务接口

    1. /** * @author fangYaJun * @date 2021/7/29 10:48 */
    2. public class RedisSpiImpl implements DbInterface {
    3. @Override
    4. public void saveData(String data) {
    5. System.out.println("RedisSpiImpl 保存了" + data);
    6. }
    7. }
  3. 在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
    在这里插入图片描述

测试工程

  1. 新建测试模块
    在这里插入图片描述
  2. pom引入接口模块,具体实现模块的依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.spi.db</groupId>
    4. <artifactId>spi-db-interface</artifactId>
    5. <version>1.0-SNAPSHOT</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>com.spi.db</groupId>
    9. <artifactId>spi-db-mysql-impl</artifactId>
    10. <version>1.0-SNAPSHOT</version>
    11. </dependency>
    12. <dependency>
    13. <groupId>com.spi.db</groupId>
    14. <artifactId>spi-db-redis-impl</artifactId>
    15. <version>1.0-SNAPSHOT</version>
    16. </dependency>
    17. </dependencies>
  3. 测试类

    1. /** * @author fangYaJun * @date 2021/7/29 11:04 * * 1、 ServiceLoader:load()指定一个接口, * 他就会加载当前系统里面所有的这个接口的【指定实现】 * 2、SPI(Service Provider Interface) * 接口工程---提供接口 * ---- 实现工程1 : 实现接口 【META-INF/services 创建文件 接口名作为文件名 实现类全路径作为文件内容】 * ---- 实现工程2 : 实现接口 * 客户端----引用 工程1、或者 工程2 */
    2. public class SpiAppTest {
    3. public static void main(String[] args) {
    4. ServiceLoader<DbInterface> load = ServiceLoader.load(DbInterface.class);
    5. for (DbInterface dbInterface : load) {
    6. dbInterface.saveData("我是数据");
    7. }
    8. }
    9. }

    运行结果:
    在这里插入图片描述

从结果中我们看出了,通过spi机制,测试类只需要通过ServiceLoader.load(DbInterface.class) 加载对应的类名,我们就可以获取到全部实现了DbInterface接口的对象,调用DbInterface接口定义的方法,我们就可以运行具体实现类实现了DbInterface接口的方法。

SPI在springMVC的应用

我们来看spring-web项目

在这里插入图片描述

从截图我们可以看出spring-web项目META-INF有SPI机制
javax.servlet.ServletContainerInitializer的规范实现了

  1. org.springframework.web.SpringServletContainerInitializer

我们来看看 ServletContainerInitializer,这是java官方定义的servlet 规范:
在这里插入图片描述

而SpringServletContainerInitializer 是spring官方对ServletContainerInitializer规范的实:
在这里插入图片描述

综上所诉,tomcat在启动的时候,会加载ServletContainerInitializer类,通过SPI机制,进而调用SpringServletContainerInitializer 的 onStartup方法来加载springweb项目

发表评论

表情:
评论列表 (有 0 条评论,257人围观)

还没有评论,来说两句吧...

相关阅读