一、Spring Boot 启动热部署
Spring Boot 启动“热部署(Hot Deployment)”,可以让你在不重启项目的情况下快速看到代码变更的效果(特别是前后端调试阶段)。
1-1、什么是热部署?
热部署是指:
修改 Java 或模板(如 HTML、Thymeleaf)代码后,无需手动重启服务,Spring Boot 能自动感知并重新加载类或资源,节省重启时间,提高开发效率。
热部署的过程:
1、重启(restart):自定义开发的代码,包括:页面,类,配置文件......,加载位置restart类加载器;
2、重载(reload):jar包(基本不会有变化),加载位置base类加载器
热部署,仅仅代码restart过程,而程序第一次启动的时候,既有restart过程,又有reload过程。
核心概念是:JVM 用两个不同的类加载器来分别处理"会变动的代码"和"不会变的依赖"。
| 名词 | 处理对象 | 类加载器 | 特点 |
|---|---|---|---|
| Restart(重启) | 你自己写的代码:页面、类、配置文件等 | RestartClassLoader | 这些经常改,需要频繁重新加载 |
| Reload(重载) | 第三方 jar 包(如 Spring、MySQL 驱动等) | BaseClassLoader | 基本不变,不需要重复加载 |
1、为什么热部署"快"?
热部署时:
只有 Restart 过程 = 只重新加载你改过的那几行代码 (jar 包已经在内存里了,直接用,不用重新读)第一次启动时:
Restart + Reload = 既要加载你的代码,也要加载所有 jar 包 (所以启动慢)2、一句话总结
热部署的"快"是因为只重启你自己写的代码,而不碰那些沉重的 jar 包依赖。
第一次启动慢,是因为 jar 包也要从头加载。
1-2、常用热部署方式
Spring Boot 推荐使用的热部署工具是Spring Boot DevTools。
1-3、使用 Spring Boot DevTools 实现热部署
1. 添加依赖:开启开发者工具
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <!-- 只在开发环境有效 --> <optional>true</optional> <!-- 可选依赖,防止被打包进生产环境 --> </dependency>2. IDE 设置:激活热部署
Spring Boot DevTools 默认基于类文件变更触发重启,即:spring-boot-devtools监控的是编译输出目录(target/classes),不是源码目录。
因此你需要确保:开启 IDEA 自动编译
(1). 启用了自动构建:
Settings → Build → Compiler → ✅ Build project automatically Settings → Advanced Settings → ✅ Allow auto-make to start even if...这样你按Ctrl+S或切到浏览器时,IDEA 自动编译到target,devtools 自动 Restart。
(2). 让 IntelliJ 在修改文件后自动编译:
Advanced Settings :✔️ “Allow auto-make to start even if developed application is currently running”
这样只要你保存了 Java 文件,IDE 就会自动编译触发 DevTools 热部署。
(3). 设置不参与热部署的文件/文件夹:
1-4、DevTools 热部署行为总结
| 变更内容 | 是否自动热部署 | 说明 |
|---|---|---|
| Java 类(Controller, Service) | ✅ 是 | 自动重新加载 |
| 静态资源(HTML, CSS, JS) | ✅ 是 | 即时刷新页面 |
| 配置文件(application.properties) | ✅ 是 | 配置了 DevTools 热部署 → 修改application.properties会触发自动重启→ 新配置会生效。 |
| 引入新依赖 | ❌ 否 | 需要重新启动项目 |
1-5、禁用热部署的功能呢
1-6、DevTools 的热部署机制
1、两个 ClassLoader
Spring Boot 的 DevTools 在启动时会启用两个 ClassLoader:
Base ClassLoader
负责加载第三方依赖 jar 包(比如 Spring Boot、Spring MVC、MySQL 驱动等)。
这些 jar 包一般不会在开发过程中频繁改动。
Base ClassLoader 在应用整个生命周期中保持不变。
Restart ClassLoader
负责加载自己写的代码(比如
Controller、Service、配置文件、前端模板等)。这些内容经常在开发调试时修改。
当 DevTools 监测到代码/配置变化时,会丢弃旧的 Restart ClassLoader,创建一个新的 Restart ClassLoader 来加载新的类。
重启 (restart):自定义开发的代码,包括页面、类、配置文件……,由Restart ClassLoader负责加载。
重载 (reload):jar 包(基本不会变化),由BaseClassLoader负责加载。
✔ 含义:
restart:只重启“自己写的代码”对应的类加载器(Restart ClassLoader)。
好处:比完整 JVM 重启快很多。
reload:Base ClassLoader 只在程序第一次启动时加载 jar 包。之后除非你改动依赖(比如改了
pom.xml重新引入 jar 包),否则不会再次 reload。
| 改动类型 | 对应过程 | 是否自动热部署 |
|---|---|---|
| 改自己写的代码、配置文件 | Restart | ✅ 自动(devtools 监控target/classes) |
改pom.xml依赖(增删改 jar 包) | Reload | ❌不会自动 |
2、启动过程和运行时的差异
程序第一次启动:
Base ClassLoader 会reload所有依赖 jar。
Restart ClassLoader 会restart你自己的业务代码。
运行时修改代码/配置文件:
只会触发RestartClassLoader 重新创建和加载。
Base ClassLoader 不变(jar 不会 reload)。
3、总结一句
热部署(restart)=丢弃并重建Restart ClassLoader(业务代码/配置/页面刷新);
reload 只发生在应用第一次启动时,用来加载 jar 包(不常变化)。
"丢弃" =让旧的 ClassLoader 及其加载的所有类变成垃圾,等待 GC 回收,然后新建一个干净的 ClassLoader 重新加载最新代码。
JVM 的类加载有个铁律:一个类一旦被某个 ClassLoader 加载,就永远跟它绑定,不能卸载单个类。
| 操作 | 能否做到 | 原因 |
|---|---|---|
替换UserService里的方法体 | ❌ 不能直接做 | 类已加载,结构定死 |
| 新增一个方法到已加载的类 | ❌ 不能 | 类结构不可变 |
| 把整个 ClassLoader 换掉 | ✅ 可以 | 旧的连带它加载的所有类一起被 GC |
┌─────────────────────────────────────────────┐ │ 旧的 RestartClassLoader(包含你修改前的代码) │ │ ───────────────────────────────────────── │ │ • 加载了 UserService.class(旧版本) │ │ • 加载了 application.yml(旧配置) │ │ • 这些类对象都在 JVM 内存里 │ │ • 一旦这个 Loader 被"丢弃",它加载的所有类 │ │ 都变成"孤儿"——JVM 垃圾回收时会清理掉 │ └─────────────────────────────────────────────┘ ↓ 检测到文件变化 ↓ ┌─────────────────────────────────────────────┐ │ 新的 RestartClassLoader(加载修改后的代码) │ │ ───────────────────────────────────────── │ │ • 重新加载 UserService.class(新版本) │ │ • 重新加载 application.yml(新配置) │ │ • 旧的类?不管了,交给 GC │ └─────────────────────────────────────────────┘