聊聊JDBC是如何破壞雙親委派模型的

資源來源《深入理解Java虛擬機(jī)》

雙親委派模型的第一次“被破壞”其實(shí)發(fā)生在雙親委派模型出現(xiàn)之前--即JDK1.2發(fā)布之前匙瘪。由于雙親委派模型是在JDK1.2之后才被引入的狞贱,而類加載器和抽象類java.lang.ClassLoader則是JDK1.0時候就已經(jīng)存在雀彼,面對已經(jīng)存在 的用戶自定義類加載器的實(shí)現(xiàn)代碼埃仪,Java設(shè)計(jì)者引入雙親委派模型時不得不做出一些妥協(xié)沙绝。為了向前兼容捌归,JDK1.2之后的java.lang.ClassLoader添加了一個新的proceted方法findClass()亿絮,在此之前告喊,用戶去繼承java.lang.ClassLoader的唯一目的就是重寫loadClass()方法,因?yàn)樘摂M在進(jìn)行類加載的時候會調(diào)用加載器的私有方法loadClassInternal()派昧,而這個方法的唯一邏輯就是去調(diào)用自己的loadClass()黔姜。JDK1.2之后已不再提倡用戶再去覆蓋loadClass()方法,應(yīng)當(dāng)把自己的類加載邏輯寫到findClass()方法中蒂萎,在loadClass()方法的邏輯里秆吵,如果父類加載器加載失敗,則會調(diào)用自己的findClass()方法來完成加載五慈,這樣就可以保證新寫出來的類加載器是符合雙親委派模型的纳寂。
雙親委派模型的第二次“被破壞”是這個模型自身的缺陷所導(dǎo)致的,雙親委派模型很好地解決了各個類加載器的基礎(chǔ)類統(tǒng)一問題(越基礎(chǔ)的類由越上層的加載器進(jìn)行加載)泻拦,基礎(chǔ)類之所以被稱為“基礎(chǔ)”毙芜,是因?yàn)樗鼈兛偸亲鳛楸徽{(diào)用代碼調(diào)用的API。但是争拐,如果基礎(chǔ)類又要調(diào)用用戶的代碼腋粥,那該怎么辦呢。
這并非是不可能的事情架曹,一個典型的例子便是JNDI服務(wù)隘冲,它的代碼由啟動類加載器去加載(在JDK1.3時放進(jìn)rt.jar),但JNDI的目的就是對資源進(jìn)行集中管理和查找绑雄,它需要調(diào)用獨(dú)立廠商實(shí)現(xiàn)部部署在應(yīng)用程序的classpath下的JNDI接口提供者(SPI, Service Provider Interface)的代碼展辞,但啟動類加載器不可能“認(rèn)識”之些代碼,該怎么辦万牺?
為了解決這個困境罗珍,Java設(shè)計(jì)團(tuán)隊(duì)只好引入了一個不太優(yōu)雅的設(shè)計(jì):線程上下文件類加載器(Thread Context ClassLoader)。這個類加載器可以通過java.lang.Thread類的setContextClassLoader()方法進(jìn)行設(shè)置杏愤,如果創(chuàng)建線程時還未設(shè)置靡砌,它將會從父線程中繼承一個已脓;如果在應(yīng)用程序的全局范圍內(nèi)都沒有設(shè)置過珊楼,那么這個類加載器默認(rèn)就是應(yīng)用程序類加載器。了有線程上下文類加載器度液,JNDI服務(wù)使用這個線程上下文類加載器去加載所需要的SPI代碼厕宗,也就是父類加載器請求子類加載器去完成類加載動作画舌,這種行為實(shí)際上就是打通了雙親委派模型的層次結(jié)構(gòu)來逆向使用類加載器,已經(jīng)違背了雙親委派模型已慢,但這也是無可奈何的事情曲聂。Java中所有涉及SPI的加載動作基本上都采用這種方式,例如JNDI,JDBC,JCE,JAXB和JBI等佑惠。

第一種比較簡單朋腋,這里就不說啦。

關(guān)于第二種膜楷,看看JDBC中是怎么實(shí)現(xiàn)的吧

首先旭咽,理解一下為什么JDBC需要破壞雙親委派模式,原因是原生的JDBC中Driver驅(qū)動本身只是一個接口赌厅,并沒有具體的實(shí)現(xiàn)穷绵,具體的實(shí)現(xiàn)是由不同數(shù)據(jù)庫類型去實(shí)現(xiàn)的。例如特愿,MySQL的mysql-connector-.jar中的Driver類具體實(shí)現(xiàn)的仲墨。 原生的JDBC中的類是放在rt.jar包的,是由啟動類加載器進(jìn)行類加載的揍障,在JDBC中的Driver類中需要動態(tài)去加載不同數(shù)據(jù)庫類型的Driver類目养,而mysql-connector-.jar中的Driver類是用戶自己寫的代碼,那啟動類加載器肯定是不能進(jìn)行加載的毒嫡,既然是自己編寫的代碼混稽,那就需要由應(yīng)用程序啟動類去進(jìn)行類加載。于是乎审胚,這個時候就引入線程上下文件類加載器(Thread Context ClassLoader)匈勋。有了這個東西之后,程序就可以把原本需要由啟動類加載器進(jìn)行加載的類膳叨,由應(yīng)用程序類加載器去進(jìn)行加載了洽洁。下面看看JDBC中是怎么去應(yīng)用的呢

//callerCL為空的時候,其實(shí)說明這個ClassLoader是啟動類加載器菲嘴,但是這個啟動類加載并不能識別rt.jar之外的類饿自,這個時候就把callerCL賦值為Thread.currentThread().getContextClassLoader();也就是應(yīng)用程序啟動類

private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        //callerCL為空的時候,其實(shí)說明這個ClassLoader是啟動類加載器龄坪,但是這個啟動類加載并不能識別rt.jar之外的類昭雌,這個時候就把callerCL賦值為Thread.currentThread().getContextClassLoader();也就是應(yīng)用程序啟動類
        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 + "\")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            //繼續(xù)看這里 
            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 we got here nobody could connect.
        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");
    }

    private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
        boolean result = false;
        if(driver != null) {
            Class<?> aClass = null;
            try {
                //這一步會對類進(jìn)行初始化的動作,而初始化之前自然也要進(jìn)行的類的加載工作
                aClass =  Class.forName(driver.getClass().getName(), true, classLoader);
            } catch (Exception ex) {
                result = false;
            }

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

        return result;
    }

希望我的理解能給你帶來幫助健田,如有錯誤的地方請指正烛卧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妓局,隨后出現(xiàn)的幾起案子总放,更是在濱河造成了極大的恐慌呈宇,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件局雄,死亡現(xiàn)場離奇詭異甥啄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)炬搭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門蜈漓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宫盔,你說我怎么就攤上這事迎变。” “怎么了飘言?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵衣形,是天一觀的道長。 經(jīng)常有香客問我姿鸿,道長谆吴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任苛预,我火速辦了婚禮句狼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘热某。我一直安慰自己腻菇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布昔馋。 她就那樣靜靜地躺著筹吐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秘遏。 梳的紋絲不亂的頭發(fā)上丘薛,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機(jī)與錄音邦危,去河邊找鬼洋侨。 笑死,一個胖子當(dāng)著我的面吹牛倦蚪,可吹牛的內(nèi)容都是我干的希坚。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼陵且,長吁一口氣:“原來是場噩夢啊……” “哼裁僧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤锅知,失蹤者是張志新(化名)和其女友劉穎播急,沒想到半個月后脓钾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體售睹,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年可训,在試婚紗的時候發(fā)現(xiàn)自己被綠了昌妹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡握截,死狀恐怖飞崖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谨胞,我是刑警寧澤固歪,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站胯努,受9級特大地震影響牢裳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叶沛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一蒲讯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灰署,春花似錦判帮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肴茄,卻和暖如春偎痛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背独郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工踩麦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氓癌。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓谓谦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贪婉。 傳聞我的和親對象是個殘疾皇子反粥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容