Java SPI 机制

本文详细介绍了Java的SPI(Service Provider Interface)机制,包括其组成部分、工作原理及应用场景。SPI机制允许Java程序在运行时发现服务实现,例如不同的数据库驱动。文中还通过模拟CPU选择的例子展示了SPI的实际运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SPI机制 1

说到SpringBoot的自动配置机制,就不能不谈Java的SPI(service provider interface)机制,SPI是JDK内置的一种服务发现机制,它主要是让Java程序在运行时再去发现服务的实现类。就拿JDBC来说,每一个数据库厂商的数据库产品暴露给外部的访问方式、传输协议都可能不一致,所以Java索性就只指定一个标准,各个厂商自己根据这个标准来实现对自己数据库产品的驱动,比如MySQL 有 MySQL connector,Oracle有odbc6 等等。

SPI的组成部分:

interface:需要使用SPI机制的接口,放在集成方,表明我只提供这个规范,怎么实现那是厂商或者其他框架的事情,比如JDBC 的java.sql.Driver
config:需要在实现方的包下建立/META-INF/services文件夹,并在这个文件夹下添加当前库提供了哪个SPI接口的实现,比如mysql connector包下 就有一个文件命名为java.sql.Driver,文件命名必须和实现的接口同名
implements:这个是对上面说的接口的实现,可以有多个,并将实现类的全限定名在上述文件中一行写一个

模拟

咱们模拟组装电脑时候的CPU,CPU厂商有很多,Intel、AMD等,我们就抽象一下CPU里面有个compute()方法。

package com.xhulife.spi;

public interface CPU {
    void compute();
}

就当咱们买了一堆零件,然后就差CPU了,我们先准备一个AMD的CPU:

package com.xhulife.spi.cpu;

import com.xhulife.spi.CPU;

public class AmdCPU implements CPU{
    @Override
    public void compute() {
        System.out.println("this is amdCPU compute");
    }
}

并且在resources/META-INF/services/com.xhulife.spi.CPU 下写入:

com.xhulife.spi.cpu.AmdCPU

然后打成jar包,放到我们的电脑上(放到classpath里面),电脑上需要写一段代码来加载CPU:

package com.xhulife.spi;

import java.util.ServiceLoader;

public class Main {
    public static void main(String[] args) {
        ServiceLoader<CPU> load = ServiceLoader.load(CPU.class);
        for (CPU c:load){
            c.compute();
        }
    }
}

打开电脑:

this is amdCPU compute

突然某一天,我想换一个Intel I9处理器了:

package com.xhulife.spi.cpu;

import com.xhulife.spi.CPU;

public class IntelCPU implements CPU {
    @Override
    public void compute() {
        System.out.println("this is Intel I9 compute");
    }
}

把之前的AMD处理器拿出来,然后把新的I9处理器打包放到电脑上,开机:

this is Intel I9 compute

这就模拟了整个流程

应用场景

SPI机制其实就是用来指定规范的,我把规范制定好,你们大家如果想在我的平台上大展拳脚,那就根据我的规范来写一套标准的操作集合。其实就拿JDBC来说,没有JDK制定的jdbc规范可以吗?肯定是可以的,不过这就苦了我们程序员,我们得根据各个数据库厂商的数据传输协议,自己写一个驱动,或者是各个数据库厂商根据各自的数据库特性,自己搞一套驱动方案,这就给我们软件开发带来了更大的挑战。
但是这个机制似乎对我们一般人没什么大的作用,我们一不会去自己根据JDBC规范来写某一个数据库的驱动,二不会发布一个规范让别人来遵守并且实现(有这一天的话,我就要上天了)。只是我们需要了解有这样一个机制,当我们在企业开发的时候如果遇到类似的需求,或许有用武之地

双亲委派模型

Java类加载器可以分为2类:引导类加载器和其他加载器,这么分的主要原因是引导类加载器是包含在Java虚拟机中的,在java程序中无法引用这个类加载器,其他加载器又可以分为java扩展类加载器和应用加载器,扩展加载器负责加载lib/ext 下的包,应用加载器负责加载我们自己写的java程序或者第三方jar包。

java的设计人员推荐使用双亲委派模型来加载java类,即当类加载器接收到一个加载类的请求之后,先委派给父加载器去加载(注意这里父加载器并不是通过继承,而是通过组合来的),父加载器反馈无法加载这个类,自己再尝试加载,所以java中越是基础的类,加载其的类加载器越是上层

双亲委派模型被破坏

Java 类的加载使用双亲委派模型,SPI是JDK内置的服务发现机制,很多SPI接口是由java的核心库提供的,这些接口是由引导类加载器(Bootstrap Classloader)加载的,但是这些类在加载的时候,又需要调用SPI实现类,在引导类加载器的搜索范围内,无法搜索到有各个厂商实对SPI接口的实现。这样就无法完成类的加载,Java的设计者提供了一种不太优雅的实现方式,线程上下文类加载器(Thread Context Classloader),这个类加载器是可以设置的,新建线程的时候会从父线程继承,如果没有设置过,那么就是应用类加载器(Application Classloader),有了这个类加载器,就可以逆向加载SPI接口的实现类


  1. https://www.jianshu.com/p/e4262536000d ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值