DriverManager 是 JDBC 架構的核心管理層,位於應用程式與具體驅動程式之間,負責驅動註冊、連接建立、日誌追蹤等核心職責。理解其工作機制,對排查連接失敗、優化連接池配置、實現多租戶數據源路由具有指導意義。
DriverManager 維護一個已註冊 java.sql.Driver 實現類的列表。所有驅動類必須包含靜態初始化塊,在類加載時創建實例並調用 DriverManager.registerDriver(this) 自動註冊。
| 方式 | 代碼示例 | 特點 | 推薦度 |
|---|---|---|---|
| Class.forName() 顯式加載 | Class.forName("com.mysql.cj.jdbc.Driver"); | 不依賴外部配置,代碼可見性強,模塊化友好 | ⭐⭐⭐⭐⭐ 強烈推薦 |
| 系統屬性 jdbc.drivers | jdbc.drivers=driver1:driver2:driver3 | 需持久化環境(屬性文件/啟動參數),DriverManager 初始化後不再檢查 | ⭐⭐ 遺留項目兼容 |
安全機制:DriverManager 記錄每個驅動的類加載器,建立連接時僅使用調用代碼同類加載器或系統類加載器加載的驅動,防止惡意代碼注入偽造驅動。
jdbc.drivers 列出的驅動優先註冊)driver.connect(url, props)null Connection 的驅動獲勝,建立連接null,拋出 SQLException: No suitable driver例如連接遠程 MySQL,可能同時匹配:
com.mysql.cj.jdbc.Driver註冊順序決定優先級。顯式 Class.forName() 可控制加載順序,確保優先使用高性能官方驅動。
// 1. 顯式加載驅動(JDBC 4.0+ SPI 機制下可省略,但建議保留以確保兼容性)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 構建 JDBC URL
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8";
// 3. 獲取連接
Connection conn = DriverManager.getConnection(url, "username", "password");
try {
// 業務操作
} finally {
if (conn != null) conn.close(); // 必須關閉,歸還連接池或釋放資源
}
驅動 JAR 的 META-INF/services/java.sql.Driver 文件列出實現類全限定名。DriverManager 初始化時透過 ServiceLoader 自動發現並加載,**無需手動 Class.forName()** 。但顯式加載仍有價值:
Properties 進階配置Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "secret");
props.setProperty("useSSL", "false");
props.setProperty("serverTimezone", "Asia/Shanghai");
props.setProperty("characterEncoding", "utf8");
props.setProperty("connectionTimeout", "5000"); // 連接超時 ms
props.setProperty("socketTimeout", "30000"); // 網路讀寫超時
props.setProperty("autoReconnect", "true"); // 自動重連(慎用,建議連接池層處理)
props.setProperty("maxReconnects", "3");
props.setProperty("rewriteBatchedStatements", "true"); // 批量寫入優化
Connection conn = DriverManager.getConnection(url, props);
生產環境**絕不直接使用 DriverManager.getConnection()**,而使用成熟連接池:
連接池內部封裝 DriverManager,負責驅動加載、連接生命週期、語句缓存、洩漏檢測等。
public class DynamicDataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenantId(); // ThreadLocal 存儲租戶標識
}
}
// Spring 配置
@Bean
public DataSource dataSource() {
DynamicDataSourceRouter router = new DynamicDataSourceRouter();
Map
此模式下 DriverManager 僅在各目標 DataSource 初始化時參與驅動加載,路由邏輯完全在應用層。
ClassNotFoundException 或 No suitable driver → 檢查 Maven 依賴/類路徑bind-address、VPNmysql_native_password vs caching_sha2_password 插件不匹配useSSL=false 或配置信任存儲serverTimezone、characterEncoding 顯式指定maxPoolSize 過小、連接洩漏(未關閉)、長事務阻塞