开发者工具

Spring Boot 包含一套额外的工具,可以使应用开发体验更加愉快。spring-boot-devtools 模块可以包含在任何项目中,以提供额外的开发时特性。要包含 devtools 支持,请将模块依赖项添加到你的构建中,如下面的 Maven 和 Gradle 清单所示

Maven
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<optional>true</optional>
	</dependency>
</dependencies>
Gradle
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 插件时,配置任务的类路径以包含 developmentOnly 配置

诊断类加载问题

重启与重载部分所述,重启功能通过使用两个类加载器实现。对于大多数应用来说,这种方法工作良好。然而,它有时可能会导致类加载问题,尤其是在多模块项目中。

要诊断类加载问题是否确实由 devtools 及其两个类加载器引起,请尝试禁用重启。如果这解决了你的问题,请自定义重启类加载器以包含你的整个项目。

属性默认值

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-detailsspring.codec.log-request-details 配置属性。

自动重启

使用 spring-boot-devtools 的应用在类路径上的文件发生变化时会自动重启。这在 IDE 中工作时是一个非常有用的特性,因为它为代码更改提供了非常快速的反馈循环。默认情况下,监视类路径上指向目录的任何条目是否发生变化。请注意,某些资源,例如静态资源和视图模板,不需要重启应用

触发重启

由于 DevTools 监视类路径资源,触发重启的唯一方法是更新类路径。无论你使用的是 IDE 还是某个构建插件,修改后的文件都必须重新编译才能触发重启。触发类路径更新的方式取决于你使用的工具

  • 在 Eclipse 中,保存修改后的文件会导致类路径更新并触发重启。

  • 在 IntelliJ IDEA 中,构建项目(Build -> Build Project)具有相同的效果。

  • 如果使用构建插件,对 Maven 运行 mvn compile 或对 Gradle 运行 gradle build 将触发重启。

如果你使用构建插件通过 Maven 或 Gradle 重启,你必须将 forking 保持为 enabled。如果禁用 forking,devtools 使用的隔离应用类加载器将不会创建,并且重启将无法正常工作。
自动重启与 LiveReload 配合使用效果非常好。有关详情,请参阅LiveReload部分。如果你使用 JRebel,自动重启将被禁用,转而使用动态类重载。其他 devtools 特性(例如 LiveReload 和属性覆盖)仍然可以使用。
DevTools 依赖应用上下文的关闭钩子在重启期间关闭应用。如果禁用了关闭钩子(SpringApplication.setRegisterShutdownHook(false)),它将无法正常工作。
DevTools 需要自定义 ApplicationContext 使用的 ResourceLoader。如果你的应用已经提供了一个,它将被包装。不支持直接覆盖 ApplicationContext 上的 getResource 方法。
使用 AspectJ weaving 时不支持自动重启。
重启与重载

Spring Boot 提供的重启技术通过使用两个类加载器来实现。不发生变化的类(例如,来自第三方 jar 的类)加载到 base 类加载器中。你正在积极开发的类加载到 restart 类加载器中。当应用重启时,restart 类加载器会被丢弃并创建一个新的。这种方法意味着应用重启通常比“冷启动”快得多,因为 base 类加载器已经可用并已填充。

如果你发现重启对你的应用来说不够快或者遇到了类加载问题,你可以考虑使用 ZeroTurnaround 的 JRebel 等重载技术。这些技术通过在类加载时重写类,使其更适合重载。

记录条件评估的变化

默认情况下,每次应用重启时,都会记录一份显示条件评估增量的报告。该报告会显示随着你进行诸如添加或移除 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 中的资源不会触发重启,但会触发实时重载。如果你想自定义这些排除项,可以使用 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 属性。

监视额外路径

当更改不在类路径上的文件时,你可能希望你的应用重启或重载。为此,请使用 spring.devtools.restart.additional-paths 属性配置要监视更改的额外路径。你可以使用前面描述的 spring.devtools.restart.exclude 属性来控制额外路径下的更改是触发完全重启还是实时重载

禁用重启

如果你不想使用重启特性,可以通过设置 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 会持续编译更改的文件,你可能更喜欢只在特定时间触发重启。为此,你可以使用“触发文件”,这是一个特殊的文件,当你想实际触发重启检查时必须修改它。

对文件的任何更新都会触发检查,但只有当 Devtools 检测到有需要处理的内容时,才会实际发生重启。

要使用触发文件,请将 spring.devtools.restart.trigger-file 属性设置为你的触发文件的名称(不包含任何路径)。触发文件必须出现在你的类路径的某个位置。

例如,如果你有一个具有以下结构的项目

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 EclipseIntelliJ IDEA (Ultimate Edition) 都支持此功能。使用 Spring Tools,你可以从控制台视图中使用“重载”按钮(只要你的 trigger-file 文件名为 .reloadtrigger)。对于 IntelliJ IDEA,你可以遵循其文档中的说明

自定义重启类加载器

重启与重载部分前面所述,重启功能通过使用两个类加载器实现。如果这导致了问题,你可以使用 spring.devtools.restart.enabled 系统属性来诊断问题,如果应用在关闭重启后能够正常工作,你可能需要自定义哪些内容由哪个类加载器加载。

默认情况下,你的 IDE 中的任何开放项目都通过“重启”类加载器加载,而任何常规的 .jar 文件都通过“基础”类加载器加载。如果你使用 mvn spring-boot:rungradle bootRun,情况也是如此:包含你的 @SpringBootApplication 的项目通过“重启”类加载器加载,其余所有内容通过“基础”类加载器加载。应用启动时会在控制台上打印类路径,这有助于识别任何有问题的条目。反射使用的类,尤其是注解,可能在应用类使用它们之前被加载到父级(固定)类加载器中,这可能导致 Spring 在应用中无法检测到它们。

你可以通过创建一个 META-INF/spring-devtools.properties 文件来指示 Spring Boot 使用不同的类加载器加载项目的某些部分。spring-devtools.properties 文件可以包含以 restart.excluderestart.include 为前缀的属性。include 元素是应被拉升到“重启”类加载器的项,而 exclude 元素是应被下推到“基础”类加载器的项。属性的值是应用于启动时传递给 JVM 的类路径的正则表达式模式。这是一个示例,其中排除了某些本地类文件,并将一些额外的库包含在重启类加载器中

restart:
  exclude:
    companycommonlibs: "/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/"
  include:
    projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"
所有属性键必须是唯一的。只要属性以 restart.include.restart.exclude. 开头,它就会被考虑在内。
加载类路径中的所有 META-INF/spring-devtools.properties 文件。你可以在项目内部打包文件,也可以在项目使用的库中打包。不能使用系统属性,只能使用属性文件。

已知限制

重启功能与使用标准 ObjectInputStream 反序列化的对象配合使用效果不佳。如果需要反序列化数据,可能需要结合使用 Spring 的 ConfigurableObjectInputStreamThread.currentThread().getContextClassLoader()

遗憾的是,一些第三方库在反序列化时没有考虑上下文类加载器。如果遇到此类问题,需要向原作者请求修复。

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 设置

  1. spring-boot-devtools.properties

  2. spring-boot-devtools.yaml

  3. spring-boot-devtools.yml

添加到这些文件中的任何属性都适用于你的机器上所有使用 devtools 的 Spring Boot 应用。例如,要配置重启总是使用触发文件,你可以将以下属性添加到你的 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 文件。这允许你与不支持 $HOME/.config/spring-boot 位置的旧版本 Spring Boot 应用共享 devtools 全局配置。

devtools 属性/yaml 文件中不支持 Profile。

.spring-boot-devtools.properties 中激活的任何 Profile 都不会影响特定 Profile 配置文件的加载。YAML 和 Properties 文件中的特定 Profile 文件名(形式为 spring-boot-devtools-<profile>.properties)以及 spring.config.activate.on-profile 文档不受支持。

配置文件系统监视器

FileSystemWatcher 通过以一定时间间隔轮询类变化,然后等待预定义的静默期以确保不再有更改。由于 Spring Boot 完全依赖 IDE 将文件编译和复制到 Spring Boot 可以读取的位置,你可能会发现有时在 devtools 重启应用时,某些更改未被反映。如果你经常观察到此类问题,请尝试将 spring.devtools.restart.poll-intervalspring.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 秒轮询一次被监视的类路径目录的变化,并保持 1 秒的静默期以确保没有额外的类更改。

远程应用

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,并且有一个部署到 Cloud Foundry 的名为 my-app 的项目,你可以执行以下操作

  • Run 菜单中选择 Run Configurations…​

  • 创建一个新的 Java Application “启动配置”。

  • 浏览找到 my-app 项目。

  • 使用 RemoteSpringApplication 作为主类。

  • https://myapp.cfapps.io 添加到 Program arguments 中(或者你的远程 URL 是什么就填什么)。

一个正在运行的远程客户端可能类似于以下列表

  .   ____          _                                              __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _          ___               _      \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |        | _ \___ _ __  ___| |_ ___ \ \ \ \
 \\/  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  \/ _ \  _/ -_) ) ) ) )
  '  |____| .__|_| |_|_| |_\__, |        |_|_\___|_|_|_\___/\__\___|/ / / /
 =========|_|==============|___/===================================/_/_/_/
 :: Spring Boot Remote ::  (v3.4.5)

2025-04-24T13:04:45.447Z  INFO 128087 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication v3.4.5 using Java 17.0.15 with PID 128087 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/3.4.5/spring-boot-devtools-3.4.5.jar started by myuser in /opt/apps/)
2025-04-24T13:04:45.472Z  INFO 128087 --- [           main] o.s.b.devtools.RemoteSpringApplication   : No active profile set, falling back to 1 default profile: "default"
2025-04-24T13:04:46.769Z  INFO 128087 --- [           main] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2025-04-24T13:04:46.873Z  INFO 128087 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 3.848 seconds (process running for 5.081)
因为远程客户端使用与实际应用程序相同的 classpath,所以它可以直接读取应用程序属性。spring.devtools.remote.secret 属性就是这样被读取并传递给服务器进行认证的。
始终建议使用 https:// 作为连接协议,以便对流量进行加密,防止密码被截获。
如果你需要使用代理来访问远程应用程序,请配置 spring.devtools.remote.proxy.hostspring.devtools.remote.proxy.port 属性。

远程更新

远程客户端监视你的应用程序 classpath 中的更改,方式与本地重启相同。任何更新的资源都会被推送到远程应用程序并(如果需要)触发重启。如果你正在迭代开发某个功能,而该功能使用了一个你本地没有的云服务,这将非常有用。一般来说,远程更新和重启比完整的重建和部署周期要快得多。

在较慢的开发环境中,可能会出现安静期不够,导致类的更改被分成多个批次。第一批类更改上传后,服务器会重启。由于服务器正在重启,下一批次无法发送到应用程序。

这通常表现为在RemoteSpringApplication日志中出现关于未能上传部分类的警告,并随之进行重试。但它也可能导致应用程序代码不一致,并在第一批更改上传后重启失败。如果你经常遇到此类问题,请尝试增加 spring.devtools.restart.poll-intervalspring.devtools.restart.quiet-period 参数的值,使其适合你的开发环境。有关配置这些属性的信息,请参阅配置文件系统监视器部分。

只有当远程客户端运行时,文件才会被监视。如果你在启动远程客户端之前更改了文件,则不会将其推送到远程服务器。