Tomcat 源代码分析之ClassLoader
此系列文章皆为Tomcat 7.0代码代码分析。
1. ClassLoader基础知识
1.1. Parent-Child委托模型
我们知道Java系统中,类加载器的默认加载方式是采用Parent-Child委托方式加载类的,即就是说,先尝试使用父类加载器加载类,如果没有找到,才自己加载该类,可以看到,这是一个递归的加载过程,核心代码大致如下:
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- synchronized (getClassLoadingLock(name)) {
- // First, check if the class has already been loaded
- Class c = findLoadedClass(name);
- if (c == null) {
- ……
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {
- // ClassNotFoundException thrown if class not found
- // from the non-null parent class loader
- }
- if (c == null) {
- // If still not found, then invoke findClass in order
- // to find the class.
- …
- c = findClass(name);
- ……
- }
- }
- ……
- return c;
- }
- }
1.2. 类加载器运行时模型:
当一个JVM启动时,至少有三个ClassLoader会被启动:
1. Bootstrap 类加载器
加载Java核心包,它们被置放在(<JAVA_HOME>/lib目录下,这部分是JVM的一部分,往往使用native code完成
2. Extensions类加载器
加载Java扩展包,即位于<JAVA_HOME>/lib/ext下的包,这里要注意的是,有些JVM的这个加载器和Bootstrap类加载器是同一个加载器,而Sun是把二者分开的,其实现类为sun.misc.Launcher$ExtClassLoader。
3. System类加载器
这个类加载器加载CLASSPATH下的类,Sun的 默认实现是sun.misc.Launcher$ExtClassLoader。
这三者之间的父子关系是:Bootstrap类加载器是Extensions类加载器的父类加载器,而Extensions类加载器是System类加载器的父类加载器。
2. Tomcat Classloader模型
Tomcat 7.0的ClassLoader加载层次模型如下图所示:
这个模型和之前Tomcat 5.5之前有些不同,因为之前的除过Common类加载器之外,还有Catalina类加载器和Shared类加载器,这个只能导致更多的配置和概念,已经不再使用了,虽然还可以进行配置。
3. 类加载器的实现
Tomcat类加载器其实有三个类实现:StandardClassLoader,WebappClassLoader和JasperLoader,这三个类加载器都是URLClassLoader的子类。不同的是,StandardClassLoader并没有客户化URLClassLoader方法,即,它也是采用委托的方式加载类的。而其他都覆写了loadClass()方法。
1. StandardClassLoader类加载器会创建Common类加载器的实例。
2. WebappClassLoader为每个应用创建类加载器实例,对于加载Web应用的类时,我们并非推荐使用父子委托模型,但是必须保证以java.*和javax.*开头的包必须由System类加载器加载,即最终由Bootstrap加载器加载。
核心代码如下:
- public Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
- ……
- // (0) Check our previously loaded local class cache
- clazz = findLoadedClass0(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Returning class from cache");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- // (0.1) Check our previously loaded class cache
- clazz = findLoadedClass(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Returning class from cache");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- // (0.2) Try loading the class with the system class loader, to prevent
- // the webapp from overriding J2SE classes
- //保证以java.*和javax.*开头的包必须由System类加载器加载
- try {
- clazz = system.loadClass(name);
- if (clazz != null) {
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- // Ignore
- }
- ……
- boolean delegateLoad = delegate || filter(name);
- // (1) Delegate to our parent if requested
- //Mbeans加载时,是否采用delegate方式
- if (delegateLoad) {
- if (log.isDebugEnabled())
- log.debug(" Delegating to parent classloader1 " + parent);
- ClassLoader loader = parent;
- if (loader == null)
- loader = system;
- try {
- clazz = loader.loadClass(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Loading class from parent");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- ;
- }
- }
- // (2) Search local repositories
- //先本地加载,不委托父加载器加载
- if (log.isDebugEnabled())
- log.debug(" Searching local repositories");
- try {
- clazz = findClass(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Loading class from local repository");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- ;
- }
- // 最后使用父加载器加载
- if (!delegateLoad) {
- if (log.isDebugEnabled())
- log.debug(" Delegating to parent classloader at end: " + parent);
- ClassLoader loader = parent;
- if (loader == null)
- loader = system;
- try {
- clazz = loader.loadClass(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Loading class from parent");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- ;
- }
- }
- throw new ClassNotFoundException(name);
- }
可以看到,并非简单的父子委托方式加载类
3. JasperLoader是为了加载jsp编译成的servlet而创建的类加载器,它覆写了loadClass()方法,除过加载org.apache.jsp.*的文件外,其他的均使用父加载器加载。
核心代码如下:
- public Class loadClass(final String name, boolean resolve)
- throws ClassNotFoundException {
- Class clazz = null;
- // (0) Check our previously loaded class cache
- clazz = findLoadedClass(name);
- if (clazz != null) {
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- // (.5) Permission to access this class when using a SecurityManager
- ……
- if( !name.startsWith(Constants.JSP_PACKAGE_NAME) ) {
- // Class is not in org.apache.jsp, therefore, have our
- // parent load it
- clazz = parent.loadClass(name);
- if( resolve )
- resolveClass(clazz);
- return clazz;
- }
- //所有的以org.apache.jsp.*开头的文件都由该类加载器加载。
- return findClass(name);
- }
今天就讲到这里了。