您的位置:首页 > 运维架构 > Tomcat

Tomcat ClassLoader研究

2009-08-30 16:23 239 查看
http://tomcat.apache.org/tomcat-4.1-doc/class-loader-howto.html

Tomcat的ClassLoader层次结构:









Bootstrap

|

System

|

Common

/      /

Catalina   Shared

/   /

Webapp1  Webapp2 ...









源代码如下:

org.apache.catalina.startup.Bootstrap类(tomcat主类)

public final class Bootstrap {
// ------------------------------------------------------- Static Variables
/**
* Debugging detail level for processing the startup.
*/
private static int debug = 0;
// ----------------------------------------------------------- Main Program
/**
* The main program for the bootstrap.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
// Set the debug flag appropriately
for (int i = 0; i < args.length; i++)  {
if ("-debug".equals(args[i]))
debug = 1;
}
// Configure catalina.base from catalina.home if not yet set
if (System.getProperty("catalina.base") == null)
System.setProperty("catalina.base", getCatalinaHome());
// Construct the class loaders we will need
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);
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);
unpacked[0] = new File(getCatalinaHome(),
"server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(),
"server" + File.separator + "lib");
catalinaLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
unpacked[0] = new File(getCatalinaBase(),
"shared" + File.separator + "classes");
packed[0] = new File(getCatalinaBase(),
"shared" + File.separator + "lib");
sharedLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
} catch (Throwable t) {
log("Class loader creation threw exception", t);
System.exit(1);
}
Thread.currentThread().setContextClassLoader(catalinaLoader);
// Load our startup class and call its process() method
try {
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Instantiate a startup class instance
if (debug >= 1)
log("Loading startup class");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// Set the shared extensions class loader
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);
// Call the process() method
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);
}
}


其中:

commonLoader =

ClassLoaderFactory.createClassLoader(unpacked, packed2, null);

创建common classloader,以AppClassLoader为父ClassLoader

catalinaLoader =

ClassLoaderFactory.createClassLoader(unpacked, packed,

commonLoader);

创建catalina classloader,以common classloader为父classloader

sharedLoader =

ClassLoaderFactory.createClassLoader(unpacked, packed,

commonLoader);

创建share classloader,以common classloader为父classloader

Thread.currentThread().setContextClassLoader(catalinaLoader);

设置ContextClassLoader为catalina classloader

org.apache.catalina.startup.ClassLoaderFactory类

public static ClassLoader createClassLoader(File unpacked[],
File packed[],
ClassLoader parent)
throws Exception {
if (debug >= 1)
log("Creating new class loader");
// Construct the "class path" for this class loader
ArrayList list = new ArrayList();
// Add unpacked directories
if (unpacked != null) {
for (int i = 0; i < unpacked.length; i++)  {
File file = unpacked[i];
if (!file.isDirectory() || !file.exists() || !file.canRead())
continue;
if (debug >= 1)
log("  Including directory " + file.getAbsolutePath());
URL url = new URL("file", null,
file.getCanonicalPath() + File.separator);
list.add(url.toString());
}
}
// Add packed directory JAR files
if (packed != null) {
for (int i = 0; i < packed.length; i++) {
File directory = packed[i];
if (!directory.isDirectory() || !directory.exists() ||
!directory.canRead())
continue;
String filenames[] = directory.list();
for (int j = 0; j < filenames.length; j++) {
String filename = filenames[j].toLowerCase();
if (!filename.endsWith(".jar"))
continue;
File file = new File(directory, filenames[j]);
if (debug >= 1)
log("  Including jar file " + file.getAbsolutePath());
URL url = new URL("file", null,
file.getCanonicalPath());
list.add(url.toString());
}
}
}
// Construct the class loader itself
String array[] = (String[]) list.toArray(new String[list.size()]);
StandardClassLoader classLoader = null;
if (parent == null)
classLoader = new StandardClassLoader(array);
else
classLoader = new StandardClassLoader(array, parent);
classLoader.setDelegate(true);
return (classLoader);
}


ClassLoaderFactory创建的是StandardClassLoader(org.apache.catalina.loader包中)

由Bootstrap类调用Catalina类的process()方法,再调用execute()方法,再调用start()来启动Server实例。

当Tomcat开启每个Context时,是调用的StandardContext的start()方法,其中:

设置Loader为WebappLoader

if (getLoader() == null) {      // (2) Required by Manager
if (getPrivileged()) {
if (debug >= 1)
log("Configuring privileged default Loader");
setLoader(new WebappLoader(this.getClass().getClassLoader()));
} else {
if (debug >= 1)
log("Configuring non-privileged default Loader");
setLoader(new WebappLoader(getParentClassLoader()));
}
}


然后调用:bindThread(),设置当前线程的ClassLoader为WebappLoader的ClassLoader,即为WebappClassLoader

private ClassLoader bindThread() {
ClassLoader oldContextClassLoader =
Thread.currentThread().getContextClassLoader();
if (getResources() == null)
return oldContextClassLoader;
Thread.currentThread().setContextClassLoader
(getLoader().getClassLoader());
DirContextURLStreamHandler.bind(getResources());
if (isUseNaming()) {
try {
ContextBindings.bindThread(this, this);
} catch (NamingException e) {
// Silent catch, as this is a normal case during the early
// startup stages
}
}
return oldContextClassLoader;
}


WebappLoader的ClassLoader的赋值如下:

private String loaderClass =
"org.apache.catalina.loader.WebappClassLoader";
......
public void start() throws LifecycleException {
// Validate and update our current component state
if (started)
throw new LifecycleException
(sm.getString("webappLoader.alreadyStarted"));
if (debug >= 1)
log(sm.getString("webappLoader.starting"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
if (container.getResources() == null)
return;
// Register a stream handler factory for the JNDI protocol
URLStreamHandlerFactory streamHandlerFactory =
new DirContextURLStreamHandlerFactory();
try {
URL.setURLStreamHandlerFactory(streamHandlerFactory);
} catch (Throwable t) {
// Ignore the error here.
}
// Construct a class loader based on our current repositories list
try {
classLoader = createClassLoader();
classLoader.setResources(container.getResources());
classLoader.setDebug(this.debug);
classLoader.setDelegate(this.delegate);
if (container instanceof StandardContext)
classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());
for (int i = 0; i < repositories.length; i++) {
classLoader.addRepository(repositories[i]);
}
// Configure our repositories
setRepositories();
setClassPath();
setPermissions();
if (classLoader instanceof Lifecycle)
((Lifecycle) classLoader).start();
// Binding the Webapp class loader to the directory context
DirContextURLStreamHandler.bind
((ClassLoader) classLoader, this.container.getResources());
} catch (Throwable t) {
throw new LifecycleException("start: ", t);
}
// Validate that all required packages are actually available
validatePackages();
// Start our background thread if we are reloadable
if (reloadable) {
log(sm.getString("webappLoader.reloading"));
try {
threadStart();
} catch (IllegalStateException e) {
throw new LifecycleException(e);
}
}
}
......
private WebappClassLoader createClassLoader()
throws Exception {
Class clazz = Class.forName(loaderClass);
WebappClassLoader classLoader = null;
if (parentClassLoader == null) {
// Will cause a ClassCast is the class does not extend WCL, but
// this is on purpose (the exception will be caught and rethrown)
classLoader = (WebappClassLoader) clazz.newInstance();
} else {
Class[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoader) constr.newInstance(args);
}
return classLoader;
}


在WebappClassLoader中,其findClass的搜索顺序与一般的ClassLoader的搜索顺序不同。

一般的ClassLoader的搜索顺序为:

将其委托给父ClassLoader,如果父ClassLoader不能载入相应类,则才交给自己处理

但是WebappClassLoader中,其是先由自己来处理,如果不行再委托给父ClassLoader

相关源代码如下:

Class clazz = null;
try {
if (debug >= 4)
log("      findClassInternal(" + name + ")");
try {
clazz = findClassInternal(name);
} catch(ClassNotFoundException cnfe) {
if (!hasExternalRepositories) {
throw cnfe;
}
} catch(AccessControlException ace) {
ace.printStackTrace();
throw new ClassNotFoundException(name);
} catch (RuntimeException e) {
if (debug >= 4)
log("      -->RuntimeException Rethrown", e);
throw e;
}
if ((clazz == null) && hasExternalRepositories) {
try {
clazz = super.findClass(name);
} catch(AccessControlException ace) {
throw new ClassNotFoundException(name);
} catch (RuntimeException e) {
if (debug >= 4)
log("      -->RuntimeException Rethrown", e);
throw e;
}
}
if (clazz == null) {
if (debug >= 3)
log("    --> Returning ClassNotFoundException");
throw new ClassNotFoundException(name);
}
} catch (ClassNotFoundException e) {
if (debug >= 3)
log("    --> Passing on ClassNotFoundException", e);
throw e;
}


以下引自tomcat的说明文档,说明了加载类的顺序

Therefore, from the perspective of a web application, class or resource
loading looks in the following repositories, in this order:

/WEB-INF/classes
of your web application

/WEB-INF/lib/*.jar
of your web application

Bootstrap classes of your JVM

System class loader classses (described above)

$CATALINA_HOME/common/classes

$CATALINA_HOME/common/endorsed/*.jar

$CATALINA_HOME/common/lib/*.jar

$CATALINA_BASE/shared/classes

$CATALINA_BASE/shared/lib/*.jar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: