# Tomcat 类加载机制剖析


<!--more-->


Java类（.java）—> 字节码⽂件(.class) —> 字节码⽂件需要被加载到jvm内存当中（这个过程就是⼀个类加载的过程）

类加载器（ClassLoader，说⽩了也是⼀个类，jvm启动的时候先把类加载器读取到内存当中去，其他的类（⽐如各种jar中的字节码⽂件，⾃⼰开发的代码编译之后的.class⽂件等等））

要说 Tomcat 的类加载机制，⾸先需要来看看 Jvm 的类加载机制，因为 Tomcat 类加载机制是在 Jvm 类加载机制基础之上进⾏了⼀些变动。

## 一、JVM类加载机制

JVM 的类加载机制中有⼀个⾮常重要的⻆⾊叫做类加载器（ClassLoader），类加载器有⾃⼰的体系，Jvm内置了⼏种类加载器，包括：

- 引导类加载器
- 扩展类加载器
- 系统类加载器

他们之间形成⽗⼦关系，通过 Parent 属性来定义这种关系，最终可以形成树形结构。

![示意图](https://img.zhaojq.top/20260727091721313.png "示意图")

| 类加载器                                      | 作用                                                         |
| --------------------------------------------- | ------------------------------------------------------------ |
| BootstrapClassLoader 引导类加载器             | c++编写，加载java核⼼库 java.*,⽐如rt.jar中的类，构造ExtClassLoader和AppClassLoader |
| ExtClassLoader 扩展类加载器                   | java编写，加载扩展库 JAVA_HOME/lib/ext⽬录下的jar中的类，如classpath中的jre ，javax.*或者java.ext.dir指定位置中的类 |
| SystemClassLoader/AppClassLoader 系统类加载器 | 默认的类加载器，搜索环境变量 classpath 中指明的路径          |

> 另外：⽤户可以⾃定义类加载器（Java编写，⽤户⾃定义的类加载器，可加载指定路径的 class ⽂件）
>
> 当 JVM 运⾏过程中，⽤户⾃定义了类加载器去加载某些类时，会按照下⾯的步骤（⽗类委托机制）
>
> 　　1. ⽤户⾃⼰的类加载器，把加载请求传给⽗加载器，⽗加载器再传给其⽗加载器，⼀直到加载器树的顶层
>
> 　　2. 最顶层的类加载器⾸先针对其特定的位置加载，如果加载不到就转交给⼦类
>
> 　　3. 如果⼀直到底层的类加载都没有加载到，那么就会抛出异常 ClassNotFoundException
>
> 因此，按照这个过程可以想到，如果同样在 classpath 指定的⽬录中和⾃⼰⼯作⽬录中存放相同的class，会优先加载 classpath ⽬录中的⽂件



## 二、双亲委派机制

- 定义

  当某个类加载器需要加载某个.class⽂件时，它⾸先把这个任务委托给他的上级类加载器，递归这个操作，如果上级的类加载器没有加载，⾃⼰才会去加载这个类。

- 作用

  - 防⽌重复加载同⼀个.class。通过委托去向上⾯问⼀问，加载过了，就不⽤再加载⼀遍。保证数据安全。

  - 保证核⼼.class不能被篡改。通过委托⽅式，不会去篡改核⼼.class，即使篡改也不会去加载，即使加载也不会是同⼀个.class对象了。不同的加载器加载同⼀个.class也不是同⼀个.class对象。这样保证了class执⾏安全（如果⼦类加载器先加载，那么我们可以写⼀些与java.lang包中基础类同名的类， 然后再定义⼀个⼦类加载器，这样整个应⽤使⽤的基础类就都变成我们⾃⼰定义的类了。）Object类 -----> ⾃定义类加载器（会出现问题的，那么真正的Object类就可能被篡改了）



## 三、Tomcat的类加载机制

Tomcat 的类加载机制相对于 Jvm 的类加载机制做了⼀些改变。**没有严格的遵从双亲委派机制**，也可以说打破了双亲委派机制

⽐如：有⼀个tomcat，webapps下部署了两个应⽤

- app1/lib/a-1.0.jar com.lagou.edu.Abc

- app2/lib/a-2.0.jar com.lagou.edu.Abc

不同版本中Abc类的内容是不同的，代码是不⼀样的

![示意图](https://img.zhaojq.top/20260727091721590.png "示意图")

- 引导类加载器 和 扩展类加载器 的作⽤不变

- 系统类加载器正常情况下加载的是 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 加载（在这最后⼀步，遵从双亲委派
>
> 机制）




