Tomcat的classloader

1 - Tomcat的類載入器的結構 
Tomcat Server在啓動的時候將構造一個ClassLoader樹,以保證模塊的類庫是私有的
Tomcat Server的ClassLoader結構如下:
其中:
- Bootstrap - 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
- System - 載入$CLASSPATH/*.class
- Common - 載入$CATALINA_HOME/common/...,它們對TOMCAT和所有的WEB APP都可見
- Catalina - 載入$CATALINA_HOME/server/...,它們僅對TOMCAT可見,對所有的WEB APP都不可見
- Shared - 載入$CATALINA_HOME/shared/...,它們僅對所有WEB APP可見,對TOMCAT不可見(也不必見)
- WebApp? - 載入ContextBase?/WEB-INF/...,它們僅對該WEB APP可見




2 - ClassLoader的工作原理
每個運行中的線程都有一個成員contextClassLoader,用來在運行時動態地載入其它類
系統默認的contextClassLoader是systemClassLoader,所以一般而言java程序在執行時可以使用JVM自帶的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類
可以使用Thread.currentThread().setContextClassLoader(...);更改當前線程的contextClassLoader,來改變其載入類的行爲


ClassLoader被組織成樹形,一般的工作原理是:
1) 線程需要用到某個類,於是contextClassLoader被請求來載入該類
2) contextClassLoader請求它的父ClassLoader來完成該載入請求
3) 如果父ClassLoader無法載入類,則contextClassLoader試圖自己來載入


注意:WebApp?ClassLoader的工作原理和上述有少許不同:
它先試圖自己載入類(在ContextBase?/WEB-INF/...中載入類),如果無法載入,再請求父ClassLoader完成


由此可得:
- 對於WEB APP線程,它的contextClassLoader是WebApp?ClassLoader
- 對於Tomcat Server線程,它的contextClassLoader是CatalinaClassLoader


3 - 部分原代碼分析
3.1 - org/apache/catalina/startup/Bootstrap.java

Tomcat Server線程的起點
構造ClassLoader樹,並設置Tomcat Server線程的contextClassLoader爲catalinaloader
載入若干類,然後轉入org.apache.catalina.startup.Catalina類中


---------------------------------------package org.apache.catalina.startup;// JDK類庫import java.io.File;import java.io.IOException;import java.lang.reflect.Method;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;// apache自己的類庫import org.apache.catalina.loader.Extension;import org.apache.catalina.loader.StandardClassLoader;/** *//** * Boostrap loader for Catalina. This application constructs a class loader * for use in loading the Catalina internal classes (by accumulating all of the * JAR files found in the "server" directory under "catalina.home"), and * starts the regular execution of the container. The purpose of this * roundabout approach is to keep the Catalina internal classes (and any * other classes they depend on, such as an XML parser) out of the system * class path and therefore not visible to application level classes. * * @author Craig R. McClanahan * @version $Revision: 1.36 $ $Date: 2002/04/01 19:51:31 $ *//** *//** * 該類的main方法的主要任務: -------------------------- * * 1,創建TOMCAT自己的類載入器(ClassLoader) +---------------------------+ | Bootstrap | | | | | * System | | | | | Common | | / \ | | Catalina Shared | * +---------------------------+ 其中: - Bootstrap - * 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar - System - 載入$CLASSPATH/*.class - * Common - 載入$CATALINA_HOME/common/,它們對TOMCAT和所有的WEB APP都可見 - Catalina - * 載入$CATALINA_HOME/server/,它們僅對TOMCAT可見,對所有的WEB APP都不可見 - Shared - * 載入$CATALINA_HOME/shared/,它們僅對所有WEB APP可見,對TOMCAT不可見(也不必見) * 注意:當一個ClassLoader被請求載入一個類時,它首先請求其父ClassLoader完成載入, * 僅當其父ClassLoader無法載入該類時,才試圖自己載入該類 2,改變本身線程的默認ClassLoader(本線程就是Tomcat * Server線程,類載入器是catalinaLoader) * 3,讓catalinaLoader載入一些類,類的位置在$CATALINA_HOME/server/lib/catalina.jar中 * 4,創建org.apache.catalina.startup.Catalina類的一個實例startupInstance,併爲其調用方法: * startupInstance.setParentClassLoader(sharedLoader); * startupInstance.process(args); * * * 有關ClassLoader的說明: ----------------------- * * 每個被DEPLOY的WEB APP都會被創建一個ClassLoader,用來載入該WEB APP自己的類 * 這些類的位置是webappX/WEB-INF/classes/*.class和webappX/WEB-INF/lib/*.jar * * ClassLoader的工作流程是: 1) 收到一個載入類的的請求 2) 請求其父ClassLoader來完成該類的載入 3) * 如果父ClassLoader無法載入,則自己試圖完成該類的載入 * * 特別注意WEB APP自己的ClassLoader的實現與衆不同: 它先試圖從WEB APP自己的目錄裏載入,如果失敗則請求父ClassLoader的代理 * 這樣可以讓不同的WEB APP之間的類載入互不干擾 * * WEB APP的ClassLoader的層次結構是: +----------------------------+ | Shared | | / \ * | | Webapp1 Webapp2 | +----------------------------+ 故對於一個WEB * APP,其類載入的優先順序如下: - /WEB-INF/classes/*.class 和 /WEB-INF/lib/*.jar - Bootstrap * classes of JVM - System class loader classes - $CATALINA_HOME/common/ - * $CATALINA_HOME/shared/ * * * 小結: ------ * * 綜上分析 - Tomcat Server線程使用的classLoader是Catalina - 每個WEB * APP線程使用的classloader是Webapp? * */public final class Bootstrap { /** *//** * DEBUG級別 */ private static int debug = 0; /** *//** * 腳本執行該程序時,提供以下的系統屬性: java.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath * "$CLASSPATH" \ java.security.manager \ * java.security.policy=="$CATALINA_BASE"/conf/catalina.policy \ * catalina.base="$CATALINA_BASE" \ catalina.home="$CATALINA_HOME" \ * java.io.tmpdir="$CATALINA_TMPDIR" \ * * @param args * Command line arguments to be processed */ public static void main(String args[]) { // 設置debug for (int i = 0; i < args.length; i++) { if ("-debug".equals(args[i])) debug = 1; } // 設置好系統屬性catalina.base,即保證其有值 if (System.getProperty("catalina.base") == null) System.setProperty("catalina.base", getCatalinaHome()); // 創建三個ClassLoader // 這三個對象是通過ClassLoaderFactory的靜態方法創建的 // 其實際類型是StandardClassLoader,完成tomcat自定義的類載入 // 這些類對非tomcat及其上的webapp的其它java程序不可見,故用自己的Classloader載入 ClassLoader commonLoader = null; ClassLoader catalinaLoader = null; ClassLoader sharedLoader = null; try { File unpacked[] = new File[1]; File packed[] = new File[1]; File packed2[] = new File[2]; ClassLoaderFactory.setDebug(debug); // $CATALINA_HOME/common/classes/*.class - 未壓縮的類 // $CATALINA_HOME/common/endorsed/*.jar - 壓縮的類(endorse:支持) // $CATALINA_HOME/common/lib/*.jar - 壓縮的類 // 這些類是被tomcat server以及所有的webapp所共享的類,由commonLoader負責載入 unpacked[0] = new File(getCatalinaHome(), "common" + File.separator + "classes"); packed2[0] = new File(getCatalinaHome(), "common" + File.separator + "endorsed"); packed2[1] = new File(getCatalinaHome(), "common" + File.separator + "lib"); commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null); // $CATALINA_HOME/server/classes/*.class // $CATALINA_HOME/server/lib/*.jar // 這些類是僅被tomcat server使用而對webapp不可見的類,由catalinaLoader負責載入 unpacked[0] = new File(getCatalinaHome(), "server" + File.separator + "classes"); packed[0] = new File(getCatalinaHome(), "server" + File.separator + "lib"); catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); // $CATALINA_BASE/shared/classes/*.class // $CATALINA_BASE/shared/lib/*.jar // 這些類是僅被tomcat的webapp使用的類,由sharedLoader負責載入 unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator + "classes"); packed[0] = new File(getCatalinaBase(), "shared" + File.separator + "lib"); sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); // 注意三個自己定置的ClassLoader的層次關係: // systemClassLoader (root) // +--- commonLoader // +--- catalinaLoader // +--- sharedLoader } catch (Throwable t) { log("Class loader creation threw exception", t); System.exit(1); } // 爲當前的線程更改其contextClassLoader // 一般的線程默認的contextClassLoader是系統的ClassLoader(所有其它自定義ClassLoader的父親) // 當該線程需要載入類時,將使用自己的contextClassLoader來尋找並載入類 // 更改contextClassLoader可以更改該線程的尋找和載入類的行爲,但不影響到其它線程 // 注意!Tomcat Server線程使用的是catalinaLoader Thread.currentThread().setContextClassLoader(catalinaLoader); // Load our startup class and call its process() method try { // 預載入catalinalLoader的一些類 SecurityClassLoad.securityClassLoad(catalinaLoader); // 獲得tomcat的啓動類:org.apache.catalina.startup.Catalina,並創建該類的一個實例 if (debug >= 1) log("Loading startup class"); Class startupClass = catalinaLoader .loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // 設置startupInstance的父ClassLoader,相當於執行: // Catalina startupInstance = new Catailina(); // startupInstance.setParentClassLoader(sharedLoader); // 詳情參考類org.apache.catalina.startup.Catalina if (debug >= 1) log("Setting startup class properties"); String methodName = "setParentClassLoader"; Class paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); // 使用main方法獲得的參數args來執行process方法,相當於: // startupInstance.process(args); // 詳情參考類org.apache.catalina.startup.Catalina if (debug >= 1) log("Calling startup class process() method"); methodName = "process"; paramTypes = new Class[1]; paramTypes[0] = args.getClass(); paramValues = new Object[1]; paramValues[0] = args; method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); } catch (Exception e) { System.out.println("Exception during startup processing"); e.printStackTrace(System.out); System.exit(2); } } /** *//** * 返回$CATALINA_HOME變量。如果該變量沒有定義,則將之賦值爲用戶的當前工作目錄。 */ private static String getCatalinaHome() { return System.getProperty("catalina.home", System .getProperty("user.dir")); } /** *//** * 返回$CATALINA_BASE變量。如果該變量沒有定義,則將之賦值爲$CATALINA_HOME。 */ private static String getCatalinaBase() { return System.getProperty("catalina.base", getCatalinaHome()); } /** *//** * 輸出LOG信息。 */ private static void log(String message) { System.out.print("Bootstrap: "); System.out.println(message); } /** *//** * 輸出由異常引起的LOG信息。 */ private static void log(String message, Throwable exception) { log(message); exception.printStackTrace(System.out); }}
-------------------------------------------------------- +-----------------------------+
| Bootstrap |
| | |
| System |
| | |
| Common |
| / \ |
| Catalina Shared |
| / \ |
| WebApp1 WebApp2 |
+-----------------------------+還有很多讓我疑惑的地方,轉過來慢慢學習。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/tangyangbuaa/archive/2008/12/31/3672450.aspx
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章