暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Tomcat深入研究文章之十一(Tomcat classloader源码分析)

中间件技术讨论圈 2016-03-17
633

上一节,在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这块就更加清晰了。


文章转载自中间件技术讨论圈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论