一、Spring Boot DevTools 主要功能
自动重启(Automatic Restart)
- 当你修改项目中的 classpath 下的文件(比如 Java 源码编译后的 class 文件,或者静态资源),DevTools 会自动重启 Spring Boot 应用。
- 这种重启是“轻量级”的,仅重启 Spring ApplicationContext,而不是 JVM 整体进程,所以速度很快。
热部署(Hot Swapping)
- 虽然 DevTools 不能完全做到像 JRebel 那样的无缝热替换,但它通过自动重启机制实现了类似的效果。
- 结合 IDE 的自动编译功能(比如 IntelliJ IDEA 的 Build Automatically),可以做到修改代码后自动重启应用。
LiveReload 支持
- 集成了 LiveReload 功能,前端页面修改后浏览器可以自动刷新显示最新内容。
- 需要在浏览器安装 LiveReload 插件,或者使用支持 LiveReload 的前端工具。
简化属性配置
- DevTools 会自动应用某些开发环境下的配置,比如关闭模板缓存(Thymeleaf、Freemarker 等),让前端模板修改后能立即生效。
- 还可以区分 application.properties/application-dev.properties 等不同环境配置文件。
远程调试(Remote Debugging)
- 支持远程重启和调试,适合在云主机或远程服务器上开发时使用。
二、工作原理
1. ClassLoader 隔离
DevTools 实现自动重启的核心原理是使用两个 ClassLoader:
- Base ClassLoader:加载第三方依赖(JAR 包),这些内容一般不会变化。
- Restart ClassLoader:加载项目自定义代码(编译后的 class 文件),每次有变更时只重启这个 ClassLoader,而不是整个 JVM。
这样可以大幅提高重启速度,并且避免了重启时重新加载所有依赖。
2. 文件监控
DevTools 会监控 classpath 路径(比如 target/classes),一旦发现文件变化(如 class 文件被 IDE 重新编译),就触发重启。
3. 配置自动切换
DevTools 会检测当前是否为开发环境,如果是,则自动调整部分配置参数(如关闭缓存、开启调试日志等),提升开发体验。
三、如何使用
1. 添加依赖
在 pom.xml
或 build.gradle
中添加依赖:
Maven:
org.springframework.boot
spring-boot-devtools
runtime
注意:建议使用
runtime
作用域,确保不会打包进生产环境。
Gradle:
developmentOnly("org.springframework.boot:spring-boot-devtools")
2. 自动重启和热部署
- 使用支持自动编译的 IDE(如 IDEA、Eclipse),每次保存代码时自动编译 class 文件,DevTools 会检测到变化并重启 Spring Boot 应用。
- IDEA 默认自动编译可在设置里开启:
File -> Settings -> Build, Execution, Deployment -> Compiler -> Build project automatically
。
3. LiveReload
- 启动 Spring Boot 应用后,DevTools 会启动一个 LiveReload 服务(默认端口35729)。
- 在浏览器安装 LiveReload 插件,修改前端文件(如 HTML、CSS、JS)后,浏览器会自动刷新页面。
4. 配置文件自动切换
- DevTools 会自动应用
application-dev.properties
、application-dev.yml
等开发环境配置。 - 某些配置项会被自动调整,比如:
spring.thymeleaf.cache=false
spring.freemarker.cache=false
spring.h2.console.enabled=true
5. 远程调试(Remote Restart)
- 需在远程服务器上配置 DevTools 的远程重启功能,具体方式可参考官方文档:Spring Boot DevTools Remote
四、常见问题与注意事项
生产环境不要启用 DevTools
- DevTools 仅用于开发环境,生产环境会自动禁用(依赖不会被打包到最终的 JAR/WAR 中)。
自动重启有时不生效?
- 检查 IDE 是否开启自动编译。
- 检查 class 文件是否真的发生变化。
- 某些资源(如静态文件)不会触发重启,但会通过 LiveReload 刷新页面。
与 Spring Security 配合时,重启后登录状态会丢失
- DevTools 重启会导致 session 丢失,需要重新登录。
IDEA 的自动编译设置
- IDEA 2020.2+ 版本需在
Registry
中开启compiler.automake.allow.when.app.running
。
- IDEA 2020.2+ 版本需在
内存泄漏和资源释放
- 虽然 DevTools 重启速度快,但频繁重启可能导致某些资源不能及时释放,建议定期完全重启 JVM。
五、整体结构
Spring Boot DevTools 的源码主要分为以下几个模块:
- 自动重启(Restart):
org.springframework.boot.devtools.restart
- LiveReload 支持:
org.springframework.boot.devtools.livereload
- 属性配置自动调整:
org.springframework.boot.devtools.autoconfigure
- 远程调试:
org.springframework.boot.devtools.remote
- 文件监控:
org.springframework.boot.devtools.filewatch
六、自动重启的核心原理
1. 启动时的条件判断
DevTools 会在 Spring Boot 应用启动时判断是否需要启用自动重启功能,入口在 DevToolsInitializer
:
public class DevToolsInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
RestartInitializer restartInitializer = new DefaultRestartInitializer();
RestartApplicationListener listener = new RestartApplicationListener(restartInitializer);
applicationContext.addApplicationListener(listener);
}
}
- 通过注册
RestartApplicationListener
,监听 Spring Boot 的启动事件。
2. ClassLoader 隔离实现
核心类:RestartClassLoader
- DevTools 会用两个 ClassLoader:
- Base ClassLoader:加载第三方依赖(JAR 包)。
- Restart ClassLoader:加载项目代码(target/classes、build/classes)。
源码片段:
public class RestartClassLoader extends URLClassLoader {
// 只加载指定路径下的 class
...
}
- 重启时,销毁旧的 RestartClassLoader,重新创建新的,加载最新的 class 文件。
3. 文件监控机制
核心类:FileSystemWatcher
- 通过定时扫描 classpath 目录(如 target/classes),检测文件是否发生变化。
源码片段:
public class FileSystemWatcher implements Runnable {
private List listeners;
private long pollInterval;
...
public void run() {
while (running) {
scanForChanges();
Thread.sleep(pollInterval);
}
}
}
scanForChanges()
方法会遍历目录,比较文件的最后修改时间,发现变化后通知监听器。
4. 触发重启流程
核心类:RestartLauncher
- 当文件变化被监听到后,
FileSystemWatcher
会调用RestartLauncher
的restart()
方法。
源码片段:
public class RestartLauncher {
public void restart() {
// 关闭旧的 ApplicationContext
// 使用新的 RestartClassLoader 启动新的 ApplicationContext
...
}
}
- 这里会重新启动 Spring Boot 的 ApplicationContext,但整个 JVM 进程不会退出。
七、LiveReload 实现
核心类:LiveReloadServer
- 启动一个监听 35729 端口的 HTTP 服务。
- 文件变化时,向浏览器插件发送 WebSocket 消息,通知自动刷新页面。
源码片段:
public class LiveReloadServer {
public void triggerReload() {
// 向所有已连接的客户端发送 reload 命令
...
}
}
八、属性自动调整
核心类:DevToolsPropertyDefaultsPostProcessor
- 在开发环境下,自动调整部分配置项,比如关闭模板缓存、开启 H2 控制台等。
源码片段:
public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
environment.getPropertySources().addLast(new MapPropertySource("devtools", getDevToolsDefaultProperties()));
}
}
九、远程调试机制
- 通过 HTTP 或 WebSocket 协议,实现远程重启和调试功能。
- 相关类在
org.springframework.boot.devtools.remote
包下,具体包含远程命令处理、身份验证等。
十、源码流程图
flowchart TD
A[Spring Boot 启动] --> B{是否引入 DevTools}
B -- Yes --> C[注册 RestartApplicationListener]
C --> D[启动 FileSystemWatcher]
D --> E{检测到 class 文件变化}
E -- Yes --> F[RestartLauncher 重启 ApplicationContext]
F --> G[重新加载项目代码]
D --> H{检测到静态资源变化}
H -- Yes --> I[LiveReloadServer 通知浏览器刷新]
十一、典型源码解读
1. 自动重启的触发点
public class RestartApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 启动文件系统监控
FileSystemWatcher watcher = new FileSystemWatcher(...);
watcher.addListener(new FileChangeListener() {
@Override
public void onChange(Set changeSet) {
RestartLauncher.restart();
}
});
watcher.start();
}
}
2. ClassLoader 隔离关键代码
public class RestartClassLoader extends URLClassLoader {
// 只加载 src/main/classes 或 target/classes 下的 class
// 其他依赖由父类加载
@Override
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 判断是否需要隔离加载
...
}
}
十二、源码分析总结
- 自动重启:通过文件监控 + ClassLoader 隔离,实现 ApplicationContext 的快速重启。
- 热部署:依赖 IDE 自动编译,将最新 class 文件加载进新的 ClassLoader。
- LiveReload:文件变化时通知前端浏览器刷新页面。
- 属性调整:启动时自动加载适合开发环境的默认配置。
- 远程调试:支持远程重启和调试(安全性需注意)。
十三、源码阅读建议
- 建议从
org.springframework.boot.devtools.restart
包开始阅读,了解重启机制。 - 关注
FileSystemWatcher
、RestartClassLoader
、RestartLauncher
三大核心类。 - 配合官方文档和源码注释,理解每一步的设计和实现。
1. FileSystemWatcher —— 文件变更监控器
主要作用
FileSystemWatcher
用于监控指定目录下的文件变化(增删改),及时通知监听器(如重启触发器)。它是自动重启机制的“感知器”。
核心成员
public class FileSystemWatcher implements Runnable, Closeable {
private final long pollInterval; // 轮询间隔
private final long quietPeriod; // 静默期,防止重复触发
private final Set listeners; // 监听器集合
private final Set directories; // 监控的目录集合
private final Map lastModifiedMap; // 文件最后修改时间
private volatile boolean running;
...
}
关键流程
- 启动监控
public void start() {
running = true;
// 启动一个线程,定时轮询目录
new Thread(this, "FileSystemWatcher").start();
}
- 轮询检测文件变化
@Override
public void run() {
while (running) {
scan();
Thread.sleep(pollInterval);
}
}
- 扫描并比对文件变更
private void scan() {
Set changeSet = new HashSet<>();
for (File dir : directories) {
// 遍历目录下所有文件
for (File file : dir.listFiles()) {
long lastModified = file.lastModified();
Long previous = lastModifiedMap.get(file);
if (previous == null || lastModified != previous) {
// 文件有变化
changeSet.add(new ChangedFiles(...));
lastModifiedMap.put(file, lastModified);
}
}
}
if (!changeSet.isEmpty()) {
notifyListeners(changeSet);
}
}
- 通知监听器
private void notifyListeners(Set changeSet) {
for (FileChangeListener listener : listeners) {
listener.onChange(changeSet);
}
}
总结
- FileSystemWatcher 持续轮询 classpath 目录,发现变更后通知重启逻辑。
- 设计上采用轮询而非 OS 文件系统事件,兼容性更好。
2. RestartClassLoader —— 隔离加载器
主要作用
RestartClassLoader
用于隔离项目代码和第三方依赖,实现只重启项目代码而不重启所有依赖,极大提升重启速度和内存利用。
继承关系
public class RestartClassLoader extends URLClassLoader {
private final Set excludedPackages; // 不隔离加载的包
...
}
关键流程
- 构造器
public RestartClassLoader(URL[] urls, ClassLoader parent, Set excludedPackages) {
super(urls, parent);
this.excludedPackages = excludedPackages;
}
urls
通常指向target/classes
、build/classes
等项目输出路径。parent
是 Base ClassLoader,加载第三方依赖。
- 类加载隔离逻辑
@Override
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 排除部分包由父加载器加载(如 JDK、Spring 本身等)
for (String pkg : excludedPackages) {
if (name.startsWith(pkg)) {
return super.loadClass(name, resolve);
}
}
// 项目代码由 RestartClassLoader 加载
Class clazz = findLoadedClass(name);
if (clazz == null) {
try {
clazz = findClass(name);
} catch (ClassNotFoundException ex) {
clazz = super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
- 项目代码和资源由 RestartClassLoader 加载,第三方依赖由父加载器加载,保证隔离。
- 重启时销毁和重建
- 每次重启时,旧的 RestartClassLoader 会被丢弃,新的实例重新加载最新 class 文件。
总结
- RestartClassLoader 是热重启的核心,利用类加载器隔离,避免全量重启。
- 通过包前缀过滤,确保 Spring、JDK 等基础依赖不会被重复加载。
3. RestartLauncher —— 重启发起器
主要作用
RestartLauncher
用于接收文件变更通知,负责执行 Spring 应用的重启流程(关闭旧的 ApplicationContext,使用新的 ClassLoader 启动新的 ApplicationContext)。
典型实现(伪代码结构)
public class RestartLauncher {
public void restart() {
// 关闭当前 Spring ApplicationContext
closeApplicationContext();
// 创建新的 RestartClassLoader,加载最新 class 文件
RestartClassLoader newClassLoader = new RestartClassLoader(...);
// 使用新的 ClassLoader 启动新的 ApplicationContext
launchApplicationContext(newClassLoader);
}
private void closeApplicationContext() {
// 通常调用 context.close(),释放资源
}
private void launchApplicationContext(ClassLoader classLoader) {
// 使用 SpringApplication.run(),指定新的 ClassLoader
// 重新启动整个 Spring Boot 应用
}
}
关键流程
- 监听文件变化
- FileSystemWatcher 发现变更后调用 RestartLauncher.restart()。
- 关闭旧上下文
- 关闭当前 Spring ApplicationContext,释放所有资源。
- 重建 ClassLoader 并启动新上下文
- 构造新的 RestartClassLoader,加载最新代码。
- 使用新的 ClassLoader 启动 Spring Boot 应用,完成“热重启”。
总结
- RestartLauncher 是 DevTools 的“重启总控”,负责协调上下文关闭与新上下文启动。
- 结合 RestartClassLoader,实现代码变更后应用的快速重启。
三者协作流程图
sequenceDiagram
participant IDE
participant FileSystemWatcher
participant RestartLauncher
participant RestartClassLoader
IDE->>FileSystemWatcher: 编译 class 文件
FileSystemWatcher->>RestartLauncher: 发现文件变化,通知重启
RestartLauncher->>RestartClassLoader: 构建新 ClassLoader
RestartLauncher->>Spring Boot: 用新 ClassLoader 重启 ApplicationContext
十四、总结
Spring Boot DevTools 是提升开发效率的利器,适合本地开发和调试阶段。它通过自动重启、热部署、LiveReload、简化配置等功能,让 Spring Boot 项目的开发变得更加流畅和高效。
- FileSystemWatcher:负责检测文件变更,触发重启流程。
- RestartClassLoader:负责隔离加载项目代码,实现轻量级重启。
- RestartLauncher:负责关闭旧上下文、创建新 ClassLoader 并重新启动 Spring Boot 应用。
这三者协同配合,实现了 Spring Boot DevTools 的高效热重启机制。