尧图精选

SpringBoot热加载:DevTools、HotSwap、JRebel对比

🕒 发布时间:2026/9/10 5:30:20 📁 来源:尧图网络
写代码最怕什么我估计十个人里有八个会说改一行代码切回 IDEA重启 Spring Boot然后盯着启动日志转圈圈。小项目还好几十秒就回来了项目一旦上了规模光启动就要两三分钟一天改几十次代码光等重启的时间就够让人崩溃。所以“热加载方案”真不是炫技而是为了不加班、不心烦把时间花在真正要解决的问题上。这篇文章我把 IDEA 里跑 Spring Boot 项目最常用的三种热加载方案一次性讲透Spring Boot DevTools、IDEA 自带的 Debug Hot Swap、以及 JRebel 插件。三者的配置成本从零到几分钟不等热替换能力也从“改改方法体”到“新增一个类都能不重启”逐级递增。不管你是刚接触 Spring Boot 的新人还是被启动速度折磨许久的老人这篇文章都值得看完。1. 先搞清楚为什么需要热加载三种方案到底在“热”什么1.1 手动重启与热加载的本质区别很多人把“热加载”和“自动重启”混为一谈其实它俩不是一个东西。Spring Boot DevTools 的默认行为本质上是自动重启。它检测到 classpath 下的文件变化帮你直接重启整个应用。虽然省去了手动点重启按钮的动作但应用的启动时间一点没少只是从“你等”变成了“它自己等”。而真正的热加载指的是在 JVM 不停止的情况下替换掉已经加载的类字节码。这样不需要重新走一遍 Bean 初始化、Spring 容器装配、连接池建立这些流程改动一编译新代码立刻生效。DevTools 做不到这一点IDEA 自带的 Hot Swap 只能做很有限的热替换JRebel 才是真正意义上的全量热加载。理解这个区别很重要因为很多人用了 DevTools 之后觉得“也就那样启动还是要二十秒”其实是选错了方案。1.2 三种热加载方案的能力边界我把三种方案的差异做了一个对比大家先建立整体认知方案配置成本方法体修改新增方法/字段新增类Spring 配置变更对启动时间的影响Spring Boot DevTools低一个依赖支持重启生效支持重启生效支持重启生效支持重启生效重启时间取决于项目规模IDEA Hot Swap零Debug 自带支持即时生效不支持不支持不支持无重启即时生效JRebel中插件授权支持即时生效支持即时生效支持即时生效大部分支持无重启即时生效注意一个关键点DevTools 在表格里看起来“什么都能改”但它是通过重启实现的不是同一个内存空间里的热替换。如果你只是想改一行日志输出DevTools 也得重启一次而 Hot Swap 和 JRebel 是真正的不重启秒生效。后面的章节我会把三种方案从接入到实战逐个拆开讲每一步都给出可以直接照抄的配置。2. 方案一Spring Boot DevTools——官方免费最适合日常开发2.1 接入 DevTools 只需三步DevTools 是 Spring Boot 官方提供的开发期增强工具目前依然是大多数团队的首选方案。接入步骤非常简单我直接给配置。第一步在pom.xml里加依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-devtools/artifactId scoperuntime/scope optionaltrue/optional /dependency这里有两个细节要解释一下。scope用runtime意思是编译期不需要运行期才需要避免开发时误用它的 API 到业务代码里。optional设为true也很关键这样项目被其他模块依赖时devtools 不会被传递出去防止生产环境把开发工具带进去。第二步改代码后编译一次让 class 文件发生变化。IDEA 里按CtrlF9Build ProjectDevTools 检测到 classpath 变化就会自动重启应用。控制台会输出类似这样的日志Restarting Spring Boot application...第三步如果你希望连 IDEA 都不切直接改代码就自动编译可以在 IDEA 设置里打开自动编译。路径是File - Settings - Build, Execution, Deployment - Compiler - Build project automatically勾选上。再进高级设置Advanced Settings把Allow auto-make to start even if developed application is currently running也勾上。这样你修改完文件IDEA 会自动编译DevTools 会自动重启手基本不需要离开键盘。2.2 让 DevTools 真正好用的几个配置默认状态下的 DevTools 能用但有几个点不优化的话体验会打折扣。第一个是静态资源的干扰。如果你在项目里同时写前端页面改一下 HTML、JS、CSS 也会触发 DevTools 重启而这类资源其实刷新浏览器就够了。解决办法是在application.properties里排除掉spring.devtools.restart.excludestatic/**,public/**,templates/**这样静态资源的改动不会触发重启改完直接CtrlShiftF9编译一下浏览器刷新就能看到效果省掉大量无意义的重启。第二个是模板缓存。如果你用的是 Thymeleaf默认生产环境下会开模板缓存开发环境需要关掉不然改了模板页面刷新还是老内容spring.thymeleaf.cachefalse spring.resources.cache-period0我见过不少同事第一次用 DevTools改模板没反应以为 DevTools 坏了其实就是模板缓存没关。第三个是触发文件。在大项目里经常会遇到“改一个依赖的静态资源结果触发了好几次重启”的情况。DevTools 支持配置一个触发文件只有这个文件变化才真正执行重启。做法是在application.properties里加spring.devtools.restart.trigger-file.trigger然后在resources目录下新建一个名为.trigger的空文件。以后你改代码、编译DevTools 不会立即重启只有当你手动更新.trigger文件的时间戳时才会触发重启。这个机制特别适合那些“依赖包内部文件变动频繁”或者“远程调试时不想频繁重启”的场景。2.3 DevTools 的原理与局限性值得你知道用 DevTools 之前我建议你了解一下它的类加载机制否则遇到奇怪问题容易懵。DevTools 会把项目里的类分成两组base classloader 加载第三方依赖 jarrestart classloader 加载你自己写的类。每次重启它会创建新的 restart classloader新的类加载器去加载工作区里最新编译出的 class 文件而第三方依赖的类还是老的 base classloader 里的不用重新加载。这个机制带来两个好处第一重启速度比完整冷启动快因为省掉了一大堆 jar 包的类加载和扫描第二很聪明地避免了“依赖没变却要重复加载”的浪费。但代价是如果你在本地临时改了某个 jar 里的类DevTools 不会感知到因为它始终从依赖仓库里加载。局限性也很明显说到底还是重启Spring 容器、数据库连接池、Redis 连接、定时任务调度器都会全部重建。项目如果启动要 40 秒DevTools 重启也要 35 秒左右省不了多少。它只是把“点按钮重启”这件事自动化了。我记得一个真实的踩坑经历之前有个定时任务在项目启动时初始化了一批内存数据我用 DevTools 改完代码后这批数据会重新初始化但缓存里对旧对象的引用还在。排查了很久才发现是重启后容器重建但外部缓存没有跟着清。所以遇到“重启后状态不一致”的诡异问题先往容器重建方向想。3. 方案二IDEA 自带的 Hot Swap——零配置但大多数人没用好3.1 Hot Swap 到底是什么如果你用的是 IDEA 社区版或者公司不让你装额外插件又想不重启改代码其实 IDEA 自带了一个被低估的能力Debug 模式下的 Hot SwapJava 虚拟机热替换。Hot Swap 的底层是 JVM 的数HotSpot虚拟机提供的RedefineClasses能力简单说就是允许你在运行中的 JVM 里用新的 class 字节码替换掉已经加载的类。这是 JDK 官方支持的机制不需要任何插件。但 Hot Swap 有一个铁律它只能替换方法体。换句话说你可以在方法里面加一行日志、减一个判断、改一个返回值类型这些都能生效但你不能新增一个方法、不能增加或删除字段、不能修改类的继承关系这些属于结构的改变JVM 不允许在运行期替换。Java 21 之后JVM 的热替换能力有所增强支持了更多操作但 IDEA 左侧 Debug 工具栏里依然只有Update按钮其核心能力还是围绕方法体做文章。3.2 Debug 模式下正确运用 Hot Swap操作步骤非常简单第一步用 Debug 模式启动 Spring Boot 应用注意不是 Run是 Debug。第二步修改代码。第三步IDEA 顶部工具栏里有个“Update”图标或者直接按快捷键CtrlF10选择Update classes把修改后的 class 文件热替换到运行中的 JVM 里。如果你开了Build project automaticallyIDEA 会在类变更后自动编译你只需要在修改完代码后切回 IDEA 窗口按CtrlF9IDEA 会自动应用热替换不需要手动点 Update。这个方案在什么时候最香我自己的高频场景是调整 SQL、修接口逻辑分支、加日志。比如一个OrderService里某个方法的计算逻辑写错了直接进 Debug 模式改一行代码按CtrlF9再看接口返回值整个过程不到五秒连请求都不用重发。注意Hot Swap 后断点建议重新打一遍IDEA 有时会对旧断点位置产生偏移这个不算 bug是字节码行号映射导致的正常现象。3.3 什么时候用 Hot Swap 就够了很多开发者一听 Hot Swap 能力有限就直接放弃我觉得有点可惜。我日常工作中有 60% 左右的代码改动是修bug、调参数、改判断条件这类改动落点都在方法体内部Hot Swap 完全够用。如果你做了架构调整需要新增一个服务类、加一个字段那就不该用 Hot Swap 硬顶应该交给 DevTools 重启或者干脆用 JRebel。Hot Swap 还有一个我特别喜欢的场景联调接口。我们经常调第三方接口联调时对方接口返回的数据格式有点问题你只需要在 Debug 模式改掉对返回值的解析逻辑热替换后继续调不用重新发请求体验非常顺滑。需要提醒的是Hot Swap 只对 JVM 内已经加载的类有效。如果你新增了一个类然后想用CtrlF10让新类生效是不行的必须重启应用。同样如果你改了 Spring 配置文件里的路由、数据库连接Hot Swap 也管不了这些只能通过重启或 JRebel 解决。4. 方案三JRebel——真正的热加载花钱买体验4.1 安装与启用 JRebelJRebel 是目前业界公认最强的 JVM 热加载插件它从一开始就是为“完全不用重启”设计的。它和 DevTools 的最大区别在于JRebel 会拦截类的加载和调用把新编译的类动态注入到运行中的 JVM 里甚至支持新增类、新增字段、修改方法签名这类结构性变化。安装很简单。IDEA 的插件市场直接搜JRebel安装后重启 IDEA然后需要一个许可证。JRebel 是商业软件支持官方试用你可以去官网申请 14 天的试用 license也可以购买正版授权。激活后JRebel 会在工具栏上多一组按钮。要让它生效需要做两件事第一步在 IDEA 的 Run/Debug Configurations 里找到你的 Spring Boot 启动类把运行器切换为JRebel Spring Boot。第二步确保 JRebel 面板里的Enable JRebel开关是打开状态并且你的项目模块出现在 JRebel 管理的模块列表里。JRebel 启动后控制台会多一行JRebel: 2024.2.0之类的版本信息。如果看到这行说明 JRebel 已经在你项目里生效了。4.2 JRebel 能热到什么程度我用了 JRebel 一年半来聊聊它的能力边界。最日常的体验是你改了 Controller 里的一个接口路径比如把/api/order改成/api/v2/order保存代码编译JRebel 能直接让新路径生效不需要重启。这在 DevTools 和 Hot Swap 下都是做不到的因为接口路径属于方法注解信息Hot Swap 不能改DevTools 只能重启。新增一个 Service 实现类、在已有的类里加一个私有方法、给 Bean 增加一个Autowired字段JRebel 都能热加载。我实测过最复杂的场景是给一个已有的Service加一个Cacheable注解JRebel 也可以生效只是偶尔需要在它弹出的对话框里点一下确认。在编辑 MyBatis 的 Mapper XML 时JRebel 也支持热加载改完 SQL 文件编译一下就能生效。但这里有个前提你的项目不能用太老版本的 MyBatis-spring-boot-starter因为版本过老时 Mapper 的 XML 索引在启动时就被缓存了JRebel 无法拦截。有一个限制要提醒网络框架级别的改动比如修改监听器、过滤器链、修改 Servlet 容器初始化逻辑JRebel 也未必能全量支持。这类改动该重启还是重启别硬刚。4.3 JRebel 与 DevTools 的冲突与选择我在早期使用 JRebel 时犯过一个错误项目里同时保留了 DevTools 的依赖导致启动后两个工具都在工作类加载被反复替换出现了各种诡异异常。后来才明白DevTools 的 restart classloader 机制和 JRebel 的动态注入机制天生冲突二者只能选一个。如果你决定用 JRebel建议直接在pom.xml里把 DevTools 的依赖注释掉或者在 Maven 配置里排除它不要让两个工具同时存在。选 DevTools 还是选 JRebel说到底是一个“免费但重启 vs 付费但更快”的选择。个人建议大家分阶段刚起步的项目团队规模小用 DevTools Hot Swap 组合已经能覆盖 80% 场景项目进入高速迭代期接口和业务逻辑频繁变动JRebel 节省的时间会非常可观尤其对大型单体项目一天省下的重启时间足够把插件钱赚回来。5. 三种方案怎么选我的日常搭配参考5.1 以“改动类型”为导向的选型很多人问“到底哪种方案最好”我觉得该反过来问“我现在改的代码属于哪种类型”如果你在改方法里的业务逻辑比如加判断、修 SQL、调返回结构Hot Swap 是最快速的连 DevTools 都不需要。Debug 模式起来改完按一下CtrlF9就完事了。如果你在加新的类、新的接口、改配置类或者调整 Spring 的 Bean 关系Hot Swap 直接没法用DevTools 重启可以JRebel 也可以。二者选谁取决于你的重启成本和预算。项目启动在 10 秒以内用 DevTools 完全够启动超过 20 秒且每天改动频繁预算允许的情况下可以上 JRebel。如果你是纯前端页面调整比如改模板文件、样式、静态资源DevTools 的排除配置已经帮你把重启避开了直接刷新浏览器就够别把后端热加载方案搅和进来。5.2 我保留的一份高效开发环境清单如果你不想踩坑可以直接参考我这套配置组合项目里保留 DevTools 依赖做好排除项和触发文件配置作为兜底方案。日常 Debug 模式启动应用利用 Hot Swap 处理高频的“方法体改动”。当需要加字段、加类这种结构性改动时用CtrlF9主动编译让 DevTools 重启一次。如果项目规模大、团队协作频繁再给每个人都配上 JRebel让它接管 DevTools 的工作。这套组合的好处是即使某天 JRebel 授权到期你也不用改任何代码DevTools 和 Hot Swap 立刻接管开发节奏几乎不受影响。6. 常见问题与排查技巧实录6.1 DevTools 不生效改完代码不重启这个问题的位置非常高频我几乎每周都能在同事的电脑上碰到一次。九成原因是IDEA 没有把修改的内容编译到target/classes目录。DevTools 监听的是类路径下文件的变化而 IDEA 默认并不会每改一次代码就自动编译。你打开自动编译开关后仍然有概率不生效因为 IDEA 的自动编译并不是“实时”的它有个延迟和触发机制。排障顺序建议从简单到复杂检查pom.xml里 devtools 是否还在且没有被其他模块的依赖过滤掉。手工按一次CtrlF9看控制台有没有Restarting日志。查看右下角有没有“Compilation completed”提示如果有编译异常DevTools 不会重启。如果以上都正常考虑触发文件配置是否误加了.trigger文件。6.2 JRebel 与 Lombok 冲突导致热加载失效JRebel 对 Lombok 有官方支持但版本不匹配时会出现类加载失败。表现为改了代码后JRebel 日志报ClassNotFound或者某个方法找不到。解决方法是统一 JRebel 与 Lombok 的版本。JRebel 官方维护了一个“兼容版本表”建议把你的 Lombok 版本升级到较新的比如 1.18.20 以上同时 IDEA 的 Lombok 插件版本也一并更新。如果问题还在试试在启动参数里加上-Dlombok.jrebeltrue这个参数强制开启 JRebel 对 Lombok 生成代码的适配实测能解决九成冲突。6.3 热加载后 Session 丢失、连接池异常热加载和重启都会重建 Spring 容器这意味着Session 里保存的登录态、内存缓存、Scheduled定时器都会在重启后重新初始化。DevTools 的“自动重启”每次都会重建容器所以影响最明显。解决思路有两个方向第一把关键状态外置。登录态放到 Redis Session、缓存放到 Redis这样重启后数据还在用户无需重新登录。第二对重启频繁的开发环境可以适当降低重启频率把零散的改动攒成一次编译用触发文件控制重启节奏。连接池异常的典型表现是重启后第一条 SQL 执行报“Connection is closed”这是因为连接池重建前的旧连接没有正确释放。DevTools 的 restart classloader 会持有旧连接池对象的引用导致新容器的数据库操作访问旧连接。比较有效的规避方案是给连接池配置较短的空闲回收时间或者在开发环境把连接池的maximum-pool-size调小一点降低连接泄露风险。6.4 快速排查工具汇总现象可能原因排查/解决路径改了代码不重启IDEA 未触发编译检查自动编译开关手工CtrlF9控制台没有 LiveReload 日志devtools 被可选依赖排除检查 pom 中optional标签确认依赖存在Hot Swap 后断点灰掉方法行号映射变化停掉断点重新打一次JRebel 报了 RedefineClassException改动触及类结构该场景需重启应用建议看控制台输出重启后 Session 丢失容器重建使用 Redis Session 或减少重启频率改模板不生效模板缓存开启设置spring.thymeleaf.cachefalse最后再分享一个实际经验我踩过不少热加载的坑之后现在的习惯是白天大块时间开发时用 DevTools Hot SwapDebug 模式一直开着改方法体靠热替换改结构靠 DevTools 重启遇到大型重构或者多模块联调时再打开 JRebel。这套组合用下来平均每天能省下至少半小时的等待时间长期积累非常可观。还有个小技巧分享一下如果你和同事在同一个模块上协同开发建议把 DevTools 的触发文件.trigger纳入 git 忽略不然每次拉取代码都可能导致不必要的重启。另外热加载方案只是工具真正重要的还是把模块拆得合理启动速度本来就会更快。工具选对思路清晰加班自然就少了。别把时间耗在等待上有时间多看看日志、多想想系统设计那才是程序员最值钱的部分。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →