關(guān)于ClassLoader In Tomcat 的研究
Posted on 2007-05-04 09:22 久城 閱讀(4143) 評(píng)論(7) 編輯 收藏 所屬分類: Java轉(zhuǎn)載收回五一假期那顆破碎的心,繼續(xù)我的程序人生。
今天打算研究下ClassLoader在Tomcat中的工作。
以下文字來(lái)自http://hi.baidu.com/wuzejian/blog/item/678c21e92aa6483eb90e2d21.html
1 - Tomcat的類載入器的結(jié)構(gòu)
Tomcat Server在啟動(dòng)的時(shí)候?qū)?gòu)造一個(gè)ClassLoader樹,以保證模塊的類庫(kù)是私有的
Tomcat Server的ClassLoader結(jié)構(gòu)如下:
其中:
- Bootstrap - 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
- System - 載入$CLASSPATH/*.class
- Common - 載入$CATALINA_HOME/common/...,它們對(duì)TOMCAT和所有的WEB APP都可見(jiàn)
- Catalina - 載入$CATALINA_HOME/server/...,它們僅對(duì)TOMCAT可見(jiàn),對(duì)所有的WEB APP都不可見(jiàn)
- Shared - 載入$CATALINA_HOME/shared/...,它們僅對(duì)所有WEB APP可見(jiàn),對(duì)TOMCAT不可見(jiàn)(也不必見(jiàn))
- WebApp? - 載入ContextBase?/WEB-INF/...,它們僅對(duì)該WEB APP可見(jiàn)
2 - ClassLoader的工作原理
每個(gè)運(yùn)行中的線程都有一個(gè)成員contextClassLoader,用來(lái)在運(yùn)行時(shí)動(dòng)態(tài)地載入其它類
系統(tǒng)默認(rèn)的contextClassLoader是systemClassLoader,所以一般而言java程序在執(zhí)行時(shí)可以使用JVM自帶的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類
可以使用Thread.currentThread().setContextClassLoader(...);更改當(dāng)前線程的contextClassLoader,來(lái)改變其載入類的行為
ClassLoader被組織成樹形,一般的工作原理是:
1) 線程需要用到某個(gè)類,于是contextClassLoader被請(qǐng)求來(lái)載入該類
2) contextClassLoader請(qǐng)求它的父ClassLoader來(lái)完成該載入請(qǐng)求
3) 如果父ClassLoader無(wú)法載入類,則contextClassLoader試圖自己來(lái)載入
注意:WebApp?ClassLoader的工作原理和上述有少許不同:
它先試圖自己載入類(在ContextBase?/WEB-INF/...中載入類),如果無(wú)法載入,再請(qǐng)求父ClassLoader完成
由此可得:
- 對(duì)于WEB APP線程,它的contextClassLoader是WebApp?ClassLoader
- 對(duì)于Tomcat Server線程,它的contextClassLoader是CatalinaClassLoader
3 - 部分原代碼分析
3.1 - org/apache/catalina/startup/Bootstrap.java
Tomcat Server線程的起點(diǎn)
構(gòu)造ClassLoader樹,并設(shè)置Tomcat Server線程的contextClassLoader為catalinaloader
載入若干類,然后轉(zhuǎn)入org.apache.catalina.startup.Catalina類中
---------------------------------------
package org.apache.catalina.startup;
// JDK類庫(kù)
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自己的類庫(kù)
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方法的主要任務(wù): --------------------------
*
* 1,創(chuàng)建TOMCAT自己的類載入器(ClassLoader) +---------------------------+ | Bootstrap | | | | |
* System | | | | | Common | | / \ | | Catalina Shared |
* +---------------------------+ 其中: - Bootstrap -
* 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar - System - 載入$CLASSPATH/*.class -
* Common - 載入$CATALINA_HOME/common/
,它們對(duì)TOMCAT和所有的WEB APP都可見(jiàn) - Catalina -
* 載入$CATALINA_HOME/server/
,它們僅對(duì)TOMCAT可見(jiàn),對(duì)所有的WEB APP都不可見(jiàn) - Shared -
* 載入$CATALINA_HOME/shared/
,它們僅對(duì)所有WEB APP可見(jiàn),對(duì)TOMCAT不可見(jiàn)(也不必見(jiàn))
* 注意:當(dāng)一個(gè)ClassLoader被請(qǐng)求載入一個(gè)類時(shí),它首先請(qǐng)求其父ClassLoader完成載入,
* 僅當(dāng)其父ClassLoader無(wú)法載入該類時(shí),才試圖自己載入該類 2,改變本身線程的默認(rèn)ClassLoader(本線程就是Tomcat
* Server線程,類載入器是catalinaLoader)
* 3,讓catalinaLoader載入一些類,類的位置在$CATALINA_HOME/server/lib/catalina.jar中
* 4,創(chuàng)建org.apache.catalina.startup.Catalina類的一個(gè)實(shí)例startupInstance,并為其調(diào)用方法:
* startupInstance.setParentClassLoader(sharedLoader);
* startupInstance.process(args);
*
*
* 有關(guān)ClassLoader的說(shuō)明: -----------------------
*
* 每個(gè)被DEPLOY的WEB APP都會(huì)被創(chuàng)建一個(gè)ClassLoader,用來(lái)載入該WEB APP自己的類
* 這些類的位置是webappX/WEB-INF/classes/*.class和webappX/WEB-INF/lib/*.jar
*
* ClassLoader的工作流程是: 1) 收到一個(gè)載入類的的請(qǐng)求 2) 請(qǐng)求其父ClassLoader來(lái)完成該類的載入 3)
* 如果父ClassLoader無(wú)法載入,則自己試圖完成該類的載入
*
* 特別注意WEB APP自己的ClassLoader的實(shí)現(xiàn)與眾不同: 它先試圖從WEB APP自己的目錄里載入,如果失敗則請(qǐng)求父ClassLoader的代理
* 這樣可以讓不同的WEB APP之間的類載入互不干擾
*
* WEB APP的ClassLoader的層次結(jié)構(gòu)是: +----------------------------+ | Shared | | / \
*
| | Webapp1 Webapp2
| +----------------------------+ 故對(duì)于一個(gè)WEB
* APP,其類載入的優(yōu)先順序如下: - /WEB-INF/classes/*.class 和 /WEB-INF/lib/*.jar - Bootstrap
* classes of JVM - System class loader classes - $CATALINA_HOME/common/
-
* $CATALINA_HOME/shared/
*
*
* 小結(jié): ------
*
* 綜上分析 - Tomcat Server線程使用的classLoader是Catalina - 每個(gè)WEB
* APP線程使用的classloader是Webapp?
*
*/
public final class Bootstrap
{
/** *//**
* DEBUG級(jí)別
*/
private static int debug = 0;
/** *//**
* 腳本執(zhí)行該程序時(shí),提供以下的系統(tǒng)屬性: 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[])
{
// 設(shè)置debug
for (int i = 0; i < args.length; i++)
{
if ("-debug".equals(args[i]))
debug = 1;
}
// 設(shè)置好系統(tǒng)屬性catalina.base,即保證其有值
if (System.getProperty("catalina.base") == null)
System.setProperty("catalina.base", getCatalinaHome());
// 創(chuàng)建三個(gè)ClassLoader
// 這三個(gè)對(duì)象是通過(guò)ClassLoaderFactory的靜態(tài)方法創(chuàng)建的
// 其實(shí)際類型是StandardClassLoader,完成tomcat自定義的類載入
// 這些類對(duì)非tomcat及其上的webapp的其它java程序不可見(jiàn),故用自己的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負(fù)責(zé)載入
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使用而對(duì)webapp不可見(jiàn)的類,由catalinaLoader負(fù)責(zé)載入
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負(fù)責(zé)載入
unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator
+ "classes");
packed[0] = new File(getCatalinaBase(), "shared" + File.separator
+ "lib");
sharedLoader = ClassLoaderFactory.createClassLoader(unpacked,
packed, commonLoader);
// 注意三個(gè)自己定置的ClassLoader的層次關(guān)系:
// systemClassLoader (root)
// +--- commonLoader
// +--- catalinaLoader
// +--- sharedLoader
} catch (Throwable t)
{
log("Class loader creation threw exception", t);
System.exit(1);
}
// 為當(dāng)前的線程更改其contextClassLoader
// 一般的線程默認(rèn)的contextClassLoader是系統(tǒng)的ClassLoader(所有其它自定義ClassLoader的父親)
// 當(dāng)該線程需要載入類時(shí),將使用自己的contextClassLoader來(lái)尋找并載入類
// 更改contextClassLoader可以更改該線程的尋找和載入類的行為,但不影響到其它線程
// 注意!Tomcat Server線程使用的是catalinaLoader
Thread.currentThread().setContextClassLoader(catalinaLoader);
// Load our startup class and call its process() method
try
{
// 預(yù)載入catalinalLoader的一些類
SecurityClassLoad.securityClassLoad(catalinaLoader);
// 獲得tomcat的啟動(dòng)類:org.apache.catalina.startup.Catalina,并創(chuàng)建該類的一個(gè)實(shí)例
if (debug >= 1)
log("Loading startup class");
Class startupClass = catalinaLoader
.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// 設(shè)置startupInstance的父ClassLoader,相當(dāng)于執(zhí)行:
// 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方法獲得的參數(shù)args來(lái)執(zhí)行process方法,相當(dāng)于:
// 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變量。如果該變量沒(méi)有定義,則將之賦值為用戶的當(dāng)前工作目錄。
*/
private static String getCatalinaHome()
{
return System.getProperty("catalina.home", System
.getProperty("user.dir"));
}
/** *//**
* 返回$CATALINA_BASE變量。如果該變量沒(méi)有定義,則將之賦值為$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 | +-----------------------------+
還有很多讓我疑惑的地方,轉(zhuǎn)過(guò)來(lái)慢慢學(xué)習(xí)。
歡迎來(lái)訪!^.^!
本BLOG僅用于個(gè)人學(xué)習(xí)交流!
目的在于記錄個(gè)人成長(zhǎng).
所有文字均屬于個(gè)人理解.
如有錯(cuò)誤,望多多指教!不勝感激!