上一节,在Userguide中的第10节:

对Tomcat ClassLoader进行了一下简单的翻译,但还是因为是介绍性的文字,不够具体,这一篇文章,通过调试Tomcat的源码的方式进行查看这几个类加载器。

1.system classloader
系统类加载器,实际上就是你在执行java xxxx.class 定义在环境变量中的path路径信息,这块得路径内容实际在catalina的脚本中就有详细的定义,一共有2条信息:


这两个路径信息附加在环境变量中预先定义的jdk的classpath信息之后,这就是Tomcat的系统类加载器可以加载的范围。
bootstrap.jar是启动包,这个必须要加载,而tomcat-juli我们也分析过,这个是Tomcat的默认的日志实现。
2.common classloader
common classloader是Tomcat的启动包中用来加载tomcat自带lib包的classloader,Tomcat自定义这个classloader,然后将对应的库信息加入到这个classloader中,看一下Bootstrap类中启动部分,第一步就是初始化Classloader:

总共分成4个步骤:
1.在initClassLoaders方法中,看到基于不同的参数会创建不同的classloader,而common就是其中最重要的一个classloader。
2.createClassloader方法中,基于不同的传递的参数,Tomcat会读取catalina.properties,这个配置文件是在bootstrap.jar中:

这个文件中,定义了CATALINA引擎的一些默认的配置,其中最重要的就是三个classloader的加载路径,
以common.loader为例,有下述记录:
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
这里涵盖的内容和UserGuide中的顺序一致,也就是当Tomcat为多实例的时候,预先加载catalina.base路径下的class和jar包,然后在加载安装目录下的class和jar包。
3.将路径从配置文件中读取了出来,下一步的操作就是将这些库的jar和class逐个的遍历出来,在createClassloade方法中都是for循环进行遍历,然后转化为URL[],存储起来。
4.最后基于上述的库的位置,和classloader的层级关系,创建URLClassloader。需要注意的是,commonClassLoader的parent是null,这也就意味着它的父亲就是system classloader。
3.shared classloader
shared classloader和common classloader的定义也在catalina.properties文件中,但是:

默认并没有明确的定义。这里值得说一下,Tomcat对于shared classloader在Tomcat5.x等老版本中进行支持,也就是说原来的lib目录中一共分为三组目录:

对于老版本的Tomcat,整个Classloader的层级结构是这样的:

SharedClassloader是webappClassloader的parentClassloader节点,对应的common目录对应的是前面讲过的commonClassloader,而CatalinaClassloader是负责Tomcat自己的lib的加载,不能给应用使用。
后来,Tomcat升级之后,觉得这个层级结构进行了调整,SharedClassloader晾在一边,直接让CatalinaClassloader作为webappClassloader的parentClassloader,这样的好处很清晰,应用中加载不到的,直接在CatalinaClassloader中进行找寻。
3.Catalina Classloader
根据上述的描述,CatalinaClassloader是作为webappClassloader的parentClassloader,但是还是可以从catalina.properties中看到,并没有配置CatalinaClassloader的库:

从代码中可以分析得出:

也即是如果没有定义,那么直接返回catalinaClassloader的父,也就是ComonClassloader作为CatalinaClassloader,也就是说,如果没有定义,整体的类加载层级就变成了二级结构:

到这里,其实我们应该明白CatalinaClassloader的作用了,它其实就是ComonClassloader加载的Tomcat默认的东西,这个一般不能动,但是我还想要加载一些各个web应用都能公用的库,原来是shared classloader,但现在shared classloader被废弃,所以我可以定义在CatalinaClassloader中;
这样,其实新版本和旧版本的tomcat的类加载的区别就在于,应用中是否可以能访问一些Tomcat内部资源的问题,关于这个老版本是绝对隔离的,而新版本这里通过WebappClassloader的实现和安全策略去进行控制而已。
4.WebApp Classloader
每一个应用对应一个WebappClassloader,但是对应Tomcat的组件中,首先一个应用是对应一个WebappLoader组件,然后在WebappLoader组件中创建WebappClassloader,这个关系如下图所示,二者的关联是由WebappLoader去持有WebappClassloader的引用:

对于WebappLoader组件和每一个应用的StanardContext是一一对应的,因为其也实现了Lifecycle接口,所以其也有startInternal方法:

1.通过StandardContext直接启动WebappLoader
2. WebappLoader也是Lifecycle,所以其也有startInternal方法,在这里进行创建Webappclassloader
这里是通过反射来创建的:

Webappclassloader其实东西并不多,它主要的逻辑都在其父类WebappclassloaderBase中
3.添加Webappclassloader的classpath,基本上是集成父类的:

对于上述的代码,一定要注意的一点是delegate属性,这个属性的实现就是:

从代码中就可以看到,如果配置了这个delegate,那么直接classpath就直接先加载父类的classlaoder的classpath
4.设置classloader安全相关的内容,与policy相关。
5.启动这个Webappclassloader:

最后,将当前应用的/web-inf/classes,/web-inf/lib中的库加到localRespositories中。
总结:
Tomcat的各个类加载器各自其职,非常清晰,下一节,我们就WebappClassloader的loadClass的过程,细致的进行分解,这样至少Classloader这块就更加清晰了。




