Tomcat中的类加载机制符合JVM推荐的双亲委派模型,关于JVM的类加载机制不多说,网上很多资料。
1. Tomcat类加载器过程。
tomcat启动初始化阶段创建几个类加载器:
private void initClassLoaders() { try { //aaa 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); } }
Bootstrap启动时创建三个URLClassLoader : common , catalina, shared
接下来:
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.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);
红色部分的代码 是将 Catalina 对象的父类加载器设置为 shared 加载器。之后,在Tomcat其他组件(Server , Service , Container 等)获取得到的父类加载都是 shared 加载器,
当创建 WebappClassLoader(即 应用程序类加载器)时指定 的父加载器是 shared加载器,网上包括一部分讲解tomcat的书里说到应用程序类加载器 的 父加载器是 common,其实是不准确的,只是默认配置下, shared 加载器 就是 common加载器。
tomcat加载类的顺序
查看WebappClassLoaderBase.loadClass()源码,可以很清楚的了解类加载顺序:
- 在 tomcat 已加载类的列表中查找
- 在jvm已加载类的列表中查找
- 尝试从JVM系统库({java.home}/jre/lib和{java.home}/jre/lib/ext)加载
- 尝试从tomcat中的当前web应用的/WEB-INF/classes目录下加载
- 尝试从tomcat中的当前web应用的/WEB-INF/lib目录下加载
- 尝试从tomcat shared指定的类库中加载
- 尝试从tomcat common指定的类库中加载
- 尝试使用系统类加载器(AppClassLoader)在classpath中加载(使用Class.forName()方法)
如果上述步骤都没有加载到类,就会抛出ClassNotFoundException
2. Tomcat 类库和 web应用类库的共享与隔离
有四种逻辑关系:
- tomcat类库与web应用隔离类库 , 由catalina类加载器来实现
- tomcat类库与web应用共享类库,由common类加载器来实现
- 多个web应用共享类库,由shared类加载器来实现
- 多个web应用隔离类库,由一个web应用对应一个唯一的应用程序类加载器来实现(WebappClassLoader)
catalina,common,shared的配置在conf/catalina.properties中
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=