Tomcat的類載入機制
Tomcat類載入分為四部分(不是步驟):
1.BootStrap 啟動類載入
2.System 系統類載入 CATALINA_HOME/conf
3.Common 通用類載入 CATALINA_HOME/lib
4.Webapp 應用類載入 WEB-INF/lib和WEB-INF/classes
當Tomcat啟動時,步驟如下:
1.BootStrap,載入JVM執行基本的類,以及標準擴充套件類(jre/lib/ext中的類)
2.System,載入catalina.bat/catalina.sh中指定位置的類,如bootstrap.jar、tomcat-juli.jar等
3.Webapp,每個應用在部署後,都會建立一個唯一的類載入器,該類載入器會載入位於WEB-INF/lib下的jar檔案和WEB-INF/classes下的class檔案(先classes資料夾再lib資料夾)
4.Common,載入Tomcat使用以及應用通用的一些類,位於CATALINA_HOME/lib下,比如servlet-api.jar
Tomcat中通過擴充套件URLClassLoader來實現自己的類載入器
Tomcat作為一個java web容器,也有自己的類載入機制,通過自定義的類載入機制以實現共享類庫的抽取、不同web應用之間的資源隔離和熱載入等功能。除了一些java自身的類載入器之外,Tomcat實現的主要類載入器有:Common ClassLoader、Catalina ClassLoader、Shared ClassLoader以及WebApp ClassLoader。
其中,Common、Catalina、Shared類載入器是URLClassLoader類的一個例項,只是它們的類載入路徑不一樣,在catalina.properties配置檔案中進行配置(common.loader、server.loader、shared.loader)。
WebAppClassLoader繼承自WebAppClassLoaderBase,基本所有的邏輯都在WebAppClassLoaderBase中實現了。
而WebAppClassLoaderBase、ExtClassLoader、AppClassLoader都繼承自URLClassLoader,因此可以看出,Tomcat所有的類載入器都以URLClassLoader為基礎進行擴充套件。
在預設配置中,Common、Catalina、Shared類載入器都是同一個物件,即commonLoader(因為Catalina、Shared的預設載入路徑為空,直接返回父類載入器commonLoader)
一個Web應用對應著一個StandardContext例項,每個web應用都擁有獨立的web應用類載入器(WebAppClassLoader),這個類載入器在StandardContext.startInternal()中被構造出來
Tomcat的類載入機制違反了雙親委託原則,對於一些未載入的非基礎類(Object、String等),各個web應用自己的類載入器會優先載入,載入不到再交給commonClassLoader走雙親委託,針對某個類的具體載入邏輯位於WebAppClassLoaderBase.loadClass()方法中:
1.先在本地快取中查詢是否已經載入過該類(對於一些已經載入了的類,會被快取在resourceEntries這個資料結構中),如果已經載入就返回;
2.讓系統類載入器(AppClassLoader)嘗試載入該類,主要是為了防止一些基礎類會被web中的類覆蓋,如果載入到就返回;
3.前兩步都沒載入到目標類,那麼web應用的類載入器將自行載入(先載入WEB-INF/classes再載入WEB-INF/lib),如果載入到就返回;
4.最後還是載入不到的話,委託父類載入器(Common ClassLoader)去載入。
第3第4兩個步驟的順序已經違反了雙親委託機制,除了Tomcat外,JDBC、JNDI、Thread.currentThread().setContextClassLoader()等都一樣違反了雙親委託。
另外,開發者也會因為粗心而犯下面的錯誤:
1.在CATALINA_HOME/lib以及WEB-INF/lib中放置了不同版本的jar包,此時就會導致某些情況下報載入不到類的錯誤;
2.如果多個應用使用同一jar包檔案,當放置了多份,就可能導致多個應用間出現類載入不到的錯誤