vlambda博客
学习文章列表

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Spring Boot DevTools

Chapter 27. Spring Boot DevTools

在本章中,我们将学习以下主题:

  • Adding Spring Boot DevTools to a project
  • Configuring LiveReload
  • Configuring dynamic application restart triggers
  • Using Remote Update

Introduction


在 DevOps、敏捷软件开发实践、微服务的引入以及越来越多的团队进行持续开发和部署的世界中,能够快速查看应用程序的代码更改而无需经历整个过程变得更加重要重新编译整个项目、重建和重新启动应用程序的过程。

Docker等容器化服务的到来,也对实际应用运行环境的接入提出了挑战。它通过抽象和封装运行时环境改变了机器的概念,消除了使用任何端口进行访问的能力。

Spring Boot DevTools 提供了使用 HTTP 远程调试隧道进行选择性类重新加载和调试在 Docker 容器中运行的应用程序的能力,以便为开发人员提供一个快速反馈循环,以查看他们的更改在运行的应用程序中的反映,而无需长时间的重建和重启周期。

Adding Spring Boot DevTools to a project


从 Spring Boot 1.3 开始,我们可以在我们的项目中利用 DevTools 组件来实现自动应用程序重启等功能在代码更改、重新加载 UI 的浏览器窗口或远程重新加载应用程序时。

DevTools 模块可用于 Maven 和 Gradle,并且可以很好地与 Eclipse 或 IntelliJ IDEA 编辑器配合使用。

Note

在本章中,我们将介绍与 Gradle 和 IntelliJ IDEA 的集成,但有关使用 Spring Boot DevTools 的详细信息,请查看文档 http://docs.spring.io/spring-boot/docs/current/reference /html/using-boot-devtools.html

How to do it...

继续我们的 BookPub 项目,我们将通过执行以下步骤将 DevTools 模块添加到主构建配置中:

  1. Add the following content to the build.gradle file located at the root of the project:
dependencies { 
    ... 
    compile("io.dropwizard.metrics:metrics-graphite:3.1.0") 
    compile("org.springframework.boot:spring-boot-devtools") 
    runtime("com.h2database:h2") 
    ... 
} 
  1. Start the application by running ./gradlew clean bootRun.
  2. After application startup, you might notice in the console log an output warning about the inability to register with Spring Boot admin (unless you have one running) that looks like this: Failed to register application as Application.... Let's make a live change to the application.properties file located in the build/resources/main directory from the root of our project and add a property entry with the following content:
spring.boot.admin.auto-registration=false 
  1. Without doing anything else, upon saving the file, we should see the console log showing us that the application context is being restarted.

How it works...

正如您现在可能已经了解到的那样,当我们添加一个 spring-boot-devtools 模块作为依赖项时,会发生一些自动配置魔术来添加许多组件。许多侦听器和自动配置扩展了应用程序上下文以处理代码更改并在本地和远程进行适当的重新启动和重新加载。

在我们的秘籍中,我们进行了快速测试,以确保重启功能正常工作,并且通过在 application.properties 文件中进行属性更改来配置所有内容。您可能已经注意到,我们没有在 src/main/resources/application.properties 中进行更改,而是对位于 build/resources/main 目录。这是因为我们在 Gradle 构建阶段使用的 info. 块的属性占位符替换。如果我们只对原始文件进行更改并使用 IntelliJ 编译选项,它将不会执行所需的替换,从而导致重新启动失败。

启用 DevTools 后,application 启动后,开始监控类路径以查看对正在运行的类的更改那个类路径。当任何类或资源发生更改时, 它将作为 DevTools 的触发器,通过刷新包含项目代码库的类加载器来重新加载应用程序(它与保存静态依赖项工件中的类的类加载器不同)。

在可重新加载的类加载器完成刷新后,应用程序上下文会自动重新启动,从而有效地导致应用程序重新启动。

Configuring LiveReload


那些在前端 Web 应用程序上工作的人可能同意一旦后端代码或资源更改后能够自动重新加载页面发生将节省几次点击,并防止忘记重新加载导致浪费调试工作和追逐不存在的错误的情况。值得庆幸的是,DevTools 通过提供 LiveReload 服务器实现来提供帮助,它可以与 LiveReload 浏览器扩展一起使用,以在后端发生更改时自动重新加载页面。

How to do it...

如果将 DevTools 模块添加到构建依赖项中,则 LiveReload 服务器已自动启动。但是,我们确实需要通过执行以下步骤来安装和启用浏览器扩展:

  1. Unless the browser already has the LiveReload extension installed, go to http://livereload.com/extensions/ and install the appropriate extension for your browser of choice (Firefox, Safari, and Chrome are supported).

Note

对于 Internet Explorer 用户,可以在 https://github.com/dvdotsenko/livereload_ie_extension/downloads.

  1. After the extension is installed, it typically needs to be enabled on the page by clicking a button in the toolbar. This is what it would look like in the Chrome browser:
    读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Spring Boot DevTools
  2. After enabling the extension, we can go ahead and make another change as we did in the previous recipe (or any other code or resource change), or simply execute the touch build/resources/main/application.properties command. We should see the application reload taking place on the backend as well as the browser page reloading after.

 

How it works...

随着 LiveReload 浏览器扩展的添加,以及嵌入到我们的 BookPub 应用程序中的运行 LiveReload 服务器,浏览器现在能够使用 Web 套接字连接到后端服务器以监控更改.当 Spring Boot DevTools 检测到应该触发重新加载的更改时,它将触发重新加载并向浏览器发送通知以重新加载页面。

Note

如果需要禁用 DevTools 功能的 LiveReload 部分,可以通过添加  spring.devtools.livereload.enabled=false 属性轻松实现任何受支持的配置选项,可以是属性文件、环境变量或系统属性。

Configuring dynamic application restart triggers


在前面的秘籍中,我们研究了 DevTools 在代码或资源更改时重新启动应用程序时的 basic 功能并与浏览器通信以重新加载页面。本节将讨论我们可以利用的各种配置选项,以向 Spring Boot DevTools 准确地指示我们希望这些事件由什么触发以及何时触发。 

How to do it...

默认情况下,将 DevTools 模块添加到项目将使其监视所有类或资源,这可能会成为不良行为,尤其是在涉及多模块存储库时。当从 IntelliJ 或 Eclipse 等 IDE 中构建和启动项目时,这一点变得真实。我们需要通过调整配置设置告诉 DevTools 将我们项目中的 db-count-starter 子模块从监视列表中排除:

  1. Let's create a file named spring-devtools.properties under the db-count-starter/src/main/resources/META-INF directory from the root of our project with the following content:
restart.exclude.db-count-starter=/db-count-starter/build/(classes|resources)/main 

 

  1. Next we need to launch our application from within an IDE by opening the BookPubApplication class located under the src/main/java/com/example/bookpub directory from the root of our project and starting the main(String[] args) method either in run or debug mode
  1. With the db-count-starter module excluded, we can safely make a change to a file, for example a spring.factories resource located under the db-count-starter/build/resources/main/META-INF directory from the root of our project, only to see the application not being restarted
  2. If we want to completely disable the restart capability, we can do so by adding the following property to application.properties located under the src/main/resources directory from the root of our project:
spring.devtools.restart.enabled=false 
  1. After relaunching our application, even the changes to the build/resources/main/application.properties file, which is what's being loaded from the classpath, will not trigger the application restart

How it works...

在这个秘籍中,我们查看了许多不同的重载触发器配置,因此让我们分别查看它们中的每一个,以了解在哪里最好地使用它们:

  • spring.devtools.restart.enabled: This property offers the simplest of controls, fully enabling or disabling the restart functionality of DevTools. With the value of false, no restart of the application will take place, regardless of the class or resource changes on the classpath.
  • spring.devtools.restart.exclude: This property provides an ability to stop specific classpaths from being reloaded. This property accepts values in a comma-separated form using the Ant Path matching pattern style. The default exclude value is "META-INF/maven/**,META-INF/resources/**,resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class,git.properties,META-INF/build-info.properties".
  • spring.devtools.restart.additional-exclude: This property provides the convenience of being able to add to the default excludes list without having to copy/paste the default values, but rather simply adding to them while retaining the original defaults. It takes the same comma-separated Ant Path matching pattern style of input.
  • spring.devtools.restart.additional-paths: This property provides the ability to watch for resources that are outside of the classpath. For example, this could be a config directory that gets loaded at application startup, and you want to restart the application if the config entry changes. It takes a comma-separated list of absolute file paths.
  • spring.devtools.restart.poll-interval: This property specifies how long to pause, in milliseconds, between checking for classpath changes. The default value is 1000 milliseconds, but if there is a need to save some CPU cycles, this will do the trick.
  • spring.devtools.restart.quiet-period: This property controls how much time should pass, in milliseconds, without any changes to the classpath before the restart will take place. This is needed to ensure the restarts don't get overwhelming if there are continuous changes taking place. The default value is 400 milliseconds, but it can be changed if needed.
  • spring.devtools.restart.trigger-file: This property provides explicit control over when a restart happens by watching a trigger file for change. This is useful for situations where the classpath gets continuously changed, and you don't want to get caught in a restart loop.

前面列出的所有属性设置通常在开发人员从事的所有应用程序项目之间共享,因此 DevTools 提供了具有在此定义的全局属性,可以方便地在多个项目之间共享开发配置,而无需在所有不同的代码库中复制/粘贴相同的值。

Note

在内部,此功能被实现为 PropertySource,它被添加到配置优先级层次结构的顶部。这意味着不仅spring.devtools 配置系列,而且添加到全局文件的任何属性都将应用于所有使用 DevTools 的应用程序。

控制重新加载触发器的另一种方法是使用 META-INF/spring-devtools.properties 和 restart.exclude。<name> ;restart.include.<name> 里面的配置。默认情况下,应用程序的重新启动仅由对直接位于类路径上且未捆绑到 JAR 中的实际类或资源的更改触发。这允许您将大多数类保留在不可重新加载的基类加载器中,从而极大地限制了需要监视更改的条目数量。

在开发人员处理多个相互依赖的项目或在多模块存储库中工作的情况下,例如 BookPub 之一,可能需要添加一些 JAR进入可重新加载的类加载器并观察它们的变化。这通常应用于指向 build/libstarget 目录的依赖项,其中的 JAR 是一个构建任务执行的直接结果,通常会频繁重建。

我们在这个秘籍中探讨的另一个用例是从观察名单。如果在 IDE 中加载多模块项目,类路径通常包含对子模块的构建目录的直接引用,而不是编译的 JAR 工件,并且根据用例,我们可能会也可能不会选择包括或排除那些触发重新加载。

键的 <name> 部分不如 long 重要因为它是独一无二的,因为所有 META-INF/spring-devtools.properties 文件都将作为复合文件加载,无论它们是位于 JAR 中还是位于项目中。建议的方法是使用子模块/工件名称,因为它通常会确保唯一性。如果应用了多个模式,则名称可以附加一个序列号,例如 restart.exclude.db-count-starter-1restart.exclude.db-count-starter-2。每个键的值应该包含一个有效的正则表达式模式,可以针对类路径中的每个条目进行评估,以确定该特定类路径 URL 是否应该进入可重新加载或基类加载器。

Using Remote Update


随着 Docker 的流行度,越来越多的应用程序被构建和部署为 Docker 容器。 Docker 的一大特色是运行时环境与主机操作系统的隔离,但同样的隔离使得在真实环境中进行持续更改和测试应用程序变得困难。每次对属性文件或 Java 类进行更改时,都需要重新构建所有内容、创建新的 Docker 映像、重新启动容器等等。每次更改都需要做很多工作。

尽管不幸的是,从 2.0 版开始,Spring Boot 已经删除了进行远程调试的功能,但仍然有非常有用的功能,可以在您处理代码时从 IDE 中远程重新加载代码更改,而无需至少重建应用程序 JAR 和 Docker 映像。

Remote Restart 功能为更好的持续开发提供了一种解决方案,并且可以像在本地计算机上一样远程执行动态应用程序重启。

How to do it...

正如您可能已经猜到的那样,Remote 重新启动涉及在本地运行的代理并向远程客户端发送指令。 DevTools 提供了这样一个代理的实现——RemoteSpringApplication

  1. In order to enable Remote Restart, we need to add a property to application.properties located under the src/main/resources directory from the root of our project with the following content:
spring.devtools.remote.secret=our-secret 
  1. The next step would be to create a Java application launch configuration for the RemoteSpringApplication class in the IDE.

Note

确保程序参数字段具有您尝试调试的应用程序的基本 URL 以及端口。 确保工作目录指向主项目,并且模块的类路径指向主项目模块也是如此。

下一页上的图显示了这样的 configuration 在 IntelliJ IDEA 中的样子。 Eclipse IDE 也有类似的形式。

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Spring Boot DevTools
  1. After filling out all the fields, we need to start RemoteSpringApplication from within our IDE by clicking Run. If all has been configured correctly, we should see a similar output in the log:
  .   ____          _                                              __ _ _ / / ___'_ __ _ _(_)_ __  __ _          ___               _         ( ( )___ | '_ | '_| | '_ / _` |        | _ ___ _ __  ___| |_ ___     /  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  / _   _/ -_) ) ) ) )  '  |____| .__|_| |_|_| |___, |        |_|____|_|_|____/_____|/ / / / =========|_|==============|___/===================================/_/_/_/ :: Spring Boot Remote ::  (v2.0.0.BUILD-SNAPSHOT)2017-12-26 21:33:28.520  INFO o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication v2.0.0.BUILD-SNAPSHOT ...2017-12-26 21:33:28.524  INFO o.s.b.devtools.RemoteSpringApplication   : No active profile set, falling back to default profiles: default2017-12-26 21:33:28.781  INFO s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6babf3bf: startup date [Tue Dec 26 21:33:28 CST 2017]; root of context hierarchy2017-12-26 21:33:29.295  WARN o.s.b.d.r.c.RemoteClientConfiguration    : The connection to http://127.0.0.1:8080 is insecure. You should use a URL starting with 'https://'.2017-12-26 21:33:29.368 DEBUG o.s.b.devtools.restart.ChangeableUrls    : Matching URLs for reloading : [file:/.../ch8/build/classes/main/, file:/.../ch8/build/resources/main/]2017-12-26 21:33:29.401  INFO o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 357292017-12-26 21:33:29.443  INFO o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 1.497 seconds (JVM running for 2.248)
  1. To simulate remoteness, we will launch the application in a separate command shell, executing the ./gradlew clean bootJar command followed by executing ./build/libs/bookpub-0.0.1-SNAPSHOT-exec.jar.
  2. Once the application has started, take a look at one of the last lines in the log that should look something like the following:
INFO 50926 --- [           main] ication$$EnhancerBySpringCGLIB$$11c0ff63 : Value of my.config.value property is:
  1. The property value of my.config.value is not being set, because we don't have one defined in our application.properties  file, and we didn't use any environment variables or startup system property settings to set it.
  2. Let's pretend we need to do a live change and modify our application.properties file located under the build/resources/main directory from the root of our project with the following content:
my.config.value=Remote Change 

 

  1. Now we should see in the console that our application has automatically restarted and, after all is done, we should see something similar to the following:
INFO 50926 --- [  restartedMain] ication$$EnhancerBySpringCGLIB$$11c0ff63 : Value of my.config.value property is: Remote Change

How it works...

它可能看起来像巫术魔法,但远程重启功能背后的科学非常简单。在后台,当包含 DevTools 模块时,/.~~spring-boot!~/restart 的 HTTP 端点处理程序会自动添加。这允许 RemoteSpringApplication 进程通过 HTTP 隧道将代码更改有效负载发送到远程应用程序并返回。

为了确保没有恶意的外部调试连接连接到我们的远程应用程序, spring.devtools.remote.secret 属性的值被发送并验证以建立请求的真实性。

在配方的第 2 步 中,我们使用程序参数值启动了 RemoteSpringApplication 进程http://127.0.0.1:8080,这就是 RemoteSpringApplication 知道如何与我们的远程应用程序通信的方式。 RemoteSpringApplication 类本身通过监视类路径来扫描来自 IDE 的本地文件更改。

在配方的第6步中,当我们在代码中将属性添加到我们的配置中时,非常重要的是要注意我们将更改为application.properties 文件位于 RemoteSpringApplication 类的运行类路径中,不在 src 下/main/resources,但在 build/resources/main 目录下,Gradle 已将所有已编译文件放置在该目录下——希望与您的 IDE 使用的目录相同作为运行 RemoteSpringApplication的类路径。如果这不是您的 IDE 使用的路径,您应该在适当的文件夹中进行更改,IDE 已在其中编译了类 - 对于 IntelliJ IDEA,这将是 out/production/resources< /code> 目录默认。

 

 

如果需要在作为 Docker 容器运行的应用程序中启用 DevTools,我们需要显式配置 build 脚本,方法是将以下内容添加到 build.gradle 主项目中的文件:

bootJar { 
    ... 
    excludeDevtools = false 
} 

我们需要这样做的原因是,默认情况下,当 Spring Boot 应用程序被重新打包以进行生产部署时,在构建 Docker 容器映像时就是这种情况,在构建期间将 DevTools 模块从类路径中排除。为了防止这种情况发生,我们需要告诉构建系统不要排除该模块以利用其功能,即远程重启。