今天在看web.xml为什么逐渐淡出我们的视野,重温了下JDK的SPI机制(Service Provider Interface)。
SPI 是什么?
面向接口编程+配置文件!
对,我总结的就是一句话。看看我们日常使用不同数据库驱动,不同日志框架,是不是都在使用SPI机制呢?下面我用数据库驱动来举例。
JDBC驱动使用SPI
一、概述
我们都知道数据库驱动 java.sql.Driver,针对不同数据库厂商有不同的实现,翻翻我们mysql驱动jar包,依次找到META-INF/services/java.sql.Driver,看看里面有啥?1
2
3# mysql 驱动实现类
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
我们看看Driver类的static方法
1 | public class Driver extends NonRegisteringDriver implements java.sql.Driver { |
二、加载驱动,获取连接
注册Driver到DriverManager -> DriverManager.getConnection(…) 获取连接
新(SPI机制)、老(反射)2种方式
1、新方式,SPI机制,省略Class.forName(“xxx.xxx.xxx”)方式(jar包下所需文件如概述描述)
驱动从 5.x 开始jar包下必须要有 META-INF/services/java.sql.Driver。
1 | // 会调用DriverManager的static方法,里面会寻找驱动jar包下面META-INF/services/java.sql.Driver 文件 |
2、老方式,通过Class.forName(“xxx.xxx.xxx”)反射
驱动4.x一般这样来写
1 | // 反射,会调用Driver里static方法,注册Driver到DriverManager |
2种方式讲解
- 新的
1 | static { |
ServiceLoader实现spi机制1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
//查找mysql驱动jar下META-INF/services/java.sql.Driver
//初始化它,调用 static方法,注册驱动(同下分析)
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
后续流程同下面老的讲解
- 老的
1 | //Driver里static方法 |
我们可以看到SPI机制核心在于:ServiceLoader、META-INF/services/需要实现接口全定限名 文件。ServiceLoader位于java.util包下,其load方法就是实现SPI的关键。有兴趣可以自己实现个SPI,试验下!