精华帖 (0) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-02-14
最后修改:2012-02-17
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; } } 很多ClassLoader的继承类都默认了这种加载方式,其中用途比较广泛的有URLClassLoader。
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$AppClassLoader 。 这三者之间的父子关系是:Bootstrap类加载器是Extensions类加载器的父类加载器,而Extensions类加载器是System类加载器的父类加载器。 2. Tomcat Classloader模型Tomcat 7.0的ClassLoader加载层次模型如下图所示:
这个模型和之前Tomcat 5.5之前有些不同,因为之前的除过Common类加载器之外,还有Catalina类加载器和Shared类加载器,这个只能导致更多的配置和概念,已经不再使用了,虽然还可以进行配置。 1. Common类加载器:加载$CATALINA_HOME/lib和$CATALINA_BASE/lib下的class文件和jar包以及旗下的资源文件,这些文件将会被所有Web应用共有,也会被Tomcat运行时用到。其配置在catalina.properties下,如果没有配置,则默认使用System类加载器,配置示例:
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
2. App类加载器:Web 应用的类加载器,Web应用下的资源文件,class文件盒jar包,按照Java Web规范的标准,它们位于Web应用的/WEB-INF/classes和/WEB-INF/lib文件夹下。
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); }
今天就讲到这里了。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-02-16
好东西,学习中!
|
|
返回顶楼 | |
发表时间:2012-02-16
最后修改:2012-02-16
少了两个貌似。
一个 sharedClassLoader用来加载app可见的包 一个 serverClassLoader用来加载tomcat服务必须的包 我没看过tomcat的源码, 但我感觉,sharedClassLoader应该是appClassloader的parent 不知道是不是 网上看到的资料,加载servlet的classloader(appclassloader?)。似乎比较特殊, 不是按标准的 先让parent加载,而是自己先加载。 有点模糊,有见解的话,麻烦给我解惑下。 |
|
返回顶楼 | |
发表时间:2012-02-16
leonayx123 写道 少了两个貌似。
一个 sharedClassLoader用来加载app可见的包 一个 serverClassLoader用来加载tomcat服务必须的包 我没看过tomcat的源码, 但我感觉,sharedClassLoader应该是appClassloader的parent 不知道是不是 网上看到的资料,加载servlet的classloader(appclassloader?)。似乎比较特殊, 不是按标准的 先让parent加载,而是自己先加载。 有点模糊,有见解的话,麻烦给我解惑下。 redhat 写道 这个模型和之前Tomcat 5.5之前有些不同,因为之前的除过Common类加载器之外,还有Catalina类加载器和Shared类加载器,这个只能导致更多的配置和概念,已经不再使用了,虽然还可以进行配置。 这个已经提到了,是以前的,本文是按照7.0说明的。 Catalina类加载器和Shared类加载器不同, Catalina类加载器加载Catalina servlet(tomcat)需要的jar包和class文件 Shared类加载器,加载所有web app共有的 common相当于Shared类加载器+Catalina类加载器。 但是这个如上述所说,很繁复,所以现在只有一种了。 |
|
返回顶楼 | |
发表时间:2012-02-17
系统类加载器SUN给出的实现应该是sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader是扩展类加载器的实现
|
|
返回顶楼 | |
发表时间:2012-02-17
xpjsky 写道 系统类加载器SUN给出的实现应该是sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader是扩展类加载器的实现
确实写错了,把ext和sys写成同一个了,谢谢指正! |
|
返回顶楼 | |
发表时间:2012-02-17
写得不错,还缺少点内容,比如为什么每个app都是独立的,jsp热部署怎么做到的,还有怎么实现java类的热部署
看到lz写的今天就到这里,应该是下面还有内容,期待lz的下文 |
|
返回顶楼 | |
发表时间:2012-02-17
缺少原理的分析,还是不缺乏JAVA学习者特点,复制粘贴的比较严重
|
|
返回顶楼 | |
发表时间:2012-02-17
+1,的确看出,不是出于理解性的阐述
|
|
返回顶楼 | |
发表时间:2012-03-18
其实挺简单的,Web类加载器设计要求:
1. 保证每个Web App应用的隔离;以免发生不同的Web App中存在同名的类,系统运行时会发生不可预测的问题。 2. 保证Web App里面的包优先于common包加载,以满足App之特别要求。 3. 充分利用Java原有的父子加载模型。保证标准java库,和tomcat命令行指定的库可以被加载进来。 这样的要求就决定了tomcat的加载顺序。 |
|
返回顶楼 | |