雙親委派模型的破壞(JDBC例子)

雙親委派模型的破壞

雙親委託模型並不是一個強制性的約束模型,而是java設計者推薦給開發者的淚加載器實現方式。但是雙親委託模型存在着缺陷,它雖然解決了各個類加載器的基礎類的統一問題,基礎被稱爲基礎,就是因爲他們總是被用戶代碼調用,但是如果基礎類又要調用回用戶代碼呢?那麼在就會使用基礎類的類加載器(啓動類加載器)去加載用戶的代碼,而啓動類加載器是加載java_home\lib目錄下的。而用戶代碼都是保存在classpath下,根本就不可能加載到啊=。=其中這個方面最典型的就是jdbc對於雙親委派模型的破壞了。

JDBC對雙親委派模型的破壞

先來看一段代碼的描述:

public class JdbcTest {
    public static void main(String[] args){
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "awakeyo");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        System.out.println(connection.getClass().getClassLoader());
        System.out.println(Thread.currentThread().getContextClassLoader());
        System.out.println(Connection.class.getClassLoader());
    }

}

輸出結果:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NIpGCJHN-1589683814595)(/Users/awakeyoyoyo/Library/Application Support/typora-user-images/image-20200517101411917.png)]

很明顯我們發現Connection類的類加載器是啓動類加載器(應用類類加載器委託給啓動類加載器、雙清委託模型),因爲它輸出的是null,而第一第二行輸出語句輸出的是appClassloader應用程序類加載器.

因此,我們證明了java.sql.Connection是委託給應用加載器加載,但其子類的getConnection卻可以加載不在lib下的類???。根據類加載機制,當被裝載的類引用了另外一個類的時候,虛擬機就會使用裝載該類的類裝載器裝載被引用的類,這不就是要用應用類加載器加載classpath下的包???這,難爲了吧,這違反了根據其定義的委託模型。那麼他是如何做到的呢?

原因在於:getConnection方法

private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
              //獲取線程上下爲類加載器
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }
        println("DriverManager.getConnection(\"" + url + "\")");
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
          		//isDriverAllowed對於mysql連接jar進行加載
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    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());
            }

        }
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }

獲取線程上下爲類加載器

callerCL = Thread.currentThread().getContextClassLoader();

isDriverAllowed對於mysql連接jar進行加載

isDriverAllowed(aDriver.driver, callerCL))

isDriverAllowed將傳入的Thread.currentThread().getContextClassLoader();拿到的應用類加載器用去Class.forName加載我們mysql連接jar,這樣子就可以加載到我們自己的mtsql連接jar

    private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
        boolean result = false;
        if(driver != null) {
            Class<?> aClass = null;
            try {
                aClass =  Class.forName(driver.getClass().getName(), true, classLoader);
            } catch (Exception ex) {
                result = false;
            }

             result = ( aClass == driver.getClass() ) ? true : false;
        }

        return result;
    }

爲什麼必須要破壞?

DriverManager::getConnection 方法需要根據參數傳進來的 url 從所有已經加載過的 Drivers 裏找到一個合適的 Driver 實現類去連接數據庫.
Driver 實現類在第三方 jar 裏, 要用 AppClassLoader 加載. 而 DriverManager 是 rt.jar 裏的類, 被 BootstrapClassLoader 加載, DriverManager 沒法用 BootstrapClassLoader 去加載 Driver 實現類(不再lib下), 所以只能破壞雙親委派模型, 用它下級的 AppClassLoader 去加載 Driver.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章