Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SPI机制

SPI机制

作者头像
晚上没宵夜
发布于 2020-05-08 07:00:42
发布于 2020-05-08 07:00:42
1.3K00
代码可运行
举报
运行总次数:0
代码可运行

1. SPI

SPI ( Service Provider Interface),是JDK提供的一种服务发现机制。可发现并自动加载在ClassPath下的jar包中META-INF/services文件下以服务接口命名的文件内的全限定类名映射的类。当服务的提供者,提供了服务接口的一种实现之后,只需在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件即可被程序加载并使用。

在Coding中,模块间使用接口编程可实现松耦合,而不进行硬编码。那么SPI的出现可用于动态地启用框架扩展和替换组件,其常见应用:

  • 数据库驱动加载接口实现类的加载
  • 日志门面接口实现类加载
  • Spring中servlet3.0规范对ServletContainerInitializer的实现

简单来说:客户端提供了自己所需服务的接口,而服务端有很多各自有各自的不同实现。那么客户端可根据自己选择的不同服务端而动态的加载并使用这些服务

使用栗子

  • 服务端提供了具体实现类后,要在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
  • 服务实现类所在的jar包要在classpath中
  • 使用java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM
  • SPI的实现类必须携带一个不带参数的构造方法
1.1 客户端提供接口
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface SPInterface {

    public abstract void eat();

}
1.2 不同服务端提供各自的实现类
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Dog implements SPInterface {
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Cat implements SPInterface {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
1.3 服务端建立Services文件

类路径下建 /META-INF/services 目录, 并创建用接口命名的文件 ,其内容为服务实现类的全限定类名(每行一个类名)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
com.howl.spi.impl.Cat
com.howl.spi.impl.Dog
1.4 使用SPI机制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class main {


    public static void main(String[] args) {
        ServiceLoader<SPInterface> spInterfaces = ServiceLoader.load(SPInterface.class);
        Iterator<SPInterface> iterator = spInterfaces.iterator();
        while (iterator.hasNext()){
            iterator.next().eat();
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
猫吃鱼
狗吃肉

2. JDBC的SPI解析

下面从源码的角度解析SPI机制在JDBC中的使用

2.1 以前我们使用jdbc的流程
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Demo {

    public static void main(String[] args) {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "123456";

        try {
            Class.forName(driver);    // 在内存中加载driver类
            Connection conn = DriverManager.getConnection(url,user,password);  // 获取连接
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}
  • 较新的jar包driver改名成了 com.mysql.cj.jdbc.Driver
  • Class.forName(driver) ,使用了桥接模式,抽象与实现分离
  • 使用 Class.forName,不用new因为不同实现子类可以通过改变配置文件而实现,若new则要修改代码编译
2.2 进入driver类
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Driver extends NonRegisteringDriver implements java.sql.Driver {

    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());  // 在静态代码块注册了driver
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
  • 这里有个更新:以前的dvier继承了 jc 的新类了,而 jc 实现了java.sql.Driver接口
  • JDBC规范中要求Driver类在使用前必须向DriverManager注册自己(静态代码块中实现了,可不用手动注册)
2.3 SPI的使用

新的版本中,Class.forName(driver)也已经自动实现了,可不用手动加载。原因:在DriverManager中使用了SPI机制

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DriverManager {

    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     *
     * 通过 检查系统变量 jdbc.properties 然后使用 ServiceLoader 机制
     * 来加载初始化的JDBC dirvers
     */
    static {
        loadInitialDrivers();   // 这里加载并实例化了Drivers
        println("JDBC DriverManager initialized");
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static void loadInitialDrivers() {
    String drivers;

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {

            // SPI机制
            // 加载Driver接口的服务类,Driver接口的包是java.sql.Driver
            // 即从找 META-INF/services/java.sql.Driver中的全限定类名
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();

			// 遍历每一个Driver类,即加载每一个驱动,以便实例化
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();  // 这里实例化
                }
            } catch(Throwable t) {
            // Do nothing
            }
            return null;
        }
    });
  • ServiceLoader.load(Driver.class) 使用了SPI机制加载了Driver类
  • 迭代器的next()方法就是创建实例,从而向Manager注册了自己
  • 在mysql-connection-java包下的META-INF/services/java.sql.Driver中有 全限定类名 com.mysql.cj.jdbc.Driver
2.4 新版的JDBC使用
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Demo {

    public static void main(String[] args) {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://47.56.143.47:3306/test";
        String user = "root";
        String password = "123456";

        try {
            Connection conn = DriverManager.getConnection(url,user,password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-05-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
面试官:说说如何打破或违反双亲委派!
SPI的全名为Service Provider Interface,主要是应用于厂商自定义组件或插件中,在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java SPI机制的思想:我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块、xml解析模块、jdbc模块等方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
业余草
2021/12/06
2.2K1
面试官:说说如何打破或违反双亲委派!
双亲委派机制,懂吧~ 那什么情况下需要破坏它,知道吗?
那么在Class的forName(className)方法中,会根据是谁调用了Class.forName(className)这个方法,那么就获得当时加载了它的那个ClassLoader,然后,再通过类加载器来负责对类进行加载操作。
爪哇缪斯
2023/08/19
3190
双亲委派机制,懂吧~ 那什么情况下需要破坏它,知道吗?
【Java——SPI机制详解】
SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。
奥耶可乐冰
2024/05/31
2K0
Java的SPI机制实践
先给出结论:“Java的SPI是一种服务发现机制,用于约定接口和动态发现实现类,体现了分层解耦的思想”。 Java的SPI机制常用于框架扩展或组件替换,最常见的Java SPI应用就是JDBC Driver,JDK提供了java.sql.Driver接口,却将具体的实现交给了相应的数据库驱动,比如:在mysql-connector-java-6.0.6.jar文件中可以看到一个遵循Java SPI机制的文件META-INF/services/java.sql.Driver,并且在该文件中定义了具体的驱动实现类完整限定名称:com.mysql.cj.jdbc.Driver。
编程随笔
2023/10/15
2310
Java的SPI机制实践
一文读懂微内核架构
微内核是一种典型的架构模式 ,区别于普通的设计模式,架构模式是一种高层模式,用于描述系统级的结构组成、相互关系及相关约束。微内核架构在开源框架中的应用非常广泛,比如常见的 ShardingSphere 还有Dubbo都实现了自己的微内核架构。那么,在介绍什么是微内核架构之前,我们有必要先阐述这些开源框架会使用微内核架构的原因。
JAVA日知录
2021/01/04
3.3K0
一文读懂微内核架构
Java SPI机制详解
什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实
aoho求索
2018/06/04
1.2K0
SPI 机制-插件化扩展功能
SPI(Service Provider Interfaces),中文直译服务提供者接口,一种服务发现机制。可能很多人都不太熟悉这个机制,但是平常或多或少都用到了这个机制,比如我们使用 JDBC 连接操作数据库的时候。
andyxh
2019/09/05
9720
SPI 机制-插件化扩展功能
温习 SPI 机制 (Java SPI 、Spring SPI、Dubbo SPI)
SPI 全称为 Service Provider Interface,是一种服务发现机制。
勇哥java实战
2024/10/29
3580
温习 SPI 机制 (Java SPI 、Spring SPI、Dubbo SPI)
深入理解 Java 中 SPI 机制
SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,本文由浅入深地介绍了Java SPI机制。
2020labs小助手
2019/09/09
8660
JAVA拾遗--关于SPI机制
JDK提供的SPI(Service Provider Interface)机制,可能很多人不太熟悉,因为这个机制是针对厂商或者插件的,也可以在一些框架的扩展中看到。其核心类 java.util.ServiceLoader可以在jdk1.8的文档中看到详细的介绍。虽然不太常见,但并不代表它不常用,恰恰相反,你无时无刻不在用它。玄乎了,莫急,思考一下你的项目中是否有用到第三方日志包,是否有用到数据库驱动?其实这些都和SPI有关。再来思考一下,现代的框架是如何加载日志依赖,加载数据库驱动的,你可能会对class.
kirito-moe
2018/04/27
9040
JAVA拾遗--关于SPI机制
Java SPI机制的运行原理是什么?
SPI的全称是(Service Provider Interface)是服务提供接口的意思。如果我们不写框架性代码或者开发插件的话,对于SPI机制可能不会那么熟悉,但如果我们阅读诸如Dubbo、JDBC数据库驱动包、Spring以及最近比较流行的Spring Boot相关starter组件源码的话,就会发现SPI机制及其思想在这些框架中有大量的应用。
用户5927304
2019/07/30
1.9K0
Java SPI机制的运行原理是什么?
JAVA SPI 是怎么实现的?
SPI(Service Provider Interface) ,是 JDK 内置的一种提供发现机制。SPI 是一种动态替换发现的机制。
王小明_HIT
2020/06/04
7680
JAVA SPI  是怎么实现的?
项目中疯狂使用SPI思想,在这里总结下
相信你对SPI机制也所了解,但在项目中估计就没用过。巧了,前段时间我们项目中疯狂使用SPI思想,今天就来总结一下。
田维常
2022/11/25
4420
项目中疯狂使用SPI思想,在这里总结下
java SPI机制的使用及原理
本片文章是针对dubbo SPI机制深入分析的平滑过渡的作用。当然咱们主要是学习优秀的思想,SPI就是一种解耦非常优秀的思想,我们可以思考在我们项目开发中是否可以使用、是否可以帮助我们解决某些问题、或者能够更加提升项目的框架等
全栈程序员站长
2021/08/05
3640
JAVA 拾遗 —— 关于SPI机制
实现一个自定义的SPI1. 项目结构2. interface 模块3. good-printer 模块4. bad-printer模块SPI 在实际项目中的应用SPI 在扩展方面的应用
芋道源码
2019/10/29
4980
JAVA 拾遗 —— 关于SPI机制
Java SPI机制和使用示例
SPI 是 Java 提供的一种服务加载方式,全名为 Service Provider Interface。根据 Java 的 SPI 规范,我们可以定义一个服务接口,具体的实现由对应的实现者去提供,即服务提供者。然后在使用的时候再根据 SPI 的规范去获取对应的服务提供者的服务实现。通过 SPI 服务加载机制进行服务的注册和发现,可以有效的避免在代码中将服务提供者写死。从而可以基于接口编程,实现模块间的解耦。
朝雨忆轻尘
2019/06/19
2.5K0
聊聊 SPI 机制
短信平台是通过 SPI 机制加载三方渠道插件,所以这篇文章,我们有必要温习 SPI 机制。
勇哥java实战
2023/11/25
3510
(转)JAVA拾遗--关于SPI机制
JDK提供的SPI(Service Provider Interface)机制,可能很多人不太熟悉,因为这个机制是针对厂商或者插件的,也可以在一些框架的扩展中看到。其核心类java.util.ServiceLoader可以在jdk1.8的文档中看到详细的介绍。虽然不太常见,但并不代表它不常用,恰恰相反,你无时无刻不在用它。玄乎了,莫急,思考一下你的项目中是否有用到第三方日志包,是否有用到数据库驱动?其实这些都和SPI有关。再来思考一下,现代的框架是如何加载日志依赖,加载数据库驱动的,你可能会对class.forName(“com.mysql.jdbc.Driver”)这段代码不陌生,这是每个java初学者必定遇到过的,但如今的数据库驱动仍然是这样加载的吗?你还能找到这段代码吗?这一切的疑问,将在本篇文章结束后得到解答。
屈定
2018/09/27
9150
(转)JAVA拾遗--关于SPI机制
源码级深度理解 Java SPI
SPI 是一种用于动态加载服务的机制。它的核心思想就是解耦,属于典型的微内核架构模式。SPI 在 Java 世界应用非常广泛,如:Dubbo、Spring Boot 等框架。本文从源码入手分析,深入探讨 Java SPI 的特性、原理,以及在一些比较经典领域的应用。
2020labs小助手
2022/11/07
8740
聊聊 Java SPI
SPI (Service Provider Interface) 是自 Java 1.6 引入的一种基于接口或抽象类的服务发现机制。得益于 Java SPI 机制,开发人员只需为第三方预留出 SPI 拓展接口,这样可以在不修改代码的前提下,通过增删第三方依赖来实现系统的灵活拓展。
程序猿杜小头
2022/12/01
8930
聊聊 Java SPI
相关推荐
面试官:说说如何打破或违反双亲委派!
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验