DriverManager
上面提到 DataSource 获取连接(Connection) 的操作实质上将委托具体的 Driver 来提供 Connection 。有两种不同的方式,包括经由 DriverManager 遍历所有处于管理下的 Driver 尝试获取连接,或者在 DataSource 实例中直接声明一个特定的 Driver 来获取连接。
对于获取连接的具体操作,挖坑-待填。只描述简单的数据库供应商提供的 Driver 如何与 java 相联系。
在 DriverManager 中注册 Driver 实例
通常在与数据库交互逻辑的 Java 代码中,都会有 Class.forName("com.mysql.jdbc.Driver")
(此处以 MySQL 提供的 mysql-connector-java-XXX.jar 为例,下同)的代码块,加载指定的 com.mysql.jdbc.Driver 为 java.lang.Class 类。
当然,在 JDBC 4.0 标准下,可以不必再显示声明 Class.forName("")
语句,Driver 也同样会在 DriverManager 初始化时自动注册。
// Class 类中对于 forName(String className) 的方法
// 作用为返回一个 java.lang.Class 实例。
public static Class<?> forName(String className) throws ClassNotFoundException {...}
同时, JVM 在加载类的过程中会执行类中的 static 代码块。下述 row 10 ~ 16 的代码片段将被执行。唯一的逻辑就是 new 一个 com.mysql.jdbc.Driver 实例,并将实例注册(registerDriver) 到 java.sql.DriverManager 中。
package com.mysql.jdbc;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// ~ Static fields/initializers
// ---------------------------------------------
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
// ~ Constructors
// -----------------------------------------------------------
/**
* 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()
}
}
下面再来看一下 DriverManager 中的 registerDriver() 方法。
public class DriverManager {
// DriverManager 维护一个线程安全的 Driver 列表
// 此处的 DriverInfo 里面即包装了 Driver
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers =
new CopyOnWriteArrayList<>();
// 在 DriverManager 中注册 Driver
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 {
/* 如果当前 Driver 不在列表中,即添加到列表。 */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
}
通过 DriverManager 获取连接(Connection)
上一节有提到过可以通过 DriverManager 来遍历获取连接,也可以直接声明具体 Driver 并获取连接。下面代码展示的是通过 DriverManager 获取连接的操作。 哈哈哈,反正最后都是由具体驱动实现获取连接。
public class DriverManager {
// 获取连接的 public 接口 (1)
public static Connection getConnection(String url,
java.util.Properties info) throws SQLException {
return (getConnection(url, info, Reflection.getCallerClass()));
}
// 获取连接的 public 接口 (2)
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
// 获取连接的 public 接口 (3)
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(
String url, java.util.Properties info, Class<?> caller)
throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
// url 是定位 DBMS 最重要的参数,不能为空
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// 遍历所有注册的 Driver ,并都尝试获取连接(Connection)
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// 判断注册的 Driver 是否由 ClassLoader callerCL 加载,不是则跳过
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
// 获取连接,:) 还是由 driver 实例自行提供
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " +
aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// 如果运行到下列代码,则表明获取连接失败,抛出错误
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
}
简单的提一嘴,Connection 仍然只是一个针对 Java -> DB Server 的上层接口,如果想要更深层次地了解 Connection 与 DB Server 的交互,可以尝试去看一下 com.mysql.jdbc.MysqlIO 类,MySQL 实现的 JDBC4Connection 类也是在使用该类来实现对 DB Server 交互。(哈哈,只看过 MySQL 提供的 Driver 包)。