前言
上篇文章分析了通過SPI技術如何來加載JDBC驅動連接堵泽。然后本篇繼續(xù)寫獲得連接之后的一些事。
JDBC驅動加載
image.png
上篇文章寫道恢总,當JDBC驅動被加載的時候會執(zhí)行這行代碼迎罗,將當前的Driver注冊到DriverManager中。而這段代碼的執(zhí)行主要是因為Driver的加載與初始化片仿。Driver的加載與初始化 主要是因為SPI的加載纹安。
image.png
當走到上述紅框內的代碼的時候會觸發(fā)c.newInstance,而c就是加載的Driver砂豌,因此會觸發(fā)其加載以及初始化钻蔑。好了,接著回到第一張圖奸鸯。將Driver注冊到DriverManager之后做了哪些事咪笑。點進去。
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) {
// 可以看到其被封裝成了一個DriverInfo加入到了registerDrivers中娄涩,registeredDrivers是一個線程安全的list.
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
然后回到下面這行代碼
Connection connection = DriverManager.getConnection(url, "root", "chusen");
開始獲得數(shù)據(jù)庫連接窗怒,跟進去,最終會跟到DriverManager中比較長的getConnection方法中蓄拣,下面截取核心代碼
// 遍歷剛才存進去的Driver
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
// 使用Driver內的connect方法來進行獲得一個數(shù)據(jù)庫連接
println(" trying " + aDriver.driver.getClass().getName());
// 繼續(xù)跟到connect內部扬虚,看其做了哪些事
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());
}
}
然后繼續(xù)向下跟進,走到了Driver的父類NonRegisteringDriver中進行連接獲得一條Connection球恤。
// 核心代碼 ---> 根據(jù)不同的類型獲得不同的連接
switch (conStr.getType()) {
case SINGLE_CONNECTION:
// 跟進到這里面去
return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost());
case LOADBALANCE_CONNECTION:
return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl) conStr);
case FAILOVER_CONNECTION:
return FailoverConnectionProxy.createProxyInstance(conStr);
case REPLICATION_CONNECTION:
return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl) conStr);
default:
return null;
}
public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
// 其直接new 了一個Connection的實現(xiàn)類辜昵,跟進去看做了哪些事
return new ConnectionImpl(hostInfo);
}
點進去可以看到大量的初始化操作,這里就不進行截圖了咽斧,仍然截取關鍵代碼堪置。
// 其中最重要的就是這一行代碼,來進行創(chuàng)建一條連接张惹。 繼續(xù)往下跟舀锨,看如何創(chuàng)建,創(chuàng)建之后做了哪些事
createNewIO(false);
unSafeQueryInterceptors();
AbandonedConnectionCleanupThread.trackConnection(this, this.getSession().getNetworkResources());
最終會跟到 connectOneTryOnly() 方法宛逗。下面為其源碼坎匿。
JdbcConnection c = getProxy();
// 進行連接MySQL服務器,拿著服務器信息,用戶名替蔬,密碼告私,還有要連接的數(shù)據(jù)庫進行連接。之后在服務器端 也可以看到連接建立了承桥。
this.session.connect(this.origHostInfo, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000, c);
// save state from old connection
// 拿到一些連接信息德挣,這里的getAutoCommit實際上是一個默認值,為true快毛,默認為自動提交。
// 在進行事務操作時番挺,我們要將其設置為false唠帝,表示手動提交。
boolean oldAutoCommit = getAutoCommit();
// 隔離級別
int oldIsolationLevel = this.isolationLevel;
boolean oldReadOnly = isReadOnly(false);
String oldDb = getDatabase();
this.session.setQueryInterceptors(this.queryInterceptors);
// Server properties might be different from previous connection, so initialize again...
// 初始化連接信息玄柏,從服務器端進行加載的襟衰。
initializePropsFromServer();
if (isForReconnect) {
// Restore state from old connection
setAutoCommit(oldAutoCommit);
setTransactionIsolation(oldIsolationLevel);
setDatabase(oldDb);
setReadOnly(oldReadOnly);
}
初始化連接,其實到這里就已經結束了粪摘。到后面就是使用MySQL數(shù)據(jù)庫連接了瀑晒。下面總結下具體的流程,其實流程并不是很復雜徘意。其實主要就是DriverManger.getConnectioin()這一行代碼做了哪些事苔悦。
image.png
小結
1. 其實,連接被獲取之后椎咧,可以進行緩存 做成數(shù)據(jù)庫連接池玖详。
2. SPI,未動態(tài)擴展勤讽,提供了強有力的支撐蟋座。