tomcat源码阅读一--------ClassLoader

阅读tomcat的源码之前有一些预备知识必须掌握,比如ClassLoader,因为我在看tomcat源码的入口类org.apache.catalina.startup.Bootstrap时,基本就是ClassLoaderFactory.createClassLoader(repositories, classLoader)在加载repositories中的*******.jar

 

 

tomcat启动过程中的类加载器层次结构图:



  

1 Java核心classloader

bootstrap

bootstrap classloader是由JVM启动的,用于加载%JAVA_HOME%/jre/lib/下的JAVA平台自身的类(比如rt.jar中的类等)。这个classloader位于JAVA类加载器链的顶端,是用C/C++开发的,而且JAVA应用中没有任何途径可以获取到这个实例,它是JDK实现的一部分 

 

extension

entension classloader用于加载%JAVA_HOME%/jre/lib/ext/下的类,它的实现类是sun.misc.Launcher$ExtClassLoader,是一个内部类 

 

2  system

System类加载器,也叫App类加载器,一般情况下,对于普通的JAVA应用,ClassLoader体系就到system为止了。平时编程时,甚至都不会感受到classloader的存在,但是对于其他一些应用,比如web server,插件加载器等,就必须和ClassLoader打交道了。这时候默认的类加载器不能满足需求了(类隔离、运行时加载等需求),需要自定义类加载器,并挂载到ClassLoader链中(默认会挂载到system classloader下面) 

在tomcat里,默认情况下(通过setclasspath.sh修改CLASSPATH的值,而忽略环境变量中的CLASSPATH,会加载%CATALINA_HOME%/bin目录下的bootstrap.jar、tomcat-juli.jar、commons-daemon.jar ,此时tomcat就相关于一个App

 

3  Common 

这个类加载器是tomcat特有的,对于所有web app可见。这个类加载器默认会加载%CATALINA_HOME%/lib下的所有jar包,这些都是tomcat的核心

 

4  WebappX 

对于部署在容器中的每一个webapp,都有一个独立的ClassLoader,在这里实现了不同应用的类隔离 

这里加载的就是我们熟悉的/WEB-INF/classes 和 /WEB-INF/lib下的jar

 

 

一般情况下ClassLoader是采用双亲委托模型加载类的,过程如下:

查找类所在位置,并将找到的Java类的字节码装入内存,生成对应的Class对象。Java的类装载器专门用来实现这样的过程,JVM并不止有一个类装载器,事实上,如果你愿意的话,你可以让JVM拥有无数个类装载器,当然这除了测试JVM外,我想不出还有其他的用途。

其实类装载器自身也是一个类,它也需要被装载到内存中来,那么这些类装载器由谁来装载呢,总得有个根吧?没错,确实存在这样的根,它就是神龙见首不见尾的Bootstrap ClassLoader. 为什么说它神龙见首不见尾呢,因为根本无法在Java代码中抓住哪怕是它的一点点的尾巴,尽管你能时时刻刻体会到它的存在,因为java的运行环境所需要的所有类库,都由它来装载,而它本身是C++写的程序,可以独立运行,可以说是JVM的运行起点。

在Bootstrap完成它的任务后,接着还会使用扩展类装载器ExtClassLoader,它用于装载Java运行环境扩展包中的类,然后会生成一个AppClassLoader,AppClassLoader可以通过调用ClassLoader.getSystemClassLoader() 来获得,假定程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类通通会由它来装载。AppClassLoader查找类的区域就是耳熟能详的Classpath,按照它的类查找范围给它取名为类路径类装载器。

还是先前假定的情况,当Java中出现新的类,AppClassLoader首先在类传递给它的父类类装载器,也就是Extion ClassLoader,询问它是否能够装载该类,如果能,那AppClassLoader就不干这活了,同样Extion ClassLoader在装载时,也会先问问它的父类装载器。我们可以看出类装载器实际上是一个树状的结构图,每个类装载器有自己的父亲,类装载器在装载类时,总是先让自己的父类装载器装载,如果父类装载器无法装载该类时,自己就会动手装载,如果它也装载不了,就抛Exception,class not found。

当由直接使用类路径装载器装载类失败抛出的是NoClassDefFoundException异常。如果使用自定义的类装载器loadClass方法或者ClassLoader的findSystemClass方法装载类,如果你不去刻意改变,那么抛出的是ClassNotFoundException。(用过spring的朋友,配错类路径时肯定碰到过这个提示)

 

值得一提jdk官方文档里对getClassLoader()的描述中指出

如果一个类是通过bootstrap 载入的,那我们通过这个类去获得classloader的话,有些jdk的实现是会返回一个null的,比如说我用 new Object().getClass().getClassLoader()的话,会返回一个null,这样的话上面的代码就会出现NullPointer异常.所以保险起见是使用我们自己写的类来获取classloader,"this.getClass().getClassLoader()“,这样一来就不会有问题。

 

tomcat的ClassLoader与标准的ClassLoader委托模型不同,在启动的时候的加载顺序为

Bootstrap--->System--->/WEB-INF/classes---> /WEB-INF/lib/*.jar---> Common

 

当webappLoader需要加载一个类的时候,首先是委托Bootstrap;然后尝试自行加载;接着是system;最后才会委托Common 

from the perspective of a web application, class or resource loading looks in the following repositories, in this order:

  • Bootstrap classes of your JVM
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application
  • System class loader classes 
  • Common class loader classes 

但如果在web.xml里配置了<Loader delegate="true"/>

委托顺序就会变成Bootstrap--》system--》Common--》尝试自行加载

  • Bootstrap classes of your JVM
  • System class loader classes 
  • Common class loader classes
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application

 

 

到目前为止,我还没搞明白为什么把Common放在最后,而不按正常的双亲委托模型来弄-.-!

 

tomcat官网有描述:

http://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html

 

 

猜你喜欢

转载自phipray.iteye.com/blog/2253471