Tomcat隔离web原理和热加载热部署
Tomcat 如何打破双亲委派机制
Tomcat 的自定义类加载器 WebAppClassLoader 打破了双亲委派机制,它首先自己尝试去加载某个类,如果找不到再代理给父类加载器,其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader 的两个方法:findClass 和 loadClass。
findClass方法
public Class<?> findClass(String name) throws ClassNotFoundException {Class<?> clazz = null;try {//1. 先在 Web 应用目录下查找类clazz = findClassInternal(name);} catch (RuntimeException e) {throw e;}if (clazz == null) {try {//2. 如果在本地目录没有找到,交给父加载器去查找clazz = super.findClass(name);} catch (RuntimeException e) {throw e;}//3. 如果父类也没找到,抛出 ClassNotFoundExceptionif (clazz == null) {throw new ClassNotFoundException(name);}return clazz;}
在 findClass 方法里,主要有三个步骤:
1)先在 Web 应用本地目录下查找要加载的类。
2)如果没有找到,交给父加载器去查找,它的父加载器就是上面提到的系统类加载器AppClassLoader。
3)如何父加载器也没找到这个类,抛出 ClassNotFound 异常。
loadClass 方法
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {Class<?> clazz = null;//1. 先在本地 cache 查找该类是否已经加载过clazz = findLoadedClass0(name); if (clazz != null) { if (resolve) resolveClass(clazz);return clazz; }//2. 从系统类加载器的 cache 中查找是否加载过clazz = findLoadedClass(name);if (clazz != null) { if (resolve)resolveClass(clazz);return clazz; }// 3. 尝试用 ExtClassLoader 类加载器类加载,为什么?ClassLoader javaseLoader = getJavaseClassLoader();try {clazz = javaseLoader.loadClass(name);if (clazz != null) {if (resolve)resolveClass(clazz);return clazz;}} catch (ClassNotFoundException e) {// Ignore }// 4. 尝试在本地目录搜索 class 并加载try {clazz = findClass(name);if (clazz != null) { if (resolve)resolveClass(clazz);return clazz; } } catch (ClassNotFoundException e) { // Ignore }// 5. 尝试用系统类加载器 (也就是 AppClassLoader) 来加载 try {clazz = Class.forName(name, false, parent);if (clazz != null) {if (resolve) resolveClass(clazz);return clazz;} } catch (ClassNotFoundException e) { // Ignore}}//6. 上述过程都加载失败,抛出异常throw new ClassNotFoundException(name);
}
loadClass 方法稍微复杂一点,主要有六个步骤:
1)先在本地 Cache 查找该类是否已经加载过,也就是说 Tomcat 的类加载器是否已经加载过这个
类。
2)如果 Tomcat 类加载器没有加载过这个类,再看看系统类加载器是否加载过。
3)如果都没有,就让ExtClassLoader去加载,这一步比较关键,目的防止 Web 应用自己的类覆盖
JRE 的核心类。因为 Tomcat 需要打破双亲委派机制,假如 Web 应用里自定义了一个叫 Object 的
类,如果先加载这个 Object 类,就会覆盖 JRE 里面的那个 Object 类,这就是为什么 Tomcat 的类加
载器会优先尝试用 ExtClassLoader 去加载,因为 ExtClassLoader 会委托给 BootstrapClassLoader
去加载,BootstrapClassLoader 发现自己已经加载了 Object 类,直接返回给 Tomcat 的类加载器,
这样 Tomcat 的类加载器就不会去加载 Web 应用下的 Object 类了,也就避免了覆盖 JRE 核心类的问
题。
4)如果 ExtClassLoader 加载器加载失败,也就是说 JRE 核心类中没有这类,那么就在本地 Web 应
用目录下查找并加载。
5)如果本地目录下没有这个类,说明不是 Web 应用自己定义的类,那么由系统类加载器去加载。这
里请你注意,Web 应用是通过Class.forName调用交给系统类加载器的,因为Class.forName的默认
加载器就是系统类加载器。
6)如果上述加载过程全部失败,抛出 ClassNotFound 异常。
从上面的过程我们可以看到,Tomcat 的类加载器打破了双亲委派机制,没有一上来就直接委托给父加
载器,而是先在本地目录下加载,为了避免本地目录下的类覆盖 JRE 的核心类,先尝试用 JVM 扩展类
加载器 ExtClassLoader 去加载。那为什么不先用系统类加载器 AppClassLoader 去加载?很显然,
如果是这样的话,那就变成双亲委派机制了,这就是 Tomcat 类加载器的巧妙之处。
Tomcat如何隔离Web应用
Tomcat 作为 Servlet 容器,它负责加载我们的 Servlet 类,此外它还负责加载 Servlet 所依赖的 JAR
包。并且 Tomcat 本身也是也是一个 Java 程序,因此它需要加载自己的类和依赖的 JAR 包。首先让
我们思考这一下这几个问题:
1)假如我们在 Tomcat 中运行了两个 Web 应用程序,两个 Web 应用中有同名的 Servlet,但是功能
不同,Tomcat 需要同时加载和管理这两个同名的 Servlet 类,保证它们不会冲突,因此 Web 应用之
间的类需要隔离。
2)假如两个 Web 应用都依赖同一个第三方的 JAR 包,比如 Spring,那 Spring 的 JAR 包被加载到
内存后,Tomcat 要保证这两个 Web 应用能够共享,也就是说 Spring 的 JAR 包只被加载一次,否则
随着依赖的第三方 JAR 包增多,JVM 的内存会膨胀。
3)跟 JVM 一样,我们需要隔离 Tomcat 本身的类和 Web 应用的类。
Tomcat类加载器的层次结构
为了解决这些问题,Tomcat 设计了类加载器的层次结构,它们的关系如下图所示:

commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访
问;
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容
器不可见;
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,比如加载
war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入
了不同的spring版本,这样实现就能加载各自的spring版本;
WebAppClassLoader
我们先来看第 1 个问题,假如我们使用 JVM 默认 AppClassLoader 来加载 Web 应用, AppClassLoader 只能加载一个 Servlet 类,在加载第二个同名 Servlet 类时,AppClassLoader 会返 回第一个 Servlet 类的 Class 实例,这是因为在 AppClassLoader 看来,同名的 Servlet 类只被加载 一次。
因此 Tomcat 的解决方案是自定义一个类加载器 WebAppClassLoader, 并且给每个 Web 应用创建 一个类加载器实例。我们知道,Context 容器组件对应一个 Web 应用,因此,每个 Context 容器负 责创建和维护一个 WebAppClassLoader 加载器实例。这背后的原理是,不同的加载器实例加载的类 被认为是不同的类,即使它们的类名相同。
SharedClassLoader我们再来看第 2 个问题,本质需求是两个 Web 应用之间怎么共享库类,并且不能重复加载相同的 类。Tomcat 的设计者又加了一个类加载器 SharedClassLoader,作为 WebAppClassLoader 的父加 载器,专门来加载 Web 应用之间共享的类。如果 WebAppClassLoader 自己没有加载到某个类,就 会委托父加载器 SharedClassLoader 去加载这个类,SharedClassLoader 会在指定目录下加载共享 类,之后返回给 WebAppClassLoader,这样共享的问题就解决了。
CatalinaClassloader
我们来看第 3 个问题,如何隔离 Tomcat 本身的类和 Web 应用的类?我们知道,要共享可以通过父 子关系,要隔离那就需要兄弟关系了。兄弟关系就是指两个类加载器是平行的,它们可能拥有同一个 父加载器,但是两个兄弟类加载器加载的类是隔离的。基于此 Tomcat 又设计一个类加载器 CatalinaClassloader,专门来加载 Tomcat 自身的类。这样设计有个问题,那 Tomcat 和各 Web 应用之间需要共享一些类时该怎么办呢?
CommonClassLoader
老办法,还是再增加一个 CommonClassLoader,作为 CatalinaClassloader 和 SharedClassLoader 的父加载器。CommonClassLoader 能加载的类都可以被 CatalinaClassLoader 和 SharedClassLoader 使用,而 CatalinaClassLoader 和 SharedClassLoader 能加载的类则与对方相 互隔离。WebAppClassLoader 可以使用 SharedClassLoader 加载到的类,但各个 WebAppClassLoader 实例之间相互隔离。
Spring 的加载问题
全盘负责委托机制
“全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依 赖及引用的类也由这个ClassLoder载入。 比如 Spring 作为一个 Bean 工厂,它需要创建业务类的实例,并且在创建业务类实例之前需要加载这 些类。Spring 是通过调用Class.forName来加载业务类的
我们在前面提到,Web 应用之间共享的 JAR 包可以交给 SharedClassLoader 来加载,从而避免重复 加载。Spring 作为共享的第三方 JAR 包,它本身是由 SharedClassLoader 来加载的,Spring 又要去 加载业务类,按照前面那条规则,加载 Spring 的类加载器也会用来加载业务类,但是业务类在 Web 应用目录下,不在 SharedClassLoader 的加载路径下,这该怎么办呢?
线程上下文加载器
于是线程上下文加载器登场了,它其实是一种类加载器传递机制。为什么叫作“线程上下文加载器” 呢,因为这个类加载器保存在线程私有数据里,只要是同一个线程,一旦设置了线程上下文加载器, 在线程后续执行过程中就能把这个类加载器取出来用。因此 Tomcat 为每个 Web 应用创建一个 WebAppClassLoarder 类加载器,并在启动 Web 应用的线程里设置线程上下文加载器,这样 Spring 在启动时就将线程上下文加载器取出来,用来加载 Bean。
cl = Thread.currentThread().getContextClassLoader();
线程上下文加载器不仅仅可以用在 Tomcat 和 Spring 类加载的场景里,核心框架类需要加载具体 实现类时都可以用到它,比如我们熟悉的 JDBC 就是通过上下文类加载器来加载不同的数据库驱动的
Tomcat热加载和热部署
在项目开发过程中,经常要改动Java/JSP 文件,但是又不想重新启动Tomcat,有两种方式:热加载和热部署。热部署表示重新部署应⽤,它的执行主体是Host。 热加载表示重新加载class,它的执行主体是Context。
热加载:在server.xml -> context 标签中 设置 reloadable=“true”
<Context docBase="D:\mvc" path="/mvc" reloadable="true" />
热部署:在server.xml -> Host标签中 设置 autoDeploy=“true”
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
它们的区别是:
热加载的实现方式是 Web 容器启动一个后台线程,定期检测类文件的变化,如果有变化,就重新加载类,在这个过程中不会清空 Session ,一般用在开发环境。
热部署原理类似,也是由后台线程定时检测 Web 应用的变化,但它会重新加载整个 Web 应用。这种方式会清空 Session,比热加载更加干净、彻底,一般用在生产环境。
相关文章:
Tomcat隔离web原理和热加载热部署
Tomcat 如何打破双亲委派机制 Tomcat 的自定义类加载器 WebAppClassLoader 打破了双亲委派机制,它首先自己尝试去加载某个类,如果找不到再代理给父类加载器,其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader 的两个方法…...
使用ffmpeg和python脚本下载网络视频m3u8(全网最全面)
网上给娃找了些好看的电影和一些有趣的短视频,如何保存下来呢?从网上找各种工具?都不方便。于是想到何不编程搞定,搞个脚本。对程序员来说这都不是事儿。且我有华为云服务器,完全可以把地址记下,后台自动下…...
【考研408常用数据结构】C/C++实现代码汇总
文章目录 前言数组多维数组的原理、作用稀疏数组 链表单向链表的增删改查的具体实现思路约瑟夫环问题(可不学)双向链表 树二叉搜索树中序线索二叉树哈夫曼树的编码与译码红黑树B树B树 堆顺序与链式结构队列实现优先队列排序算法(重点…...
Flink学习笔记(二):Flink内存模型
文章目录 1、配置总内存2、JobManager 内存模型3、TaskManager 内存模型4、WebUI 展示内存5、Flink On YARN 模式下内存分配6、Flink On Yarn 集群消耗资源估算6.1、资源分配6.2、Flink 提交 Yarn 集群的相关命令6.3、Flink On Yarn 集群的资源计算公式 1、配置总内存 Flink J…...
信息系统项目管理师第四版学习笔记——项目绩效域
干系人绩效域 干系人绩效域涉及与干系人相关的活动和职能。在项目整个生命周期过程中,有效执行本绩效域可以实现的预期目标主要包含:①与干系人建立高效的工作关系;②干系人认同项目目标;③支持项目的干系人提高了满意度…...
PyTorch 深度学习之加载数据集Dataset and DataLoader(七)
1. Revision: Manual data feed 全部Batch:计算速度,性能有问题 1 个 :跨越鞍点 mini-Batch:均衡速度与性能 2. Terminology: Epoch, Batch-Size, Iteration DataLoader: batch_size2, sheffleTrue 3. How to define your Dataset 两种处…...
小谈设计模式(26)—中介者模式
小谈设计模式(26)—中介者模式 专栏介绍专栏地址专栏介绍 中介者模式分析角色分析抽象中介者(Mediator)具体中介者(ConcreteMediator)抽象同事类(Colleague)具体同事类(C…...
7种设计模式
1. 工厂模式 优点:封装了对象的创建过程,降低了耦合性,提供了灵活性和可扩展性。 缺点:增加了代码的复杂性,需要创建工厂类。 适用场景:当需要根据不同条件创建不同对象时,或者需要隐藏对象创建…...
el-table合计行合并
效果如下 因为合计el-table的合并方法是不生效的,所以需要修改css下手 watch: {// 应急物资的合计合并planData: {immediate: true,handler() {setTimeout(() > {const tds document.querySelectorAll(".pro_table .el-table__footer-wrapper tr>td");tds[0]…...
新手如何快速上手HTTP爬虫IP?
对于刚接触HTTP爬虫IP的新手来说,可能会感到有些困惑。但是,实际上HTTP爬虫IP并不复杂,只要掌握了基本的操作步骤,就可以轻松使用。本文将为新手们提供一个快速上手HTTP爬虫IP的入门指南,帮助您迅速了解HTTP爬虫IP的基…...
(十五)VBA常用基础知识:正则表达式的使用
vba正则表达式的说明 项目说明Pattern在这里写正则表达式,例:[\d]{2,4}IgnoreCase大小写区分,默认false:区分;true:不区分Globaltrue:全体检索;false:最小匹配Test类似p…...
vue配置@路径
第一步:安装path,如果node_module文件夹中有path就不用安装了 安装命令:npm install path --save 第二步:在vue.config.js文件(如果没有就新建)中配置 const path require("path"); function …...
Ubuntu 18.04 OpenCV3.4.5 + OpenCV3.4.5 Contrib 编译
目录 1 依赖安装 2 下载opencv3.4.5及opencv3.4.5 contrib版本 3 编译opencv3.4.5 opencv3.4.5_contrib及遇到的问题 1 依赖安装 首先安装编译工具CMake,命令安装即可: sudo apt install cmake 安装Eigen: sudo apt-get install libeigen3-…...
【网络基础】IP 子网划分(VLSM)
目录 一、 为什么要划分子网 二、如何划分子网 1、划分两个子网 2、划分多个子网 一、 为什么要划分子网 假设有一个B类IP地址172.16.0.0,B类IP的默认子网掩码是 255.255.0.0,那么该网段内IP的变化范围为 172.16.0.0 ~ 172.16.255.255,即…...
【OCR】合同上批量贴印章
一、需求 OCR算法在处理合同等文件时,会由于印章等遮挡导致文本误识别。因此在OCR预处理时,有一个很重要的步骤是“去除印章”。其中本文主要聚焦在“去除印章”任务中的数据构建步骤:“合同伪印章”的数据构建。下面直接放几张批量合成后效果…...
Stable diffusion 用DeOldify给黑白照片、视频上色
老照片常常因为当时的技术限制而只有黑白版本。然而现代的 AI 技术,如 DeOldify,可以让这些照片重现色彩。 本教程将详细介绍如何使用 DeOldify 来给老照片上色。. 之前介绍过基于虚拟环境的 基于DeOldify的给黑白照片、视频上色,本次介绍对于新手比较友好的在Stable diff…...
在服务器上解压.7z文件
1. 更新apt sudo apt-get update2. 安装p7zip sudo apt-get install p7zip-full3. 解压.7z文件 7za x WN18RR.7z...
【opencv】windows10下opencv4.8.0-cuda C++版本源码编译教程
【opencv】windows10下opencv4.8.0-cuda C版本源码编译教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【opencv】windows10下opencv4.8.0-cuda C版本源码编译教程前言准备工具cuda/cudnncmakeopencv4.8.0opencv_contrib CMake编译VS2019编…...
软碟通制作启动盘
一、下载并安装软碟通 二、插入U盘,打开软碟通; 三、在软碟通中选择“文件”-“打开镜像文件”,选择要制作成启动盘的ISO镜像文件; 1.打开要制作的iso文件 选择对应的iso文件 四、在软碟通中选择“启动”-“写入硬盘”ÿ…...
Tomcat和HPPT协议
1.介绍 1.Java EE 规范 JavaEE(java Enterprise Edition):java企业版 JavaEE 规范是很多的java开发技术的总称。这些技术规范都是沿用自J2EE的。一共包括了13个技术规范 2.WEB概述 WEB在计算机领域中代表的是网络 像我们之前所用的WWW&…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
