热重载的核心价值在于减少开发过程中的等待时间。传统 Spring Boot 项目修改一段业务逻辑后,需要手动停止进程、重新打包、再次启动,整个过程动辄几十秒;如果项目依赖较多,启动时间甚至可能超过一分钟。频繁执行这套操作会严重打断编程心流,而热重载工具正是为了把「改代码—看效果」的周期缩短到几秒甚至亚秒级。Spring Boot 生态中并没有一个官方命名为 Spring Boot Reload 的独立模块,但开发者通常会把 devtools、spring-loaded 这类能实现运行时重载的组件统一称作 Reload 工具,本文也沿用这一习惯。

spring-boot-devtools 的自动重启是怎么工作的
spring-boot-devtools 是 Spring Boot 官方提供的开发期工具,它实现热重载的方式并不是真正的「热替换」,而是通过两个类加载器协同完成快速重启。应用启动时,基础类库(如第三方 jar 包)会被一个 base classloader 加载,而项目自己的类则由 restart classloader 负责。检测到 classpath 下的类文件发生变化时,devtools 只销毁并重建 restart classloader,base classloader 保持不变。这种设计避免了重新加载那些几乎不会改变的第三方依赖,从而把重启时间从全量启动的几十秒降低到三五秒左右。
要启用 devtools,首先需要在 Maven 或 Gradle 中添加依赖。以 Maven 为例,在 pom.xml 中加入以下配置即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
标记为 runtime 和 optional 能确保 devtools 不会传递到生产环境。依赖添加完成后,项目就具备了自动重启能力:在 IDE 中修改 Java 文件并保存,devtools 会监听 classpath 变化并触发重启。默认情况下,classpath 监控会忽略一些目录,比如 target/classes 下的部分资源,但这些规则可以通过 spring.devtools.restart.additional-paths 进行调整。需要注意的是,devtools 并不会监听所有文件类型的变化,例如修改 properties 或 yml 配置文件时,默认也会触发重启,这会让人误以为配置变化需要重启才能生效;实际上如果只想刷新配置而不重启,可以结合 spring-boot-configuration-processor 和 @RefreshScope 来处理。
静态资源与模板的实时更新机制
除了 Java 类的自动重启,devtools 对静态资源和模板文件也提供了更轻量的处理方式。对于 src/main/resources/static 目录下的 JS、CSS、图片等静态文件,devtools 默认会在文件变化时直接刷新浏览器看到的资源,并不需要重启应用。这是因为 Spring Boot 内置的静态资源处理器在 devtools 存在时会关闭缓存,每次请求都会从磁盘重新读取。如果你的项目使用了 Thymeleaf、FreeMarker 或 Groovy 模板,devtools 也会禁用模板引擎的缓存,因此修改 HTML 模板后刷新页面就能立即看到变化,全程无需重启 JVM。
但在实际开发中,前端资源往往由专门的构建工具管理,比如 Webpack 或 Vite。如果 devtools 的 classpath 也包括了前端构建输出目录,频繁的自动重启反而会干扰开发节奏。此时可以在 application.yml 中配置排除目录:
spring:
devtools:
restart:
exclude: static/**,public/**
additional-paths: src/main/resources/templates
上述配置把 static 和 public 目录排除在触发重启的监听范围之外,同时把模板目录加入监听。这样 Java 类变化仍然会自动重启,而静态资源的变化只做浏览器刷新,模板变化则直接生效。合理划分监听范围能让 devtools 的行为更符合你的开发习惯,而不是所有文件变化都无脑重启。
基于类加载器的轻量级 Reload 方案
如果你希望实现比 devtools 更接近「真热替换」的效果,可以关注 spring-loaded 这个老牌项目。spring-loaded 通过 Java Agent 机制在 JVM 启动时修改类的加载行为,当某个类的字节码发生变化时,它能直接在运行中的 JVM 里替换这个类的定义,不需要重启整个应用。它的优势是重启时间几乎为零,因为只有被修改的类会被重新定义;缺点也很明显:对某些语法特性支持不完整,比如 lambda 表达式、方法引用等场景偶尔会失效,而且项目已经多年没有更新,兼容新版 JDK 和 Spring Boot 时可能会遇到不稳定问题。
还有一种商业方案 JRebel,它同样基于 JVM Agent 实现真正的类热替换,并且对 Spring、Hibernate 等框架有深度支持,可以动态刷新 Bean 定义。JRebel 的体验很好,但授权费用不低。对于个人开发者或小团队来说,devtools 的快速重启已经能解决大部分效率问题;如果对热替换要求特别高,可以尝试使用免费的 DCEVM(Dynamic Code Evolution VM)配合 HotswapAgent,这套组合支持大部分 Java 8 到 17 的语法。下面是一个使用 HotswapAgent 的启动参数示例,展示如何通过 JVM 参数挂载 agent:
java -javaagent:/path/to/hotswap-agent.jar \
-XXaltjvm=dcevm \
-jar my-spring-boot-app.jar
反斜杠在这里表示命令行换行,实际在 Windows 的 cmd 中需要使用 ^ 符号,而 Linux 或 macOS 的 Shell 可以直接用反斜杠续行。挂载 agent 后,修改 Java 类并重新编译,HotswapAgent 会自动检测 class 文件变化并尝试热替换方法体,大部分情况下无需重启。这种方案比 devtools 更激进,适合已经熟悉 JVM 底层机制的开发者。
整合 Reload 时常见的坑与规避方法
第一个坑是 IDE 的自动编译设置。很多开发者加了 devtools 依赖却发现修改代码后没有反应,原因往往是 IDE 没有开启自动编译。以 IntelliJ IDEA 为例,需要勾选 Build project automatically 选项,并且对于某些版本还要在注册表中启用 compiler.automake.allow.when.app.running。如果使用 Eclipse,默认的自动构建一般已经开启。保存文件后,IDE 编译生成的 class 文件写入 target/classes,devtools 才能检测到变化。
第二个坑是配置文件触发的重启过于频繁。devtools 默认把 application.properties 和 application.yml 的变更也视为需要重启,这在调试多套环境配置时会很烦人。可以通过设置 spring.devtools.restart.trigger-file 来指定一个触发文件,只有这个文件变化时才触发重启,从而把配置变化与重启解耦。例如:
spring:
devtools:
restart:
trigger-file: .restart-trigger
之后如果要手动触发重启,只需在项目根目录更新一下 .restart-trigger 文件的时间戳即可。这种方式能让你精确控制重启时机,避免每次改配置都打断服务。
第三个坑是远程调试场景下的热重载失效。devtools 主要面向本地开发,虽然它提供了远程重启的端点,但默认是关闭的,而且远程重启的安全性需要额外注意。如果必须远程更新代码,建议改用轻量级的 rsync 同步 class 文件配合重启脚本,或者直接使用容器编排工具做滚动更新。热重载不是银弹,在生产环境中最稳妥的做法仍然是完整的发布流程。
Spring Boot 热重载Spring Boot Reloadspring-boot-devtools修改时间:2026-09-23 03:27:01