内容输出来源:
拉勾教育Java高薪训练营
- 什么是类的加载?
我们知道,我们平时编写的代码要先经过编译,变成**.class字节码文件**,才能被JVM运行,那么**.class字节码文件被加载到JVM内存中的过程就叫做类的加载**。
想要搞清楚Tomcat的类加载机制,就要先弄明白JVM的类加载机制,因为Tomcat的类加载机制就是在JVM的类加载机制的基础上做了一些变动。
1. JVM的类加载机制
-
类加载器(ClassLoader)
在JVM的类加载机制中,**类加载器(ClassLoader)**是一个非常重要的角色,JVM内置了几种类加载器:引导类加载器、拓展类加载器、系统类加载器,他们之间是父子关系,可以形成树形结构。
| 类加载器 | 作用 |
|---|---|
| 引导类加载器 BootStrapClassLoader | c++编写,用来加载Java核心类库java.*,如rt.jar中的类,构建ExtClassLoader和AppClassLoader |
| 拓展类加载器 ExtClassLoader | java编写,加载拓展库JAVA_HOME/lib/ext目录下的jar中的类,如classpath下的jre,javax.*或者java.ext.dir指定位置中的类 |
| 系统类加载器 SystemClassLoader/AppClassLoader | 默认的类加载器,搜索环境变量classpat中指明的路径 |
除此之外: ⽤户可以⾃定义类加载器(Java编写,⽤户⾃定义的类加载器,可加载指定路径的 class ⽂件) 当 JVM 运⾏过程中,⽤户⾃定义了类加载器去加载某些类时,会按照下⾯的步骤(⽗类委托机制)
- 1)⽤户⾃⼰的类加载器,把加载请求传给⽗加载器,⽗加载器再传给其⽗加载器,⼀直到加载器 树的顶层
- 2)最顶层的类加载器⾸先针对其特定的位置加载,如果加载不到就转交给⼦类
- 3)如果⼀直到底层的类加载都没有加载到,那么就会抛出异常 ClassNotFoundException 因此,按照这个过程可以想到,如果同样在 classpath 指定的⽬录中和⾃⼰⼯作⽬录中存放相同的 class,会优先加载 classpath ⽬录中的⽂件
2. 双亲委派机制
2.1. 什么是双亲委派机制?
当某个类加载器加载某个.class文件时,首先不自己加载,而是把任务委托给它的上级类加载器,递归这个操作,上级类加载器再委托给上级类加载器,直到类加载器树的顶层,如果上级的类加载器都没有加载这个类,再自己去加载。
2.2. 双亲委派机制的作用
- 1)防止重复加载同一个
.class,当要加载一个.class时,通过委托的方式向上级问一问,如果加载过了就不再加载。 - 2)保证核心的.class文件不被篡改。通过委托的方式,不会篡改核心的
.class,即使篡改了也不会被加载。(如果子类加载器先加载,那么我们可以自定义一些java.lang包中的基础类,然后通过自定义类加载器,从而篡改了原来的基础类。比如:自定义一个Object类,通过自定义类加载器加载,这样整个应用中真正的Object类就被篡改了)
3. Tomcat的类加载机制
Tomcat的类加载机制相对于 JVM 的类加载机制做了⼀些改变。
没有严格的遵从双亲委派机制,也可以说打破了双亲委派机制。
- 引导类加载器 和 扩展类加载器 的作⽤不变
- 系统类加载器正常情况下加载的是 CLASSPATH 下的类,但是 Tomcat 的启动脚本并未使⽤该变量,⽽是加载tomcat启动的类,⽐如
bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin下 - Common 通⽤类加载器加载Tomcat使⽤以及应⽤通⽤的⼀些类,位于CATALINA_HOME/lib下,⽐如servlet-api.jar
- Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问
- Shared ClassLoader ⽤于加载应⽤程序共享类,这些类服务器不会依赖
- Webapp ClassLoader,每个应⽤程序都会有⼀个独⼀⽆⼆的Webapp ClassLoader,他⽤来加载 本应⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。
tomcat 8.5 默认改变了严格的双亲委派机制
- ⾸先从 Bootstrap Classloader加载指定的类
- 如果未加载到,则从 /WEB-INF/classes加载
- 如果未加载到,则从 /WEB-INF/lib/*.jar 加载
- 如果未加载到,则依次从 System、Common、Shared 加载(在这最后⼀步,遵从双亲委派机制)