Tomcat打破雙親委派機(jī)制實(shí)現(xiàn)隔離Web應(yīng)用的方法
Tomcat通過自定義類加載器WebAppClassLoader打破雙親委派,即重寫了JVM的類加載器ClassLoader的findClass方法和loadClass方法,以優(yōu)先加載Web應(yīng)用目錄下的類。
Tomcat負(fù)責(zé)加載我們的Servlet類、加載Servlet所依賴的JAR包。Tomcat本身也是個(gè)Java程序,因此它需要加載自己的類和依賴的JAR包。
若在Tomcat運(yùn)行兩個(gè)Web應(yīng)用程序,它們有功能不同的同名Servlet,Tomcat需同時(shí)加載和管理這兩個(gè)同名的Servlet類,保證它們不會(huì)沖突。所以Web應(yīng)用之間的類需要隔離
若兩個(gè)Web應(yīng)用都依賴同一三方j(luò)ar,比如Spring,則Spring jar被加載到內(nèi)存后,Tomcat要保證這兩個(gè)Web應(yīng)用能共享之,即Spring jar只被加載一次,否則隨著三方j(luò)ar增多,JVM的內(nèi)存會(huì)占用過大。
所以,和 JVM 一樣,需要隔離Tomcat本身的類和Web應(yīng)用的類。
Tomcat類加載器的層次結(jié)構(gòu)
Tomcat的類加載器層次結(jié)構(gòu)

前三個(gè)是加載器實(shí)例名,不是類名。

WebAppClassLoader
若使用JVM默認(rèn)的AppClassLoader加載Web應(yīng)用,AppClassLoader只能加載一個(gè)Servlet類,在加載第二個(gè)同名Servlet類時(shí),AppClassLoader會(huì)返回第一個(gè)Servlet類的Class實(shí)例。
因?yàn)樵贏ppClassLoader眼里,同名Servlet類只能被加載一次。
于是,Tomcat自定義了一個(gè)類加載器WebAppClassLoader, 并為每個(gè)Web應(yīng)用創(chuàng)建一個(gè)WebAppClassLoader實(shí)例。
每個(gè)Web應(yīng)用自己的Java類和依賴的JAR包,分別放在WEB-INF/classes和WEB-INF/lib目錄下,都是WebAppClassLoader加載的。
Context容器組件對(duì)應(yīng)一個(gè)Web應(yīng)用,因此,每個(gè)Context容器創(chuàng)建和維護(hù)一個(gè)WebAppClassLoader加載器實(shí)例。
不同加載器實(shí)例加載的類被認(rèn)為是不同的類,即使類名相同。這就相當(dāng)于在JVM內(nèi)部創(chuàng)建相互隔離的Java類空間,每個(gè)Web應(yīng)用都有自己的類空間,Web應(yīng)用之間通過各自的類加載器互相隔離。
SharedClassLoader
兩個(gè)Web應(yīng)用之間怎么共享庫類,并且不能重復(fù)加載相同的類?
雙親委派機(jī)制的各子加載器都能通過父加載器去加載類,于是考慮把需共享的類放到父加載器的加載路徑。
應(yīng)用程序即是通過該方式共享JRE核心類。
Tomcat搞了個(gè)類加載器SharedClassLoader,作為WebAppClassLoader的父加載器,以加載Web應(yīng)用之間共享的類。
若WebAppClassLoader未加載到某類,就委托父加載器SharedClassLoader去加載該類,SharedClassLoader會(huì)在指定目錄下加載共享類,之后返回給WebAppClassLoader,即可解決共享問題。
CatalinaClassLoader
如何隔離Tomcat本身的類和Web應(yīng)用的類?
兄弟關(guān)系:兩個(gè)類加載器是平行的,它們可能擁有同一父加載器,但兩個(gè)兄弟類加載器加載的類是隔離的。
于是,Tomcat搞了CatalinaClassLoader,專門加載Tomcat自身的類。
問題是,當(dāng)Tomcat和各Web應(yīng)用之間需要共享一些類時(shí)該怎么辦?
CommonClassLoader
共享依舊靠父子關(guān)系。
再增加個(gè)CommonClassLoader,作為CatalinaClassLoader和SharedClassLoader的父加載器。
CommonClassLoader能加載的類都可被CatalinaClassLoader、SharedClassLoader 使用,而CatalinaClassLoader和SharedClassLoader能加載的類則與對(duì)方相互隔離。WebAppClassLoader可以使用SharedClassLoader加載到的類,但各個(gè)WebAppClassLoader實(shí)例之間相互隔離。
Spring的加載問題
JVM默認(rèn)情況下,若一個(gè)類由類加載器A加載,則該類的依賴類也由相同的類加載器加載。
比如Spring作為一個(gè)Bean工廠,它需要?jiǎng)?chuàng)建業(yè)務(wù)類的實(shí)例,并且在創(chuàng)建業(yè)務(wù)類實(shí)例之前需要加載這些類。Spring是通過調(diào)用Class.forName來加載業(yè)務(wù)類的,我們來看一下forName的源碼:
public static Class<?> forName(String className) {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
會(huì)使用調(diào)用者,即Spring的加載器去加載業(yè)務(wù)類。
Web應(yīng)用之間共享的jar可交給SharedClassLoader加載,以避免重復(fù)加載。Spring作為共享的三方j(luò)ar,本身由SharedClassLoader加載,Spring又要去加載業(yè)務(wù)類,按照前面那條規(guī)則,加載Spring的類加載器也會(huì)用來加載業(yè)務(wù)類,但是業(yè)務(wù)類在Web應(yīng)用目錄下,不在SharedClassLoader的加載路徑下,這該怎么辦呢?
線程上下文加載器
于是有了線程上下文加載器,一種類加載器傳遞機(jī)制。因?yàn)樵擃惣虞d器保存在線程私有數(shù)據(jù)里,只要是同一個(gè)線程,一旦設(shè)置了線程上下文加載器,在線程后續(xù)執(zhí)行過程中就能把這個(gè)類加載器取出來用。因此Tomcat為每個(gè)Web應(yīng)用創(chuàng)建一個(gè)WebAppClassLoader類加載器,并在啟動(dòng)Web應(yīng)用的線程里設(shè)置線程上下文加載器,這樣Spring在啟動(dòng)時(shí)就將線程上下文加載器取出來,用來加載Bean。Spring取線程上下文加載的代碼如下:
cl = Thread.currentThread().getContextClassLoader();
在StandardContext的啟動(dòng)方法,會(huì)將當(dāng)前線程的上下文加載器設(shè)置為WebAppClassLoader。

啟動(dòng)方法結(jié)束時(shí),會(huì)恢復(fù)線程的上下文加載器:
Thread.currentThread().setContextClassLoader(originalClassLoader);
這是為什么呢?
線程上下文加載器其實(shí)是線程的一個(gè)私有數(shù)據(jù),跟線程綁定,這個(gè)線程完成啟動(dòng)Context組件后,會(huì)被回收到線程池,之后被用來做其他事情,為了不影響其他事情,需恢復(fù)之前的線程上下文加載器。
優(yōu)先加載web應(yīng)用的類,當(dāng)加載完了再改回原來的。
線程上下文的加載器就是指定子類加載器來加載具體的某個(gè)橋接類,比如JDBC的Driver的加載。
總結(jié)
Tomcat的Context組件為每個(gè)Web應(yīng)用創(chuàng)建一個(gè)WebAppClassLoader類加載器,由于不同類加載器實(shí)例加載的類是互相隔離的,因此達(dá)到了隔離Web應(yīng)用的目的,同時(shí)通過CommonClassLoader等父加載器來共享第三方JAR包。而共享的第三方JAR包怎么加載特定Web應(yīng)用的類呢?可以通過設(shè)置線程上下文加載器來解決。
多個(gè)應(yīng)用共享的Java類文件和JAR包,分別放在Web容器指定的共享目錄:
CommonClassLoader
對(duì)應(yīng) <Tomcat>/common/*
CatalinaClassLoader
對(duì)應(yīng) <Tomcat >/server/*
SharedClassLoader
對(duì)應(yīng) <Tomcat >/shared/*
WebAppClassloader
對(duì)應(yīng) <Tomcat >/webapps/<app>/WEB-INF/*
可以在Tomcat conf目錄下的Catalina.properties文件里配置各種類加載器的加載路徑。
當(dāng)出現(xiàn)ClassNotFound錯(cuò)誤時(shí),應(yīng)該檢查你的類加載器是否正確。
線程上下文加載器不僅僅可以用在Tomcat和Spring類加載的場(chǎng)景里,核心框架類需要加載具體實(shí)現(xiàn)類時(shí)都可以用到它,比如我們熟悉的JDBC就是通過上下文類加載器來加載不同的數(shù)據(jù)庫驅(qū)動(dòng)的。
到此這篇關(guān)于Tomcat打破雙親委派機(jī)制實(shí)現(xiàn)隔離Web應(yīng)用的方法的文章就介紹到這了,更多相關(guān)Tomcat 隔離Web應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
tomcat 安全規(guī)范(tomcat安全加固和規(guī)范)
tomcat是一個(gè)開源Web服務(wù)器,基于Tomcat的Web運(yùn)行效率高,可以在一般的硬件平臺(tái)上流暢運(yùn)行,因此,頗受Web站長的青睞。不過,在默認(rèn)配置下其存在一定的安全隱患,可被惡意攻擊2019-11-11
Tomcat配置必備的10個(gè)小技巧用法總結(jié)
Tomcat具有免費(fèi)、跨平臺(tái)等諸多特性,并且更新得很快,現(xiàn)在非常的流行,你所需要做的就是:按照你的需求配置Tomcat,只要你正確配置,Tomcat一般都能適合你的要求,下面是一系列關(guān)于Tomcat的配置技巧,希望對(duì)你有所幫助2023-05-05
Eclipse/tomcat 如何實(shí)現(xiàn)熱部署和熱啟動(dòng)
這篇文章主要介紹了Eclipse/ tomcat 如何實(shí)現(xiàn)熱部署和熱啟動(dòng),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
tomcat部署web應(yīng)用的4種方法總結(jié)(推薦)
本篇文章主要介紹了tomcat部署web應(yīng)用的方法,主要有4中方法,包括Tomcat自動(dòng)部署、控制臺(tái)進(jìn)行部署、增加自定義的Web部署文件、手動(dòng)修改%Tomcat_Home%\conf\server.xml文件來部署web應(yīng)用,有興趣的可以了解一下。2016-11-11
Tomcat在linux環(huán)境中開機(jī)自啟(定時(shí)重啟)的方法
我們經(jīng)常會(huì)遇到服務(wù)器斷電或異常,而異常后tomcat中部署的web項(xiàng)目需要我手動(dòng)去啟動(dòng),為此,特別貢獻(xiàn)出Linux環(huán)境中Tomcat開機(jī)自啟的方式供學(xué)習(xí)使用,需要的朋友可以參考下2023-10-10
tomcat報(bào)錯(cuò):地址localhost:8080已在使用中的解決方法
今天使用tomcat發(fā)現(xiàn)一個(gè)問題,本文就介紹一下報(bào)錯(cuò)地址localhost:8080已在使用中的解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-05-05

