JDBC代碼中常見:
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, pwd);
為什麼Class.forName()能神奇地註冊驅動?本文從源碼級揭示機制。
以下寫法效果完全一致:
// 方案一:顯式實例化
com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver();
// 或 new com.mysql.jdbc.Driver();
// 方案二:Class.forName().newInstance() (舊版JDBC規範要求)
Class.forName("com.mysql.jdbc.Driver").newInstance();
核心要求:在DriverManager.getConnection()前,確保驅動類完成類初始化(<clinit>)。
// 僅加載類,不觸發初始化
ClassLoader cl = new ClassLoader();
cl.loadClass("com.mysql.jdbc.Driver"); // 無靜態塊執行
// 未實例化的引用
com.mysql.jdbc.Driver driver = null; // 無任何類加載動作
| 角色 | JDBC對應 | 實例 |
|---|---|---|
| Abstraction | DriverManager | 統一入口 |
| Implementor | java.sql.Driver | 接口定義 |
| ConcreteImplementor | 具體驅動類 | com.mysql.jdbc.Driver |
關鍵問題:DriverManager如何持有Driver引用? 代碼中從未顯式註冊!
MySQL驅動源碼關鍵片段:
package com.mysql.jdbc;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// 靜態初始化塊 - 類加載時自動執行
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException e) {
throw new RuntimeException("Can't register driver!");
}
}
// 公共無參構造器 - 供Class.forName().newInstance()調用
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
Class.forName("com.mysql.jdbc.Driver")Driver實例 → 調用DriverManager.registerDriver()DriverManager將驅動加入內部registeredDrivers列表getConnection(url...)遍歷列表,調用driver.connect(url...)「When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager」
——java.sql.Driver接口文檔
META-INF/services/java.sql.Drivercom.mysql.jdbc.Driver// 兼容所有版本的通用寫法
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// JDBC 4.0+可能無需此行,但保留無害
}
Connection conn = DriverManager.getConnection(url, user, pwd);
| 誤區 | 真相 |
|---|---|
| 必須用Class.forName() | JDBC 4.0+可省略;本質是觸發類初始化 |
| newInstance()是必需的 | 僅JDBC 1/2規範要求,現代驅動靜態塊已處理 |
| 多次調用會重複註冊 | DriverManager內部去重,同類加載器下單例 |
| 註冊失敗會拋出異常 | 驅動靜態塊捕獲SQLException轉為RuntimeException |
Class.forName()本質是觸發驅動類的靜態初始化,驅動類在<clinit>中主動註冊自身到DriverManager。這是橋接模式中ConcreteImplementor自我註冊到Abstraction的經典實現。JDBC 4.0+通過SPI機制實現零代碼註冊,但保留Class.forName()兼容舊代碼無副作用。