解读MySQL驱动加载逻辑
我们很早之前就知道最基础的JDBC编写,先执行Class.forName方法,加载MySQL驱动。但是为什么加载过驱动后,后续的接口层的调用就会自动切换到MySQL的相关代码去执行呢?(作者:高元)
常见jdbc编写
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/gdjt_db","root","root");
ps = connection.prepareStatement("select * from sys_user where id = ? ");
ps.setString(1, "1");
rs = ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if(rs != null)rs.close();
}catch (Exception e){}
try {
if(ps != null)ps.close();
}catch (Exception e){}
try {
if(connection != null)connection.close();
}catch (Exception e){}
}
当然,我们这里使用MySQL来演示,所以需要导入相关依赖
mysql
mysql-connector-java
5.1.44
以上是最简单的JDBC实现,下面我们主要查看Class.forName("com.mysql.jdbc.Driver");具体的执行逻辑。
查看第一步具体做了什么?
Class.forName("com.mysql.jdbc.Driver");
1、我们先看Class.forName
@CallerSensitive
public static Class> forName(String className) throws ClassNotFoundException {
/** 获取调用者Class */
Class> caller = Reflection.getCallerClass();
/**
* 1、获取调用者的类加载器
* 2、执行类加载
* 3、执行初始化方法(initialize = true)
*/
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
具体内容见代码中的注解,可以发现,Class.forName主要就是类加载,加载了类com.mysql.jdbc.Driver
2、接着看mysql的Driver源码实现
/**
* java.sql.Driver 是jdk封装的驱动接口
*/
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
/** 上一步骤类加载器加载类时,执行了初始化方法,就是执行这里的static中的代码 */
static {
try {
/** 将当前mysql的Driver注册到DriverManager中 */
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
具体注册代码如下:
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
if(driver != null) {
// 将mysql的driver封装成DriverInfo对象,并传入registeredDrivers链表中。
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
throw new NullPointerException();
}
}
其中 registeredDrivers 是全局的静态变量
private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
这样,无论在哪里,都可以通过registeredDrivers来获取当前的驱动。
3、继续看Connection如何获取到对应的连接的
Connection conn = DriverManager.getConnection(url,username,password);
我们进入getConnection查看,代码稍微长点,不过没关系,我们只要看如下一行代码即可
private static Connection getConnection(String url, java.util.Properties info, Class> caller) throws SQLException {
...
// 看到了熟悉的身影 registeredDrivers
for(DriverInfo aDriver : registeredDrivers) {
...
// 从registeredDrivers获取之前存入的mysql对应的Driver
Connection con = aDriver.driver.connect(url, info);
...
}
...
}
接着执行 driver.connect( ) 方法,之前我们看过 Driver 的源码
Driver extends NonRegisteringDriver implements java.sql.Driver
所以 driver.connect 的方法,不是在 Driver 中就是在 NonRegisteringDriver 中。 结果我们在 NonRegisteringDriver 中如愿以偿的找到了connect方法(118行左右)
public Connection connect(String url, Properties info) throws SQLException {
...
// 重要代码在这里,获取具体的连接实例
com.mysql.jdbc.Connection newConn = ConnectionImpl.getInstance(this.host(props), this.port(props), props, this.database(props), url);
return newConn;
...
}
接下来的步骤就不一一演示了。
总结
通过类加载器加载Driver并初始化,将Driver添加到DriverManager的registeredDrivers中;
通过registeredDrivers获取到Driver;
调用Driver的connect方法,获取Mysql中的MySQLConnection;
博客
开源中国博客地址
个人博客地址
欢迎关注我的个人微信订阅号:(据说这个头像程序猿专用)
