Tomcat 为什么重写类加载器
双亲委派机制的缺点
- 无法实现隔离性:如果使用默认类加载器,那么会无法加载两个相同类库的不同版本的类,默认类加载器只关心全限定名而不关心版本,同一个类只有一份,一个 web 容器可能需要部署两个甚至多个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,因此要保证每一个应用程序的类库相互独立、互相隔离。同时部署在同一个 web 容器中的相同类库的相同版本可以共享,否则会有重复的类库被加载进 JVM,web 容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
- 无法实现热替换:jsp 文件实际上是 class 文件,如果发生修改,但是类名没有修改,类加载器会直接读取方法区中的类,修改后的 jsp 文件并不会被重新加载
打破双亲委派机制的方法
- OSGI:基于 Java 的动态模块化规范,类加载器之间是网状结构,更加灵活,但是更加复杂
- JNDI 服务:使用线程上文类加载器,父类加载器去使用子类加载器
Tomcat 的实现
Tomcat 自己定义的类加载器
- CommonClassLoader:Tomcat 最基本的类加载器,加载路径中的 class 可以被 Tomcat 和各个 webapp 访问,对应目录结构:/common/*
- CatalinaClassLoader:Tomcat 私有的类加载器,webapp 不能访问器加载路径下的 class,即 webapp 不可见,对应目录结构:/server/*
- SharedClassLoader:各个 webapp 共享的类加载器,对 Tomcat 不可见,对应目录结构:/shared/*
- WebAppClassLoader:webapp 私有的类加载器,只对当前 webapp 可见,对应目录结构:/WEB-INF/*
- 每一个 web 应用程序对应一个 WebappClassLoader,每一个 jsp 文件对应一个 JasperLoader,因此这两个类加载器有多个实例
工作原理
- CommonClassLoader 能加载的类都可以被 CatalinaClassLoader 和 SharedClassLoader 使用,实现了公共类库的共用
- CatalinaClassLoader 和 SharedClassLoader 自己能加载的类与对方互相隔离
- WebAppClassLoader 可以使用 SharedClassLoader 加载到的类,但各个 WebAppClassLoader 实例之间互相隔离,多个 WebAppClassLoader 之间是同级关系
- JasperLoader 的加载范围仅仅是这个 jsp 文件所编译出来的哪一个 class 文件,它出现的目的就是为了被丢弃,当 web 容器检测到 jsp 文件被修改时,会替换当前 JasperLoader 的实例,并再建立一个新的 jsp 类加载器,以此实现 jsp 的 HstSwap 功能(热加载)
默认情况下,conf目录下的catalina.properties文件,没有指定server.loader以及shared.loader,所以tomcat没有建立CatalinaClassLoader和SharedClassLoader的实例,这两个都会使用CommonClassLoader来代替。Tomcat6之后,把common、shared、server目录合成了一个lib目录。所以在我们的服务器里看不到common、shared、server目录。