本文有三个点:
Jdk 源码包的 rt.jar定义接口 Driver
第三方 jar 包实现接口,同时为了可以被 SPI 机制发现(在META-INF/services文件中,以接口的全限定名来命名文件名,文件里面写该接口的实现)。
??
SPI 机制将所有第三方 jar 包实现类加载到 Jvm 中
开发人员根据自己的逻辑合理选择 jar 包实现。
参考源码分析流程
在执行DriverManager.registerDriver(new Driver());
?前,jvm 类加载系统会先将 DriverManager.class字节码文件加载到 jvm 中。并执行静态方法loadInitialDrivers();
?
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
? 这里用到了 SPI 机制。Iterator<Driver> driversIterator = loadedDrivers.iterator();
?private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers"); //null
}
});
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); //loadedDrivers = java.sql.Driver接口
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
// 在 hasNext()中找到待实例化的全类名,在 next()中实例化该类。
while(driversIterator.hasNext()) {
driversIterator.next();
}
}
return null;
}
});
}
父加载器委托子加载器加载,打破了双亲委派机制。
这种父类加载器委托子类加载的行为边打破了双亲委派机制
??
其实我们可以直接通过这种方式进行驱动注册。
DriverManager.getConnection(String url,Properties info,Class<?> caller);
而不是下文的这种调用方式,下文这种 Class.forName注册驱动其实是可以省略的。看代码:
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
// 2. 加载 JDBC 驱动
Class clazz= Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
// 3. 注册驱动
DriverManager.registerDriver(driver);
// 4. 打开链接
System.out.println("连接数据库...");
conn = (Connection)DriverManager.getConnection(DB_URL,USER,PASS);
}
调用 Class.forName(driverName);注册驱动。这一部是非必须的,这样写不会打破双亲委派机制。可以直接第三步
DriverManager.registerDriver(new Driver());
??在执行DriverManager.registerDriver(new Driver());
?前,jvm 类加载系统会先将 DriverManager.class字节码文件加载到 jvm 中。并执行静态方法loadInitialDrivers();
?SPI 机制 加载配置的 jdbc.drivers 属性中的多个驱动
DriverManager.getConnection(String url,Properties info,Class<?> caller); 获取连接??
?