common 类加载器是在 Bootstrap 的 initClassLoaders 初始化的,源码如下:
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();
}
// 指定仓库路径配置文件前缀和父加载器,创建 ClassLoader 实例
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
可以看到分别创建了三个类加载器,createClassLoader 就是根据配置获取资源仓库地址,最后返回一个 StandardClassLoader 实例,核心
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent; // 如果没有配置,则返回传入的父加载器
ArrayList repositoryLocations = new ArrayList();
ArrayList repositoryTypes = new ArrayList();
...
// 获取资源仓库路径
String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);
// 创建一个 StandardClassLoader 对象
ClassLoader classLoader = ClassLoaderFactory.createClassLoader
(locations, types, parent);
...
return classLoader;
}
类加载器初始化完毕后,会创建一个 Catalina 对象,最终会调用它的 load 方法,解析 server.xml 初始化容器内部组件。那么容器,比如 Engine,又是怎么关联到这个设置的父加载器的呢?
Catalina 对象有一个 parentClassLoader 成员变量,它是所有组件的父加载器,默认是 AppClassLoader,在此对象创建完毕时,会反射调用它的 setParentClassLoader 方法,将父加载器设为 sharedLoader。
而 Tomcat 内部顶级容器 Engine 在初始化时,Digester 有一个 SetParentClassLoaderRule 规则,会将 Catalina 的 parentClassLoader 通过 Engine.setParentClassLoader 方法关联起来。
4. 如何打破双亲委托机制
答案是使用 Thread.getContextClassLoader() - 当前线程的上下文加载器,该加载器可通过 Thread.setContextClassLoader() 在代码运行时动态设置。
默认情况下,Thread 上下文加载器继承自父线程,也就是说所有线程默认上下文加载器都与第一个启动的线程相同,也就是 main 线程,它的上下文加载器是 AppClassLoader。
Tomcat 就是在 StandardContext 启动时首先初始化一个 WebappClassLoader 然后设置为当前线程的上下文加载器,最后将其封装为 Loader 对象,借助容器之间的父子关系,在加载 Servlet 类时使用。
5. Web 应用的类加载
Web 应用的类加载是由 WebappClassLoader 的方法 loadClass(String, boolean) 完成,核心代码如下:









