浅谈Java SPI原理与其在JDBC、Flink中的应用
API:由被调方提供的实现了某个完整功能的接口,主调方直接调用该接口来享用该功能,而无需关注该接口的具体实现。比如使用 JDK 的 InputStream#read 从文件系统中读取数据。
SPI:被调方(框架)提供的功能扩展点接口,主调方可实现这些接口与被调方(框架)进行互动。
MySQL 应用
JDBC 中的 DriverManager 中定义的模板代码:将 Driver 实现类的 Class 的加载工作委托给 SPI。
// From DriverManager.java
private static final String JDBC_DRIVERS_PROPERTY = "jdbc.drivers";
private static Connection getConnection(
// 省略代码 ...
// 通过 SPI 加载 Driver,而无需再通过 Class.forName("com.mysql.jdbc.Driver")
ensureDriversInitialized();
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
// 「驱动实现类」的 Class 被加载后,就会通过「静态代码块」将当前驱动对象注册到 registeredDrivers 中,比如见后文的 MySQL 驱动类 FabricMySQLDriver.java 的实现
for (DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if (isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// 省略代码...
}
private static void ensureDriversInitialized() {
if (driversInitialized) {
return;
}
synchronized (lockForInitDrivers) {
if (driversInitialized) {
return;
}
String driv