jrebel支持秒级热更新与失败自动回滚,通过双classloader隔离、类结构快照、健康检查及框架钩子实现新旧版本原子切换。
类加载器本身不提供“快照机制”,但可以通过外部设计+classloader协作实现热部署失败时的秒级回滚——关键不是让类加载器记住历史,而是让应用能快速卸载新类、重新激活旧类定义。
构建可回滚的类版本快照
每次热更新前,把当前正在运行的类结构、资源哈希、依赖上下文打包成轻量快照,存于内存或本地临时目录:
- 记录每个关键类的 ClassLoader 实例标识(如 identity hash)和加载时间戳
- 计算核心 classpath 资源(如
application.yml、templates/)的 SHA256 哈希值 - 保存 CDI/Spring 上下文中活跃 Bean 的类型签名与作用域信息(例如:
@ApplicationScoped GreetingService) - 快照不存字节码本身,只存定位信息(如:jar 路径 + entry 名,或 dev-mode 下的 .class 文件绝对路径)
用双 ClassLoader 隔离新旧版本
避免单 ClassLoader 内部重定义失败导致状态污染。启动时就准备两个隔离的 ClassLoader:
- BaseClassLoader:只加载框架核心(Spring、Quarkus runtime)、不可变基础类,生命周期贯穿整个 dev 过程
- AppClassLoader:仅加载业务代码与配置资源,每次热更都新建一个实例,旧实例保留不销毁
- 回滚时,直接切换应用线程使用的 ClassLoader 引用,并触发上下文刷新(如 Spring 的
ContextRefresher或 Quarkus 的AugmentedClassLoader切换)
失败检测与原子回切逻辑
不能等 JVM 报 NoClassDefFoundError 才行动,要在热更流程中主动埋点验证:
- 在新 AppClassLoader 加载完所有类后,执行一组轻量“健康检查方法”(如调用
GreetingService.class.getDeclaredMethods().length > 0) - 若检查失败或超时(如 >200ms),立即放弃新 ClassLoader,将流量/请求分发器指向上一个可用的 AppClassLoader 实例
- 配合 JVM TI 或 Instrumentation API 拦截首次类初始化异常,在
premain阶段注册ClassFileTransformer捕获早期失败
适配主流框架的回滚钩子
不同框架暴露的刷新入口不同,需针对性对接:
-
Spring Boot DevTools:监听
ContextRefreshedEvent,失败时调用ConfigurableApplicationContext.close()并重启旧上下文 -
Quarkus:利用
io.quarkus.runtime.configuration.ConfigRecorder和AugmentPhase回调,在DevModeMain.redeploy()失败时调用ClassLoaderManager.restorePrevious() -
JRebel:通过其
rebel.xml中的<rollback-on-failure></rollback-on-failure>标签开启自动回滚,并指定 fallback classpath 目录










