Jetty9原始碼剖析 - 基礎元件 - WebAppClassLoader
之前在WebAppContext章節有提到過Jetty的類載入器WebAppClassLoader,不過講的不是很深入,這裡再重新梳理一次
二、概念
類載入器的重要性不言而喻,而對於像Jetty這樣的Web容器來說,更是非常特別,它最特殊的地方就是破壞了雙親委派原則,實現了一套特殊的類載入機制,接下來我們就來分析
三、為什麼要破壞雙親委派原則
通常的情況下對於雙親委派原則是具備很多優點的,它要求所有的類載入都先委託父類載入器載入,這樣保證同一個類名在記憶體中只存在一份定義,這樣可以既可以防止重複載入,還能保證系統類(如:java. , javax. 等)的安全性。但是在某些場景下我們雙親委派就不是那麼靈活了,我們需要破壞這種規則
破壞雙親委派原則主要是由於以下原因:
-
類隔離
對於Web容器而言,它裡面會整合很多Web應用,例如我們可以用Jetty的同一個程序下面部署多個網站,每個網站具備不同的url字首來區分,而對於不同的網站應用它們可能依賴了同一個jar包庫的不同版本。如果使用雙親委派,那整個Jetty容器就只存在一份庫中類的定義,那就可能導致其中某些的網站無法使用,因此對於Jetty而言需要做 類隔離 ,類隔離就是破壞雙親委派最核心的一個因素,它允許在同一個Web程序下面存在多份同名類的定義
-
熱載入(熱部署)
對於Web容器來說,熱載入也是一個比較實用的技術,例如可以實現應用的熱替換,我們可以在網站執行的時候直接替換裡面類定義,而不需要重啟Web容器
四、載入規則
從文件中可以看出:
- WEB-INF/lib、WEB-INF/classes下的類優先使用容器定義的類載入器載入
- Java系統類java. 、javax. 等系統類不能使用容器類載入器載入
- 容器實現的服務端類(例如Server、Handler)的類不能使用其他的類載入器載入,也就是隻能用容器類載入器載入
那在Jetty裡面核心的類載入器自然就是WebAppClassLoader
五、原始碼剖析
1. 建立
如果程式沒有顯式指定類載入器,在使用WebAppContext的時候會預設建立WebAppClassLoader作為類載入器
上圖構造方法,首先super初始化父類(WebAppClassLoader繼承自URLClassLoader),獲取父類載入器,然後是新增jar、zip兩種格式的檔案字尾
2. 載入類
通常我們使用類載入器就是呼叫loadClass方法,如上圖,最終我們呼叫了圖中的第二個方法,resolve=false,表示不解析該類
同常規的類載入一樣,都需要加一把鎖,防止併發問題,然後呼叫如下圖findLoadedClass看當前是否已經載入過該類,在利用WebAppContext裡面存放的系統類名、服務端類名,來判斷是否是系統、服務端的類,如果既是系統類又是服務端類,則返回null,否則當前如果該類沒有載入過,並且父類載入器存在,並且配置的是父類載入器優先或是系統類,並且不是服務端類,就使用父類載入器載入,從這裡可以看到:如果設定了父類載入器優先,只能載入系統類、應用類,但不能載入容器的服務端類。通常情況下,父類載入器優先是關閉的,也就是使用容器類載入器優先。如果上面都沒載入成功,呼叫WebAppClassLoader自身的findClass來載入(後面講)。如果容器類載入器也載入不了,如果沒使用過父類載入器載入就使用父類載入器載入(前提是這個類不是服務端類),這樣其實可以看到,預設這個WebAppClassLoader是自己優先載入,最後才委託父類載入器載入。最後如果設定了resolve,就呼叫resolveClass解析該類
3. 查詢類
接下來繼續分析findClass方法
如果定義了類轉換器,就會將當前類的位元組資料傳入轉換器進行轉換,並定義class,否則直接呼叫父類的findClass查詢,很簡單,就不多講了
六、Spring Boot中的Jetty類載入器
其實前面講了都是關於一個Web容器應該完成的多應用部署,所以必須自定義類載入器,並且還得打破雙親委派原則,但是還有一種情況,就是我們部署的時候就是一個單應用,不存在多應用,也不需要熱部署,那就是通常我們用的Spring Boot巢狀式Web容器,一個程序就一個服務,那其實我們就不用這個WebAppClassLoader了,只需要使用雙親委派的這種載入模式,從Spring Boot的實現裡面就能看到,如下圖
Spring Boot在整合內嵌Jetty的時候,會給WebAppContext設定一個類載入器,而這個類載入器是this.resourceLoader.getClassLoader(),可以看到是sun.misc.Launcher$AppClassLoader,即AppClassLoader,這個是Java自帶的雙親委派模式的類載入器
所以我們可以看到Spring Boot預設使用的不一定就是WebAppClassLoader
七、總結
Java類載入器是比較重要的一個概念,很多時候我們面試的時候會經常被問到,為什麼要自定義類載入器,為什麼要破壞雙親委派原則,其實從這篇文章我們可以比較清楚的看到是什麼原因了,希望對大家有所幫助~