在 Spring Boot 项目里引入 spring-boot-devtools 之后,应用会在类文件变动时自动重启。这个能力的核心开关之一是 @EnableRestart 注解,它通常随着 devtools 的自动配置被间接激活,也可以由开发者显式使用。该注解本身并不复杂,但它背后串联起了重启类加载器、资源监控以及配置排除等一整套热部署机制。如果不清楚它的作用范围,就容易出现改了代码不重启、或者重启后状态丢失的问题。

EnableRestart 注解的源码结构与自动配置关系
@EnableRestart 是 spring-boot-devtools 提供的一个组合注解,它的主要工作是导入 RestartAutoConfiguration 这一配置类。从源码层面看,该注解使用了 @Import 元数据,将重启相关的 Bean 定义注册到容器里,其中包括重启类加载器工厂、文件监听服务以及重启策略控制器。开发者一般在主启动类上并不手动写它,因为引入 devtools 依赖后,spring.factories 中的 EnableAutoConfiguration 已经包含了对应配置,但了解它的存在有助于排查某些模块未生效的情况。
在实际工程中,如果你构建的是一个多模块 Maven 项目,并且希望某个被依赖的子模块也参与热重启,就需要注意 EnableRestart 所绑定的默认扫描路径。默认情况下,重启类加载器只加载项目自身 target/classes 下的资源,而对于通过 jar 形式引入的依赖,devtools 会让其由基类加载器加载,从而不参与重启。这种划分是为了兼顾重启速度,但也导致很多人误以为注解失效。通过显式配置 spring.devtools.restart.additional-paths 可以扩展监控目录。
下面是一段简化的注解使用与配置示例,展示如何在启动类上明确引入重启能力,并追加监控路径:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.devtools.autoconfigure.EnableRestart;
@SpringBootApplication
@EnableRestart
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
对应的 application.properties 配置可以这么写,把本地另一个模块的源码目录也纳入重启范围:
spring.devtools.restart.enabled=true spring.devtools.restart.additional-paths=../common-module/src/main/java spring.devtools.restart.exclude=static/**,public/**
重启类加载器与双亲委派机制的变形
EnableRestart 激活之后,devtools 会创建两个类加载器:一个是普通的基类加载器,负责加载第三方 jar 包;另一个是重启类加载器,负责加载应用自身变动的类。当文件监听发现变动时,SpringBoot 会丢弃旧的重启类加载器,新建一个来加载最新字节码,而基类加载器保持不变。这种做法本质上是打破了严格的双亲委派,让应用代码可以被快速替换,而不用重新加载整个 JVM 依赖树。
这种类加载隔离带来一个常见副作用:如果我们在静态变量或缓存里保存了跨重启需要保留的对象,重启后会因为类加载器不同而出现类型转换异常。比如模块 A 在基类加载器中加载,模块 B 在重启类加载器中加载,两者相互引用同一个“看似相同”的类,却会因为类加载器不同而被 JVM 判定为不同类型。理解 EnableRestart 背后的加载模型,可以帮助我们在设计全局缓存、线程池时避开这类坑。
此外,devtools 在重启时并不会重置 JVM 的系统属性,也不会重新执行 JVM 参数相关的初始化。因此一些依赖 System.setProperty 的组件要特别小心。从性能角度看,重启类加载器只加载少量类,通常重启耗时在几百毫秒到两秒之间,远小于冷启动。但该机制在容器环境或某些应用服务器中可能与自身的类加载体系冲突,此时需要通过 spring.devtools.restart.enabled=false 关闭。
整合中的常见误用与排查思路
第一种典型误用是在生产环境遗漏了依赖范围控制。devtools 应当声明为 runtime 或 test 范围,如果被打进生产包,不仅会增加体积,还可能因 EnableRestart 的自动配置导致意外重启行为。Maven 中正确写法是将依赖放在 optional=true 或使用 spring-boot-maven-plugin 的排除规则,确保发布物干净。
第二种误用是认为所有文件变动都会触发重启。实际上 EnableRestart 关联的监听服务默认忽略 static、public 等静态资源目录,这些资源由 LiveReload 直接推送到浏览器而不重启 JVM。如果改了模板页却不刷新,应检查是否落在排除列表中,而不是怀疑注解未生效。我们可以通过启动日志中 Restarting application ... 字样确认重启确实发生。
当遇到“改了代码没反应”时,建议依次确认:devtools 依赖是否真正存在于运行时的 classpath;spring.devtools.restart.enabled 是否为 true;附加路径是否覆盖目标模块;IDE 是否开启了自动编译且将编译输出指向了被监控目录。下面给出一段用于打印当前类加载器信息的诊断代码,方便在控制台确认类来自哪个加载器:
public void printLoaderInfo() {
ClassLoader loader = this.getClass().getClassLoader();
System.out.println("当前类加载器: " + loader);
if (loader != null) {
System.out.println("父加载器: " + loader.getParent());
}
}
通过上述输出,如果看到重启类加载器(通常为 RestartClassLoader)说明 EnableRestart 链路正常;如果始终是 AppClassLoader 或容器自有加载器,则热重启并未覆盖该类。此时应回头检查模块依赖形态与路径配置,而不是盲目重启 IDE 或服务。
Spring_BootEnableRestartdevtools修改时间:2026-08-13 09:12:45