Java SPI机制
首先来说下 SPI 的全称: Service Provider Interface,直译过来就是“服务提供接口”。拆分出来就是 服务、提供、接口,三个词。
SPI不是一个框架,更不是一个具体实现,更偏向于是一种机制、规约。使用这种机制,遵循这种既定的规约,就可以提高系统的灵活性、可扩展性、模块化程度等。
他是干什么的呢???
查阅了相关资料,我来举两个例子哈
JDBC连接
JDBC 3.0 及之前的方式
在 JDBC 3.0 及之前,程序员需要显式地加载数据库驱动。这包括几个步骤:
手动加载数据库驱动类:使用 Class.forName()
方法加载数据库驱动类。这是必要的,因为数据库驱动不会自动被注册到 JDBC 驱动管理器中。
获取数据库连接:使用 DriverManager.getConnection()
方法获取连接。
下面是一个完整的例子:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseUtil {
public static Connection getConnection() throws SQLException {
// 手动加载数据库驱动
try {
Class.forName("com.mysql.jdbc.Driver"); // 旧版本的驱动类名
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new SQLException("Driver not found", e);
}
// 获取数据库连接
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "user";
String password = "password";
return DriverManager.getConnection(url, user, password);
}
}
JDBC 4.0 及之后的方式
在 JDBC 4.0 及之后,Java 引入了服务提供者接口(SPI)机制,使得数据库驱动可以自动注册。这简化了数据库驱动的管理和加载过程。
不再需要手动加载驱动:驱动会通过 SPI 机制自动加载。你只需要确保 JDBC 驱动 JAR 文件在类路径中,并且该 JAR 文件包含 META-INF/services/java.sql.Driver
文件。
获取数据库连接:直接使用 DriverManager.getConnection()
方法获取连接,无需手动加载驱动。
下面是 JDBC 4.0 及之后的代码示例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseUtil {
public static Connection getConnection() throws SQLException {
// 驱动会自动注册,无需手动加载
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "user";
String password = "password";
return DriverManager.getConnection(url, user, password);
}
}
驱动 JAR 文件中的注册机制
为了使 JDBC 4.0 的自动注册机制生效,驱动 JAR 文件必须包含一个特定的文件:
META-INF/services/java.sql.Driver
文件
这个文件包含了驱动类的全限定名,例如:
com.mysql.cj.jdbc.Driver
当 Java 应用程序启动时,JDBC 驱动管理器会扫描所有类路径中的 JAR 文件,查找这个文件并加载列出的驱动类。这样,数据库驱动就会自动注册到 JDBC 驱动管理器中,无需程序员手动调用 Class.forName()
。
如果不是很清楚MySQL的驱动如何加载,可以查看MySQL驱动加载【底层实现】
案例二
Java 实用工具库(java.util.ServiceLoader)是 Java 提供的一种机制,用于动态加载服务提供者的实现。这种机制允许你在运行时发现和加载实现特定接口或抽象类的服务提供者,从而实现插件架构。ServiceLoader 是一个用来简化服务发现过程的工具。
假设你正在构建一个应用程序,支持多种不同的日志框架。你可以定义一个通用的日志接口,并让不同的日志框架实现这个接口,然后通过 ServiceLoader 动态加载这些实现。
-
第一步先定义一个日志接口
public interface Logger { void log(String message); }
-
创建服务提供者的实现
public class ConsoleLogger implements Logger { @Override public void log(String message) { System.out.println("Console: " + message); } }
再创建一个实现类,用于生成文件
import java.io.FileWriter; import java.io.IOException; public class FileLogger implements Logger { private FileWriter writer; public FileLogger() throws IOException { writer = new FileWriter("log.txt", true); } @Override public void log(String message) { try { writer.write(message + System.lineSeparator()); writer.flush(); } catch (IOException e) { e.printStackTrace(); } } }
-
在服务提供者 JAR 文件中注册服务
在每个实现的 JAR 文件中,创建一个文件 META-INF/services/com.example.Logger。这个文件的内容是服务接口的实现类的全限定名。例如:
com.example.ConsoleLogger com.example.FileLogger
这个文件使得 ServiceLoader 能够发现并加载这些实现类。
-
使用 ServiceLoader 加载服务实现
import java.util.ServiceLoader; public class LoggerFactory { public static void main(String[] args) { ServiceLoader<Logger> loader = ServiceLoader.load(Logger.class); for (Logger logger : loader) { logger.log("Hello, SPI!"); } } }
按照以上例子,大概已经明白了个所以然。
接着我看了一些视频和评论,分享如下👇
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术