tomcat类加载器 - 969251639/study GitHub Wiki
tomcat从设计开始就支持多个context一起运行,那么多个context运行又如何保证它们之间的jar互不影响,而且也能共用tomcat的公共jar包呢?为此tomcat设计了自己的类加载模式
在tomcat中,最重要的一个类加载器时Common加载器,它的父类时jdk应用程序类加载器,负责加载$CATALINA_BASE/lib、$CATALINA_HOME/lib两个目录下的所有class和jar包
在tomcat的启动类Bootstrap中,会调用Bootstrap的初始化方法,初始化方法中会创建好一套tomcat自己的类加载器
public final class Bootstrap {
...
public static void main(String args[]) {
...
Bootstrap bootstrap = new Bootstrap();
...
bootstrap.init();
...
}
public void init() throws Exception {
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("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);
catalinaDaemon = startupInstance;
}
...
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
...
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
...
}
}
上面的代码就创建了如下的类加载器结构
需要注意的是catalina(server)加载器和shared加载器在catalina.properties中都是空的,所以它们都是指向common加载器,如果需要改变它,则需要配置catalina.properties
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
另外tomcat还有一个很重要的类加载器是webapp class loader,主要用于加载每个context下面的web应用程序,它的父类是common类加载器,一个webapp应用程序对应一个webapp class loader。这样就能很好的隔离tomcat多个webapp应用程序互不影响,而Tomcat的common类加载器又能共用的效果。
因为webapp class loader是在context级别的加载器那么在创建context的时候也肯定会去创建自己的webapp class loader
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
protected synchronized void startInternal() throws LifecycleException {
...
if (getLoader() == null) {
//getParentClassLoader返回的时common类加载器
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
...
Loader loader = getLoader();
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
...
}
}
public class WebappLoader extends LifecycleMBeanBase
implements Loader, PropertyChangeListener {
...
private String loaderClass = WebappClassLoader.class.getName();
...
@Override
protected void startInternal() throws LifecycleException {
...
classLoader = createClassLoader();
classLoader.setResources(context.getResources());
classLoader.setDelegate(this.delegate);
...
((Lifecycle) classLoader).start();
...
}
private WebappClassLoaderBase createClassLoader()
throws Exception {
Class<?> clazz = Class.forName(loaderClass);
WebappClassLoaderBase classLoader = null;
if (parentClassLoader == null) {
parentClassLoader = context.getParentClassLoader();
}
Class<?>[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor<?> constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoaderBase) constr.newInstance(args);
return classLoader;
}
}
从上面的代码可以看到在context中创建了WebappLoader,而WebappLoader在内部又去创建了WebappClassLoader去真正加载jar和class
//WebappClassLoader的父类
public abstract class WebappClassLoaderBase extends URLClassLoader
implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {
//对应WebappLoader.startInternal方法中的((Lifecycle) classLoader).start();
@Override
public void start() throws LifecycleException {
state = LifecycleState.STARTING_PREP;
WebResource classes = resources.getResource("/WEB-INF/classes");
if (classes.isDirectory() && classes.canRead()) {
localRepositories.add(classes.getURL());
}
WebResource[] jars = resources.listResources("/WEB-INF/lib");
for (WebResource jar : jars) {
if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
localRepositories.add(jar.getURL());
jarModificationTimes.put(
jar.getName(), Long.valueOf(jar.getLastModified()));
}
}
...
}
}
可以看到WebappLoader是委托WebappClassLoader去加载自己webapp下面的/WEB-INF/classes和/WEB-INF/lib下的jar包和class文件