Java-51 深入浅出 Tomcat 手写 Tomcat 类加载机制 双亲委派机制 生命周期 插件化

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

目前2025年06月13日更新到:
AI炼丹日志-28 - Audiblez 将你的电子书epub转换为音频mp3 做有声书,持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年06月11日更新到:
Java-44 深入浅出 Nginx - 底层进程机制 Master Worker 机制原理 常用指令

MyBatis 已完结,Spring 已完结,深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!

目前2025年06月13日更新到:
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

JVM类加载机制

JVM 的类加载机制(Class Loading Mechanism)是 Java 虚拟机中一个非常核心的部分,它决定了类从哪里来、怎么加载、什么时候初始化等一系列行为。下面我会从 整体流程、类加载器模型、双亲委派机制、类生命周期 等几个方面详细描述。

类加载整体流程

Java 类的加载过程可以分为 五个阶段(有些资料是六阶段,把"使用"也列进去):

  • 加载(Loading)
  • 验证(Verification)
  • 准备(Preparation)
  • 解析(Resolution)
  • 初始化(Initialization)

加载(Loading)

  • 把 .class 文件读入内存,生成一个 java.lang.Class 对象。
  • 由类加载器完成,可能来自本地磁盘、网络、甚至是动态生成的字节码。
  • 指定类的二进制数据的获取方式,不等同于实例化对象。

验证(Verification)

  • 确保字节码符合 JVM 规范,保证运行时安全。
  • 检查格式是否正确、语义是否符合规定、权限是否合法等。

准备(Preparation)

  • 为类的静态变量分配内存,并设置初始默认值(不是显式赋值)。

解析(Resolution)

  • 把常量池中的符号引用转换为直接引用。
  • 如:类名、字段名、方法签名 -> 实际地址。

初始化(Initialization)

  • 正式执行 () 方法(类构造器),也就是执行静态变量的初始化和静态代码块。
  • 这是类加载过程的最后一步,之后类才可以使用。

类加载器模型(ClassLoader)

JVM 中的类加载器负责把类加载进 JVM,并维持一个"类名-类实例"的映射表。

  • Bootstrap ClassLoader(启动类加载器):JVM 内置,用 C++ 实现,JAVA_HOME/lib 核心类,如 java.lang.*
  • Extension ClassLoader(扩展类加载器):加载扩展库,JAVA_HOME/lib/ext
  • App ClassLoader(应用类加载器):最常用,默认加载你的代码,classpath 路径下的类
  • 自定义 ClassLoader:开发者自定义实现,如热部署、解密等

JVM的类机制中有一个非常重要的角色叫做类加载器(ClassLoader),类加载器有自己的体系,JVM内置了几种类加载器,包括:

● 引导类加载器

● 扩展类加载器

● 系统类加载器

双亲委派机制(Parent Delegation Model)

  • 类加载器在加载类之前,会先把请求交给父类加载器,一直向上委托,直到 Bootstrap。
  • 只有当父类加载器无法加载,子加载器才会尝试加载。

这样做的原因是:

  • 防止核心类被"篡改"或"重复加载",保证安全性和一致性。
  • 如:你不能加载一个伪造的 java.lang.String 类。

比如会进行这样的加载:

shell 复制代码
AppClassLoader -> ExtClassLoader -> BootstrapClassLoader

类的生命周期(包含类加载阶段)

被主动使用时才会触发类加载(尤其是初始化)

  • new 实例
  • 访问静态变量
  • 调用静态方法
  • 反射调用
  • 子类初始化

类卸载

  • JVM 不再使用某个类,并且加载它的 ClassLoader 被回收,才可能卸载该类(主要出现在 OSGi、Tomcat 这类容器中)。

类加载机制与热部署/插件化的关系

  • 热部署(如 Spring Boot DevTools)通常通过自定义 ClassLoader 隔离加载上下文。
  • 插件系统(如 IDEA 插件)也依赖于 ClassLoader 的命名空间隔离性。

常见场景

  • Tomcat热部署:每次发布使用新的 ClassLoader
  • SPI机制:基于 Thread.currentThread().getContextClassLoader()
  • 解密加载:重写 findClass 方法
  • 沙箱隔离:不同插件使用不同的 ClassLoader
  • AOP/热更新:动态修改或替换 Class 对象

图示内容

它们之间形成父子关系,通过Parent属性来定义这种关系,最终可以形成树形结构。

● 引导启动类加载器 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运行过程中,用户自定义了类加载器去加载某些类时,会按照下面的步骤(父类委托机制):

● 用户自己的类加载器,把加载请求传给父加载器,父加载器再传给其父加载器,一直到加载器树的顶层

● 最顶层的类加载器首先针对其特定的位置加载,如果加载不到就转交给子类

● 如果一直到底层的类加载都没有加载到,那么会抛出异常: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.wzk.Abc

● app2/lib/a-2.0jar com.wzk.Abc

不同版本的 Abc 类的内容是不同的,代码是不一样的

● 引导类加载器 和 扩展类加载器 的作用不变

● 系统类加载器正常情况下加载的是 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

Tomcat 8.5 默认改变了严格的双亲委派机制:

● 首先从 Bootstrap ClassLoader 加载指定的类

● 如果未加载到,则从 /WEB-INF/classes 加载

● 如果未加载到,则从 /WEB-INF/*.jar 加载

● 如果未加载到,则依次从 System、Common、Shared 加载(在这最后一步,遵从双亲委派机制)

相关推荐
小周同学:10 分钟前
在 Vue2 中使用 pdf.js + pdf-lib 实现 PDF 预览、手写签名、文字批注与高保真导出
开发语言·前端·javascript·vue.js·pdf
最初的↘那颗心42 分钟前
Java 泛型类型擦除
java·flink
teeeeeeemo1 小时前
跨域及解决方案
开发语言·前端·javascript·笔记
uhakadotcom1 小时前
使用postgresql时有哪些简单有用的最佳实践
后端·面试·github
IT毕设实战小研1 小时前
基于Spring Boot校园二手交易平台系统设计与实现 二手交易系统 交易平台小程序
java·数据库·vue.js·spring boot·后端·小程序·课程设计
bobz9651 小时前
QT 字体
后端
泉城老铁1 小时前
Spring Boot 中根据 Word 模板导出包含表格、图表等复杂格式的文档
java·后端
用户4099322502121 小时前
如何在FastAPI中玩转APScheduler,实现动态定时任务的魔法?
后端·github·trae
极客BIM工作室1 小时前
谈谈《More Effective C++》的条款30:代理类
java·开发语言·c++
孤狼程序员1 小时前
【Spring Cloud 微服务】1.Hystrix断路器
java·spring boot·spring·微服务