面試官:雙親委派模型你了解嗎?


面試官要不你今天來詳細講講雙親委派機制?

候選者:嗯,好的。

候選者:上次提到了:class文件是通過「類加載器」裝載至JVM中的

候選者:為了防止內存中存在多份同樣的字節碼,使用了雙親委派機制(它不會自己去嘗試加載類,而是把請求委托給父加載器去完成,依次向上)

候選者:JDK 中的本地方法類一般由根加載器(Bootstrp loader)裝載,JDK 中內部實現的擴展類一般由擴展加載器(ExtClassLoader )實現裝載,而程序中的類文件則由系統加載器(AppClassLoader )實現裝載。

候選者:這應該很好理解吧?

面試官:雀食(確實)!

面試官順着話題,我想問問,打破雙親委派機制是什么意思?

候選者:很好理解啊,意思就是:只要我加載類的時候,不是從APPClassLoader->Ext ClassLoader->BootStrap ClassLoader 這個順序找,那就算是打破了啊

候選者:因為加載class核心的方法在LoaderClass類的loadClass方法上(雙親委派機制的核心實現)

候選者:那只要我自定義個ClassLoader,重寫loadClass方法(不依照往上開始尋找類加載器),那就算是打破雙親委派機制了。

面試官:這么簡單?

候選者:嗯,就是這么簡單

面試官那你知道有哪個場景破壞了雙親委派機制嗎?

候選者:最明顯的就Tomcat啊

面試官:詳細說說?

候選者:在初學時部署項目,我們是把war包放到tomcat的webapp下,這意味着一個tomcat可以運行多個Web應用程序(:

候選者:是吧?

面試官:嗯..

候選者:那假設我現在有兩個Web應用程序,它們都有一個類,叫做User,並且它們的類全限定名都一樣,比如都是com.yyy.User。但是他們的具體實現是不一樣的

候選者:那么Tomcat是如何保證它們是不會沖突的呢?

候選者:答案就是,Tomcat給每個 Web 應用創建一個類加載器實例(WebAppClassLoader),該加載器重寫了loadClass方法,優先加載當前應用目錄下的類,如果當前找不到了,才一層一層往上找(:

候選者:那這樣就做到了Web應用層級的隔離

面試官嗯,那你還知道Tomcat還有別的類加載器嗎?

候選者:嗯,知道的

候選者:並不是Web應用程序下的所有依賴都需要隔離的,比如Redis就可以Web應用程序之間共享(如果有需要的話),因為如果版本相同,沒必要每個Web應用程序都獨自加載一份啊。

候選者:做法也很簡單,Tomcat就在WebAppClassLoader上加了個父類加載器(SharedClassLoader),如果WebAppClassLoader自身沒有加載到某個類,那就委托SharedClassLoader去加載。

候選者:(無非就是把需要應用程序之間需要共享的類放到一個共享目錄下嘛)

面試官:嗯..

候選者:為了隔絕Web應用程序與Tomcat本身的類,又有類加載器(CatalinaClassLoader)來裝載Tomcat本身的依賴

候選者:如果Tomcat本身的依賴和Web應用還需要共享,那么還有類加載器(CommonClassLoader)來裝載進而達到共享

候選者:各個類加載器的加載目錄可以到tomcat的catalina.properties配置文件上查看

候選者:我稍微畫下Tomcat的類加載結構圖吧,不然有點抽象

面試官:嗯,還可以,我聽懂了,有點意思。

面試官順便,我想問下,JDBC你不是知道嗎,聽說它也是破壞了雙親委派模型的,你怎么理解的。

候選者:Eumm,這個有沒有破壞,見仁見智吧。

候選者:JDBC定義了接口,具體實現類由各個廠商進行實現嘛(比如MySQL)

候選者:類加載有個規則:如果一個類由類加載器A加載,那么這個類的依賴類也是由「相同的類加載器」加載。

候選者:我們用JDBC的時候,是使用DriverManager進而獲取Connection,DriverManager在java.sql包下,顯然是由BootStrap類加載器進行裝載

候選者:當我們使用DriverManager.getConnection()時,得到的一定是廠商實現的類。

候選者:但BootStrap ClassLoader會能加載到各個廠商實現的類嗎?

候選者:顯然不可以啊,這些實現類又沒在java包中,怎么可能加載得到呢

面試官:嗯..

候選者:DriverManager的解決方案就是,在DriverManager初始化的時候,得到「線程上下文加載器」

候選者:去獲取Connection的時候,是使用「線程上下文加載器」去加載Connection的,而這里的線程上下文加載器實際上還是App ClassLoader

候選者:所以在獲取Connection的時候,還是先找Ext ClassLoader和BootStrap ClassLoader,只不過這倆加載器肯定是加載不到的,最終會由App ClassLoader進行加載

面試官:嗯..

候選者:那這種情況,有的人覺得破壞了雙親委派機制,因為本來明明應該是由BootStrap ClassLoader進行加載的,結果你來了一手「線程上下文加載器」,改掉了「類加載器」

候選者:有的人覺得沒破壞雙親委派機制,只是改成由「線程上下文加載器」進行類加載,但還是遵守着:「依次往上找父類加載器進行加載,都找不到時才由自身加載」。認為"原則"上是沒變的。

面試官:那我了解了

本文總結

  • 前置知識:JDK中默認類加載器有三個:AppClassLoader、Ext ClassLoader、BootStrap ClassLoader。AppClassLoader的父加載器為Ext ClassLoader、Ext ClassLoader的父加載器為BootStrap ClassLoader。這里的父子關系並不是通過繼承實現的,而是組合。

  • 什么是雙親委派機制:加載器在加載過程中,先把類交由父類加載器進行加載,父類加載器沒找到才由自身加載。

  • 雙親委派機制目的:為了防止內存中存在多份同樣的字節碼(安全)

  • 類加載規則:如果一個類由類加載器A加載,那么這個類的依賴類也是由「相同的類加載器」加載。

  • 如何打破雙親委派機制:自定義ClassLoader,重寫loadClass方法(只要不依次往上交給父加載器進行加載,就算是打破雙親委派機制)

  • 打破雙親委派機制案例:Tomcat

    • 為了Web應用程序類之間隔離,為每個應用程序創建WebAppClassLoader類加載器
    • 為了Web應用程序類之間共享,把ShareClassLoader作為WebAppClassLoader的父類加載器,如果WebAppClassLoader加載器找不到,則嘗試用ShareClassLoader進行加載
    • 為了Tomcat本身與Web應用程序類隔離,用CatalinaClassLoader類加載器進行隔離,CatalinaClassLoader加載Tomcat本身的類
    • 為了Tomcat與Web應用程序類共享,用CommonClassLoader作為CatalinaClassLoader和ShareClassLoader的父類加載器
    • ShareClassLoader、CatalinaClassLoader、CommonClassLoader的目錄可以在Tomcat的catalina.properties進行配置
  • 線程上下文加載器:由於類加載的規則,很可能導致父加載器加載時依賴子加載器的類,導致無法加載成功(BootStrap ClassLoader無法加載第三方庫的類),所以存在「線程上下文加載器」來進行加載。

歡迎關注我的微信公眾號【Java3y】來聊聊Java面試,對線面試官系列持續更新中!

【對線面試官-移動端】系列 一周兩篇持續更新中!

【對線面試官-電腦端】系列 一周兩篇持續更新中!


免責聲明!

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



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