一文了解Spring的SPI机制

文章目录

  • 一文了解Spring的SPI机制
  • Java SPI
    • ServiceLoader
  • Spring SPI
    • Springboot利用Spring SPI开发starter

一文了解Spring的SPI机制

Java SPI

SPI 全称 Service Provider Interface ,是 Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件 。 SPI的作用就是为这些被扩展的API 寻找服务实现 。本质是通过基于接口的 编程+策略模式+配置文件 实现动态加载。可以实现 解耦 (接口和实现分离),提高框架的 可拓展性 (第三方可以自己实现,达到插拔式的效果)。
我们来开发SPI看一下。
首先定义一个接口。

public interface HelloSpi {
    String getName();
    void handle();
}

定义不同的实现类。

public class OneHelloSpiImpl implements HelloSpi {
    @Override
    public String getName() {
        return "One";
    }
    @Override
    public void handle() {
        System.out.println(getName() + "执行");
    }
}

public class TwoHelloSpiImpl implements HelloSpi {
    @Override
    public String getName() {
        return "Two";
    }
    @Override
    public void handle() {
        System.out.println(getName() + "执行");
    }
}

在指定目录(META-INF.services)下创建文件,(为什么是这个目录接下来会讲到)。

在这里插入图片描述

文件名是接口的全类名,文件内容是实现类的全类名。

com.shuaijie.impl.OneHelloSpiImpl
com.shuaijie.impl.TwoHelloSpiImpl

编写测试单元测试。是通过 ServiceLoader 这个类实现的功能,接下来会详细讲解这个类的实现原理。

@Test
public void testSpi() {
    ServiceLoader<HelloSpi> load = ServiceLoader.load(HelloSpi.class);
    Iterator<HelloSpi> iterator = load.iterator();
    while (iterator.hasNext()) {
        HelloSpi next = iterator.next();
        System.out.println(next.getName() + " 准备执行");
        next.handle();
    }
    System.out.println("执行结束");
}

执行结果为

One 准备执行
One执行
Two 准备执行
Two执行
执行结束

通过执行结果我们可以看出, HelloSpi 接口的所有实现类都得到了调用,我们可以通过这种机制根据不同的业务场景实现拓展的效果。示例是通过 ServiceLoader 实现的,我们来看一下这个类。

ServiceLoader

ServiceLoader 是一个简单的服务提供者加载工具。是JDK6引进的一个特性。

public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

load 方法是通过获取当前线程的 线程上下文类加载器 实例来加载的。Java应用运行的初始线程的上下文类加载器默认是系统类加载器。这里其实 破坏了双亲委派模型 ,因为Java应用收到类加载的请求时,按照双亲委派模型会向上请求父类加载器完成,这里并没有这么做(有些面试官会问到破坏双亲委派模型相关问题,简单了解)。
iterator.hasNext() 主要是通过 hasNextService() 来实现的,我们来看一下主要代码。

private static final String PREFIX = "META-INF/services/";
private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            String fullName = PREFIX + service.getName();
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

该方法会去加载 PREFIX 变量路径下的配置,PREFIX 是一个固定路径,这也就是我们为什么要在META-INF/services/下创建文件的原因。并根据 PREFIX 加上全类名获取到实现类所在的全路径。
在这里插入图片描述

Java中有许多我们常见的框架使用SPI机制的地方,JDBC,Dubbo,Logback等,Spring中也有使用。

Spring SPI

Spring SPI 对 Java SPI 进行了封装增强。我们只需要在 META-INF/spring.factories 中配置接口实现类名,即可通过服务发现机制,在运行时加载接口的实现类。
我们将讲解 Java SPI 的例子通过Spring SPI来实现一下。

[图片]

测试代码如下。

@Test
public void testSpringSpi() {
    List<HelloSpi> helloSpiList = SpringFactoriesLoader.loadFactories(HelloSpi.class,this.getClass().getClassLoader());
    Iterator<HelloSpi> iterator = helloSpiList.iterator();
    while (iterator.hasNext()) {
        HelloSpi next = iterator.next();
        System.out.println(next.getName() + " 准备执行");
        next.handle();
    }
    System.out.println("执行结束");
}

执行结果,与Java SPI执行结果一致。

One 准备执行
One执行
Two 准备执行
Two执行
执行结束

Springboot利用Spring SPI开发starter

刚接触 Springboot 的时候引入一个个starter依赖,当时十分好奇这个东西,这一个个的starter就是通过Spring SPI来实现的。
我们也可以自己编写一个starter提供一些功能,传入公司的maven仓库,需要用到的项目就可以直接引入,减少项目对某些模块的代码硬侵入。
先来编写接口和实现。

package com.shuaijie.service;
public interface SpringbootStarterService {
    void handle();
}

package com.shuaijie.service.impl;
public class SpringbootStarterServiceImpl implements SpringbootStarterService {
    @Override
    public void handle() {
        System.out.println("SpringbootStarterServiceImpl执行");
    }
}

spring.factories中内容。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.shuaijie.service.impl.SpringbootStarterServiceImpl

pom.xml指定groupId,artifactId,version。

<groupId>com.shuaijie</groupId>
<artifactId>spring-boot-starter-shuaijie</artifactId>
<version>1.0.0</version>

然后通过maven打包传入maven仓库。其他的Springboot项目就可以直接使用了。

@Autowired
private SpringbootStarterService springbootStarterService;

@Test
public void testSpringbootStarter() {
    springbootStarterService.handle();
}

本文参考https://juejin.cn/post/7197070078361387069,手写starter部分后面我会写出详细过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/457278.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Webpack学习记录

记录学习笔记&#xff0c;欢迎指正 1.大型项目为什么需要打包 1.1 使用打包工具原因 编译或转译文件&#xff1a; 项目中可能用到ES6语法&#xff0c;可能有浏览器不支持。需要打包工具将代码编译输出为ES5语法的代码。项目中可能使用Sass&#xff0c;Less等预处理器&#xff…

【微服务】nacos注册中心

Nacos注册中心 国内公司一般都推崇阿里巴巴的技术&#xff0c;比如注册中心&#xff0c;SpringCloudAlibaba也推出了一个名为Nacos的注册中心。 1.1.认识和安装Nacos Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c;在…

Python collections模块

collections 模块是Python标准库中提供的一个模块&#xff0c;用于提供一些额外的数据容器和工具&#xff0c;扩展了内置的数据类型。它包含了一些有用的类和函数&#xff0c;用于处理各种数据结构和算法问题。下面是 collections 模块中常用的几个类的详细介绍&#xff1a; 1.…

【数学】【位运算】LeetCoce810. 黑板异或游戏

作者推荐 视频算法专题 本文涉及知识点 数学 位运算 LeetCoce810. 黑板异或游戏 黑板上写着一个非负整数数组 nums[i] 。 Alice 和 Bob 轮流从黑板上擦掉一个数字&#xff0c;Alice 先手。如果擦除一个数字后&#xff0c;剩余的所有数字按位异或运算得出的结果等于 0 的话…

代码随想录day19(1)二叉树:完全二叉树节点个数(leetcode222)

题目要求&#xff1a;求一个完全二叉树的节点个数 思路&#xff1a;首先完全二叉树可以用普通二叉树的方法来求&#xff0c;但是需要遍历所有的节点。 但是对于完全二叉树来说&#xff0c;只有最底层右侧的节点可能没满&#xff0c;其余每层节点都达到了最大值。所以我们可以…

浏览器同源策略及跨域问题

同源策略&#xff1a;同源策略是一个重要的安全策略&#xff0c;它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档&#xff0c;减少可能被攻击的媒介。 同源策略的作用&#xff1a;保护浏览器中网站的安全&#xff0c;限制ajax只…

YOLO v8:目标检测的最新王者

本文来自公众号“AI大道理” —————— Yolov8是Yolo系列模型的最新王者&#xff0c;各种指标全面超越现有目标检测模型。 Yolov8借鉴了Yolov5、Yolov6、YoloX等模型的设计优点&#xff0c;全面改进了Yolov5模型结构&#xff0c;同时保持了Yolov5工程化简洁易用的优势。 …

为什么要用scrapy爬虫库?而不是纯python进行爬虫?

为什么要用scrapy爬虫库&#xff1f;而不是纯python进行爬虫&#xff1f; Scrapy的优点Scrapy节省的工作使用纯Python编写爬虫的不足 Scrapy是一个使用Python编写的开源和协作的web爬虫框架&#xff0c;它被设计用于爬取网页数据并从中提取结构化数据。Scrapy的强大之处在于其广…

IBFKJ-299 8AI/AO,DI/DO开关量模拟量同时数据采集

产品特点&#xff1a; ● DC12-30V宽压供电&#xff1b; ● RS485通讯光电隔离&#xff0c;输入光耦隔离&#xff0c;继电器输出触点隔离&#xff1b; ●通讯接口支持RS232、RS485&#xff1b; ●支持标准Modbus RTU/TCP/ASCII协议 ●具有闪开、闪断功能&#xff0c;可以在…

C#操作像素替换图片中的指定颜色

待处理的图片&#xff0c;其特征是包含有限数量颜色&#xff0c;不同的颜色相互交叉使用&#xff0c;相同颜色并未完全连贯&#xff0c;需要将图片中的指定颜色替换为另一颜色。虽然很多图片处理工具都支持类似操作&#xff0c;最后还是自己动手编写简单的处理程序。   程序的…

8-图像放大

其实&#xff0c;就是开辟一个zoomwidth&#xff0c;zoomheight的内存&#xff0c;再分别赋值即可。 void CDib::Maginify(float xZoom, float yZoom) { //指向原图像指针 LPBYTE p_data GetData(); //指向原像素的指针 LPBYTE lpSrc; //指向缩放图像对应像素的指针 LPBYTE l…

当word表格复制到excel出现分行问题的解决小技巧

在word文档中将^p&#xff08;回车符号&#xff09;替换成其他&#xff0c;比如 全选复制粘贴到excel中后分行问题已经解决&#xff0c;将换回原本的回车即可&#xff0c;ctrshiftj&#xff08;回车&#xff09;

新零售SaaS架构:什么是线上商城系统?

零售商家为什么要建设线上商城 传统的实体门店服务范围有限&#xff0c;只能吸引周边500米内的消费者。因此&#xff0c;如何拓展服务范围&#xff0c;吸引更多消费者到店&#xff0c;成为了店家迫切需要解决的问题。 缺乏忠实顾客&#xff0c;客户基础不稳&#xff0c;往往是…

Git提交代码进入coding

安装Git后建一个文件在文件里右键点击Git Bash使用命令配置用户名和邮箱git config --global user.name "你的用户名"和git config --global user.email "你的邮箱"命令git init来初始化&#xff0c;自动将当前仓库设置为master创建一个项目&#xff08;一…

Linux - 安装 nacos(详细教程)

目录 一、简介二、安装前准备三、下载与安装四、基本配置五、单机模式 一、简介 官网&#xff1a;https://nacos.io/ GitHub&#xff1a;https://github.com/alibaba/nacos Nacos 是阿里巴巴推出的一个新开源项目&#xff0c;它主要是一个更易于构建云原生应用的动态服务发现…

什么是接口

接口定义 接口的定义分为接口的声明和接口体 接口构成 接口声明&#xff1a; ① 声明&#xff1a;关键字interface ② 格式&#xff1a;interfac接口名字 接口体 ① 两部分&#xff1a;常量和方法定义。 ② 内容&#xff1a;仅声明抽象方法&#xff0c; 不实现方法(没有方法体…

如何实现幂等性,java多线程面试题及答案整理

订单创建接口&#xff0c;第一次调用超时了&#xff0c;然后调用方重试了一次。是否会多创建一笔订单&#xff1f; 订单创建时&#xff0c;我们需要去扣减库存&#xff0c;这时接口发生了超时&#xff0c;调用方重试了一次。是否会多扣一次库存&#xff1f; 当这笔订单开始支…

【单调栈】代码随想录算法训练营第五十九天 |503.下一个更大元素II, 42. 接雨水 (待补充)

503.下一个更大元素II 1、题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2、文章讲解&#xff1a;代码随想录 3、题目&#xff1a; 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每个元素的下一个…

使用R语言计算模拟二项分布

二项分布理论 二项分布是一种离散概率分布&#xff0c;描述了在n次独立重复的伯努利试验中成功的次数的概率分布。其中&#xff0c;每次试验的结果只有两个可能&#xff1a;成功或失败&#xff0c;且每次试验的成功概率p是相同的。 具体来说&#xff0c;如果随机变量X表示在n次…

婚恋源码-婚恋交友系统-源码婚恋交友系统-APP小程序H5公众号-源码交付-支持二开!

本婚恋系统是一款专门为单身人士打造的相亲交友软件&#xff0c;所有用户都必须要身份认证&#xff0c;还有职业认证、学历认证等等全方位认证。智能匹配是本婚恋系统的核心功能&#xff0c;当我们完善好个人资料通过审核&#xff0c;系统会根据个人信息进行匹配&#xff0c;自…
最新文章