开发者工具
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
系统属性来控制这一行为。要启用开发者工具,无论用于启动应用程序的类加载器是什么,都可以设置 -Dspring.devtools.restart.enabled=true
系统属性。在生产环境中,运行开发者工具存在安全风险,因此不应执行此操作。要禁用开发者工具,可以排除相关依赖项或设置 -Dspring.devtools.restart.enabled=false
系统属性。
在 Maven 中将依赖标记为可选,或在 Gradle 中使用 developmentOnly
配置(如上所示),可以防止 devtools 被传递性地应用到使用你项目的其他模块中。
重新打包的归档文件默认不包含开发工具。如果你想使用某个远程开发工具功能,你需要手动将其包含进来。使用 Maven 插件时,将 excludeDevtools
属性设置为 false
。使用 Gradle 插件时,配置任务的类路径以包含 developmentOnly 配置。
诊断类加载问题
如 Restart 与 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.codec.log-request-details
配置属性。
自动重启
使用 spring-boot-devtools
的应用程序在类路径上的文件发生变化时会自动重启。这在 IDE 中工作时可能是一个有用的功能,因为它为代码更改提供了非常快速的反馈循环。默认情况下,类路径上指向目录的任何条目都会被监控以检测更改。请注意,某些资源(如静态资源和视图模板)不需要重启应用程序。
如果你正在使用 Maven 或 Gradle 的构建插件进行重启,必须将 forking
设置为 enabled
。如果禁用 forking,devtools 使用的隔离应用程序类加载器将不会被创建,重启将无法正常工作。
自动重启功能与 LiveReload 配合使用时效果非常好。详情请参阅 LiveReload 部分。如果使用 JRebel,则会禁用自动重启功能,转而支持动态类重载。其他 devtools 功能(如 LiveReload 和属性覆盖)仍然可以使用。
DevTools 依赖于应用程序上下文的关闭钩子来在重启期间关闭它。如果您禁用了关闭钩子(SpringApplication.setRegisterShutdownHook(false)
),它将无法正常工作。
DevTools 需要自定义 ApplicationContext 所使用的 ResourceLoader。如果你的应用程序已经提供了一个,它将会被包装。不支持直接覆盖 ApplicationContext 上的 getResource
方法。
使用 AspectJ 编织时不支持自动重启。
条件评估中的变更日志
默认情况下,每次应用程序重新启动时,都会记录一份显示条件评估差异的报告。该报告展示了随着您进行更改(例如添加或删除 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.enabled
属性来禁用它。在大多数情况下,您可以在 application.properties
文件中设置此属性(这样做仍然会初始化重启类加载器,但不会监视文件更改)。
如果你需要完全禁用重启支持(例如,因为它在某个特定库中无法正常工作),你需要在调用 SpringApplication.run(…)
之前将 spring.devtools.restart.enabled
系统属性设置为 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 Eclipse 和 IntelliJ IDEA(Ultimate 版) 都支持这种功能。在 Spring Tools 中,你可以使用控制台视图中的“重新加载”按钮(只要你的 trigger-file
文件名为 .reloadtrigger
)。对于 IntelliJ IDEA,你可以按照其文档中的说明 操作。
自定义重启类加载器
如前面重启与重载部分所述,重启功能是通过使用两个类加载器来实现的。如果这导致了问题,你可以使用 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
元素是应该被提升到“重启”类加载器中的项目,而 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 的 ConfigurableObjectInputStream,并结合 Thread.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 设置:
-
spring-boot-devtools.properties
-
spring-boot-devtools.yaml
-
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 文件中使用 profiles。
在 .spring-boot-devtools.properties
中激活的任何 profiles 都不会影响特定 profile 的配置文件的加载。不支持特定 profile 的文件名(格式为 spring-boot-devtools-<profile>.properties
)以及 YAML 和 Properties 文件中的 spring.config.activate.on-profile
文档。
配置文件系统监视器
FileSystemWatcher 通过以一定的时间间隔轮询类文件的变化,然后等待预定义的静默期以确保没有更多的更改。由于 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 秒轮询一次以检查更改,并且保持 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
属性。像任何重要的密码或密钥一样,该值应该是唯一且强大的,以确保它不能被猜测或暴力破解。
远程开发工具支持分为两部分:一个接受连接的服务器端端点和一个你在 IDE 中运行的客户端应用程序。当设置了 spring.devtools.remote.secret
属性时,服务器组件会自动启用。客户端组件必须手动启动。
远程开发工具不支持 Spring WebFlux 应用程序。
运行远程客户端应用程序
远程客户端应用程序设计用于在您的 IDE 中运行。您需要使用与连接的远程项目相同的类路径来运行 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.4.2)
2025-01-23T11:42:43.824Z INFO 126039 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication v3.4.2 using Java 17.0.14 with PID 126039 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/3.4.2/spring-boot-devtools-3.4.2.jar started by myuser in /opt/apps/)
2025-01-23T11:42:43.837Z INFO 126039 --- [ main] o.s.b.devtools.RemoteSpringApplication : No active profile set, falling back to 1 default profile: "default"
2025-01-23T11:42:44.608Z INFO 126039 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2025-01-23T11:42:44.692Z INFO 126039 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 2.753 seconds (process running for 3.93)
由于远程客户端使用与实际应用程序相同的类路径,因此它可以直接读取应用程序的属性。这就是 spring.devtools.remote.secret
属性被读取并传递给服务器进行身份验证的方式。
建议始终使用 https://
作为连接协议,这样可以加密流量,防止密码被截获。
如果需要使用代理访问远程应用程序,请配置 spring.devtools.remote.proxy.host
和 spring.devtools.remote.proxy.port
属性。
远程更新
远程客户端会以与本地重启相同的方式监控你的应用程序类路径中的更改。任何更新的资源都会被推送到远程应用程序,并且(如果需要)会触发重启。如果你正在迭代一个使用了本地没有的云服务的功能,这会很有帮助。通常,远程更新和重启比完整的重建和部署周期要快得多。
在较慢的开发环境中,可能会发生静默期不足的情况,类的更改可能会被分成多个批次。当第一批类更改上传后,服务器会重新启动。由于服务器正在重启,下一批更改无法发送到应用程序。
这通常会在 RemoteSpringApplication 日志中表现为一条关于某些类上传失败的警告,并随之进行重试。但它也可能会导致应用程序代码不一致,并且在第一批更改上传后无法重新启动。如果你持续观察到此类问题,请尝试将 spring.devtools.restart.poll-interval
和 spring.devtools.restart.quiet-period
参数增加到适合你开发环境的值。有关配置这些属性的信息,请参阅 配置文件系统监视器 部分。
文件仅在远程客户端运行时才会被监控。如果在启动远程客户端之前更改了文件,该文件不会被推送到远程服务器。