DriverManager.getConnection() 内部运用了SPI机制,扫描mysql的jar包的META-INF/services/
获取全路径名并使用Class.forName(cn, false, loader),c.newInstance()
加载目标驱动。
另外一方面,也解决了为什么不使用Class.forName() 也可以破坏双亲委派,因为getConnection内部封装了Class.forName(cn, false, loader);
false
表示 加载字节码返回class对象.但并不去初始化,也就是不触发static代码块
c.newInstance
则一定会实例化对象,触发static代码块
以下概念拓展至Effective java page 6 (中文版)
SPI (Service Provider Interface),用于拓展工程实例的接口
对于JDBC ,Connection 就是其服务接口的一部分
Service Interface
JDK 提供了标准,具体的数据库驱动由各大数据库厂商提供
Provider Registration API
JDK 提供注册用的API,注册的API会调用new Driver()
Driver必须有空参构造方法,为了支持Class.forName().newInstance()
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
Provider access API
开发人员使用的代码
@CallerSensitive
public static Connection getConnection(String url)
throws SQLException {
java.util.Properties info = new java.util.Properties();
return (getConnection(url, info, Reflection.getCallerClass()));
}
private static Connection getConnection(
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
// 注册SPI目录下的所有驱动
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
return (con);
}
}
}
}
}
}
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
Class<?> aClass
= Class.forName(driver.getClass().getName(), true, classLoader);
result = ( aClass == driver.getClass() ) ? true : false;
}
return result;
}
最调用了Class.forName
关于getContextClassLoader,点击此链接,拉至文章底部
Thread.currentThread().getContextClassLoader(); // 获取的是AppClassLoader
在加载核心类DriverManager后,可以使用通过线程上下文的 AppClassLoader 加载 SPI实现类
Class.forName(driver.getClass().getName(), true, classLoader);
Class.forName 使用的是 getContextClassLoader() 中的AppClassLoader加载
第一次使用getConnection(String url)
会触发 DriverManager 的 static 方法
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
ServiceLoader
就是SPI的正在执行容器
private static void loadInitialDrivers() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
while(driversIterator.hasNext()) {
driversIterator.next(); // next() 方法是SPI的最终实现
}
return null;
}
});
}
public final class ServiceLoader<S>implements Iterable<S>{
private static final String PREFIX = "META-INF/services/";
}
ServiceLoader.nextService
用于遍历所有SPI(Iterable.next封装了这个方法)
private S nextService() {
String cn = nextName;//cn是SPI实现者全限定名
nextName = null;
Class<?> c = null;
try {
// 加载字节码返回class对象.但并不去初始化,也就是不触发static代码块
c = Class.forName(cn, false, loader);
}
try {
// cast 不是主要业务逻辑。 c.newInstance触发了 com.mysql.jdbc.Driver的静态方法
// 静态方法完成了Driver的实例化与注册
S p = service.cast(c.newInstance());
providers.put(cn, p);//本地缓存 (全限定名,实现类对象)
return p;
}
}
Driver 的静态方法被触发,完成将SPI实现类注册到DriverManager
因为是由c.newInstance
触发的,这里的new Driver() 已经被替换成了SPI实现类,注册完成
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
DriverManager.getConnection(url) // 如何取到驱动
步骤一,线程开启, 双亲委派指派核心类加载,DriverManager没有被加载时
public class Launcher {
static class AppClassLoader extends URLClassLoader {}
static class ExtClassLoader extends URLClassLoader {}
public Launcher() {
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
}
// 把AppClassLoader设置为本线程上下文的类加载器
Thread.currentThread().setContextClassLoader(this.loader);
}
}
步骤二,线程继续执行,DriverManager被加载, 触发loadInitialDrivers(),直接触发SPI读取的工具类实例化
static {
loadInitialDrivers(); // 内部使用的是ServiceLoader 的api
println("JDBC DriverManager initialized");
}
// loadInitialDrivers(); 内初始化ServiceLoader
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
步骤三,ServiceLoader< Driver> 初始化,一路携带AppClassLoader
public static <S> ServiceLoader<S> load(Class<S> service) {
// 获得的是【步骤一】中设置的AppClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}
// 把中设置的AppClassLoader 赋值给 loader
private ServiceLoader(Class<S> svc, ClassLoader cl) {
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
}
步骤四,ServiceLoader< Driver> 扫描目录,初始化SPI实现类
public final class ServiceLoader<S>implements Iterable<S>{
// 规范目录,MySQL驱动的Jar包下一定有此文件,里面是SPI实现类的全路径名
private static final String PREFIX = "META-INF/services/";
private S nextService() {
String cn = nextName;//SPI实现者全限定名
nextName = null;
Class<?> c = null;
try {
// 加载字节码返回class对象.但并不去初始化,也就是不触发static代码块
c = Class.forName(cn, false, loader);
}
try {
// cast 不是主要业务逻辑。 c.newInstance触发了 Driver的静态方法
S p = service.cast(c.newInstance());
providers.put(cn, p);//本地缓存 (全限定名,实现类对象)
return p;
}
}
}
步骤五,SPI实现类 com.mysql.jdbc.Driver 初始化后,用 static 调用 DriverManager 注册驱动自身,也就是注册com.mysql.jdbc.Driver完成
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
DriverManager.getConnection() 内部运用了SPI机制,扫描mysql的jar包的META-INF/services/
获取全路径名并使用Class.forName(cn, false, loader),c.newInstance()
加载目标驱动。
另外一方面,也解决了为什么不使用Class.forName() 也可以破坏双亲委派,因为getConnection内部封装了Class.forName(cn, false, loader);