Tomcat是如何隔離Web應用的?

Tomcat通過自定義的類加載器WebAppClassLoader打破了雙親委託機制,目的就是爲了優化加載Web應用目錄下的類。Tomcat 作爲 Servlet 容器,它負責加載我們Servlet 類,此外它還負責加載 Servlet 所依賴的 JAR 包。並且Tomcat 本身也是也是一個 Java 程序,因此它需要加載自己的類和依賴的 JAR 包。
如果Tomcat裏面運行了兩個Web應用程序,兩個Web應用程序中有同名的Servlet,但功能不同,Tomcat需要同時加載和管理這兩個同名的 Servlet 類,保證它們不會衝突,因此 Web 應用之間的類需要隔離。
如果如兩個 Web 應用都依賴同一個第三方的 JAR 包,比如 Spring,那 Spring 的 JAR 包被加載到內存後,Tomcat 要保證這兩個 Web 應用能夠共享,也就是說Spring 的 JAR 包只被加載一次,否則隨着依賴的第三方JAR 包增多,JVM 的內存會膨脹。
同時,跟JVM一樣,我們需要隔離Tomcat本身的類和Web應用類。

一、Tomcat的類加載器的層次結構
爲了解決AppClassLoader,同類名的Servlet類只能被加載一次。Tomcat 自定義一個類加載器WebAppClassLoader, 並且給每個 Web 應用創建一個類加載器實例。Context 容器組件對應一個 Web應用,因此,每個 Context 容器負責創建和維護一個WebAppClassLoader 加載器實例。原理是,不同的加載器實例加載的類被認爲是不同的類,即使它們的類名相同。這就相當於在 Java 虛擬機內部創建了一個個相互隔離的 Java 類空間,每一個 Web 應用都有自己的類空間,Web 應用之間通過各自的類加載器互相隔離。
在這裏插入圖片描述
爲了Tomcat可以存放多個Web應用,Tomcat實現了Web應用的隔離,從而達到可以加載不同Web應用下相同名的Servlet類,從而編寫了自己的類加載器,其內部類加載器模型如上圖所示
(1)SharedClassLoader:該類加載器存在是爲了解決不同Web應用之間共享類庫,並且不會重複加載相同的類。它作爲WebAppClassLoader的父加載器,專門加載Web應用之間的共享類。
(2)CatalinaClassloader:該類加載器專門加載Tomcat自身的類,從而和web應用的類做一個隔離。
(3)CommonClassLoad:CatalinaClassLader實現了Tomcat類和web應用類的隔離,如果二者之間需要共享一些類怎麼辦?這裏就需要CommonClassLoad,它所加載的所有類都可以被SharedClassLoader和CatalinaClassLoader使用,從而實現web應用和tomcat對一些類的共享。
三、補充Spring的加載問題
在 JVM 的實現中有一條隱含的規則,默認情況下,如果一個類由類加載器 A 加載,那麼這個類的依賴類也是由相同的類加載器加載。比如 Spring 作爲一個 Bean 工廠,它需要創建業務類的實例,並且在創建業務類實例之前需要加載這些類。Spring 是通過調用Class.forName來加載業務類的。
我在前面提到,Web 應用之間共享的 JAR 包可以交給SharedClassLoader 來加載,從而避免重複加載。Spring作爲共享的第三方 JAR 包,它本身是由SharedClassLoader 來加載的,Spring 又要去加載業務類,按照前面那條規則,加載 Spring 的類加載器也會用來加載業務類,但是業務類在 Web 應用目錄下,不在SharedClassLoader 的加載路徑下,這該怎麼辦呢?
於是線程上下文加載器登場了,它其實是一種類加載器傳遞機制。爲什麼叫作“線程上下文加載器”呢,因爲這個類加載器保存在線程私有數據裏,只要是同一個線程,一旦設置了線程上下文加載器,在線程後續執行過程中就能把這個類加載器取出來用。因此 Tomcat 爲每個 Web 應用創建一個WebAppClassLoarder 類加載器,並在啓動 Web 應用的線程裏設置線程上下文加載器,這樣 Spring 在啓動時就將線程上下文加載器取出來,用來加載 Bean。

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