SPI机制在JDK/Spring/SpringBoot的区别?

news/2024/11/28 15:34:52/文章来源:https://www.cnblogs.com/hld123/p/18574388

SPI机制在JDK/Spring/SpringBoot的区别?


   概要

   SPI (Service Provider Interface) 是一种服务发现机制,它允许第三方提供者为核心库或主框架提供实现或扩展。这种设计允许核心库/框架在不修改自身代码的情况下,通过第三方实现来增强功能。

   一、JDK原生的SPI

   1.  定义和发现

  JDK的SPI主要通过在META-INF/services/目录下放置特定的文件来指定哪些类实现了给定的服务接口。这些文件的名称应为接口的全限定名,内容为实现该接口的全限定类名。

   2. 加载机制

   ServiceLoader类使用Java的类加载器机制从META-INF/services/目录下加载和实例化服务提供者。例如,ServiceLoader.load(MyServiceInterface.class)会返回一个实现了MyServiceInterface的实例迭代器。

   3. 应用示例

   在Java的生态系统中,SPI 是一个核心概念,允许开发者提供扩展和替代的实现,而核心库或应用不必更改,下面举出一个例子来说明。

   1)首先定义一个接口。

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

   2)定义不同的实现类。

   全部代码和步骤如下:

   步骤1:定义一个服务接口,文件名: MessageService.java

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

   3)在指定目录(META-INF.services)下创建文件

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

   这里创建的文件名是 org.example.chapter15.HelloSpi , 里面的内容为:

org.example.chapter15.OneHelloSpiImpl
org.example.chapter15.TwoHelloSpiImpl

   4)测试

public class Test {public static void main(String[] args) {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实现的,我们来看一下这个类。

   4. ServiceLoader

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

   部分源码如下:

public final class ServiceLoader<S>  implements Iterable<S>
{//指定服务提供者配置文件的基本路径private static final String PREFIX = "META-INF/services/"; private final ClassLoader loader;//构造器private ServiceLoader(Class<S> svc, ClassLoader cl) {service = Objects.requireNonNull(svc, "Service interface cannot be null");//默认是系统类加载器(也叫做应用程序类加载器)loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;reload();}public static <S> ServiceLoader<S> load(Class<S> service) {//获取当前线程的线程上下文类加载器ClassLoader cl = Thread.currentThread().getContextClassLoader();//调用下面的方法return ServiceLoader.load(service, cl);}//根据指定的服务接口(service)和类加载器(cl)加载服务提供者public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader) {return new ServiceLoader<>(service, loader);}//...
}

    load方法是通过获取当前线程的线程上下文类加载器实例来加载的。Java应用运行的初始线程的上下文类加载器默认是系统类加载器。这里其实破坏了双亲委派模型,因为Java应用收到类加载的请求时,按照双亲委派模型会向上请求父类加载器完成,这里并没有这么做 。

   5. 缺点

   JDK原生的SPI每次通过ServiceLoader加载时都会初始化一个新的实例,没有实现类的缓存,也没有考虑单例等高级功能。

   6. 应用场景

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

   二、Spring的SPI

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

   Spring的SPI不仅仅是服务发现,它提供了一套完整的插件机制。Spring的核心框架提供了很多接口和抽象类,如BeanPostProcessor, PropertySource, ApplicationContextInitializer等,这些都可以看作是Spring的SPI。开发者可以实现这些接口来扩展Spring的功能。这些接口允许开发者在Spring容器的生命周期的不同阶段介入,实现自己的逻辑。

   1.  应用示例

   1)配置

   还是采用上面的接口定义与实现,配置放在 META-INF/spring.factories中

com.yp.service.HelloSpi=com.yp.service.impl.OneHelloSpiImpl,com.yp.service.impl.TwoHelloSpiImpl

   2) 测试

public class HelloSpiTest {@Testpublic 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("执行结束");}
}//执行结果为
sql 代码解读复制代码One 准备执行
One执行
Two 准备执行
Two执行
执行结束

 

   

1. 与IoC集成
与JDK的SPI不同,Spring的SPI与其IoC (Inversion of Control) 容器集成,使得在SPI实现中可以利用Spring的全部功能,如依赖注入。

2. 条件匹配:Spring提供了基于条件的匹配机制,这允许在某些条件下只加载特定的SPI实现,例如,可以基于当前运行环境的不同来选择加载哪个数据库驱动。

3. 配置
Spring允许通过spring.factories文件在META-INF目录下进行配置,这与JDK的SPI很相似,但它提供了更多的功能和灵活性。


三、SpringBoot的SPI


Spring Boot有一个与SPI相似的机制,但它并不完全等同于Java的标准SPI。

Spring Boot的自动配置机制主要依赖于spring.factories文件。这个文件可以在多个jar中存在,并且Spring Boot会加载所有可见的spring.factories文件。我们可以在这个文件中声明一系列的自动配置类,这样当满足某些条件时,这些配置类会自动被Spring Boot应用。

1. spring.factories机制

spring.factories是Spring Boot的一个特性,允许开发者自定义自动配置。通过spring.factories文件,开发者可以定义自己的自动配置类,这些类在Spring Boot启动时会被自动加载。
在这种情况下,SpringFactoriesLoader的使用,尤其是通过spring.factories文件来加载和实例化定义的类,可以看作是一种特定的SPI实现方式,但它特定于Spring Boot

   参考链接:

   https://juejin.cn/post/7197070078361387069

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS手机免越狱群控系统:实现同步投屏与多设备管理的新工具

免越狱群控系统概述 免越狱群控系统是一种基于苹果官方接口和网络通信技术的电脑端软件,通过合法合规的方式接入并操控多台iOS设备。该系统无需对iOS设备进行越狱,即可实现集中化、自动化控制。其核心功能包括:同步投屏:实时查看和控制多台iOS设备的屏幕。 批量操作:一键执…

NeRF学习笔记

NeRF 学习笔记参考资料十分钟带你快速入门NeRF原理_哔哩哔哩_bilibili 任务概述网络结构:输入 1. 采样点位置数据集是五维数据。theta phi决定了射线的方向,xyz是相机位置。 但是感觉x,y,z,theta phi为什么不直接用xyz表示?感觉剩下两个信息是冗余的。因为可能和射线有关,所…

考研打卡(29)

开局(29) 开始时间 2024-11-28 14:45:31 结束时间 2024-11-28 15:17:32 明天是1125今天去学冠领了几份资料数据结构具有5层节点的AVL树至少有_______个节点。(南昌大学 2015年) A 10 B 12 C 15 D 17B 答案设Nh表示深度为h的平衡二叉树中含有的最少节点数…

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统国内某水库安全监测项目需要监测点分散,且无外接供电。项目年限为4年,不允许使用太阳能电板。因此,我们需要设备具备低功耗且内置电池的功能。为了满足客户的要求,我们的研发团队将采集仪从NLM511T升级到电池…

Android11修改摄像头前后置方法,触觉智能RK3568开发板演示

RK3566/3568安卓Android11系统下,修改摄像头前后置的方法,触觉智能EVB3568开发板演示本文介绍在Android11系统下,修改摄像头前后置属性的方法。使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、An…

70%效率提升:开源AI技术在医疗用药咨询中的应用

一、系统概述 在医疗行业中,信息的准确性和实时性至关重要。我们的开源免费软件——思通数科AI多模态能力平台,通过集成先进的语音识别(ASR)技术,为医疗行业提供了一个全新的解决方案。该平台不仅能够理解多人对话中的语音指令,还能提供精准的药物咨询和用药指导,极大地…

Symbolic Discovery of Optimization Algorithms

目录概Lion代码Chen X., Liang C., Huang D., Real E., Wang K., Liu Y., Pham H., Dong X., Luong T., Hsieh C., Lu Y. and Le Q. V. Symbolic discovery of optimization algorithms. NeurIPS, 2024.概 本文搜索出了一个优雅的, 且经验上似乎更好的优化器: Lion. Lion作者通…

RAG实验:块大小分割实验、矢量存储;FAISS 与 Chroma、向量存储和 Top k、向量存储中的距离度量

比较 RAG 第 1 部分:块大小分割实验我探索了 RAG 模型中的各种块大小,并使用专为评估检索器组件而设计的 RAGAS 评估器对其进行了评估。如您所知,检索器部分会生成随后输入到语言模型 (LLM) 中的“上下文”。 在这个实验中,我采用了BGE作为嵌入技术(它在 HuggingFace 的排…

ssh登录出现sign_and_send_pubkey: no mutual signature supported

加上-o PubkeyAcceptedKeyTypes=+ssh-rsa 例如:ssh -i key.txt stinky@172.16.1.143 -o PubkeyAcceptedKeyTypes=+ssh-rsa

win小工具合集(持续更新)

日常、工作使用的win小工具推荐 一、Snipaste(截屏)安静的躺在后台,随时随地F1光速截屏和编辑,贴图也OK。 二、Ditto(剪贴板)安静的躺在后台,随时随地Ctrl + ~查看和粘贴曾经复制过的内容(包括图片),且支持搜索,再也不用频繁Ctrl + c了。 三、PotPlayer(音视频播放…

高性能C++内存映射库mio使用心得

背景 在C++编程中,高效的数据访问至关重要,而内存映射文件(Memory Mapped Files)提供了一种强大的工具,它允许我们直接将文件内容加载到进程地址空间,从而以极高的效率进行读写操作。今天,我们要向大家推荐一个轻量级且易于使用的开源库——mio。 项目介绍 mio是一个头文…

destoon8.0开启根据时间归档

destoon8.0开启根据时间归档,代码如下:<?php define(DT_REWRITE, true); require ../common.inc.php; $EXT[archiver_enable] or dheader(DT_PATH); //$DT_BOT or dheader(DT_PATH); $N = $M = $T = array(); $mid or $mid = 5; $vmid = $list = 0; foreach($MODULE as $…