1.5 tomcat是如何打破雙親委派機制的?


一. tomcat是如何打破雙親委派機制的?

首先, 來舉個例子, 通常,一個tomcat要加載幾個應用程序呢? 當然是n多個應用程序, 加入我們使用的都是spring的框架, 那我們能保證所有的應用程序都是用spring4 或者spring5 么? 不可能, 他可能既有spring4的項目, 又有spring5的項目. 那么tomcat在加載spring4項目的war包是, 會不會和spring5項目的war包沖突呢? 因為spring4, 和 spring5中有很多類的類名是一樣的, 但是實現不一樣. 如果都是交給父類加載器加載, 那么肯定只能加載一份. 也就是spring4和spring5的項目不能共存. 而實際上的情況呢? 我們的tomcat可以加在各種各樣類型的war包, 相互之間沒有影響. 他是怎么做到的呢?

因為tomcat打破了雙親委派機制, 下面我們就來看看tomcat是如何打破雙親委派機制的?

 

 

 如上圖, 上面的橙色部門還是和原來一樣, 采用雙親委派機制. 而黃色部分是tomcat第一部分自定義的類加載器, 這部分主要是加載tomcat包中的類, 這一部分依然采用的是雙親委派機制, 而綠色部分是tomcat第二部分自定義類加載器, 正事這一部分, 打破了類的雙親委派機制. 先面我們就來詳細看看tomcat自定的類加載器

1. tomcat第一部分自定義類加載器(黃色部分)

這部分類加載器, 在tomcat7及以前是tomcat自定義的三個類加載器, 分別在家不同文件加載的jar包. 而到了tomcat8及以后, tomcat將這三個文件夾合並了, 合並成了一個lib包. 也就是我們現在看到的lib包

 

 我們來看看這三個類加載器的主要功能.

  • commonClassLoader: tomcat最基本的類加載器, 加載路徑中的class可以被tomcat容器本身和各個webapp訪問;
  • catalinaClassLoader: tomcat容器中私有的類加載器, 加載路徑中的class對於webapp不可見
  • sharedClassLoader: 各個webapps共享的類加載器, 加載路徑中的class對於所有的webapp都可見, 但是對於tomcat容器不可見.

這一部分類加載器, 依然采用的是雙親委派機制, 原因是, 他只有一份. 如果有重復, 那么也是以這一份為准. 

2. tomcat第二部分自定義類加載器(綠色部分)

 綠色部分是java項目在打war包的時候, tomcat自動生成的類加載器, 也就是說 , 每一個項目打成一個war包, tomcat都會自動生成一個類加載器, 專門用來加載這個war包. 而這個類加載器打破了雙親委派機制. 我們可以想象一下, 加入這個webapp類加載器沒有打破雙親委派機制會怎么樣?

如果沒有打破, 他就會委托父類加載器去加載, 一旦加載到了,  子類加載器就沒有機會在加載了. 那么, spring4和spring5的項目想共存, 那是不可能的了. 

所以, 這一部分他打破了雙親委派機制

 這樣一來, webapp類加載器不需要在讓上級去加載, 他自己就可以加載對應war里的class文件. 當然了, 其他的項目文件, 還是要委托上級加載的. 

 

下面我們來實現一個自定義的類加載器

二. 自定義tomcat的war包類加載器

如何打破雙親委派機制, 我們已經寫過一個demo了. 詳見: https://www.cnblogs.com/ITPower/p/13211490.html

那么, 現在我有兩個war包, 分處於不同的文件夾, tomcat如何使用各自的類加載器加載自己包下的class類呢?

我們來舉個例子, 比如: 在我的home目錄下有兩個文件夾, tomcat-test和tomcat-test1. 用這兩個文件夾來模擬兩個項目.

 

 在他們的下面都有一個com/lxl/jvm/User1.class

 

 雖然類名和類路徑都是一樣的,但是他們的內容是不同的

 

 

 

 

這個時候,如果tomcat要同時加載這兩個目錄下的User1.class文件, 我們如何操作呢?

 其實,非常簡單, 按照上面的思路, tomcat只需要為每一個文件夾生成一個新的類加載器就可以了.

public static void main(String[] args) throws Exception {
     // 第一個類加載器 DefinedClassLoaderTest classLoader
= new DefinedClassLoaderTest("/Users/luoxiaoli/tomcat-test"); Class<?> clazz = classLoader.loadClass("com.lxl.jvm.User1"); Object obj = clazz.newInstance(); Method sout = clazz.getDeclaredMethod("sout", null); sout.invoke(obj, null); System.out.println(clazz.getClassLoader().getClass().getName());      // 第二個類加載器 DefinedClassLoaderTest classLoader1 = new DefinedClassLoaderTest("/Users/luoxiaoli/tomcat-test1"); Class<?> clazz1 = classLoader1.loadClass("com.lxl.jvm.User1"); Object obj1 = clazz1.newInstance(); Method sout1 = clazz1.getDeclaredMethod("sout", null); sout1.invoke(obj1, null); System.out.println(clazz1.getClassLoader().getClass().getName()); }

他們都是只加載自己目錄下的文件.  我們來看看執行結果:

調用了user1的sout方法
com.lxl.jvm.DefinedClassLoaderTest

調用了另外一個項目user1的sout方法, 他們是不同的
com.lxl.jvm.DefinedClassLoaderTest

 

 

三. 延伸思考: 我們看到上面tomcat自定義的類加載器中, 還有一個jsp類加載器. jsp是可以實現熱部署的, 那么他是如何實現的呢?

我們都知道jsp其實是一個servlet容器, 有tomcat加載. tomcat會為每一個jsp生成一個類加載器. 這樣每個類加載器都加載自己的jsp, 不會加載別人的. 當jsp文件內容修改時, tomcat會有一個監聽程序來監聽jsp的改動. 比如文件夾的修改時間, 一旦時間變了, 就重新加載文件夾中的內容. 

具體tomcat是怎么實現的呢? tomcat自定義了一個thread, 用來監聽不同文件夾中文件的內容是否修改, 如何監聽呢? 就看文件夾的update time有沒有變化, 如果有變化了, 那么就會重新加載.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM