1. 先看看原生jdbc執行sql的步驟

// 在程序啟動的時候需要注册一次mysql驅動,必須引入 mysql-connnector-java 的包
Class.forName("com.mysql.jdbc.Driver"); // 創建數據庫連接
Connection connection = DriverManager.getConnection("jdbcUrl", "userName", "password");
// 創建執行語句
Statement statment = connection.createStatement();
// 執行sql,返回結果集
ResultSet resultSet = statement.executeQuery("select * from table;");
// 最後,關閉連接
connection.close();

2. 為啥 Class.forName("com.mysql.jdbc.Driver") 加載一下類就能注册驅動了?

看看 com.mysql.jdbc.Driver 類的代碼 (版本5.1.47)

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}

當類加載的時候會執行靜態代碼塊,從上面可以看到,mysql的Driver靜態代碼塊裏邊 將自己注册到 DriverManager 裏了; 所以後續的 DriverManager#getConnection 的過程能使用到mysql的driver

3. DriverManager 在上述jdbc執行過程中的作用

3.1 DriverManager.registerDriver 顯式/主動注册驅動

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); //驅動列錶
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException { /* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}

registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); 驅動的注册過程就是將其加入到 一個 copyOnWrite 的列錶裏;

3.2 DriverManager#getConnection 連接創建過程

 for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
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());
}
}

4. DriverManager 自動加載驅動

除了主動調用 DriverManager#registerDriver() 注册驅動外,DriverManager 其實還有兩種自動加載驅動的機制:

  • 系統變量 jdbc.drivers 定義的驅動, 然後內部也是通過 Class.forName 去注册這些驅動類
  • 基於 ServiceLoader 的 service-provicer SPI 機制, 即數據庫驅動提供方在其類路徑下存在文件 /META-INF/services/java.sql.Driver 指定其驅動的實現類,就能被DriverManager自動加載到;

具體邏輯: 在 DriverManager 的靜態代碼塊裏,會執行 loadInitialDrivers() 一個自動驅動加載邏輯

 private static void loadInitialDrivers() {
String drivers;
try {
//1. 加載系統變量 jdbc.drivers 設置的冒號隔開的驅動
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
} AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() { // 2. service-provider 加載機制
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
}); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
// 內部也是利用 Class.forName 去加載 jdbc.drivers 定義的Driver類名稱
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}

總結

System.getProperty('jdbc.drivers") 這種方式個人覺得適合用於在應用初始化脚本中指定

基於 ServiceLoader 的SPI方式,只要符合 spi 標准的驅動包引入了就會自動加載

我們在基於jdbc實現或封裝數據庫連接池、中間件的時候,就不需要再 Class.forName 的方式去顯示注册驅動了,特別是當需要支持多種數據庫的時候,還得根據 jdbcUrl 去判斷目標 driverClassName 是啥, 如果要再支持新的關系型數據庫時候 還得再改代碼

看看常用關系數據庫廠商的 spi 支持情况

  • mysql-connector-java 早在5.0.0(2005-12-22) 版本就添加了 META-INF/services/java.sql.Driver 文件支持 service-provicer SPI; 現在一般用8.x的版本
  • pg connector 也在Version 42.2.13(2020-06-04) 之後支持

所以如果不用支持古董版本的驅動,基本可以放心直接 DriverManager#getConnection 去創建db連接了

jdbc如何注册數據庫驅動Driver的?的更多相關文章

  1. jdbc 加載數據庫驅動如何破壞雙親委托模式

    導讀      通過jdbc鏈接數據庫,是每個學習Java web 方向的人必然一開始會寫的代碼,雖然現在各路框架都幫大家封裝好了jdbc,但是研究一下jdbc鏈接的套路還是很意義     術語以及相 ...

  2. JDBC加載數據庫驅動的方式

    JDBC作為數據庫訪問的規範接口,其中只是定義一些接口.具體的實現是由各個數據庫廠商來完成. 一.重要的接口: 1.public interface Driver 每個驅動程序類必須實現的接口.Jav ...

  3. JDBC 學習筆記(四)—— JDBC 加載數據庫驅動,獲取數據庫連接

    1. 加載數據庫驅動 通常來說,JDBC 使用 Class 類的 forName() 靜態方法來加載驅動,需要輸入數據庫驅動代錶的字符串. 例如: 加載 MySQL 驅動: Class.forName ...

  4. JDBC鏈接mysql數據庫

    Unit_1 首先:JDBC:java database connectivity SUN公司提供的一套操作數據庫的標准規範. JDBC與數據庫驅動的關系是接口與實現的關系. JDBC涉及到四個核心的 ...

  5. Java -- JDBC 學習--獲取數據庫鏈接

    數據持久化 持久化(persistence): 把數據保存到可掉電式存儲設備中以供之後使用.大多數情况下,特別是企業級應用,數據持久化意味著將內存中的數據保存到硬盤上加以”固化”,而持久化的實現過程大 ...

  6. 在java程序中使用JDBC連接mysql數據庫

    在java程序中我們時常會用到數據庫中的數據或操作數據庫中的數據,如果java程序沒有和我們得數據庫連接,就不能實現在java程序中直接操作數據庫.使用jdbc就能將java程序和數據庫連起來,此時我 ...

  7. Java第三十五天,用JDBC操作MySQL數據庫(一),基礎入門

    一.JDBC的概念 Java DataBase Connectivity 從字面意思我們也不難理解,就是用Java語言連接數據庫的意思 JDBC定義了Java語言操作所有關系型數據庫的規則(接口).即 ...

  8. JDBC | 第一章: 快速開始使用JDBC連接Mysql數據庫?

    開始使用基於java的JDBC技術來連接mysql進行msyql數據庫簡單的CRUD操作 下載對應mysql驅動包 這裏我創建maven項目基於maven下載 <!--mysql 驅動--> ...

  9. 老調重彈:JDBC系列 之 &amp;lt;驅動載入原理全面解析&amp;gt;

    前言 近期在研究Mybatis框架,因為該框架基於JDBC.想要非常好地理解和學習Mybatis,必需要對JDBC有較深入的了解.所以便把JDBC 這個東東翻出來.好好總結一番,作為自己的筆記,也是給 ...

  10. Symfony 沒有找到數據庫驅動An exception occured in driver: could not find driver

    如果一直報這個錯誤, 第一,你本地沒有相關的數據庫驅動(mysql:-->pdo_myql,postgresql-->pdo_pgsql等); 需要執行 php -m|grep -i pd ...

隨機推薦

  1. Ubuntu下iperf的安裝

    (1)下載 鏈接:http://sourceforge.net/projects/iperf/files/?source=navbar   資源:iperf-2.0.5.tar.gz (2)解壓 #t ...

  2. Linux 網絡編程詳解四(流協議與粘包)

    TCP/IP協議是一種流協議,流協議是字節流,只有開始和結束,包與包之間沒有邊界,所以容易產生粘包,但是不會丟包. UDP/IP協議是數據報,有邊界,不存在粘包,但是可能丟包. 產生粘包問題的原因 . ...

  3. 對象Clone

    //================================================= // File Name : Clone_demo //-------------------- ...

  4. [課程設計]Scrum 1.7 多魚點餐系統開發進度(點餐菜式內容添加及美化)

    [課程設計]Scrum 1.7 多魚點餐系統開發進度(點餐菜式內容添加及美化) 1.團隊名稱:重案組 2.團隊目標:長期經營,積累客戶充分准備,伺機而行 3.團隊口號:矢志不渝,追求完美 4.團隊選題 ...

  5. Linux(ubuntu)下jdk&amp;tomcat的安裝

    1.下載相應版本的jdk及tomcat:sudo wget ${url} 2.解壓: tar zxvf jdk-7u79-linux-x64.tar.gz​ tar zxvf apache-tomca ...

  6. Thinkphp導入外部類的方法

    相信很多人在使用TP時候都苦惱使用外部類各種不成功 下面為大家詳細介紹下引用方法和注意細節 手動加載第三方類庫 由於第三發類庫沒有具體的命名空間,所以需要使用以下幾種方法手動導入 1.import方法 ...

  7. String StringBuilder 包裝類

    1. String 概述 程序中直接寫上雙引號的字符串就在字符串常量池中,new的不在池當中 java6之前常量池在方法區,java7以後將字符串常量池放在堆中 因為字符串是對象,應該在堆中 相同的字 ...

  8. MFC之幾類消息的區別

    1.ON_COMMAND與ON_UPDATE_COMMAND_UI 開發MFC程序,給菜單子項添加消息處理函數時,會碰到ON_COMMAND和ON_UPDATE_COMMAND_UI兩個消息. ON_ ...

  9. Windows 安裝補丁的另外一種方法

    Windows的補丁安裝時經常出現异常提示: 如果安裝不上的話 可以使用dism的方式來進行安裝: 具體方法: 1. 將補丁包 一般為msu 或者是exe文件,改成rar後綴,並且解壓縮,獲取cab文 ...

  10. BZOJ2863[SHOI2012]魔法樹——樹鏈剖分+線段樹

    題目描述 輸入 輸出 樣例輸入 4 0 1 1 2 2 3 4 Add 1 3 1 Query 0 Query 1 Query 2 樣例輸出 3 3 2   樹鏈剖分模板題,路徑修改子樹查詢,注意節點 ...