2010-11-17 68 views
35

我剛剛瞭解到java.sql package。它使用Class.forName()動態加載延伸DriverManager的驅動程序。 然後我們使用DriverManager.getConnection()方法得到連接。Class.forName()如何工作?

那麼整個事情是如何工作的?
DriverManager類如何知道如何在不使用實際驅動程序的類名的情況下獲得連接。

也可以,我們使用的Class.forName()的自定義應用程序......如果這是一個例子,我會很高興的解釋。

+0

另見http://stackoverflow.com/a/8053125/632951 – Pacerier 2014-08-24 22:44:07

回答

51

Class.forName簡單地裝入類,包括運行它的靜態初始化,這樣的:

class Foo { 
    static { 
     System.out.println("Foo initializing"); 
    } 
} 

public class Test { 
    public static void main(String [] args) throws Exception { 
     Class.forName("Foo"); 
    } 
} 

所有你在談論的過程的其餘部分是JDBC特有的。驅動程序 - 實現Driver,它不擴展DriverManager - 只需使用DriverManager.registerDriver註冊適當的實例。然後,當DriverManager需要爲特定連接字符串查找驅動程序時,它會依次在每個註冊的驅動程序上調用acceptsURL,直到有人說「是的,我可以作爲該連接的驅動程序」。

請注意,這種註冊驅動程序的方式是相當老式的 - 查看DriverManager的文檔以獲取數據源的更多現代方式。

+0

我的查詢是從第1行解決您接聽先生。 – Simmant 2014-06-20 05:58:35

+2

@Jon很好的例子,但'Class.forName(「com.mysql.jdbc.Driver」)的'com.mysql.jdbc.Driver'類在哪裏''因爲我試圖導入com.mysql.jdbc.Driver '仍然無效 – UnKnown 2015-10-18 10:32:00

+2

@UnKnown:它在MySQL jar文件中。但是你不需要導入它 - 你通常讓JDBC直接初始化它,並且你只能在你的代碼中引用JDBC類。 – 2015-10-18 12:09:43

12

Class.forName(..)加載並初始化目標類。這又意味着,靜態初始化塊調用(在static { .. }定義的代碼

如果你看一下,例如,MySQL的驅動程序,在靜態塊司機正在註冊:DriverManager.registerDriver(new Driver());

可以省略Class.forName(..)並自己註冊驅動程序,如果你能「負擔」MySQL驅動程序的編譯時間依賴性。

也就是說,使用Class.forName(..)初始化應用程序中的類很少有用,因爲編譯時間依賴性是這不是一個問題。

還要注意的是Class.forName(..)is no longer required爲JDBC因爲版本4.使用service provider機制可以指示驅動程序管理器通過什麼系統屬性加載。

3

爲什麼Class.forName()經常在SQL實例中提到的原因,是因爲沒有特效的告訴JDBC的DriverManager 如何映射提供給真正的驅動程序的JDBC URL。

E.g. 「mysql」應映射到給定的MySQL類,「thin」映射到Oracle類,「as400」映射到DB2/400類。

通過顯式加載類,這允許運行在DriverManager中註冊自己的類中的代碼。

這些天的魔鉤子都存在允許JVM來自動發現驅動程序(如果有足夠的新的),所以調用是多餘的,但出於習慣許多仍然使用它。

4

當我們使用new操作符創建一個類的instace,它做了兩件事

  1. 加載類到內存中,如果沒有加載它 - 這意味着創建內存之類的表示從.class文件中刪除,以便可以創建一個實例。這包括初始化靜態變量(解析該類)
  2. 創建該類的實例並存儲對該變量的引用。

Class.forName只做第一件事。 它將類加載到內存中,並將該引用作爲Class的實例返回。如果我們想創建一個實例,那麼我們可以調用該類的newInstance方法。這將調用默認的構造函數(無參數構造函數)。 請注意,如果默認構造函數不可訪問,則newInstance方法將拋出一個IllegalAccessException。如果這個類是一個抽象類或接口,或者它沒有默認構造函數,那麼它將拋出InstantiationException。如果在解決該課程時出現任何異常情況,則會拋出一個ExceptionInInitializerError

如果未定義默認構造函數,那麼我們必須使用反射API調用defiend構造函數。

但是,Class.forName的主要優點是,它可以接受類名作爲String參數。所以我們可以動態地傳遞類名。但是,如果我們使用new運算符創建類的實例,則不能動態更改類名稱。

Class.forName() inturn將調用調用方ClassLoader的loadClass方法(調用Class.forName的類的ClassLoder)。

默認情況下,Class.forName()解析該類。這意味着,初始化該類中的所有靜態變量。 同樣可以使用過載方法Class.forName(String name,boolean initialize,ClassLoader loader)

使用Class.forName()加載jdbc驅動程序的主要原因是,驅動程序可以動態更改。 在靜態塊中,所有的驅動程序都將創建一個自己的實例,並使用DriverManager.registerDriver()方法向DriverManager註冊該類。由於Class.forName(String className)默認解析類,它會初始化靜態初始化器。 所以當我們調用Class.forName("com.sun.jdbc.odbc.JdbcOdbcDriver"), Driver類會被加載,實例化,並與DriverManager的

註冊

所以,如果你正在使用新的操作你必須做以下的事情。
代碼:

Driver drv = new com.sun.jdbc.odbc.JdbcOdbcDriver(); 
DriverManager.registerDriver(drv);