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

标准服务接口提供方

  1. 先新建工程,新建模块spi-db-interface
    在这里插入图片描述

  2. 定义标准接口

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

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

  1. 新建模块spi-db-mysql-impl,如下截图
    在这里插入图片描述

  2. 对标准接口的实现

    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);
        }
    }
    
  3. 在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
    在这里插入图片描述

服务提供方2

  1. 新建模块spi-db-redis-impl,如下截图
    在这里插入图片描述

  2. 实现标准服务接口

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

测试工程

  1. 新建测试模块
    在这里插入图片描述

  2. 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>
    
  3. 测试类

    /**
     * @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项目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值