这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战。
Tomcat 类加载需要解决以下问题
- 一个web容器可能要部署两个或多个应用,不同应用可能需要依赖相同第三方库的不同版本,需要保证隔离多个容器。
- web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离。
- web容器支持jssp文件修改后不用重启,jsp文件也要编译成class文件的,支持HotSwap功能。
- 部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类库被加载进JVM中。
Tomcat 类加载器的问题
Tomcat 为什么要打破双亲委派机制?
使用Java默认的类加载器无法加载两个相同类库的不同版本,它只在乎类的全限定类名,并且只有一份,所以无法解决上面的问题1和问题4,也就是相关隔离的问题。
同时在修改jsp文件后,因为类名一样,默认的类加载器不会重新加载,而是使用方法区中已经存在的类,所以需要每个jsp对应一个唯一的类加载器,当修改jsp的时候,直接卸载唯一的类加载器, 然后重新创建类加载器,并加载jsp文件。
Tomcat 的类加载机制
Tomcat 有多个自定义类加载器
- CommonClassLoader:tomcat 最基本的类加载器,加载路径中class可以被tomcat和各个webapp访问。
- CatalinaClassLoader:tomcat 私有类加载器,webapp不能访问其加载路径下的class,即对webapp不可见。
- SharedClassLoader:各个webapp共享的类加载器,对tomcat不可见。
- WebappClassLoader:webapp 私有的类加载器,只对当前webapp可见。
- JasperClassLoader:JSP的类加载器,每个web应用程序都对应一个WebappClassLoader,每个 jsp文件对应一个JasperClassLoader,所以这两个类加载器有多个实例。
工作原理
- CommonClassLoader能加载的类都可以被CatalinaClassLoader使用,从而实现了公有类库的共用。
- CatalinaClassLoader和SharedClasLoader自己能加载的类则与对方相互隔离。
- WebappClasLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离,多个WebAppClassLoader是同级关系。
- JspClassLoader的加载范围仅仅是这个JSP文件所编译出来的那一个class 文件,它出现的目的就是为了被丢弃;当web容器检测到JSP文件被修改时,会替换掉目前的JasperClassLoader实例,并通过在创建一个 Jsp类加载器来实现JSP文件的HotSwap功能。
- tomcat目录结构,与上面的类加载器对应:
1 | bash复制代码- /common/* |
- 默认情况下,conf 目录下的 catalina.properties 文件,没有指定 server.loader 以及sharedloader,所以tomcat没有建立CatalinaClasLoader和SharedClassLoader实例,这两个都会使用CommonClassLoader来代替。
Tomcat6之后,把common、shared. server 目录合成一个lib目录,所以我.们服务器里看不到common、shared、 server 目录。
Tomcat 应用的默认加载顺序
- 先从JVM的BootStrapClassLoader中加载。
- 加载Web应用下/WEB- INF/classes中的类。
- 加载Web应用下/WEB-INF/lib/* .jap中的jar包中的类。
- 加载上面定义的System路径下面的类。
- 加载上面定义的Common路径下面的类。
Tomcat 类加载过程
- 先在本地缓存中查找是否已经加载过该类(对于一些己经加载了的类,会被缓存在resourceEntries这个数据结构中),如果已经加载即返回,否则继续下一步。
- 让系统类加载器(ApplicationClassLoader)尝试加载该类,主要是为了防止一些基础类会被web中的类覆盖,如果加载到即返回,返回继续。
- 前两步均没有加载到目标列,主要是为了防止一些基础类会被web中的类覆盖,如果加载到即返回,返回继续。
- 前两步均没加载到目标类,那么web应用的类加载器将自行加载,如果加载到则返回,否则继续下一步。
- 最后还是加载不到的话,则委托父类父类加载器(Common ClassLoader)去加载。
Tomcat 打破双亲委派
如上图所示,上面的橙色部分还是和原来一样,采用的双亲委派机制,黄色部分是tomcat第一部分自定义的类加载器,这部分主要是加载tomcat包中的类,这一部分依然采用的是双亲委派机制;
而绿色部分是tomcat第二部分自定义类加载器,正是这一部分,打破了类的双亲委派机制。
Tomcat第一部分自定义类加载器(黄色部分)
这部分类加载器,在tomcat7及以前是tomcat自定义的三个类加载器,分别在不同文件加载的jar包,而到了tomcat8及以后,tomcat 将这三个文件夹合并了,合并成一个lib包,也就是我们现在看到的lib包。
Tomcat第二部分自定义类加载器(绿色部分)
绿色是Java项目在打war包的时候,tomcat 自动生成的类加载器,也就是说,每一个项目打成war包,tomcat都会自动生成一个类加载器,专门用来加载这个war包,而这个类加载器打破了双亲委派机制,我们可以想象一下,加入这个
webapp类没有打破双亲委派机制会怎么样?
如果没有打破,它就会委托父类加载器去加载,一 旦加载到了,自定义加载器就没有机会加载了,那么Spring4和Spring5的项目就没有可能共存了。所以,这一部分它打破了双亲委派机制,这样一来webapp类加载器就不需要在让上级类去加载,它自己就可以加载对应的war里的class文件,当然了,它的项目文件还是要委托上级加载的。
参考:
tomcat是如何打破双亲委派机制的?
打破双亲委派机制
本文转载自: 掘金