前面介绍了java中类加载的一般模型:双亲委派模型,这个模型适用于大多数类加载的场景,但对于web容器却是不适用的;这是因为servlet规范对web容器的类加载做了一些规定,简单的来说有以下几条:
1.WEB-INF/classes和WEB-INF/lib路径下的类会优先于父容器中的类加载,比如WEB-INF/classes下有个ABC类,CLASSPATH下也有个ABC类,jetty会优先加载WEB-INF/classes下的,这与双亲委托模型下的加载行为相反。
2.java.lang.Object等系统类不遵循第一条, WEB-INF/classes或WEB-INF/lib中的类不能替换系统类。对于哪些是系统类,其实没有做出具体规定,jetty中是通过枚举了一些类来进行判断的。
3.Server容器的实现类不被应用中的类引用,即Server的实现类不能被任何应用类加载器加载。对于哪些是系统类,也是通过枚举类的全限定名来实现的。
为了实现上面的三个要求并实现不同部署应用间依赖的隔离,jetty定义了自己的类加载器WebAppClassLoader,通过对这个类加载器使用线程上下文加载模式来达到目的。
1.WebAppClassLoader
WebAppClassLoader是URLClassLoader的子类,重写了其loadClass方法。下面就从这个方法的源码入手:
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
//先检查是否已经加载
Class<?> c= findLoadedClass(name);
ClassNotFoundException ex= null;
//是否父加载器优先,这里默认是false
boolean tried_parent= false;
//要加载的类是否是系统类
boolean system_class=_context.isSystemClass(name);
//要加载的类是否是Server类
boolean server_class=_context.isServerClass(name);
//如果即是系统类又是Server类,则不进行任何加载操作,直接返回
if (system_class && server_class)
{
return null;
}
//如果类还没加载过并且父加载器不为空并且是系统类或父加载器优先并且不是server类,则用父加载器加载
if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class)
{
tried_parent= true;
try
{
c= _parent.loadClass(name);
if (LOG.isDebugEnabled())
LOG.debug("loaded " + c);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
//优先使用当前WebAppClassLoader的findClass()方法进行加载
if (c == null)
{
try
{
c= this.findClass(name);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
//如果当前类加载器加载不到