众所周知,Java中默认的类加载器是以父子关系存在的,实现了双亲委派机制进行类的加载,在前文中,我们提到了,双亲委派机制的设计是为了保证类的唯一性,这意味着在同一个JVM中是不能加载相同类库的不同版本的类。
然而与许多服务器应用程序一样,Tomcat 允许容器的不同部分以及在容器上运行的不同Web应用程序可以访问的各种不同版本的类库,这就要求Tomcat必须打破这种双亲委派机制,通过实现自定义的类加载器(即实现了java.lang.ClassLoader)进行类的加载。下面,就让我们来看看Tomcat类加载原理是怎样的。
Tomcat中有两个最重要的类加载器,第一个便是负责Web应用程序类加载的WebappClassLoader,另一个便是JSP Servlet类加载器`JasperLoader。
Web应用程序类加载器(WebappClassLoader)
上代码:
public class WebappClassLoader extends WebappClassLoaderBase {
public WebappClassLoader() {
super();
}
public WebappClassLoader(ClassLoader parent) {
super(parent);
}
...
}
我们来看看WebappClassLoader继承的WebappClassLoaderBase中实现的类加载方法loadClass
public abstract class WebappClassLoaderBase extends URLClassLoader
implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {
... 省略不需要关注的代码
protected WebappClassLoaderBase() {
super(new URL[0]);
// 获取当前WebappClassLoader的父加载器系统类加载器
ClassLoader p = getParent();
if (p == null) {
p = getSystemClassLoader();
}
this.parent = p;
// javaseClassLoader变量经过以下代码的执行,
// 得到的是扩展类加载器(ExtClassLoader)
ClassLoader j = String.class.getClassLoader();
if (j == null) {
j = getSystemClassLoader();
while (j.getParent() != null) {
j = j.getParent();
}
}
this.javaseClassLoader = j;
securityManager = System.getSecurityManager();
if (securityManager != null) {
refreshPolicy();
}
}
...省略不需要关注的代码
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
if (log.isDebugEnabled()) {
log.debug("loadClass(" + name + ", " + resolve + ")");
}
Class<?> clazz = null;
// Web应用程序停止状态时,不允许加载新的类
checkStateForClassLoading(name);
// 如果之前加载过该类,就可以从Web应用程序类加载器本地类缓存中查找,
// 如果找到说明WebappClassLoader之前已经加载过这个类
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Returning class from cache");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
// Web应用程序本地类缓存中没有,可以从系统类加载器缓存中查找,
// 如果找到说明AppClassLoader之前已经加载过这个类
clazz = findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Returning class from cache");
}