Java中加載資料庫驅動的方式有幾種?背後的原理是什麼?

2020-12-11 IT技術專家孫鑫

1、Class.forName()加載

這是最常見的一種方式,也是從早期沿用下來的方式。下面看一下在這背後發生的事情。

Driver接口

先來了解下java.sql.Driver接口,java.sql.Driver是所有JDBC驅動程序需要實現的接口。這個接口是提供給資料庫廠商使用的,不同廠商實現該接口的類名是不同的,例如MySQL 8.x的JDBC驅動的類名是:com.mysql.cj.jdbc.Driver。

Driver接口接口中一個connect方法,用來建立到資料庫的連接,,該方法的籤名如下所示:

Connection connect(String url, Properties info) throws SQLException在程序中不需要直接去訪問這些實現了Driver接口的類,而是由驅動程序管理器去調用這些驅動。我們通過JDBC驅動程序管理器註冊每個驅動程序,使用驅動程序管理器類提供的方法來建立資料庫連接,而驅動程序管理器類的連接方法則調用驅動程序類的connect方法建立資料庫連接,如下圖所示:

JDBC驅動程序管理器與JDBC驅動程序通信

加載JDBC驅動是調用Class類的靜態方法forName,向其傳遞要加載的JDBC驅動的類名。在運行時,類加載器從CLASSPATH路徑中定位和加載JDBC驅動類。在加載驅動程序類後,需要註冊驅動程序類的一個實例。

DriverManager類是驅動程序管理器類,負責管理驅動程序,這個類中所有的方法都是靜態的。在DriverManager類中提供了registerDriver方法來註冊驅動程序類的實例,該方法的籤名如下所示:

public static void registerDriver(Driver driver) throws SQLException加載與註冊JDBC驅動

加載JDBC驅動是調用Class類的靜態方法forName,向其傳遞要加載的JDBC驅動的類名。在運行時,類加載器從CLASSPATH路徑中定位和加載JDBC驅動類。在加載驅動程序類後,需要註冊驅動程序類的一個實例。

DriverManager類是驅動程序管理器類,負責管理驅動程序,這個類中所有的方法都是靜態的。在DriverManager類中提供了registerDriver方法來註冊驅動程序類的實例,該方法的籤名如下所示:

public static void registerDriver(Driver driver) throws SQLException通常不需要我們親自去調用registerDriver方法來註冊驅動程序類的實例,因為實現Driver接口的驅動程序類都包含了靜態代碼塊,在這個靜態代碼塊中,會調用DriverManager.registerDriver方法來註冊自身的一個實例。當這個類被加載時(調用Class.forName),類加載器會執行該類的靜態代碼塊,從而註冊驅動程序類的一個實例。

下面我們看一下MySQL 8.0.20的驅動程序類對自身實例註冊的代碼片段。

MySQL 8.0.20 JDBC驅動程序類的代碼片段// com.mysql.cj.jdbc.Driver

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!");

}

}

...

}

可以看到Driver類所包含的靜態代碼塊是調用DriverManager類的靜態方法registerDriver來註冊Driver類的實例。

2、服務加載

JDBC API定義了Driver接口,不同的資料庫廠商給出該接口的實現,如果廠商遵循SPI機制,那麼就可以利用服務加載器來找到並加載Driver實現類。

用解壓縮軟體打開mysql-connector-java-8.0.20.jar,可以看到在METF-INF\services目錄下有一個java.sql.Driver文件,文件內容如下:

com.mysql.cj.jdbc.Driver

這正是MySQL的JDBC驅動的類名。

查看java.sql.DriverManager類的原始碼,可以發現在ensureDriversInitialized方法中,實現了JDBC驅動類的加載,如以下代碼所示。

DriverManager類的ensureDriversInitialized方法// java.sql.DriverManager

private static void ensureDriversInitialized() {

if (driversInitialized) {

return;

}

synchronized (lockForInitDrivers) {

if (driversInitialized) {

return;

}

String drivers;

try {

drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {

public String run() {

return System.getProperty(JDBC_DRIVERS_PROPERTY);

}

});

} catch (Exception ex) {

drivers = null;

}

AccessController.doPrivileged(new PrivilegedAction<Void>() {

public Void run() {

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("")) {

String[] driversList = drivers.split(":");

println("number of Drivers:" + driversList.length);

for (String aDriver : driversList) {

try {

println("DriverManager.Initialize: loading " + aDriver);

Class.forName(aDriver, true,

ClassLoader.getSystemClassLoader());

} catch (Exception ex) {

println("DriverManager.Initialize: load failed: " + ex);

}

}

}

driversInitialized = true;

println("JDBC DriverManager initialized");

}

}

熟悉的配方,熟悉的味道。只要mysql-connector-java-8.0.20.jar在類路徑中能夠找到,DriverManager就可以利用SPI機制找到並加載MySQL的JDBC驅動類。換句話說,對於支持SPI機制的JDBC驅動,可以不用調用Class.forName來加載驅動類。

3、系統變量

上述代碼中有如下一句代碼:

System.getProperty(JDBC_DRIVERS_PROPERTY);

JDBC_DRIVERS_PROPERTY是DriverManager類中定義的一個私有的靜態字符串常量,值為:jdbc.drivers。

還有一種設置JDBC驅動類名的方式就是利用系統屬性jdbc.drivers來設置,在執行程序時,使用-D選項給出JDBC驅動的類名,如下所示:

java -Djdbc.drivers=com.mysql.cj.jdbc.Driverch24.GetDBInfo

如果要設置多個JDBC驅動類名,以冒號(:)分隔即可。採用這種方式,也不需要調用Class.forName。這種方式並不常用。

由於歷史遺留原因,大多數的資料庫訪問程序中,仍然習慣使用Class.forName來加載JDBC驅動類。

我是專注於軟體開發和IT教育的孫鑫老師,喜歡我的文章歡迎關注、轉發、評論、點讚和收藏,我會經常與大家分享IT技術、程式語言的文章和教學視頻。目前已發布完整的《Vue.js從入門到實戰》教學視頻,正在發布《Java無難事》教學視頻。

Java無難事

#Java#

相關焦點

  • Java類隔離加載實現原理是什麼?
    Java類隔離加載實現原理是什麼? JVM 提供一個全局類加載器的設置接口,直接替換全局類加載器,但無法解決多個自定義類加載器同時存在的問題。然而JVM會選擇當前類的類加載器來加載所有該類的引用的類。類隔離技術是什麼?
  • JAVA專業術語面試100問
    Error與Exception有什麼區別?17、java中的throw 和 throws關鍵字有什麼區別?18、列舉幾個你了解的幾個常見的運行時異常?19、final, finally, finalize有什麼區別?
  • Java反射是什麼?看這篇絕對會了!
    天道有輪迴,蒼天...(淨會在這瞎bibi)在學習反射之前,先來了解正射是什麼。我們平常用的最多的 new 方式實例化對象的方式就是一種正射的體現。假如我需要實例化一個HashMap,代碼就會是這樣子。
  • 步進電機有哪幾種驅動方式
    如今,很多用戶都想知道步進電機哪家技術好,而提及步進電機的技術好壞,與其驅動方式也有很大關係,因為不同的驅動方式所具有的優勢和特點不同,促使不同驅動方式的步進電機具有的性能也有所差別。那麼步進電機有哪幾種驅動方式呢?
  • Java編程中基礎反射詳細解析
    有時會把這一整個流程統稱為類加載或類初始化。類加載指的是將類的class文件讀入內存中,並為之創建一個 java.lang.Class對象,也就是說程序使用任何類的時候,都會為其創建一個class對象。
  • 如何用Eclipse中插件SQLExplorer開發資料庫
    這樣,如果你下載了多個插件就可以如法炮製建立多個Link文件,想加載哪個插件就把哪個插件的Link文件放到%ECLIPSE_HOME%\links的目錄中即可,使用與管理都很方便,建議千萬不要放在默認的安裝目錄中;  3、如果你的%ECLIPSE_HOME%與此不同,請修改XYZ.link文件裡的路徑  刪除,關閉Eclipse
  • Java程式設計師必備基礎:Java代碼是怎麼運行的?
    java源文件編譯為class字節碼 類加載器把字節碼加載到虛擬機的方法區。 字節碼文件加載到虛擬機的方法區後,在程序運行過程,通過 class字節碼文件創建與其對應的對象信息 。 創建對象的方式有:new關鍵字,反射等。 Java堆內存是線程共享的區域,創建後的對象信息就保存在Java堆內存中。
  • Maven是什麼?它打包的三種方式?
    要想jar包能直接通過java -jar xxx.jar運行,需要滿足:1、要能加載到依賴包。使用Maven有以下幾種方法可以生成能直接運行的jar包,可以根據需要選擇一種合適的方法。2、在jar包中的META-INF/MANIFEST.MF中指定Main-Class,這樣才能確定程序的入口在哪裡;方法一:使用maven-assembly-plugin插件打包在pom.xml中配置:打包方式:mvn package assembly:single打包後會在target目錄下生成一個xxx-jar-with-dependencies.jar
  • Java經典面試題Spring是什麼 Spring框架入門詳解
    ApplicationContext外還有一個BeanFactory ,那麼他們有什麼區別呢?BeanFactory 是一種懶加載的方式,那麼當訪問的bean過多的時候伺服器壓力就變大了,所以beanFactory實際上是一種淘汰了的容器,而applicationContext更類似於一種緩存機制,所以它受眾更高,下面看一下列印結果可以看到User的toString方法已經打出,補充一下以上代碼我並沒有運行在java的main線程裡面,而是使用的junit
  • 你知道java反射機制中class.forName和classloader的區別嗎?
    前兩天頭條有朋友留言說使用class.forName找不到類,可以使用classloader加載。趁此機會總結一下,正好看到面試中還經常問到。一、類加載機制上面兩種加載類的方式說到底還是為了加載一個java類,因此需要先對類加載的過程進行一個簡單的了解。
  • Java 反射:框架設計的靈魂
    比如 C 語言;Java 嚴格來說也是編譯型語言,但又介於編譯型和解釋型之間;Java 不直接生成機器碼而是生成中間碼:編譯期間,是將源碼交給編譯器生成 class 文件(字節碼),這個過程中只做了翻譯的工作,並沒有把代碼放入內存運行;當進入運行期,字節碼才被 Java 虛擬機加載、解釋成機器語言並運行。
  • java軟體工程師的這些要求你有嗎?
    如今java是IT界數一數二的語言,許多程式設計師都想要成為java軟體工程師,那大家知道企業對於java軟體工程師的技術要求有哪些嗎?java軟體工程師的技術要求一.java技術要求:1.具有良好的Java語言基礎,面向對象編程基礎2.熟悉Struts、Hibernate、Spring等主流技術框架3.熟悉XML解析、Excel導出、文件上傳、發送E-mail等常見業務的實現二.資料庫技術要求1.掌握、運用SQLServer
  • Java反射機制深入詳解
    Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的 defineClass 方法自動構造的。1 Person p1 = new Person(); 2 //下面的這三種方式都可以得到字節碼 3 CLass c1 = Date.class(); 4 p1.getClass(); 5 //若存在則加載,否則新建,往往使用第三種,類的名字在寫源程序時不需要知道,到運行時再傳遞過來 6 Class.forName("java.lang.String");CLass.forName
  • 什麼是JAVA反射機制,詳細解讀JAVA面試的核心技術
    一、什麼叫Java反射機制?Java中的反射機制是指在運行狀態中,對於任意一個類,能夠動態獲取這個類中的屬性和方法;對於任意一個對象,都能夠任意調用它的屬性和方法。這種動態獲取類的信息以及動態調用對象方法的功能稱為Java的反射機制。
  • 徹底搞懂MyBatis插件原理及PageHelper原理
    MyBatis插件實現原理—目錄前言MyBatis中插件是如何實現的MyBatis插件的使用MyBatis插件實現原理插件的加載插件如何進行攔截攔截Executor對象其他對象插件解析插件執行流程假如一個對象被代理很多次PageHelper插件的使用PageHelper插件原理為什麼PageHelper只對startPage後的第一條select語句有效不通過插件能否改變
  • 阿里巴巴Java方向面試題匯總(含答案)
    面小易說:對於伺服器而言,在面試中可能並不會過多涉及,相對而言,面小易認為像是Liunx、Tomcat這些背後的原理可能更受面試官的青睞。五、GET,POST請求之間的區別?基礎知識:HTTP的請求格式如下。
  • Java反射初探 ——「當類也學會照鏡子」
    Class對象 所以我們今天要講的第一個內容是:  有別於平時使用的實例對象的——Class對象取得Class對象的三種方式 我們假設有這麼一個類叫MyClass:public class MyClass {  } 那麼取得該類對應Class對象的方法有三種
  • 資料庫是什麼?怎樣學習SQL Server資料庫?
    對於剛開始學習編程的新手來說,資料庫的概念相對來說也不是很好理解。這裡,我用通俗的語言說明一下什麼是資料庫。前提了解資料庫之前,首先要知道什麼是windows服務。windows服務可以在「計算機管理-服務和應用程式-服務」中找到。
  • Java編程領域你需要懂得技術名詞解釋與常用開源框架理解
    數據訪問中間件:解決應用訪問資料庫的共性問題。分布式系統:一定是有多個節點組成的系統,一般一個節點就是一臺伺服器,節點之間是相互連通協作的,這些連通的節點上部署了我們的組件,共同服務於一個大型系統。比如淘寶網,在對瀏覽器發送請求的背後,是一個大型的分布式系統為我們服務,整個系統有的負責請求處理,有的負責存儲,有的負責計算,最終通過相互的協作把請求的結果返回給瀏覽器,並呈現給我們。
  • Java中如何顯示不同時區的時間(原理詳解)
    Java中如何顯示不同時區的時間(原理詳解) 電子發燒友網 發表於 2019-01-01 14:58:00 在Java中,如何獲取不同時區的當前時間?