解讀MySQL驅動載入邏輯
我們很早之前就知道最基礎的JDBC編寫,先執行Class.forName方法,載入MySQL驅動。但是為什麼載入過驅動後,後續的介面層的調用就會自動切換到MySQL的相關代碼去執行呢?(作者:高元)
常見jdbc編寫
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/gdjt_db","root","root");
ps = connection.prepareStatement("select * from sys_user where id = ? ");
ps.setString(1, "1");
rs = ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if(rs != null)rs.close();
}catch (Exception e){}
try {
if(ps != null)ps.close();
}catch (Exception e){}
try {
if(connection != null)connection.close();
}catch (Exception e){}
}
當然,我們這裡使用MySQL來演示,所以需要導入相關依賴
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
以上是最簡單的JDBC實現,下面我們主要查看Class.forName("com.mysql.jdbc.Driver");具體的執行邏輯。
查看第一步具體做了什麼?
Class.forName("com.mysql.jdbc.Driver");
1、我們先看Class.forName
@CallerSensitive
public static Class<?> forName(String className) throws ClassNotFoundException {
/** 獲取調用者Class */
Class<?> caller = Reflection.getCallerClass();
/**
* 1、獲取調用者的類載入器
* 2、執行類載入
* 3、執行初始化方法(initialize = true)
*/
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
具體內容見代碼中的註解,可以發現,Class.forName主要就是類載入,載入了類com.mysql.jdbc.Driver
2、接著看mysql的Driver源碼實現
/**
* java.sql.Driver 是jdk封裝的驅動介面
*/
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
/** 上一步驟類載入器載入類時,執行了初始化方法,就是執行這裡的static中的代碼 */
static {
try {
/** 將當前mysql的Driver註冊到DriverManager中 */
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can"t register driver!");
}
}
}
具體註冊代碼如下:
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
if(driver != null) {
// 將mysql的driver封裝成DriverInfo對象,並傳入registeredDrivers鏈表中。
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
throw new NullPointerException();
}
}
其中 registeredDrivers 是全局的靜態變數
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
這樣,無論在哪裡,都可以通過registeredDrivers來獲取當前的驅動。
3、繼續看Connection如何獲取到對應的連接的
Connection conn = DriverManager.getConnection(url,username,password);
我們進入getConnection查看,代碼稍微長點,不過沒關係,我們只要看如下一行代碼即可
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
...
// 看到了熟悉的身影 registeredDrivers
for(DriverInfo aDriver : registeredDrivers) {
...
// 從registeredDrivers獲取之前存入的mysql對應的Driver
Connection con = aDriver.driver.connect(url, info);
...
}
...
}
接著執行 driver.connect( ) 方法,之前我們看過 Driver 的源碼
Driver extends NonRegisteringDriver implements java.sql.Driver
所以 driver.connect 的方法,不是在 Driver 中就是在 NonRegisteringDriver 中。 結果我們在 NonRegisteringDriver 中如願以償的找到了connect方法(118行左右)
public Connection connect(String url, Properties info) throws SQLException {
...
// 重要代碼在這裡,獲取具體的連接實例
com.mysql.jdbc.Connection newConn = ConnectionImpl.getInstance(this.host(props), this.port(props), props, this.database(props), url);
return newConn;
...
}
接下來的步驟就不一一演示了。
總結
- 通過類載入器載入Driver並初始化,將Driver添加到DriverManager的registeredDrivers中;
- 通過registeredDrivers獲取到Driver;
- 調用Driver的connect方法,獲取Mysql中的MySQLConnection;
※為什麼我們做分散式的使用都是用Redis?
※區塊鏈教程之基礎開發通過介面查詢幣種提幣情況bch
TAG:程序員小新人學習 |