實質是:
Class.forName(“com.mysql.jdbc.Driver”)是 強制JVM將com.mysql.jdbc.Driver這個類加載入內存,並將其注冊到DriverManager類,然后根據DriverManager.getConnection(url,user,pwd)中的url找到相應的驅動類,最后調用該該驅動類的connect(url, info)來獲得connection對象。
JDBC的驅動管理機制的 具體底層代碼分析如下:
1. 分析JDBC的驅動程序管理部分的實現代碼:
在 JDBC的層次上,sun主要定義了1個接口Driver和兩個類:DirverManager和DriverInfo。每個JDBC驅動程序必須實現 Driver接口(在MySql的Connector/J驅動中,這個叫做com.mysql.jdbc.Driver)。而DriverManager 則負責管理所有的Driver對象,包含注冊Driver;選擇合適的Driver來建立到某個數據庫的連接;以及進行一些Driver的信息管理等。 DriverInfo非常簡單,用於保存Driver的信息,只有3個成員變量,Driver,DriverClass和 DriverClassName,意義非常明顯。
先看一下在DriverManager.java中的關鍵代碼:
private static java.util.Vector drivers = new java.util.Vector();
所有的Driver對象保存在一個Vector數組中。
注冊Driver的函數叫registerDriver,將需要注冊的Driver對象傳入即可:
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { if (!initialized) { //如果沒有初始化,則先初始化 initialize(); } DriverInfo di = new DriverInfo(); // 實際保存的不是Driver,而是一個DriverInfo對象,但是DriverInfo的其它成員完全可以由Driver推導出來,所以個人覺得 DriverInfo對象可有可無,直接使用Driver應該就可以了。 di.driver = driver; di.driverClass = driver.getClass(); di.driverClassName = di.driverClass.getName(); drivers.addElement(di); //將DriverInfo對象添加到數組中 println("registerDriver: " + di); }
在一個類加載入內存的時候,類中的靜態初始化過程會執行,這樣就完成了驅動程序的注冊過程。然后重點看一下建立數據庫連接的代碼,在getConnection函數中,省略了一些非關鍵代碼:
private static synchronized Connection getConnection( String url, java.util.Properties info, ClassLoader callerCL) throws SQLException { SQLException reason = null; //輪詢所有的DriverInfo對象。 for (int i = 0; i < drivers.size(); i++) { DriverInfo di = (DriverInfo)drivers.elementAt(i); try { //使用DriverInfo中的Driver對象去做實際的連接數據庫的工作 Connection result = di.driver.connect(url, info); if (result != null) { // Success! println("getConnection returning " +&bsp;di); return (result); //一旦成功連接,直接返回Connection對象,然后推出 } } catch (SQLException ex) { ... } } //這就是經常看到的出錯信息--找不到合適的驅動程序。 println("getConnection: no suitable driver"); throw new SQLException("No suitable driver", "08001"); }
由 上面的getConnection函數可以看到,真正實現數據庫連接的是Driver對象的connect函數。而且可以看到,由於 DriverManager.getConnection使用的是一種輪詢的方式,注冊的驅動程序越多,連接速度會越慢。JDBC連接數據庫的速度很慢, 是不是和這種實現方式有關聯呢?懷着這個問題,本人下載了MySql的Connector/J驅動包,開始分析其connect函數的實現。
2. 分析MySql的注冊和建立連接部分的代碼:
打開MySql的源碼包,首先分析其Driver類的實現。發現Driver類的實現非常簡單,
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { // Required for Class.forName().newInstance() }
可 以看到,有一段static代碼,調用了DriverManager的registerDriver方法。這其實就解釋了 Class.forName(“com.mysql.jdbc.Driver”)能夠完成MySql驅動注冊的問題。因為forName會導致這段 static代碼被調用,從而間接調用了registerDriver,完成注冊過程。
com.mysql.jdbc.Driver 從com.mysql.jdbc.NonRegisteringDriver繼承而來,實際上是NonReisteringDriver完成了 java.sql.Driver接口的實現工作。轉移目標,分析NonRegisteringDriver的connect函數。
NonRegisteringDriver.connect的實現也比較簡單,正合我意:
public java.sql.Connection connect(String url, Properties info)
throws SQLException {
//1. 分析傳入的連接字符串.
if ((props = parseURL(url, info)) == null) { return null; } try { //2. 建立一個Connection對象完成實際的數據庫連接工作 Connection newConn = new com.mysql.jdbc.Connection(host(props), port(props), props, database(props), url, this); return newConn; }
非 常簡單,先parseURL,然后使用Connection去建立連接。parseURL只是簡單的字符串分析,主要是分析傳入的連接字符串是否滿足 “jdbc:mysql://host:port/database“的格式,如果不滿足,直接返回null,然后由DriverManager去試驗下 一個Driver。如果滿足,則建立一個Connection對象建立實際的數據庫連接,這不是本人關注的問題,源碼分析就此打住。
由此可見1中的問題答案是:DriverManager的輪詢查詢注冊的Driver對象的工作方式所帶來的性能代價並不是很大,主工作量只是parseURL函數。