JDBC项目实践与源码解析——加载驱动
本专栏为系列文章,如果想要系统学习JDBC,作者建议从第一篇文章,顺序读完全部文章。
文中项目源码地址:https://github.com/bethanwang/jdbc-study
通过反射机制,生成驱动类的实例,即生成com.mysql.cj.jdbc.Driver
类的对象,并将驱动类实例注册到java.sql.DriverManager
进行管理。
//驱动类
private static final String DRIVER_CLASS = "com.mysql.cj.jdbc.Driver";
//加载驱动
Class.forName(DRIVER_CLASS);
关于反射和类的加载机制本文不展开讲解。
源码解析:
先来看一下代码中private static final String DRIVER_CLASS = "com.mysql.cj.jdbc.Driver"
这一行里面com.mysql.cj.jdbc.Driver
类的源码,如下:
package com.mysql.cj.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// Register ourselves with the DriverManager
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* Construct a new driver and register it with DriverManager
*
* @throws SQLException
* if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
当我们执行Class.forName("com.mysql.cj.jdbc.Driver")
这一行代码时,会通过反射实例化com.mysql.cj.jdbc.Driver
类的对象,通过查看com.mysql.cj.jdbc.Driver
类的源码,我们知道在实例化其对象时会执行static
代码块中的java.sql.DriverManager.registerDriver(new Driver())
方法。
java.sql.DriverManager
类中的源码:
package java.sql;
public class DriverManager {
/*
一个线程安全的List集合,注册后的com.mysql.cj.jdbc.Driver的对象,封装到DriverInfo的对象中,然后将DriverInfo的对象,放到registeredDrivers集合内,完成注册
*/
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
registerDriver(driver, null);
}
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
}
class DriverInfo {
final Driver driver;
DriverAction da;
DriverInfo(Driver driver, DriverAction action) {
this.driver = driver;
da = action;
}
}
通过java.sql.DriverManager
的源码我们看到,执行registerDriver(java.sql.Driver driver)
方法后,会调用registerDriver(java.sql.Driver driver, DriverAction da)
方法;通过registeredDrivers.addIfAbsent(new DriverInfo(driver, da))
这一行代码可以看出,创建了一个DriverInfo
对象,并将com.mysql.cj.jdbc.Driver
的对象封装到DriverInfo
中,最后将DriverInfo
对象添加到registeredDrivers
集合中,完成驱动的注册。
*扩展:基于SPI隐式加载驱动:
所谓Driver
隐式加载,就是Class.forName(DRIVER_CLASS);
这一行代码不需要再写了。
JDBC4.0开始,这个显式的初始化驱动的代码就可以不用写了,很多时候之所以这样写是考虑到线上兼容。
在java 6以后,加入了SPI(Service Provider Interface)功能,SPI提供了一种JVM级别的服务发现机制,只需要在jar包中按照SPI的格式要求进行配置,JVM就会在运行时通过懒加载,帮我们找到所需的服务并加载。如果系统中没有使用到这个服务,则不会被加载,避免资源浪费。
SPI的配置路径是:/META-INF/services/下面,我们程序中使用的”mysql-connector-java-8.0.19.jar”也添加了对应的配置,所以在我们的项目中,可以隐式加载驱动,即可以不写`Class.forName(DRIVER_CLASS);`这一行代码。
mysql-connector-java-8.0.19.jar
中关于SPI的配置:
上一篇:系统学习JDBC——JDBC实践
下一篇:JDBC深入讲解——打开数据库连接
受作者水平限制,文中难免有不足之处,若读者阅读过程中发现问题,还望及时指正,感谢支持!
作者邮箱:547317812@qq.com