20. Developer tools

Spring Boot 包括一组额外的工具,这些工具可以使应用程序开发体验更加愉快。 spring-boot-devtools模块可以包含在任何项目中,以提供其他开发时功能。要包含 devtools 支持,只需将模块依赖项添加到您的构建中:

Maven.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

Gradle.

dependencies {
    compile("org.springframework.boot:spring-boot-devtools")
}

Note

运行完全打包的应用程序时,将自动禁用开发人员工具。如果您的应用程序是使用java -jar启动的,或者使用特殊的类加载器启动的,则将其视为“生产应用程序”。将依赖项标记为可选是一种最佳做法,可防止使用您的项目将 devtools 过渡性地应用于其他模块。 Gradle 不支持现成的optional依赖关系,因此您可能希望同时查看propdeps-plugin

Tip

重新打包的存档默认情况下不包含 devtools。如果要使用某些远程 devtools 功能,则需要禁用excludeDevtools build 属性以包含它。 Maven 和 Gradle 插件均支持该属性。

20.1 属性默认值

Spring Boot 支持的一些库使用缓存来提高性能。例如,template engines将缓存编译的模板,以避免重复分析模板文件。而且,Spring MVC 可以在提供静态资源时向响应添加 HTTP 缓存 Headers。

尽管缓存在 Producing 非常有用,但在开发过程中可能适得其反,从而使您无法看到自己刚刚在应用程序中所做的更改。因此,默认情况下,spring-boot-devtools 将禁用这些缓存选项。

缓存选项通常由application.properties文件中的设置配置。例如,Thymeleaf 提供了spring.thymeleaf.cache属性。无需手动设置这些属性,spring-boot-devtools模块将自动应用合理的开发时配置。

Tip

有关所应用属性的完整列表,请参见DevToolsPropertyDefaultsPostProcessor

20.2 自动重启

每当 classpath 上的文件更改时,使用spring-boot-devtools的应用程序将自动重新启动。在 IDE 中工作时,这可能是一个有用的功能,因为它为代码更改提供了非常快速的反馈循环。默认情况下,将监视 Classpath 上指向文件夹的任何条目的更改。请注意,某些资源(例如静态资产和视图模板不需要重新启动应用程序)。

Triggering a restart

当 DevTools 监视 Classpath 资源时,触发重启的唯一方法是更新 Classpath。导致 Classpath 更新的方式取决于您使用的 IDE。在 Eclipse 中,保存修改后的文件将导致 Classpath 被更新并触发重新启动。在 IntelliJ IDEA 中,构建项目(Build -> Make Project)将具有相同的效果。

Note

您还可以通过受支持的构建插件(即 Maven 和 Gradle)启动应用程序,因为启用了派生功能,因为 DevTools 需要隔离的应用程序类加载器才能正常运行。当 Gradle 和 Maven 在 Classpath 上检测到 DevTools 时,默认情况下会这样做。

Tip

与 LiveReload 一起使用时,自动重新启动效果很好。 See below了解详情。如果使用 JRebel,将禁用自动重新启动,而支持动态类重新加载。仍可以使用其他 devtools 功能(例如 LiveReload 和属性替代)。

Note

DevTools 依靠应用程序上下文的关闭钩子在重新启动期间将其关闭。如果禁用了关机钩子(SpringApplication.setRegisterShutdownHook(false)),它将无法正常工作。

Note

在确定 Classpath 上的条目是否应在更改后触发重新启动时,DevTools 会自动忽略名为spring-bootspring-boot-devtoolsspring-boot-autoconfigurespring-boot-actuatorspring-boot-starter的项目。

Note

DevTools 需要自定义ApplicationContext所使用的ResourceLoader:如果您的应用程序已经提供了ResourceLoader,它将被包装。不支持在ApplicationContext上直接覆盖getResource方法。

Restart vs Reload

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

如果您发现重新启动还不够快,或者遇到类加载问题,则可以考虑从 ZeroTurnaround 重新加载诸如JRebel之类的技术。这些方法通过在加载类时重写类来使其更易于重新加载。 Spring Loaded提供了另一种选择,但是它不支持那么多框架,也不受商业支持。

20.2.1 排除资源

某些资源在更改时不一定需要触发重新启动。例如,Thymeleaf 模板只能就地编辑。默认情况下,更改/META-INF/maven/META-INF/resources/resources/static/public/templates中的资源不会触发重新启动,但会触发live reload。如果要自定义这些排除项,可以使用spring.devtools.restart.exclude属性。例如,要仅排除/static/public,可以设置以下内容:

spring.devtools.restart.exclude=static/**,public/**

Tip

如果要保留这些默认值并“ *添加”其他排除项,请改用spring.devtools.restart.additional-exclude属性。

20.2.2 观看其他路径

当您对不在 Classpath 上的文件进行更改时,您可能希望重新启动或重新加载应用程序。为此,使用spring.devtools.restart.additional-paths属性配置其他路径以监视更改。您可以使用spring.devtools.restart.exclude属性described above来控制其他路径下的更改将触发完全重启还是仅触发live reload

20.2.3 禁用重新启动

如果您不想使用重新启动功能,则可以使用spring.devtools.restart.enabled属性将其禁用。在大多数情况下,您可以在application.properties中进行设置(这仍会初始化重新启动类加载器,但不会监视文件更改)。

例如,如果您需要完全禁用重启支持,因为它不适用于特定的库,则需要在调用SpringApplication.run(…)之前设置System属性。例如:

public static void main(String[] args) {
    System.setProperty("spring.devtools.restart.enabled", "false");
    SpringApplication.run(MyApp.class, args);
}

20.2.4 使用触发文件

如果使用持续编译更改文件的 IDE,则可能更喜欢仅在特定时间触发重新启动。为此,您可以使用“触发文件”,这是一个特殊文件,当您要实际触发重新启动检查时必须对其进行修改。更改文件只会触发检查,并且只有在 Devtools 检测到必须执行某些操作时才会重新启动。触发文件可以手动更新,也可以通过 IDE 插件更新。

要使用触发文件,请使用spring.devtools.restart.trigger-file属性。

Tip

您可能希望将spring.devtools.restart.trigger-file设置为global setting,以便所有项目的行为都相同。

20.2.5 自定义重启类加载器

如上面的重新启动与重新加载部分所述,重新启动功能是通过使用两个类加载器实现的。对于大多数应用程序,此方法效果很好,但是有时可能会导致类加载问题。

默认情况下,将使用“重新启动”类加载器加载 IDE 中任何打开的项目,而使用“基本”类加载器加载任何常规.jar文件。如果您在一个多模块项目上工作,并且没有将每个模块都导入到 IDE 中,则可能需要自定义内容。为此,您可以创建一个META-INF/spring-devtools.properties文件。

spring-devtools.properties文件可以包含restart.exclude.restart.include.前缀的属性。 include元素是应上拉到“重新启动”类加载器中的项目,而exclude元素是应下推到“基本”类加载器中的项目。该属性的值是一个将应用于 Classpath 的正则表达式模式。

For example:

restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar

Note

所有属性键都必须是唯一的。只要属性以restart.include.restart.exclude.开头,它就会被考虑。

Tip

来自 Classpath 的所有META-INF/spring-devtools.properties将被加载。您可以将文件打包在项目内部或项目使用的库中。

20.2.6 已知限制

重新启动功能不适用于使用标准ObjectInputStream反序列化的对象。如果需要反序列化数据,则可能需要结合使用 Spring 的ConfigurableObjectInputStreamThread.currentThread().getContextClassLoader()

不幸的是,一些第三方库在不考虑上下文类加载器的情况下反序列化。如果发现这样的问题,则需要向原始作者请求修复。

20.3 LiveReload

spring-boot-devtools模块包括一个嵌入式 LiveReload 服务器,该服务器可用于在更改资源时触发浏览器刷新。可从livereload.com免费为 Chrome,Firefox 和 Safari 使用 LiveReload 浏览器扩展。

如果您不想在应用程序运行时启动 LiveReload 服务器,则可以将spring.devtools.livereload.enabled属性设置为false

Note

一次只能运行一台 LiveReload 服务器。在启动应用程序之前,请确保没有其他 LiveReload 服务器正在运行。如果从 IDE 启动多个应用程序,则只有第一个将具有 LiveReload 支持。

20.4 全局设置

您可以通过将名为.spring-boot-devtools.properties的文件添加到$HOME文件夹中来配置全局 devtools 设置(请注意,文件名以“.”开头)。添加到该文件的所有属性将应用于使用 devtools 的机器上的全部 Spring Boot 应用程序。例如,要将重新启动配置为始终使用trigger file,则应添加以下内容:

~/.spring-boot-devtools.properties.

spring.devtools.reload.trigger-file=.reloadtrigger

20.5 远程应用程序

Spring Boot 开发人员工具不仅限于本地开发。远程运行应用程序时,您还可以使用多种功能。远程支持是可选的,要启用它,您需要确保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属性,例如:

spring.devtools.remote.secret=mysecret

Warning

在远程应用程序上启用spring-boot-devtools是安全隐患。您永远不应在生产部署上启用支持。

远程 devtools 支持分为两个部分:有一个服务器端端点接受连接,还有一个在 IDE 中运行的 Client 端应用程序。设置spring.devtools.remote.secret属性后,将自动启用服务器组件。Client 端组件必须手动启动。

20.5.1 运行远程 Client 端应用程序

远程 Client 端应用程序旨在在您的 IDE 中运行。您需要使用与要连接到的远程项目相同的 Classpath 来运行org.springframework.boot.devtools.RemoteSpringApplication。传递给应用程序的* non-option *参数应该是您要连接的远程 URL。

例如,如果您使用的是 Eclipse 或 STS,并且有一个名为my-app的项目已部署到 Cloud Foundry,则可以执行以下操作:

  • Run菜单中选择Run Configurations…

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

  • 浏览my-app项目。

  • 使用org.springframework.boot.devtools.RemoteSpringApplication作为主要类。

  • https://myapp.cfapps.io添加到Program arguments(或任何远程 URL)。

正在运行的远程 Client 端将如下所示:

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

2015-06-10 18:25:06.632  INFO 14938 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code/spring-boot-samples/spring-boot-sample-devtools)
2015-06-10 18:25:06.671  INFO 14938 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[emailprotected]2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy
2015-06-10 18:25:07.043  WARN 14938 --- [           main] o.s.b.d.r.c.RemoteClientConfiguration    : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.
2015-06-10 18:25:07.074  INFO 14938 --- [           main] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2015-06-10 18:25:07.130  INFO 14938 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105)

Note

因为远程 Client 端使用与真实应用程序相同的 Classpath,所以它可以直接读取应用程序属性。这就是读取spring.devtools.remote.secret属性并将其传递给服务器进行身份验证的方式。

Tip

始终建议使用https://作为连接协议,以便对流量进行加密并且不能截获密码。

Tip

如果需要使用代理来访问远程应用程序,请配置spring.devtools.remote.proxy.hostspring.devtools.remote.proxy.port属性。

20.5.2 远程更新

远程 Client 端将以与local restart相同的方式监视应用程序 Classpath 中的更改。任何更新的资源将被推送到远程应用程序,并且*(如果需要)*会触发重新启动。如果您要迭代使用本地没有的云服务的功能,这将非常有用。通常,远程更新和重新启动比完整的重建和部署周期要快得多。

Note

仅在远程 Client 端正在运行时监视文件。如果在启动远程 Client 端之前更改文件,则不会将其推送到远程服务器。

20.5.3 远程调试隧道

诊断远程应用程序中的问题时,Java 远程调试非常有用。不幸的是,当您的应用程序部署在数据中心之外时,并非总是能够启用远程调试。如果您使用的是基于容器的技术(例如 Docker),则设置远程调试也很棘手。

为了解决这些限制,devtools 支持通过 HTTP 构建远程调试流量的隧道。远程 Client 端在端口8000上提供了本地服务器,您可以将远程调试器连接到该本地服务器。构建连接后,调试流量将通过 HTTP 发送到远程应用程序。如果要使用其他端口,可以使用spring.devtools.remote.debug.local-port属性。

您需要确保在启用了远程调试的情况下启动了远程应用程序。通常,这可以通过配置JAVA_OPTS来实现。例如,使用 Cloud Foundry,您可以将以下内容添加到manifest.yml

---
    env:
        JAVA_OPTS: "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n"

Tip

请注意,您不需要将address=NNNN选项传递给-Xrunjdwp。如果省略,Java 只会选择一个随机的空闲端口。

Note

通过 Internet 调试远程服务可能会很慢,并且您可能需要增加 IDE 中的超时。例如,在 Eclipse 中,您可以从Preferences…中选择JavaDebug并将Debugger timeout (ms)更改为更合适的值(在大多数情况下60000效果很好)。

Warning

当将远程调试隧道与 IntelliJ IDEA 一起使用时,必须将所有断点配置为挂起线程而不是 VM。默认情况下,IntelliJ IDEA 中的断点将挂起整个 VM,而不是仅挂起命中断点的线程。这具有挂起 Management 远程调试隧道的线程的有害副作用,从而导致调试会话冻结。当将远程调试隧道与 IntelliJ IDEA 一起使用时,应将所有断点配置为挂起线程而不是 VM。有关更多详细信息,请参见IDEA-165769