面試官:JDBC 是如何打破雙親委派模型的?
在 Java 中,JDBC(Java Database Connectivity)是用于與數(shù)據(jù)庫(kù)進(jìn)行交互的標(biāo)準(zhǔn) API。JDBC 并不是直接打破雙親委派模型,而是通過(guò) SPI 機(jī)制加上線程上下文類加載器(Thread.currentThread().getContextClassLoader())的方式,繞過(guò)了傳統(tǒng)的類加載委托機(jī)制。這種設(shè)計(jì)既保留了雙親委派模型的核心優(yōu)勢(shì),又實(shí)現(xiàn)了對(duì)第三方 JDBC 驅(qū)動(dòng)程序的動(dòng)態(tài)加載。
1. JDBC 打破雙親委派模型的緣由
在 Java 中,DriverManager 是 JDBC 的核心類,用于管理 JDBC 驅(qū)動(dòng)程序并建立數(shù)據(jù)庫(kù)連接。但是DriverManager 所在的類加載器(擴(kuò)展類加載器或啟動(dòng)類加載器)通常無(wú)法直接加載第三方 JDBC 驅(qū)動(dòng)程序(例如 MySQL 的 com.mysql.cj.jdbc.Driver),因?yàn)檫@些驅(qū)動(dòng)程序是由應(yīng)用程序類加載器加載的。
為了能夠加載第三方 JDBC 驅(qū)動(dòng)程序,JDBC 會(huì)使用線程上下文類加載器(Thread.currentThread().getContextClassLoader())來(lái)加載驅(qū)動(dòng)程序。線程上下文類加載器是一個(gè)特殊機(jī)制,它允許線程在其上下文中切換類加載器。通過(guò)這種方式,JDBC 可以繞過(guò)傳統(tǒng)的雙親委派模型,使用特定的類加載器(通常是應(yīng)用程序類加載器)來(lái)加載 JDBC 驅(qū)動(dòng)程序。
2. 線程上下文類加載器+SPI 機(jī)制
- Java 提供了 SPI 機(jī)制來(lái)發(fā)現(xiàn)和加載服務(wù)實(shí)現(xiàn)。對(duì)于 JDBC,各個(gè)數(shù)據(jù)庫(kù)廠商實(shí)現(xiàn) java.sql.Driver 接口,并將實(shí)現(xiàn)類的全限定名配置在 META - INF/services/java.sql.Driver 文件中。下面是MySQL驅(qū)動(dòng)中的SPI配置和Oracle驅(qū)動(dòng)中的SPI配置
image.png
image.png
- 當(dāng)應(yīng)用程序需要使用 JDBC 驅(qū)動(dòng)時(shí),核心類庫(kù)中的 DriverManager 類負(fù)責(zé)加載驅(qū)動(dòng)。DriverManager 使用線程上下文類加載器( Thread.currentThread().getContextClassLoader() )來(lái)加載 JDBC 驅(qū)動(dòng)實(shí)現(xiàn)類。
- 線程上下文類加載器通常是應(yīng)用程序類加載器( AppClassLoader ),它可以加載應(yīng)用程序的類路徑下的類,包括各個(gè)數(shù)據(jù)庫(kù)廠商提供的 JDBC 驅(qū)動(dòng)實(shí)現(xiàn)類。這就打破了雙親委派模型中類加載器自下而上委派的規(guī)則,因?yàn)樗皇菑膯?dòng)類加載器開(kāi)始查找驅(qū)動(dòng)類,而是從應(yīng)用程序類加載器開(kāi)始查找,使得數(shù)據(jù)庫(kù)廠商的 JDBC 驅(qū)動(dòng)類能夠被正確加載。
通過(guò)這種方式,JDBC 打破了雙親委派模型,允許第三方 JDBC 驅(qū)動(dòng)程序由應(yīng)用程序類加載器加載,而 DriverManager 仍然由擴(kuò)展類加載器加載。