Java SPI机制

引言

        最近在重新学习Java基础,学习到SPI相关知识时发现大部分文章并没能描述清楚其核心意义及思想,于是结合自己的感悟梳理了一篇相对全面、易懂的SPI文章,仅供大家学习参考

一、概念

        SPI全称:Service Provider Interface,即服务提供接口。是Java官方提供的一种服务发现机制,它允许在程序运行时动态地加载实现特定接口的实现类,极大的增强了程序的灵活性、降低了耦合性。       

        一套完整的逻辑闭环的SPI简单来说就是服务方提供某一个服务的接口, 提供给服务开发者或者服务生产商来进行实现。场景举例:JDBC与MySql厂商、Oracle厂商,JDBC就是服务方,提供了java.sql.Driver接口,MySql厂商与Oracle厂商来分别实现此接口,以提供自家的数据库驱动程序;在此过程中,JDBC的开发者不关注java.sql.Driver接口的具体实现,他们仅需与java.sql.Driver接口交互即可。而我们在引用JDBC包的时候,就可以直接使用到Driver的实现类

二、原理

        思考:在上述过程中,JDBC的Driver实现类是由别人提供的,也就是说此实现类并不能存在于JDBC的src目录下,那么JDBC的项目是如何加载到这个实现的呢?

具体实现逻辑如下图:

        由图可知,除开JDBC提供接口、数据库厂商实现接口这两个环节之外,还有一个非常重要的动作将这两个环节融合到一起,即上图中标黄的元素:ServiceLoader。至此,我们认识到了SPI中的所有角色,分别是:

                1.Service:一个公开的接口,或定义了一个抽象的功能模块

                2.ServiceProvider:Service接口的实现类

                3.ServiceLoader:SPI机制中的核心组件,负责在程序运行过程中发现并加载ServiceProvider

三、实例

        模拟需求:有一家公司A,它需要接入互联网。它定义了一个连接网络的API,由中国移动和中国联通提供网络服务。此场景下共涉及三个角色,如下图所示:

        在此需求中spi-core项目中的ClassName接口即为SPI的Service

        spi-moblie、spi-unicom项目分别实现spi-core中的ClassName接口

        同时ServiceProvider项目的resource目录下需要添加一个配置文件,文件格式如图:简单来说就是需要让ServiceLoader清楚的知道需要加载哪个接口,此接口又有哪些实现类(当实现类为多个时,用回车分割多个全路径名即可)

 至此,一套简易的SPI就搭建完成了,当我们在程序中依赖了spi-unicom时,调用如下代码即可访问到spi-unicom对spi-core的实现

package com.example.web.test;

import com.example.spicore.core.ClassName;

import java.util.ServiceLoader;

/**
 * @author cui
 * @version 1.0.0
 * @ClassName RunName.java
 * @Description
 * @createTime 2023年09月18日 17:35:00
 */
public class RunName {
    public static void main(String[] args) {
        ServiceLoader<ClassName> classNames = ServiceLoader.load(ClassName.class);
        for (ClassName className : classNames) {
            className.soutName();
        }
    }
}

输出结果如下:

        注:SPI的三大要素:

                1.规范的配置文件:

                        ·文件路径:必须在Jar包的META-INF/service目录下

                        ·文件名称:必须为service接口的全限定名

                        ·文件路径:必须为ServiceProvider类的全限定名,如果有多个实现类,那么每一个 实现类在文件中单独占一行,用回车符分割即可

                2.ServiceProvider必须具备无参的构造方法:

                        ServiceProvider的实例化是通过反射技术实现的,所以必须要有无参构造器

                3.保证能加载到配置文件和ServiceProvider类:

                        常用的方式是将ServiceProvider的Jar包放到classpath路径下;还可以将jar包安装到JRE的扩展目录中(不建议修改JRE扩展目录中的文件);或者自定义一个class Loader

                        

总结

        JavaSPI提供了一种组件发现和注册的方式,可以用于实现各种插件,或者零或替换框架所使用的组件,它面向接口编程,优雅的实现模块之间的解耦,其根本设计思想为:“面向接口”+“配置文件”+“反射技术”

        SPI的本质是一种程序的扩展思路,程序将接口的具体实现交由服务开发者或者服务生产商来进行实现,从而保证程序与程序之间高度灵活的同时,耦合度并不高。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值