Developer Tools
Spring Boot 包含了一组额外的工具,可以让应用程序开发体验更加愉快。spring-boot-devtools 模块可以包含在任何项目中,以提供额外的开发时功能。要启用 devtools 支持,请将该模块依赖项添加到你的构建配置中,如下列 Maven 和 Gradle 的示例所示:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
}
Devtools 可能会导致类加载问题,尤其是在多模块项目中。诊断类加载问题 介绍了如何诊断和解决这些问题。
在运行完全打包的应用程序时,开发者工具会自动禁用。如果你的应用程序是通过 java -jar 启动的,或者它是通过一个特殊的类加载器启动的,那么它会被视为“生产环境应用”。你可以通过使用 spring.devtools.restart.enabled 系统属性来控制此行为。若要启用 devtools(无论使用何种类加载器启动你的应用程序),请设置 -Dspring.devtools.restart.enabled=true 系统属性。在生产环境中切勿这样做,因为在生产环境中运行 devtools 会带来安全风险。若要禁用 devtools,可以排除该依赖项,或设置 -Dspring.devtools.restart.enabled=false 系统属性。
在 Maven 中将依赖标记为可选,或在 Gradle 中使用 developmentOnly 配置(如上所示),可以防止 devtools 被传递性地应用到使用你项目的其他模块中。
重新打包的归档文件默认不包含 devtools。如果你想使用 某些远程 devtools 功能,则需要显式包含它。使用 Maven 插件时,将 excludeDevtools 属性设置为 false。使用 Gradle 插件时,配置任务的 classpath 以包含 developmentOnly 配置。
诊断类加载问题
如 Restart vs Reload 部分所述,重启功能是通过使用两个类加载器实现的。对于大多数应用程序来说,这种方法效果良好。然而,在某些情况下(尤其是在多模块项目中),它可能会引发类加载问题。
属性默认值
Spring Boot 支持的多个库使用缓存来提升性能。例如,模板引擎会缓存已编译的模板,以避免重复解析模板文件。此外,Spring MVC 在提供静态资源时,可以向响应中添加 HTTP 缓存头。
虽然缓存对生产环境非常有益,但在开发过程中却可能适得其反,导致你无法看到刚刚在应用程序中所做的更改。因此,spring-boot-devtools 默认会禁用缓存选项。
缓存选项通常通过 application.properties 文件中的配置进行设置。例如,Thymeleaf 提供了 spring.thymeleaf.cache 属性。无需手动设置这些属性,spring-boot-devtools 模块会自动应用合理的开发时配置。
下表列出了所有应用的属性:
| 名称 | 默认值 |
|---|---|
server.error.include-binding-errors | always |
server.error.include-message | always |
server.error.include-stacktrace | always |
server.servlet.jsp.init-parameters.development | true |
server.servlet.session.persistent | true |
spring.docker.compose.readiness.wait | only-if-started |
spring.freemarker.cache | false |
spring.graphql.graphiql.enabled | true |
spring.groovy.template.cache | false |
spring.h2.console.enabled | true |
spring.mustache.servlet.cache | false |
spring.mvc.log-resolved-exception | true |
spring.reactor.netty.shutdown-quiet-period | 0s |
spring.template.provider.cache | false |
spring.thymeleaf.cache | false |
spring.web.resources.cache.period | 0 |
spring.web.resources.chain.cache | false |
如果你不希望应用属性默认值,可以在 application.properties 中将 spring.devtools.add-properties 设置为 false。
由于在开发 Spring MVC 和 Spring WebFlux 应用程序时,你需要更多关于 Web 请求的信息,开发者工具建议你为 web 日志组启用 DEBUG 日志级别。这将为你提供有关传入请求、处理该请求的处理器、响应结果以及其他详细信息。如果你希望记录所有请求的详细信息(包括可能敏感的信息),可以启用 spring.mvc.log-request-details 或 spring.http.codecs.log-request-details 配置属性。
自动重启
使用 spring-boot-devtools 的应用程序在 classpath 上的文件发生更改时会自动重启。在 IDE 中开发时,这一特性非常有用,因为它能为代码变更提供极快的反馈循环。默认情况下,classpath 中指向目录的任何条目都会被监控是否有变化。请注意,某些资源(例如静态资源和视图模板)无需重启应用程序。
如果你使用构建插件通过 Maven 或 Gradle 进行重启,必须将 forking 保持为 enabled。如果禁用 forking,devtools 使用的隔离应用类加载器将不会被创建,从而导致重启无法正常工作。
当与 LiveReload 一起使用时,自动重启效果非常好。详情请参阅 LiveReload 部分。如果你使用 JRebel,则会禁用自动重启,转而使用动态类重载。其他 devtools 功能(例如 LiveReload 和属性覆盖)仍然可以使用。
DevTools 依赖于应用上下文的关闭钩子(shutdown hook)在重启期间将其关闭。如果你禁用了关闭钩子(SpringApplication.setRegisterShutdownHook(false)),它将无法正常工作。
DevTools 需要自定义 ApplicationContext 所使用的 ResourceLoader。如果你的应用程序已经提供了一个 ResourceLoader,它将会被包装。不支持直接重写 ApplicationContext 上的 getResource 方法。
使用 AspectJ weaving 时,不支持自动重启。
记录条件评估中的变更
默认情况下,每次应用程序重启时,都会记录一份显示条件评估差异(delta)的报告。该报告展示了在你进行更改(例如添加或删除 Bean、设置配置属性)时,应用程序自动配置的变化情况。
要禁用报告的日志记录,请设置以下属性:
- Properties
- YAML
spring.devtools.restart.log-condition-evaluation-delta=false
spring:
devtools:
restart:
log-condition-evaluation-delta: false
排除资源
某些资源在发生更改时不一定需要触发重启。例如,Thymeleaf 模板可以直接就地编辑。默认情况下,更改 /META-INF/maven、/META-INF/resources、/resources、/static、/public 或 /templates 目录下的资源不会触发重启,但会触发 live reload。如果想要自定义这些排除项,可以使用 spring.devtools.restart.exclude 属性。例如,若仅排除 /static 和 /public,可设置以下属性:
- Properties
- YAML
spring.devtools.restart.exclude=static/**,public/**
spring:
devtools:
restart:
exclude: "static/**,public/**"
如果你想保留这些默认值并添加额外的排除项,请改用 spring.devtools.restart.additional-exclude 属性。
监听额外的路径
你可能希望在修改了不在 classpath 上的文件时,应用程序能够重启或重新加载。为此,可以使用 spring.devtools.restart.additional-paths 属性来配置额外需要监视变更的路径。你可以使用 spring.devtools.restart.exclude 属性(如前所述)来控制这些额外路径下的变更是否触发完整重启,还是仅触发 live reload。
禁用重启
如果你不想使用重启功能,可以通过设置 spring.devtools.restart.enabled 属性来禁用它。在大多数情况下,你可以在 application.properties 中设置该属性(这样做仍然会初始化重启类加载器,但不会监视文件更改)。
如果你需要完全禁用重启支持(例如,因为它与某个特定库不兼容),你需要在调用 SpringApplication.run(…) 之前将 spring.devtools.restart.enabled System 属性设置为 false,如下例所示:
- Java
- Kotlin
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApplication.class, args);
}
}
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
object MyApplication {
@JvmStatic
fun main(args: Array<String>) {
System.setProperty("spring.devtools.restart.enabled", "false")
SpringApplication.run(MyApplication::class.java, *args)
}
}
使用触发文件
如果你使用的 IDE 会持续编译已更改的文件,你可能更希望仅在特定时间触发重启。为此,你可以使用一个“触发文件”(trigger file),这是一个特殊文件,只有在你希望实际触发重启检查时才需要修改它。
对文件的任何更新都会触发一次检查,但只有当 Devtools 检测到有需要处理的内容时,才会真正执行重启。
要使用触发文件,请将 spring.devtools.restart.trigger-file 属性设置为你的触发文件的名称(不包括任何路径)。该触发文件必须出现在 classpath 中的某个位置。
例如,如果你有一个具有以下结构的项目:
src
+- main
+- resources
+- .reloadtrigger
那么你的 trigger-file 属性将会是:
- Properties
- YAML
spring.devtools.restart.trigger-file=.reloadtrigger
spring:
devtools:
restart:
trigger-file: ".reloadtrigger"
现在仅当 src/main/resources/.reloadtrigger 被更新时才会发生重启。
你可能希望将 spring.devtools.restart.trigger-file 设置为全局设置,以便所有项目的行为保持一致。
一些 IDE 提供了无需手动更新触发文件的功能。Spring Tools for Eclipse 和 IntelliJ IDEA(Ultimate Edition) 均支持此类功能。在 Spring Tools 中,你可以从控制台视图使用 “reload” 按钮(前提是你的 trigger-file 命名为 .reloadtrigger)。对于 IntelliJ IDEA,你可以参考其文档中的相关说明。
自定义 Restart Classloader
如前文 Restart vs Reload 一节所述,重启功能是通过使用两个类加载器(classloader)来实现的。如果这导致了问题,你可以通过使用 spring.devtools.restart.enabled 系统属性来诊断问题。如果关闭重启功能后应用程序可以正常工作,你可能需要自定义哪些内容由哪个类加载器加载。
默认情况下,IDE 中任何打开的项目都会通过 “restart” 类加载器加载,而任何常规的 .jar 文件则通过 “base” 类加载器加载。如果你使用 mvn spring-boot:run 或 gradle bootRun 也是如此:包含你的 @SpringBootApplication 的项目会通过 “restart” 类加载器加载,其余所有内容则通过 “base” 类加载器加载。启动应用时,控制台会打印出类路径,这有助于识别任何有问题的条目。通过反射使用的类(尤其是注解)可能会在应用程序类加载之前,被提前加载到父级(固定)类加载器中,这可能导致 Spring 在应用程序中无法检测到它们。
你可以通过创建一个 META-INF/spring-devtools.properties 文件,指示 Spring Boot 使用不同的类加载器加载项目中的某些部分。spring-devtools.properties 文件可以包含以 restart.exclude 和 restart.include 为前缀的属性。include 元素表示应被提升到 “restart” 类加载器中的项,而 exclude 元素表示应被下推到 “base” 类加载器中的项。属性的值是一个正则表达式模式,该模式会应用于 JVM 启动时传入的 classpath。以下是一个示例,其中一些本地类文件被排除,而一些额外的库被包含在 restart 类加载器中:
restart:
exclude:
companycommonlibs: "/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/"
include:
projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"
所有属性键必须唯一。只要属性以 restart.include. 或 restart.exclude. 开头,就会被考虑。
所有 classpath 中的 META-INF/spring-devtools.properties 都会被加载。你可以在项目内部或项目所依赖的库中打包这些文件。不能使用系统属性,只能使用属性文件。
已知限制
重启功能与使用标准 ObjectInputStream 反序列化的对象配合得不好。如果你需要反序列化数据,可能需要结合 Thread.currentThread().getContextClassLoader() 使用 Spring 的 ConfigurableObjectInputStream。
不幸的是,一些第三方库在反序列化时没有考虑上下文类加载器(context classloader)。如果你遇到此类问题,需要向原作者请求修复。
LiveReload
spring-boot-devtools 模块包含一个嵌入式的 LiveReload 服务器,可用于在资源发生更改时触发浏览器刷新。LiveReload 浏览器扩展可免费用于 Chrome、Firefox 和 Safari。你可以在所选浏览器的市场或商店中搜索 “LiveReload” 来找到这些扩展。
如果你不希望在应用程序运行时启动 LiveReload 服务器,可以将 spring.devtools.livereload.enabled 属性设置为 false。
你一次只能运行一个 LiveReload 服务器。在启动你的应用程序之前,请确保没有其他 LiveReload 服务器正在运行。如果你从 IDE 启动多个应用程序,只有第一个应用程序具有 LiveReload 支持。
要实现在文件更改时触发 LiveReload,必须启用 自动重启。
全局设置
你可以通过将以下任意文件添加到 $HOME/.config/spring-boot 目录来配置全局的 devtools 设置:
-
spring-boot-devtools.properties -
spring-boot-devtools.yaml -
spring-boot-devtools.yml
添加到这些文件中的任何属性都会应用于你机器上所有使用 devtools 的 Spring Boot 应用程序。例如,若要配置 restart 始终使用 trigger file,你可以将以下属性添加到你的 spring-boot-devtools 文件中:
- Properties
- YAML
spring.devtools.restart.trigger-file=.reloadtrigger
spring:
devtools:
restart:
trigger-file: ".reloadtrigger"
默认情况下,$HOME 是用户的主目录。要自定义此位置,请设置 SPRING_DEVTOOLS_HOME 环境变量或 spring.devtools.home 系统属性。
如果在 $HOME/.config/spring-boot 中未找到 devtools 配置文件,则会在 $HOME 目录的根目录中查找是否存在 .spring-boot-devtools.properties 文件。这样,你就可以与那些使用较旧版本 Spring Boot(不支持 $HOME/.config/spring-boot 位置)的应用程序共享 devtools 的全局配置。
在 devtools 的 properties/yaml 文件中不支持 profiles。
在 .spring-boot-devtools.properties 中激活的任何 profile 都不会影响 profile-specific 配置文件 的加载。形式为 spring-boot-devtools-<profile>.properties 的 profile-specific 文件名,以及 YAML 和 Properties 文件中的 spring.config.activate.on-profile 文档均不受支持。
配置 File System Watcher
FileSystemWatcher 通过以一定的时间间隔轮询类文件的变更,然后等待一段预定义的静默期(quiet period),以确保不再有更多变更。由于 Spring Boot 完全依赖 IDE 将编译后的文件复制到 Spring Boot 可以读取的位置,因此你可能会发现某些更改在 devtools 重启应用时未被反映出来。如果你经常遇到此类问题,请尝试增大 spring.devtools.restart.poll-interval 和 spring.devtools.restart.quiet-period 参数的值,使其适应你的开发环境:
- Properties
- YAML
spring.devtools.restart.poll-interval=2s
spring.devtools.restart.quiet-period=1s
spring:
devtools:
restart:
poll-interval: "2s"
quiet-period: "1s"
现在每 2 秒轮询一次被监控的 classpath 目录以检查更改,并保持 1 秒的静默期,以确保没有额外的类更改。
Remote Applications
Spring Boot 开发者工具不仅限于本地开发。在远程运行应用程序时,你也可以使用其中的若干功能。远程支持是可选的,因为启用它可能存在安全风险。只有在受信任的网络中运行,或通过 SSL 进行保护时,才应启用该功能。如果你无法满足上述任一条件,则不应使用 DevTools 的远程支持。在生产环境中部署时,绝不应启用该支持。
要启用它,你需要确保 devtools 被包含在重新打包的归档文件中,如下列所示:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>
然后你需要设置 spring.devtools.remote.secret 属性。与任何重要的密码或密钥一样,该值应当是唯一且强壮的,以确保无法被猜测或暴力破解。
远程 devtools 支持由两部分组成:一个接受连接的服务器端端点和一个在 IDE 中运行的客户端应用程序。当设置了 spring.devtools.remote.secret 属性时,服务器组件会自动启用。客户端组件必须手动启动。
Spring WebFlux 应用程序不支持远程 devtools。
运行远程客户端应用程序
远程客户端应用程序设计为在 IDE 内运行。你需要使用与所连接的远程项目相同的 classpath 来运行 RemoteSpringApplication。该应用程序唯一必需的参数是它要连接的远程 URL。
例如,如果你正在使用 Eclipse 或 Spring Tools,并且你有一个名为 my-app 的项目已部署到 Cloud Foundry,你可以按以下步骤操作:
-
从
Run菜单中选择Run Configurations…。 -
创建一个新的
Java Application“启动配置”。 -
浏览并选择
my-app项目。 -
使用 RemoteSpringApplication 作为主类。
-
将
https://myapp.cfapps.io添加到Program arguments中(或替换为你的远程 URL)。
一个正在运行的远程客户端可能类似于以下代码清单:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: (v3.5.10)
2026-01-22T10:18:03.011Z INFO 88939 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication v3.5.10 using Java 17.0.18 with PID 88939 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/3.5.10/spring-boot-devtools-3.5.10.jar started by myuser in /opt/apps/)
2026-01-22T10:18:03.020Z INFO 88939 --- [ main] o.s.b.devtools.RemoteSpringApplication : No active profile set, falling back to 1 default profile: "default"
2026-01-22T10:18:03.811Z INFO 88939 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2026-01-22T10:18:03.893Z INFO 88939 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 2.318 seconds (process running for 3.231)
由于远程客户端使用与实际应用程序相同的 classpath,因此可以直接读取应用程序属性。这就是 spring.devtools.remote.secret 属性被读取并传递给服务器进行身份验证的方式。
建议始终使用 https:// 作为连接协议,以便对流量进行加密,防止密码被截获。
如果你需要使用代理来访问远程应用程序,请配置 spring.devtools.remote.proxy.host 和 spring.devtools.remote.proxy.port 属性。
远程更新
远程客户端以与本地重启相同的方式监控你的应用程序类路径中的变更。任何更新的资源都会被推送到远程应用程序,并(如有需要)触发一次重启。如果你正在开发一个依赖于本地没有的云服务的功能,这种方式会很有帮助。通常,远程更新和重启的速度比完整的重新构建和部署周期要快得多。
在较慢的开发环境中,可能会出现静默期(quiet period)不足的情况,导致类的更改被拆分成多个批次。在第一批类更改上传后,服务器会重启。由于服务器正在重启,下一批更改无法发送到应用程序。
这通常表现为 RemoteSpringApplication 日志中出现有关某些类上传失败的警告,并随之进行重试。但也可能导致应用程序代码不一致,以及在第一批更改上传后无法重启。如果你持续遇到此类问题,请尝试增大 spring.devtools.restart.poll-interval 和 spring.devtools.restart.quiet-period 参数的值,使其适应你的开发环境。有关如何配置这些属性,请参见 配置文件系统监视器 一节。
仅当远程客户端运行时才会监控文件。如果在启动远程客户端之前更改了文件,则不会将其推送到远程服务器。