1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        秒懂雙親委派機(jī)制

        共 5863字,需瀏覽 12分鐘

         ·

        2024-08-14 14:21

        ?? 歡迎加入小哈的星球,你將獲得: 專屬的項(xiàng)目實(shí)戰(zhàn) / 1v1 提問 / Java 學(xué)習(xí)路線 / 學(xué)習(xí)打卡 / 每月贈書 / 社群討論

        • 新項(xiàng)目:《從零手?jǐn)]:仿小紅書(微服務(wù)架構(gòu))》 正在持續(xù)爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 點(diǎn)擊查看項(xiàng)目介紹;
        • 《從零手?jǐn)]:前后端分離博客項(xiàng)目(全棧開發(fā))》 2期已完結(jié),演示鏈接:http://116.62.199.48/;

        截止目前,累計(jì)輸出 53w+ 字,講解圖 2330+ 張,還在持續(xù)爆肝中.. 后續(xù)還會上新更多項(xiàng)目,目標(biāo)是將 Java 領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM 即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),解鎖全部項(xiàng)目,已有1900+小伙伴加入

        前言

        雙親委派機(jī)制是Java中非常重要的類加載機(jī)制,它保證了類加載的完整性和安全性,避免了類的重復(fù)加載。

        這篇文章就跟大家一起聊聊,Java中類加載的雙親委派機(jī)制到底是怎么回事,有哪些破壞雙親委派機(jī)制的案例,為什么要破壞雙親委派機(jī)制,希望對你會有所幫助。

        1 為什么要雙親委派機(jī)制?

        我們的Java在運(yùn)行之前,首先需要把Java代碼轉(zhuǎn)換成字節(jié)碼,即class文件。

        然后JVM需要把字節(jié)碼通過一定的方式加載到內(nèi)存中的運(yùn)行時(shí)數(shù)據(jù)區(qū)。

        這種方式就是類加載器(ClassLoader)。

        再通過加載、驗(yàn)證、準(zhǔn)備、解析、初始化這幾個(gè)步驟完成類加載過程,然后再由jvm執(zhí)行引擎的解釋器和JIT即時(shí)編譯器去將字節(jié)碼指令轉(zhuǎn)換為本地機(jī)器指令進(jìn)行執(zhí)行。

        我們在使用類加載器加載類的時(shí)候,會面臨下面幾個(gè)問題:

        1. 如何保證類不會被重復(fù)加載?類重復(fù)加載會出現(xiàn)很多問題。
        2. 類加載器是否允許用戶自定義?
        3. 如果允許用戶自定義,如何保證類文件的安全性?
        4. 如何保證加載的類的完整性?

        為了解決上面的這一系列的問題,我們必須要引入某一套機(jī)制,這套機(jī)制就是:雙親委派機(jī)制。

        2 什么是雙親委派機(jī)制?

        接下來,我們看看什么是雙親委派機(jī)制。

        雙親委派機(jī)制的基本思想是:當(dāng)一個(gè)類加載器試圖加載某個(gè)類時(shí),它會先委托給其父類加載器,如果父類加載器無法加載,再由當(dāng)前類加載器自己進(jìn)行加載。

        這種層層委派的方式有助于保障類的唯一性,避免類的重復(fù)加載,并提高系統(tǒng)的安全性和穩(wěn)定性。

        在Java中默認(rèn)的類加載器有3層:

        1. 啟動(dòng)類加載器(Bootstrap Class Loader):負(fù)責(zé)加載 %JAVA_HOME%/jre/lib 目錄下的核心Java類庫,比如:rt.jar、charsets.jar等。它是最頂層的類加載器,通常由C++編寫。

        2. 擴(kuò)展類加載器(Extension Class Loader):負(fù)責(zé)加載Java的擴(kuò)展庫,一般位于<JAVA_HOME>/lib/ext目錄下。

        3. 應(yīng)用程序類加載器(Application Class Loader):也稱為系統(tǒng)類加載器,負(fù)責(zé)加載用戶類路徑(ClassPath)下的應(yīng)用程序類。

        用一張圖梳理一下,雙親委派機(jī)制中的3種類加載器的層次關(guān)系:

        但這樣不夠靈活,用戶沒法控制,加載自己想要的一些類。

        于是,Java中引入了自定義類加載器。

        創(chuàng)建一個(gè)新的類并繼承ClassLoader類,然后重寫findClass方法。

        該方法主要是實(shí)現(xiàn)從那個(gè)路徑讀取 ar包或者.class文件,將讀取到的文件用字節(jié)數(shù)組來存儲,然后可以使用父類的defineClass來轉(zhuǎn)換成字節(jié)碼。

        如果想破壞雙親委派的話,就重寫loadClass方法,否則不用重寫。

        類加載器的層次關(guān)系改成:

        雙親委派機(jī)制流程圖如下:

        具體流程大概是這樣的:

        1. 需要加載某個(gè)類時(shí),先檢查自定義類加載器是否加載過,如果已經(jīng)加載過,則直接返回。
        2. 如果自定義類加載器沒有加載過,則檢查應(yīng)用程序類加載器是否加載過,如果已經(jīng)加載過,則直接返回。
        3. 如果應(yīng)用程序類加載器沒有加載過,則檢查擴(kuò)展類加載器是否加載過,如果已經(jīng)加載過,則直接返回。
        4. 如果擴(kuò)展類加載器沒有加載過,則檢查啟動(dòng)類加載器是否加載過,如果已經(jīng)加載過,則直接返回。
        5. 如果啟動(dòng)類加載器沒有加載過,則判斷當(dāng)前類加載器能否加載這個(gè)類,如果能加載,則加載該類,然后返回。
        6. 如果啟動(dòng)類加載器不能加載該類,則交給擴(kuò)展類加載器。擴(kuò)展類加載器判斷能否加載這個(gè)類,如果能加載,則加載該類,然后返回。
        7. 如果擴(kuò)展類加載器不能加載該類,則交給應(yīng)用程序類加載器。應(yīng)用程序類加載器判斷能否加載這個(gè)類,如果能加載,則加載該類,然后返回。
        8. 如果應(yīng)用程序類加載器不能加載該類,則交給自定義類加載器。自定義類加載器判斷能否加載這個(gè)類,如果能加載,則加載該類,然后返回。
        9. 如果自定義類加載器,也無法加載這個(gè)類,則直接拋ClassNotFoundException異常。

        這樣做的好處是:

        1. 保證類不會重復(fù)加載。加載類的過程中,會向上問一下是否加載過,如果已經(jīng)加載了,則不會再加載,這樣可以保證一個(gè)類只會被加載一次。
        2. 保證類的安全性。核心的類已經(jīng)被啟動(dòng)類加載器加載了,后面即使有人篡改了該類,也不會再加載了,防止了一些有危害的代碼的植入。

        3 破壞雙親委派機(jī)制的場景

        既然Java中引入了雙親委派機(jī)制,為什么要破壞它呢?

        答:因?yàn)樗幸恍┤秉c(diǎn)。

        下面給大家列舉一下,破壞雙親委派機(jī)制最常見的場景。

        3.1 JNDI

        JNDI是Java中的標(biāo)準(zhǔn)服務(wù),它的代碼由啟動(dòng)類加載器去加載。

        但JNDI要對資源進(jìn)行集中管理和查找,它需要調(diào)用由獨(dú)立廠商在應(yīng)用程序的ClassPath下的實(shí)現(xiàn)了JNDI接口的代碼,但啟動(dòng)類加載器不可能“認(rèn)識”這些外部代碼。

        為了解決這個(gè)問題,Java后來引入了線程上下文類加載器(Thread Context ClassLoader)。

        這個(gè)類加載器可以通過java.lang.Thread類的setContextClassLoader()方法進(jìn)行設(shè)置。

        如果創(chuàng)建線程時(shí)沒有設(shè)置,他將會從父線程中繼承一個(gè),如果在應(yīng)用程序的全局范圍內(nèi)都沒有設(shè)置過的話,那這個(gè)類加載器默認(rèn)就是應(yīng)用程序類加載器。

        有了線程上下文加載器,JNDI服務(wù)就可以使用它去加載所需要的SPI代碼,也就是父類加載器請求子類加載器去完成類加載的動(dòng)作,這樣就打破了雙親委派機(jī)制。

        3.2 JDBC

        原生的JDBC中Driver驅(qū)動(dòng)本身只是一個(gè)接口,并沒有具體的實(shí)現(xiàn),具體的實(shí)現(xiàn)是由不同數(shù)據(jù)庫類型去實(shí)現(xiàn)的。

        例如,MySQL的mysql-connector.jar中的Driver類具體實(shí)現(xiàn)的。

        原生的JDBC中的類是放在rt.jar包,是由啟動(dòng)類加載器進(jìn)行類加載的。

        在JDBC中需要?jiǎng)討B(tài)去加載不同數(shù)據(jù)庫類型的Driver實(shí)現(xiàn)類,而mysql-connector.jar中的Driver實(shí)現(xiàn)類是用戶自己寫的代碼,啟動(dòng)類加載器肯定是不能加載的,那就需要由應(yīng)用程序啟動(dòng)類去進(jìn)行類加載。

        為了解決這個(gè)問題,也可以使用線程上下文類加載器(Thread Context ClassLoader)。

        3.3  Tomcat容器

        Tomcat是Servlet容器,它負(fù)責(zé)加載Servlet相關(guān)的jar包。

        此外,Tomcat本身也是Java程序,也需要加載自身的類和一些依賴jar包。

        這樣就會帶來下面的問題:

        1. 一個(gè)Tomcat容器下面,可以部署多個(gè)基于Servlet的Web應(yīng)用,但如果這些Web應(yīng)用下有同名的Servlet類,又不能產(chǎn)生沖突,需要相互獨(dú)立加載和運(yùn)行才行。
        2. 但如果多個(gè)Web應(yīng)用,使用了相同的依賴,比如:SpringBoot、Mybatis等。這些依賴包所涉及的文件非常多,如果全部都獨(dú)立,可能會導(dǎo)致JVM內(nèi)存不足。也就是說,有些公共的依賴包,最好能夠只加載一次。
        3. 我們還需要將Tomcat本身的類,跟Web應(yīng)用的類隔離開。

        這些原因?qū)е?,Tomcat沒有辦法使用傳統(tǒng)的雙親委派機(jī)制加載類了。

        那么,Tomcat加載類的機(jī)制是怎么樣的?

        • CommonClassLoader:是Tomcat最基本的類加載器,它加載的類可以被Tomcat容器和Web應(yīng)用訪問。
        • CatalinaClassLoader:是Tomcat容器私有的類加載器,加載類對于Web應(yīng)用不可見。
        • SharedClassLoader:各個(gè)Web應(yīng)用共享的類加載器,加載的類對于所有Web應(yīng)用可見,但是對于Tomcat容器不可見。
        • WebAppClassLoader:各個(gè)Web應(yīng)用私有的類加載器,加載類只對當(dāng)前Web應(yīng)用可見。比如不同war包應(yīng)用引入了不同的Spring版本,這樣能加載各自的Spring版本,相互隔離。

        3.4 熱部署

        由于用戶對程序動(dòng)態(tài)性的追求,比如:代碼熱部署、代碼熱替換等功能,引入了OSGi(Open Service Gateway Initiative)。

        OSGi中的每一個(gè)模塊(稱為Bundle)。

        當(dāng)程序升級或者更新時(shí),可以只停用、重新安裝然后啟動(dòng)程序的其中一部分,對企業(yè)來說這是一個(gè)非常誘人的功能。

        OSGi的Bundle類加載器之間只有規(guī)則,沒有固定的委派關(guān)系。

        各個(gè)Bundle加載器是平級關(guān)系。

        不是雙親委派關(guān)系。

        ?? 歡迎加入小哈的星球,你將獲得: 專屬的項(xiàng)目實(shí)戰(zhàn) / 1v1 提問 / Java 學(xué)習(xí)路線 / 學(xué)習(xí)打卡 / 每月贈書 / 社群討論

        • 新項(xiàng)目:《從零手?jǐn)]:仿小紅書(微服務(wù)架構(gòu))》 正在持續(xù)爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 點(diǎn)擊查看項(xiàng)目介紹
        • 《從零手?jǐn)]:前后端分離博客項(xiàng)目(全棧開發(fā))》 2期已完結(jié),演示鏈接:http://116.62.199.48/;

        截止目前,累計(jì)輸出 53w+ 字,講解圖 2330+ 張,還在持續(xù)爆肝中.. 后續(xù)還會上新更多項(xiàng)目,目標(biāo)是將 Java 領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM 即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),解鎖全部項(xiàng)目,已有1900+小伙伴加入


             
                

        1. 我的私密學(xué)習(xí)小圈子~

        2. 動(dòng)圖展示 10 大 Git 命令,讓你輕松掌握Git

        3. Spring 純注解開發(fā),有點(diǎn)強(qiáng)?。?/a>

        4. 京東一面:為什么 IDEA 建議去掉 StringBuilder,而要使用 “+” 拼接字符串?

        最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

        獲取方式:點(diǎn)“在看”,關(guān)注公眾號并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

        PS:因公眾號平臺更改了推送規(guī)則,如果不想錯(cuò)過內(nèi)容,記得讀完點(diǎn)一下在看,加個(gè)星標(biāo),這樣每次新文章推送才會第一時(shí)間出現(xiàn)在你的訂閱列表里。

        點(diǎn)“在看”支持小哈呀,謝謝啦 

        瀏覽 257
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲日韩一区二区 | 操逼挺好我看看 | 午夜伦欧美伦电影理论片 | 精品无码人妻一区二区媚黑 | 丝袜美腿秘书视频二区 | 亚洲精品成人AV电影 | 久久久久久久国产精品 | 啪啪激情网 | 欧美精品不卡免费在线 | 成人AV一AV二 |