Tomcat類加載

1、類加載器實例之——Tomcat

主流的Java Web服務器,如Tomcat、Jetty、WebLogic、WebSphere,都實現了本身定義的類加載器(通常都不止一個)。由於一個功能健全的Web服務器,要解決以下問題:java

  • 部署在同一個服務器上的兩個Web應用程序所使用的Java類庫可使用相互隔離。這是最基本的要求,由於不一樣應用可能依賴同一個類庫的不一樣版本,不能要求每一個類庫在一個服務器只有一個版本,應當保證兩個應用程序的類庫能夠互相獨立使用
  • 部署在同一個服務器上的兩個Web應用程序所使用的Java類庫能夠互相共享。這個也很正常,好比我有10個應用程序,都用到Spring,那麼我不可能放10份Spring相關的類庫吧。
  • 服務器須要儘量地保證自身的安全不受部署的Web應用程序影響。目前不少Java Web服務器也是Java實現的,所以確定有庫依賴問題。通常來講,基於安全考慮,服務器所使用的類庫應該和應用程序的類庫相互獨立
  • 支持Jsp應用的Web服務器,十有八九都須要支持HotSwap功能。由於Jsp本質上仍是須要被編譯成.class文件才能被JVM執行,但Jsp在運行時被修改的機率比較大。所以主流Web服務器都會支持Jsp生成類的熱替換

因爲以上幾個要求,若是隻有一個CLASSPATH就很難實現了。因此各類Web服務器都會提供好幾個CLASSPATH路徑供用 戶存放第三方類庫,這些路徑通常都以lib或者classes命名。被放置在不一樣目錄的類庫,具有不一樣的訪問範圍和服務對象。一般而言,每個目錄都會有 一個對應的自定義類加載器去加載設置在裏面的Java類庫。那麼咱們就以Tomcat爲例來看一下。程序員

1. Tomcat目錄結構

在Tomcat目錄結構中,有三組目錄能夠存放Java類庫,另外加上應用程序自身的目錄,一共四組:web

  1. 放置在/common目錄中:類庫可被Tomcat和全部Web應用程序共同使用
  2. 放置在/server目錄中:類庫只能被Tomcat使用
  3. 放置在/shared目錄中:類庫能夠Web應用程序共同使用,可是Tomcat不能使用
  4. 放置在/webapp/WEB-INF目錄中:僅僅能夠被這個Web應用程序使用,Tomcat和其它Web應用程序不能使用

爲了支持這套目錄,並對目錄內的類庫進行加載和隔離,Tomcat自定義了幾個類加載器,這些類加載器按照經典的雙親委派模型來實現,以下圖所示:安全

img

  • 其中最上層的3個是JVM的類加載器(由於Tomcat也至關於一個Java程序,它只是將程序員寫的Java程序作了一個封裝)
  • CommonClassLoader:對應/common目錄
  • CatalinaClassLoader:對應/server目錄
  • SharedClassLoader:對應/shared目錄
  • WebAppClassLoader:對應/webapp/WEB-INF目錄

其中WebAppClassLoader和JsperClassLoader會存在多個實例,每個WebApp對應一個WebApp類加載器,每個Jsp文件對應一個Jsp類加載器。服務器

上面的圖咱們也能看出它們之間的隔離性:架構

  • Common類加載器加載的類能夠被Catalina類加載器和Shared類加載器使用
  • Catalina類加載器和Shared類加載器相互隔離
  • WebApp類加載器可使用Shared類加載器加載的類
  • 各個WebApp類加載器之間加載的類相互隔離
  • 各個Jsp類加載器加載的僅僅是這個Jsp文件編譯出來的那個.class文件,它出現的目的就是被丟棄,若是修改,就會新建一個Jsp類加載器替換原來的那個,實現HotSwap

2. Tomcat版本升級

對於Tomcat的6.x版本,只有指定了conf/catalina.properties配置文件中的server.loader和 share.loader纔會真正創建CatalinaClassLoader和SharedClassLoader的實例,不然會以 CommonClassLoader代替。而默認的配置裏面是沒有設置這兩個loader的,因此Tomcat 6.x瓜熟蒂落的把/commmon、/server、/shared這三個目錄合併爲一個/lib目錄。這是Tomcat團隊爲了簡化大多數部署場景所 作的一項改進,若是默認設置不能知足需求,再經過修改配置完成3種類加載器的協同分工。app

2、OSGi:靈活的類加載器結構

傳說Java社區流傳這樣一句話:學習JEE規範,去看JBoss源碼;學習類加載器,去看OSGi源碼。可見,OSGi的類加載器機制確實值得學習。webapp

OSGi(Open Service Gateway Initiative)是OSGi聯盟制定的一個基於Java語言的動態模塊化規範,最初是Sun、IBM、愛立信等公司聯合發起,目的是使服務提供商經過住宅網關爲各類家用智能設備提供各類服務,後來這個規範在Java其餘技術領域都有不錯的發展,如今已經成爲Java世界中「事實上」的模塊化標準。OSGi在程序員中最著名的應用案例Eclipse。你對Eclipse進行設置後,不須要重啓就能夠完成某個功能的開啓或關閉,這就是OSGi的功勞。模塊化

看了以後,給個人感受就是這東西很是靈活。OSGi的基本單位是Bundle,每一個Bundle均可以有本身的類加載器和父加載器。而Bundle 和類也差很少,內部也是package和class組成。可是一個Bundle能夠聲明它依賴的Java Packeage(經過Import-Package描述),也能夠聲明它容許導出發佈的Java Package(經過Export-Package描述)。在OSGi中,Bundle之間的依賴關係從傳統的上層模塊依賴疊層模塊轉變爲平級模塊之間的依賴,並且類庫的可見性獲得了很是精確的控制,一個模塊裏只有被Export過的Package才能被外界訪問,其餘的Package和class會被隱藏。因此,OSGi才能支持熱插拔技術。學習

而OSGi擁有這麼誘人特性的緣由就是它的類加載器架構。舉一個簡單的例子:

  • Bundle A:聲明發布了packageA,依賴了java.*的包
  • Bundle B:聲明依賴了packageA和packageC,同時也依賴了java.*的包
  • Bundle C:聲明發布了packageC,依賴了packageA

那麼,三者之間的關係能夠用下圖表示:

img

從上圖能夠看出來,OSGi裏面的加載器再也不是雙親委派模型的樹形結構,而是進一步發展成了一種運行時才能肯定的網狀結構。更靈活的同時確定帶來了更復雜的使用方法,這點在實際應用中確實值得權衡。

通常來講,在OSGi中,加載一個類可能發生的查找行爲和委派關係會比上圖複雜的多,大致規則能夠總結爲:

  1. 以java.*開頭的類,委派給父類加載器加載
  2. 委派列表名單內的類,委派給父類加載器加載
  3. Import列表中的類,委派給Export這個類的Bundle的類加載器加載
  4. 查找當前Bundle的CLASSPATH,使用本身的類加載器加載
  5. 不然,查找是否在本身的Fragment Bundle中,若是是則委派給Fragment Bundle的類加載器加載
  6. 不然,查找Dynamic Import列表的Bundle,委派給對應Bundle的類加載器加載
  7. 不然,類查找失敗
相關文章
相關標籤/搜索