在 JDBC API 中,大家非常熟悉的 Driver 類就是橋接物件。使用 JDBC 時通過 Class.forName() 方法可以動態載入各個資料庫廠商實現的 Driver 類。
下面以 MySQL 的實現為例,具體使用者端應用程式碼如下:
// 1. 註冊JDBC驅動
// 反射機制載入驅動類
Class.forName("com.mysql.jdbc.Driver");
// 2. 獲取連線Connection
conn = DriverManager.getConnection(DB_URL,USER,PASS);
// 3. 獲取sql語句的物件Statement
Statement stmt = conn.createStatement();
// 4. 執行sql語句,並返回結果
ResultSet rs = stmt.executeQuery(sql);
以下為 Driver 介面的定義。
public interface Driver {
Connection connect(String url, java.util.Properties info) throws SQLException;
boolean acceptsURL(String url) throws SQLException;
DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException;
int getMajorVersion();
int getMinorVersion();
boolean jdbcCompliant();
public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}
Driver 在 JDBC 中並沒有具體實現,具體的功能實現由各廠商完成,下面是 MySQL 中 Driver 的具體實現。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
Driver 實現類的程式碼特別簡短,其中只呼叫了 DriverManager 中的 registerDriver 方法來註冊驅動。當驅動註冊完成後,就會開始呼叫 DriverManager 中的 getConnection 方法了。
當我們執行到 Class.forName("com.mysql.jdbc.Driver") 方法的時候,就會執行 com.mysql.jdbc.Driver 類的靜態塊中的程式碼。靜態塊中的程式碼只是呼叫了一下 DriverManager 的 registerDriver() 方法,然後將 Driver 物件註冊到 DriverManager 中。下面繼續跟進 DriverManager 類,相關程式碼如下。
public class DriverManager {
// List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
private static volatile int loginTimeout = 0;
......
private final static Object logSync = new Object();
private DriverManager(){}
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
......
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
......
}
在註冊之前,將傳過來的 Driver 物件封裝成一個 DriverInfo 物件。
class DriverInfo {
final Driver driver;
DriverInfo(Driver driver) {
this.driver = driver;
}
public boolean equals(Object other) {
return (other instanceof DriverInfo)
&& this.driver == ((DriverInfo) other).driver;
}
public int hashCode() {
return driver.hashCode();
}
public String toString() {
return ("driver[className=" + driver + "]");
}
}
DriverInfo 本身其實就是 Driver。接下來繼續執行使用者端程式碼的第二步,呼叫 DriverManager 的 getConnection() 方法獲取連線物件,下面跟進原始碼。
public class DriverManager{
......
public static Connection getConnection(String url,
java.util.Properties info) throws SQLException {
return (getConnection(url, info, Reflection.getCallerClass()));
}
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 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 {
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized (DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
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");
}
}
在 getConnection() 中,又會呼叫各自廠商實現的 Driver 的 connect() 方法獲得連線物件。這樣就巧妙地避開了使用繼承,為不同的資料庫提供了相同的介面。JDBC API 中的 DriverManager 就是橋,如下圖所示。