Spring SPI

 SPI 服务供给接口(Service Provider Interface)。是Java 1.5新添加的一个内置标准,允许不同的开发者去实现某个特定的服务。

1 SPI 介绍

一个接口,可能会有许多个实现,我们在编写代码时希望能动态切换具体实现,例如:

Interface interface = new Implement1(); // 创建一个具体的interface

上面是硬编码方式,我们希望在不修改代码的情况下,更换interface的具体实现。当然我们可以使用配置文件方式来实现这个需求,伪代码如下:

ResourceBundle rb = ResourceBundle.getBundle(“interface.properties”);

String impName = rb.getString(“impName”);

Interface interface = (Interface) Class.forName(impName).newInstance();

SPI 的实现则类似于上面的方法。让系统找到具体的实现。

 1.1 SPI的使用

图 示例代码的项目结构说明

1)定义一个接口,在spi_example_interface项目中定义MakeMoney接口。

public interface MakeMoney {void hardWord();
}

2) 在自定义项目中实现接口,在spi_example_implement项目编写TeacherMakeMoney和ProgrammerMakeMoney两个类并实现MakeMoney接口。

public class ProgrammerMakeMoney implements MakeMoney {public ProgrammerMakeMoney() {System.out.println("程序员实例被创建了");}@Overridepublic void hardWord() {System.out.println("敲代码");}
}public class TeacherMakeMoney implements MakeMoney {public TeacherMakeMoney() {System.out.println("教师实例被创建了");}@Overridepublic void hardWord() {System.out.println("教书");}
}

 3)在spi_example_implement项目中,resources文件下新建META-INF/services 文件夹,并在该文件夹下面创建由接口完全限定名命名的文件,在文件中依次列出该接口实现类的完全限定名。

图 接口实现类说明文件

4)使用定义的接口,利用Java提供的ServiceLoader类发现这个接口的实现,并使用它们。

public class SpiUse {public static void main(String[] args) {ServiceLoader<MakeMoney> makeMonies = ServiceLoader.load(MakeMoney.class);Iterator<MakeMoney> iterator = makeMonies.iterator();while (iterator.hasNext()) {MakeMoney imp = iterator.next(); // 实现被加载的系统imp.hardWord();}}
}
/*
运行结果:
程序员实例
敲代码
教师实例被创建了
教书
*/

1.2 java.sql.Driver与SPI

在Java中定义了接口java.sql.Driver,其并没有具体的实现,具体的实现都是由不同的厂商提供。下面将以mysql的驱动为例,来大致介绍Java如何管理JDBC服务。

1)实现java.sql.Driver接口。

图 mysql-connector-java jar包中Driver的定义

2) 在META-INFA/services文件夹下编写以Driver接口全限定名命名的文档,来引导ServiceLoader发现mysql实现的Driver的接口。

图 mysql jar包下的引导文件

3)注册并管理JDBC服务。

图 jdbc服务的调用过程

我们在使用jdbc 服务时,第一步是获取对数据库的连接,即执行上图的DriverManager.getConnection(url)方法。

图 DriverManager的getConnection()方法的部分代码块

以下代码是模拟数据库厂商实现java.sql.Driver这个接口:

定义SqlDriver接口,全限定名是 com.huangmingfu.SqlDriver:

public abstract class SqlDriver {private static List<SqlDriver> driverList = new ArrayList<>();/*** 执行sql*/public abstract void execute(String sql);public abstract Boolean connect(String url);public static void register(SqlDriver sqlDriver) {driverList.add(sqlDriver);}public static SqlDriver getConnect(String url) {for (SqlDriver driver : driverList)if (driver.connect(url)) return driver;return null;}}

第三方项目中对SqlDriver接口的实现(mysql和oracle)

public class MySqlDriver extends SqlDriver {public MySqlDriver() {System.out.println("MySqlDriver实例被创建");}static {System.out.println("MySqlDriver实例被创建被加载到虚拟机了,进行注册:SqlDriver.register");SqlDriver.register(new MySqlDriver());}@Overridepublic void execute(String sql) {System.out.println("mysql数据驱动,执行sql:" + sql);}@Overridepublic Boolean connect(String url) {return url.startsWith("mysql");}
}public class OracleDriver extends SqlDriver {public OracleDriver() {System.out.println("OracleDriver 实例被创建");}static {System.out.println("OracleDriver实例被创建被加载到虚拟机了,进行注册:SqlDriver.register");SqlDriver.register(new OracleDriver());}@Overridepublic void execute(String sql) {System.out.println("oracle数据驱动,执行sql:" + sql);}@Overridepublic Boolean connect(String url) {return url.startsWith("oracle");}}

 在第三方项目的META-INF/com.huangmingfu.SqlDriver 引导文件中写入实现类的全限定名:

com.custom.MySqlDriver
com.custom.OracleDriver

使用Driver的实现类,来获取数据库连接:

public class UserDriver {private static SqlDriver sqlDriver;public static void main(String[] args) throws Exception{System.out.println("项目启动....");
//        classForName();spi();}/*** 反射形式*/private static void classForName() throws Exception {System.out.println("尝试先通过class.forName的形式");sqlDriver = (SqlDriver)Class.forName("com.custom.MySqlDriver").newInstance();sqlDriver.execute("SELECT VERSION();");}/*** spi形式*/private static void spi() {ServiceLoader<SqlDriver> serviceLoader = ServiceLoader.load(SqlDriver.class);Iterator<SqlDriver> iterator = serviceLoader.iterator();while (iterator.hasNext()) iterator.next(); //只是做加载动作SqlDriver driver = SqlDriver.getConnect("mysql://");if (driver != null) driver.execute("SELECT VERSION()");}
}
/*
运行结果
项目启动....
MySqlDriver实例被创建被加载到虚拟机了,进行注册:SqlDriver.register
MySqlDriver实例被创建
MySqlDriver实例被创建
OracleDriver实例被创建被加载到虚拟机了,进行注册:SqlDriver.register
OracleDriver 实例被创建
OracleDriver 实例被创建
mysql数据驱动,执行sql:SELECT VERSION()*/

2 SPI 原理

java实现SPI的是ServiceLoader类,其实现步骤一共有两步:1)根据接口的全限定名查找META-INF/services下的接口实现引导文件记录的实现类全限定名集合;2)通过Class.forName(全限定名).newInstance()方法来将这些实现类加载进jvm中。

图 第一步ServiceLoader获取接口实现类的全限定名

图 第二步 ServiceLoader创建实现类的实例

3 SPI的优缺点及应用场景

spi 能扩展服务,将接口与实现解耦。通过服务接口和服务提供者,实现了服务规范的制定和服务具体实现的分离。

API

在大多数情况下,都是实现方制定接口并完成对接口的实现。调用方仅仅依赖接口调用,且无权选择不同实现。API是直接被应用开发人员使用。

SPI

是调用方来制定接口规范,提供给外部来实现。调用方在调用时则选择自己需要的外部实现。SPI是被框架扩展人员使用。

表 API与SPI的对比

缺点:

1)不能按需加载,需要遍历所有的实现并实例化,然后在循环中才能找到我们需要的实现。

2)多个并发多线程使用ServiceLoader类的实例是不安全的。

应用场景:

有关组织和公司定义接口标准,第三方提供具体实现。例如JDBC。

4 Spring Boot 中的spring.factories

在Spring Boot项目中,怎么将pom.xml文件里添加的依赖中的bean注册到Spring Boot项目的容器中呢?

在项目中,@ComponentScan注解只会扫描项目包内的bean并注册到Spring容器中,项目依赖包中的bean不会被扫描和注册。此时可以利用SPI来对这些依赖包中的bean进行加载注册。

META-INF/spring.factories 文件类似于SPI中的接口实现类引导文件。有spring-core包中的SpringFactoriesLoader类充当着类似ServiceLoader的作用。

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

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

相关文章

基于STM32的循迹小车项目实战

循迹小车是一种能够沿着预定路线行驶的智能小车&#xff0c;通过巡线传感器检测路面的线路&#xff0c;并根据检测结果调整行驶方向。本项目将基于STM32微控制器实现一个简单的循迹小车&#xff0c;通过学习和实践&#xff0c;帮助初学者熟悉STM32的开发流程和掌握循迹小车的实…

linux运行java程序

这个帖子实现的是linux上运行java代码 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 事情发生的原因是博洋需要知道海外城市的数量&#xff0c;我一开始准备将全量数据拉取到本地&#xff0c;用代码遍历一遍。但是打包好全量数据&…

CentOS中安装常用环境

一、CentOS安装 redis ①&#xff1a;更新yum sudo yum update②&#xff1a;安装 EPEL 存储库 Redis 通常位于 EPEL 存储库中。运行以下命令安装 EPEL 存储库 sudo yum install epel-release③&#xff1a;安装 Redis sudo yum install redis④&#xff1a;启动 Redis 服…

MYSQL中的触发器TRIGGER

1.概念 触发器是一个特殊的存储过程&#xff0c;当触发器保护的数据发生变更时就会触发。 2.特性 1.触发器与表息息相关&#xff0c;一般我们一个表创建六个触发器。 2.六个触发器其实是三种类六个 insert 类型 before | after insertupdate 类型 before | af…

STM32中断看这一篇就够了

&#x1f64c;秋名山码民的主页 &#x1f602;oi退役选手&#xff0c;Java、大数据、单片机、IoT均有所涉猎&#xff0c;热爱技术&#xff0c;技术无罪 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 获取源码&#xff0c;添加WX 目录 1. 前言…

基于SSM的教学管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【Proteus仿真】【Arduino单片机】HC05蓝牙通信

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用PCF8574、LCD1602液晶、HC05蓝牙传感器等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示蓝牙接收数据。 二、软件设计 /* 作者…

MR外包团队:MR、XR混合现实技术应用于游戏、培训,心理咨询、教育成为一种创新的各行业MR、XR形式!

随着VR、AR、XR、MR混合现实等技术逐渐应用于游戏开发、心理咨询、培训、教育各个领域&#xff0c;为教育、培训、心理咨询等行业带来了全新的可能性。MR、XR游戏开发、心理咨询是利用虚拟现实技术模拟真实场景&#xff0c;让学生身临其境地参与学习和体验&#xff0c;从而提高…

MapApp 地图应用

1. 简述 1.1 重点 1&#xff09;更好地理解 MVVM 架构 2&#xff09;更轻松地使用 SwiftUI 框架、对齐、动画和转换 1.2 资源下载地址: Swiftful-Thinking:https://www.swiftful-thinking.com/downloads 1.3 项目结构图: 1.4 图片、颜色资源文件图: 1.5 启动图片配置图: 2. Mo…

Linux | 安装openGauss数据库

Linux 安装openGauss数据库 今天我们来安装一下国产数据库openGauss~~ 下载openGauss 首先在官网:https://opengauss.org/zh/download/下载对应的安装包&#xff0c;我们这里下载LInux 极简版来演示安装 下载后,使用root用户上传到Linux ,这边上传到/usr/local/目录下, 使用…

IoC和DI

Spring 是包含众多工具的 IoC 容器,存的是对象,对象这个词在 Spring 的范围内,称之为 bean IoC 是控制反转 控制权进行了反转,比如对某一个东西的控制权在 A 手上,结果变成了 B ,Spring 管理的是 bean ,所以这里的控制权指的是 bean 的控制权,也就是对象的控制权进行了反转 …

SAP PI/PO中使用UDF解决按字节拆分字符串的需求

需求背景&#xff1a; SAP需要将采购订单信息通过PI发送到SFTP服务器上&#xff0c;生成文件&#xff0c;一般对日项目上文件内容通常都是按照指定的字节数拆分的&#xff0c;而不是字符数&#xff0c;类似下面的格式。 问题点&#xff1a; 如果是使用FTP适配器&#xff0c;则…

MySQL JDBC编程

MySQL JDBC编程 文章目录 MySQL JDBC编程1. 数据库编程的必备条件2. Java的数据库编程&#xff1a;JDBC3. JDBC工作原理4. JDBC使用5. JDBC常用接口和类5.1 JDBC API5.2 数据库连接Connection5.3 Statement对象5.4 ResultSet对象 1. 数据库编程的必备条件 编程语言&#xff1a;…

[PyTorch][chapter 63][强化学习-时序差分学习]

目录&#xff1a; 蒙特卡罗强化学习的问题 基于转移的策略评估 时序差分评估 Sarsa-算法 Q-学习算法 一 蒙特卡罗强化学习的的问题 有模型学习&#xff1a; Bellman 等式 免模型学习: 蒙特卡罗强化学习 迭代&#xff1a; 使用策略 生成一个轨迹&#xff0c; for t…

京联易捷科技与劳埃德私募基金管理有限公司达成合作协议签署

京联易捷科技与劳埃德私募基金管理有限公司今日宣布正式签署合作协议,双方在数字化进程、资产管理与投资以及中英金融合作方面将展开全面合作。 劳埃德(中国)私募基金管理有限公司是英国劳埃德私募基金管理有限公司的全资子公司,拥有丰富的跨境投资经验和卓越的募资能力。该集…

LEEDCODE 220 存在重复元素3

class Solution { public:int getId(int a, int valuediff){// 值// return a/(valuediff1);return a < 0 ? (a ) -) / (valuediff 1) - 1 : a / (valuediff 1);}public: unordered_map<int, int> bucket;bool containsNearbyAlmostDuplicate(vector<int>&am…

【Java实现图书管理系统】

图书管理系统 1. 设计背景2. 设计思路3. 模块展示代码演示3.1 Book类3.2 BookList类&#xff08;书架类&#xff09;3.4 用户类 - User类3.5 子类管理员类 -- AdminUser类3.6 子类普通用户类 -- NormalUser类3.7 操作接口3.8 操作类3.8.1 查找操作 -- FindOperation类3.8.2 增加…

【excel技巧】Excel表格里的图片如何批量调整大小?

Excel表格里面插入了很多图片&#xff0c;但是每张图片大小不一&#xff0c;如何做到每张图片都完美的与单元格大小相同&#xff1f;并且能够根据单元格来改变大小&#xff1f;今天分享&#xff0c;excel表格里的图片如何批量调整大小。 方法如下&#xff1a; 点击表格中的一…

VBA技术资料MF83:将Word文档批量另存为PDF文件

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

JS原生-弹框+阿里巴巴矢量图

效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&q…