【Java杂记】SPI机制:使用示例及原理浅析 傷城~ 2022-12-13 12:47 146阅读 0赞 ## 1.SPI是什么 ## SPI全称Service Provider Interface,是 JDK 内置的一种**服务提供发现机制**,目前市面上有很多框架都是用它来做服务的扩展发现。简单来说,它是一种动态替换发现的机制。它可以用来启用框架扩展和替换组件。整体机制图如下: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center] Java SPI 实际上是“**基于接口的编程+策略模式+配置文件**”组合实现的**动态加载机制**。 * 接口:调用方需要的服务 * 策略模式:多个提供方,会提供自己的接口实现 * 配置文件:提供方告知调用方,具体实现类在哪里,然后调用方去加载(本质上是一种通信方式) ==> 所以SPI的核心思想就是**解耦**。另外,三个重要概念:扩展点:一个Java接口 ; 扩展:扩展点的实现类 ;扩展实例:扩展点实现类的实例 ## 2.应用场景 ## 概括地说,适用于:**调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略**。比如,数据库驱动加载接口实现类的加载JDBC加载不同类型数据库的驱动: * java只提供了Driver接口,无具体实现(因为数据库有很多种) * MysqlDriver和OracleDriver由具体厂商编写提供 * Driver工程引入提供方jar包后,如何知道具体实现类在哪? 通过读取提供方名为META/INF.Driver全类名的配置文件,里面写了具体实现类是什么,然后加载就好 ![在这里插入图片描述][20201011180136297.png_pic_center] 除此之外,应用到 SPI 的地方还有: * 日志门面接口实现类加载SLF4J加载不同提供商的日志实现类 * Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等 * Dubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口 ## 3.使用示例 ## 要使用Java SPI,应遵循以下流程: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 1] 1. 为什么配置文件是要放在META-INF/services? 原因是ServiceLoader默认是加载该目录下文件 ![在这里插入图片描述][20201011184102453.png_pic_center] 2. ServiceLoader有什么用?当接口实现类所在的jar包放在主程序的classpath中,ServiceLoder会根据配置文件中的全类名将实现类加载到JVM中 再注意一点,SPI的实现类必须携带一个不带参数的构造方法(默认空参构造) **示例代码** 1)定义一组接口 (假设是com.example.spitestDemo.SpiTestDemo),表示服务调用方 // 服务调用方需要的服务 public interface SpiTestDemo { void way1(); void way2(); } 2)令起两个项目,分别实现该接口(引入接口jar包),表示两个服务提供方 // 提供方1的实现 public class SpiTestDemoImpl1 implements SpiTestDemo { @Override public void way1() { System.out.println("SpiTestDemoImpl1------way1"); } @Override public void way2() { System.out.println("SpiTestDemoImpl1------way2"); } } // 提供方2的实现 public class SpiTestDemoImpl2 implements SpiTestDemo { @Override public void way1() { System.out.println("SpiTestDemoImpl2------way1"); } @Override public void way2() { System.out.println("SpiTestDemoImpl2------way2"); } } 在两个提供方的src/main/resources/ 下建立 /META-INF/services 目录 * 新增一个**以接口命名的文件** (com.example.spitestDemo.SpiTestDemo文件) - src -main -resources - META-INF - services - com.example.spitestDemo.SpiTestDemo * **内容是要应用的实现类全类名**(这里是SpiTestDemoImpl1和SpiTestDemoImpl2,每行一个类) com.example.spitestdemo.SpiTestDemoImpl1 com.example.spitestdemo.SpiTestDemoImpl2 3)调用方引入提供方的两个jar包,使用 **ServiceLoader** 来加载配置文件中指定的实现。 class SpitestdemoApplicationTests { public void testWay(){ // 传入接口作为参数,返回解析到的实现类(这里会将两个Jar包的实现类都加载进来) ServiceLoader<SpiTestDemo> serviceLoader = ServiceLoader.load(SpiTestDemo.class); // 不同的实现类,有不同的逻辑 for(SpiTestDemo spiTestDemo : serviceLoader){ spiTestDemo.way1(); spiTestDemo.way2(); } } } ![在这里插入图片描述][20201011182107767.png_pic_center] ## 4.原理浅析 ## 这里就拿最熟悉的mysql jar包中jdbc加载驱动这一步骤源码简单分析其结构 1)查看源码,可以知道mysql的jar包中就能显示整个spi机制的所有的配置文件及源码。 2)在连接数据库的时候,第一步加载驱动,就是使用spi机制,spi机制会自动找到相关的驱动实现,如下图完整结构 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 2] 3)而这个接口服务是在jdk中 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 3][] 4)初始化加载主要是通过ServiceLoader来实现服务查找 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 4][] ## 5.总结 ## **优点**:使用Java SPI机制的优势是实现解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离,而不是耦合在一起。应用程序可以根据实际业务情况启用框架扩展或替换框架组件。 **缺点**: * JDK 标准的 SPI 会一次性加载实例化扩展点的所有实现,就是如果你在 META-INF/service 下的文件里面加了 N 个实现类,那么 JDK 启动的时候都会一次性全部加载。那么如果有的扩展点实现初始化很耗时或者如果有些实现类并没有用到, 那么会很浪费资源 * 如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因 * 多个并发多线程使用ServiceLoader类的实例是不安全的。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center]: /images/20221123/9317100d24a5457a93d6096f8deeb6be.png [20201011180136297.png_pic_center]: /images/20221123/353113ddea61459e9d64d5d73eeea920.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 1]: /images/20221123/283672515a2a45aaa733a75af737c18e.png [20201011184102453.png_pic_center]: /images/20221123/1c9640978c9a43deaae373ebc2b49788.png [20201011182107767.png_pic_center]: /images/20221123/1ca19401d9b2437b8e7f3b209bed94f5.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 2]: /images/20221123/ac238730e3874a19970df6cc3bb79607.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 3]: /images/20221123/2a68d9b1b6254586bd76eab5df038eb6.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkzNTkyNw_size_16_color_FFFFFF_t_70_pic_center 4]: /images/20221123/1fc5578640394d779f753362bf790e06.png
相关 Java垃圾回收机制浅析及问题示例 Java的垃圾回收机制是Java语言设计的重要组成部分,它自动管理内存,避免了内存泄漏等问题。 1. **简单理解**:垃圾回收主要通过追踪对象引用关系来决定哪些不再使用的对 约定不等于承诺〃/ 2024年09月10日 16:48/ 0 赞/ 27 阅读
相关 SPI协议及其工作原理浅析 一、概述. SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. 灰太狼/ 2023年01月03日 04:54/ 0 赞/ 149 阅读
相关 【Java杂记】SPI机制:使用示例及原理浅析 1.SPI是什么 SPI全称Service Provider Interface,是 JDK 内置的一种服务提供发现机制,目前市面上有很多框架都是用它来做服务的扩展发现。 傷城~/ 2022年12月13日 12:47/ 0 赞/ 147 阅读
相关 java spi_Java SPI机制和使用示例 JAVA SPI 简介 SPI 是 Java 提供的一种服务加载方式,全名为 Service Provider Interface。根据 Java 的 SPI 规范,我们可以 蔚落/ 2022年11月01日 15:00/ 0 赞/ 178 阅读
相关 Storm 原理机制杂记 Storm 原理机制杂记 作者:[paul\_wei2008][paul_wei2008] ![SouthEast][] Storm: Storm是[Twit 深碍√TFBOYSˉ_/ 2022年05月30日 12:36/ 0 赞/ 438 阅读
相关 Java的SPI机制浅析与简单示例 一、SPI机制 这里先说下SPI的一个概念,SPI英文为Service Provider Interface单从字面可以理解为Service提供者接口,正如从 Love The Way You Lie/ 2022年05月17日 11:24/ 0 赞/ 160 阅读
相关 Java SPI机制简介,及dubbo SPI 转自:https://www.jianshu.com/p/46aa69643c97 SPI 简介 SPI 全称为 (Service Provider Interface 朴灿烈づ我的快乐病毒、/ 2022年03月21日 15:58/ 0 赞/ 194 阅读
相关 Java的SPI机制及其原理 Apache 已经宣布Dubbo成为顶级的项目了,今天在看dubbo的源码导读.看到有个SPI,就了解一下了. 发现是一种接口和实现分开的解耦方式,挺有意思的. 这篇 刺骨的言语ヽ痛彻心扉/ 2022年01月28日 07:11/ 0 赞/ 214 阅读
相关 Java SPI机制,Spring SPI 机制 Java spi 机制 (Service Provider Interface) 简述 Service Provider Interface(SPI) 是一种服... 小灰灰/ 2021年03月11日 14:04/ 0 赞/ 1833 阅读
还没有评论,来说两句吧...