1、tomcat是個web容器,要解決如下問題html
1. 一個web容器可能要部署兩個或者多個應用程序,不一樣的應用程序,可能會依賴同一個第三方類庫的不一樣版本,所以要保證每個應用程序的類庫都是獨立、相互隔離的。web
2. 部署在同一個web容器中的相同類庫的相同版本能夠共享,不然,會有重複的類庫被加載進JVM緩存
3. web容器也有本身的類庫,不能和應用程序的類庫混淆,須要相互隔離tomcat
4. web容器支持jsp文件修改後不用重啓,jsp文件也是要編譯成.class文件的,支持HotSwap功能服務器
2、tomcat使用Java默認類加載器的問題數據結構
1. 默認的類加載器沒法加載兩個相同類庫的不一樣版本,它只在意類的全限定類名,而且只有一份,因此沒法解決上面1和3,相互隔離的問題架構
2. 修改jsp文件後,由於類名同樣,默認的類加載器不會從新加載,而是使用方法區中已經存在的類;因此須要每一個jsp對應一個惟一的類加載器,當修改jsp的時候,直接卸載惟一的類加載器,而後從新建立類加載器,並加載jsp文件app
3、tomcat的類加載機制webapp
1. 架構圖jsp
2. tomcat本身定義的類加載器:
CommonClassLoader:tomcat最基本的類加載器,加載路徑中的class能夠被tomcat和各個webapp訪問
CatalinaClassLoader:tomcat私有的類加載器,webapp不能訪問其加載路徑下的class,即對webapp不可見
SharedClassLoader:各個webapp共享的類加載器,對tomcat不可見
WebappClassLoader:webapp私有的類加載器,只對當前webapp可見
JspClassLoader
3. 每個web應用程序對應一個WebappClassLoader,每個jsp文件對應一個JspClassLoader,因此這兩個類加載器有多個實例
4. 工做原理:
a. CommonClassLoader能加載的類均可以被Catalina ClassLoader和SharedClassLoader使用,從而實現了公有類庫的共用
b. CatalinaClassLoader和Shared ClassLoader本身能加載的類則與對方相互隔離
c. WebAppClassLoader可使用SharedClassLoader加載到的類,但各個WebAppClassLoader實例之間相互隔離,多個WebAppClassLoader是同級關係
d. 而JasperLoader的加載範圍僅僅是這個JSP文件所編譯出來的那一個.Class文件,它出現的目的就是爲了被丟棄:當Web容器檢測到JSP文件被修改時,會替換掉目前的JasperLoader的實例,並經過再創建一個新的Jsp類加載器來實現JSP文件的HotSwap功能
5. tomcat目錄結構,與上面的類加載器對應
/common/*
/server/*
/shared/*
/WEB-INF/*
6. 默認狀況下,conf目錄下的catalina.properties文件,沒有指定server.loader以及shared.loader,因此tomcat沒有創建CatalinaClassLoader和SharedClassLoader的實例,這兩個都會使用CommonClassLoader來代替。Tomcat6以後,把common、shared、server目錄合成了一個lib目錄。因此在咱們的服務器裏看不到common、shared、server目錄。
4、tomcat類加載器和雙親委派模型的關係
1. tomcat爲了實現隔離性和熱替換,沒有使用默認的類加載器,而是本身實現了類加載器:
每一個webappClassLoader加載本身目錄下的class文件
每一個jasper類加載器加載一個jsp文件
2. 雙親委派模型的標準是:每一個類加載器要加載類的時候,先傳給父類加載器加載,父類加載器加載不了的時候,才由本身加載
3. webappClassLoader和jasperClassLoader沒有傳給父類加載器去加載,仍是傳給了父類加載器而父類加載器加載不了?先本身加載
4. 從WebappClassLoader.loadClass源碼上看,確實沒有傳給父類加載器去加載,確實破壞了雙親委派模型,對於一些未加載的非基礎類(非Object,String等),各個web應用本身的類加載器(WebAppClassLoader)會優先加載,加載不到時再交給commonClassLoader走雙親委託
hasExternalRepositories && searchExternalFirst 默認爲false
5. 爲何要破壞?不破壞行不行?每一個webapp有本身的目錄和類庫,好比一個webapp使用類庫A1.0版本,一個webapp使用類庫A2.0版本,父類加載器加載類庫A1.0版本,若是使用雙親委派,會由commonClassLoader去加載類庫A1.0版本,這樣第二個webapp會有問題
6. 能夠經過在Context.xml文件中加上<Loader delegate = "true">
開啓正統的「雙親委派」加載機制
public Class<?> findClass(String name) throws ClassNotFoundException { // 其餘代碼略去..... // Ask our superclass to locate this class, if possible // (throws ClassNotFoundException if it is not found) Class<?> clazz = null; try { if (log.isTraceEnabled()) log.trace(" findClassInternal(" + name + ")"); // (1)默認爲false if (hasExternalRepositories && searchExternalFirst) { try { clazz = super.findClass(name); } catch(ClassNotFoundException cnfe) { // Ignore - will search internal repositories next } catch(AccessControlException ace) { log.warn("WebappClassLoaderBase.findClassInternal(" + name + ") security exception: " + ace.getMessage(), ace); throw new ClassNotFoundException(name, ace); } catch (RuntimeException e) { if (log.isTraceEnabled()) log.trace(" -->RuntimeException Rethrown", e); throw e; } } // (2) if ((clazz == null)) { try { clazz = findClassInternal(name); } catch(ClassNotFoundException cnfe) { if (!hasExternalRepositories || searchExternalFirst) { throw cnfe; } } catch(AccessControlException ace) { log.warn("WebappClassLoaderBase.findClassInternal(" + name + ") security exception: " + ace.getMessage(), ace); throw new ClassNotFoundException(name, ace); } catch (RuntimeException e) { if (log.isTraceEnabled()) log.trace(" -->RuntimeException Rethrown", e); throw e; } } //其餘代碼略去........ return (clazz); }
加載過程:
resourceEntries
這個數據結構中),若是已經加載即返回,不然 繼續下一步。
5、其餘破壞了雙親委派模型的技術
1. OSGI是基於Java語言的動態模塊化規範,類加載器之間是網狀結構,更加靈活,可是也更復雜
2. JNDI服務,使用線程上線文類加載器,父類加載器去使用子類加載器
參考文檔:
http://www.javashuo.com/article/p-nqgssvfe-d.html