Class.forName(String driverClassName)加載JDBC驅動程序時,底層都做了些什么???


實質是:

 

Class.forName“com.mysql.jdbc.Driver”)是 強制JVMcom.mysql.jdbc.Driver這個類加載入內存,並將其注冊到DriverManager類,然后根據DriverManager.getConnection(url,user,pwd)中的url找到相應的驅動類,最后調用該該驅動類的connect(url, info)來獲得connection對象。

 

JDBC的驅動管理機制的 具體底層代碼分析如下:

    

   

1.     分析JDBC的驅動程序管理部分的實現代碼:
    
        在 JDBC的層次上,sun主要定義了1個接口Driver和兩個類:DirverManagerDriverInfo。每個JDBC驅動程序必須實現 Driver接口(在MySqlConnector/J驅動中,這個叫做com.mysql.jdbc.Driver)。而DriverManager 則負責管理所有的Driver對象,包含注冊Driver;選擇合適的Driver來建立到某個數據庫的連接;以及進行一些Driver的信息管理等。 DriverInfo非常簡單,用於保存Driver的信息,只有3個成員變量,DriverDriverClass和 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連接數據庫的速度很慢, 是不是和這種實現方式有關聯呢?懷着這個問題,本人下載了MySqlConnector/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代碼,調用了DriverManagerregisterDriver方法。這其實就解釋了 Class.forName“com.mysql.jdbc.Driver”)能夠完成MySql驅動注冊的問題。因為forName會導致這段 static代碼被調用,從而間接調用了registerDriver,完成注冊過程。
        
        com.mysql.jdbc.Driver com.mysql.jdbc.NonRegisteringDriver繼承而來,實際上是NonReisteringDriver完成了 java.sql.Driver接口的實現工作。轉移目標,分析NonRegisteringDriverconnect函數。

        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函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM