SpringBoot Development - What is Hot Deployment and Hot Reload? What is the principle of devtool?

In SpringBoot development and debugging, if I need to restart and debug each line of code modification, it may take time; SpringBoot team provides spring-boot-devtools (devtools for short) plug-in for this problem, which tries to improve the efficiency of development and debugging . @pdai

Prepare knowledge points

What is hot deployment and hot reloading?

Hot deployment and hot reloading are the ability to automatically update (reload or replace classes, etc.) the application while the application is running. (PS: The solution provided by spring-boot-devtools also needs to be restarted, but it can be automatically loaded without manual restart.)

Strictly speaking, we need to distinguish between hot deployment and hot loading. For Java projects:

  • hot deployment

    • Redeploy the project while the server is running
    • It directly reloads the entire application, which frees up memory, is cleaner and more thorough than hot reloading, but also takes more time.
  • hot loading

    • Reloads the class at runtime to upgrade the application.
    • The implementation principle of hot loading mainly depends on the class loading mechanism of java. The implementation method can be summarized as starting a background thread when the container starts, and regularly detects the change of the timestamp of the class file. If the timestamp of the class changes, the Class reloading.
    • Compared with the reflection mechanism, reflection is to obtain class information at runtime and change program behavior through dynamic calls; hot loading is to change class information at runtime by reloading and directly change program behavior.

What is LiveLoad?

LiveLoad是提供浏览器客户端自动加载更新的工具,分为LiveLoad服务器和Liveload浏览器插件两部分; devtools中已经集成了LiveLoad服务器,所以如果我们开发的是web应用,并且期望浏览器自动刷新, 这时候可以考虑LiveLoad.

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

配置devtools实现热部署

我们通过如下配置来实现自动重启方式的热部署。

POM配置

添加spring-boot-devtools的依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional> <!-- 可以防止将devtools依赖传递到其他模块中 -->
    </dependency>
</dependencies>

IDEA配置

如果你使用IDEA开发工具,通常有如下两种方式:

  • 方式一: 无任何配置时,手动触发重启更新(Ctrl+F9)

(也可以用mvn compile编译触发重启更新)

  • 方式二: IDEA需开启运行时编译,自动重启更新

设置1

File->Setting->Build,Execution,Deployment->Compile

勾选:Make project automatically

设置2

快捷键:ctrl+alt+shift+/

选择:Registry

勾选:compiler.automake.allow.when.app.running

新版本的IDEA可以在File->setting->Advanced Setttings里面的第一个设置:

application.yml配置

spring:
  devtools:
    restart:
      enabled: true  #设置开启热部署
      additional-paths: src/main/java #重启目录
      exclude: WEB-INF/**
  thymeleaf:
    cache: false #使用Thymeleaf模板引擎,关闭缓存

使用LiveLoad

spring-boot-devtools模块包含嵌入式LiveReload服务器,可以在资源更改时用于触发浏览器刷新。 LiveReload浏览器扩展程序支持Chrome,Firefox和Safari,你可以从livereload.com免费下载。

或者从浏览器插件中心下载,比如firefox:

安装完之后,可以通过如下图标管理

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

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

进一步理解

虽然一些开发者会使用devtool工具,但是很少有能够深入理解的;让我们理解如下几个问题,帮助你进一步理解。@pdai

devtool的原理?为何会自动重启?

为什么同样是重启应用,为什么不手动重启,而是建议使用spring-boot-devtools进行热部署重启?

spring-boot-devtools使用了两个类加载器ClassLoader,一个ClassLoader加载不会发生更改的类(第三方jar包),另一个ClassLoader(restart ClassLoader)加载会更改的类(自定义的类)。

后台启动一个文件监听线程(File Watcher)监测的目录中的文件发生变动时, 原来的restart ClassLoader被丢弃,将会重新加载新的restart ClassLoader

因为文件变动后,第三方jar包不再重新加载,只加载自定义的类,加载的类比较少,所以重启比较快。

这也是为什么,同样是重启应用,为什么不手动重启,建议使用spring-boot-devtools进行热部署重启。

在自动重启中有几点需要注意:

  • 自动重启会记录日志的

(记录在什么情况下重启的日志)

可以通过如下关闭

spring:
  devtools:
    restart:
      log-condition-evaluation-delta: false
  • 排除一些不需要自动重启的资源

某些资源在更改时不一定需要触发重新启动。默认情况下,改变资源/META-INF/maven,/META-INF/resources,/resources,/static,/public,或/templates不触发重新启动,但确会触发现场重装。如果要自定义这些排除项,可以使用该spring.devtools.restart.exclude属性。例如,要仅排除/static,/public你将设置以下属性:

spring:
  devtools:
    restart:
      exclude: "static/**,public/**"

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

  • 自定义重启类加载器

重启功能是通过使用两个类加载器来实现的。对于大多数应用程序,这种方法效果很好。但是,它有时会导致类加载问题。

默认情况下,IDE 中的任何打开项目都使用“重启”类加载器加载,任何常规.jar文件都使用“基本”类加载器加载。如果你处理一个多模块项目,并且不是每个模块都导入到你的 IDE 中,你可能需要自定义一些东西。为此,你可以创建一个META-INF/spring-devtools.properties文件。

该spring-devtools.properties文件可以包含以restart.exclude和为前缀的属性restart.include。该include元素是应该被拉高到“重启”的类加载器的项目,以及exclude要素是应该向下推入“Base”类加载器的项目。该属性的值是应用于类路径的正则表达式模式,如以下示例所示:

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

更多相关的信息可以在这里查看。

devtool是否会被打包进Jar?

devtool原则上来说应该是只在开发调试的时候使用,而在生产环境运行jar包时是不需要的,所以Spring打包会不会把它打进JAR吗?

  • 默认情况下,不会被打包进JAR

运行打包的应用程序时,开发人员工具会自动禁用。如果你通过 java -jar或者其他特殊的类加载器进行启动时,都会被认为是“生产环境的应用”。

  • 如果我们期望远程调试应用

生产环境勿用,只有在受信任的网络上运行或使用 SSL 进行保护时,才应启用它

在这种情况下,devtool也具备远程调试的能力:远程客户端应用程序旨在从你的 IDE 中运行。你需要org.springframework.boot.devtools.RemoteSpringApplication使用与你连接的远程项目相同的类路径运行。应用程序的唯一必需参数是它连接到的远程 URL。

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

  1. 选择Run Configurations…​从Run菜单。
  2. 创建一个新的Java Application“启动配置”。
  3. 浏览my-app项目。
  4. 使用org.springframework.boot.devtools.RemoteSpringApplication作为主类。
  5. 添加myapp.cfapps.io到Program arguments(或任何你的远程 URL)。

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

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

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-project/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code)
2015-06-10 18:25:06.671  INFO 14938 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@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)

devtool为何会默认禁用缓存选项?

Spring Boot 支持的一些库使用缓存来提高性能。例如,模板引擎缓存已编译的模板以避免重复解析模板文件。此外,Spring MVC 可以在提供静态资源时向响应添加 HTTP 缓存标头。

虽然缓存在生产中非常有益,但在开发过程中可能会适得其反,使你无法看到刚刚在应用程序中所做的更改。出于这个原因, spring-boot-devtools 默认禁用缓存选项。

比如Thymeleaf 提供了spring.thymeleaf.cache来设置模板引擎的缓存,使用spring-boot-devtools模块时是不需要手动设置这些属性的,因为spring-boot-devtools会自动进行设置。

那么会自动设置哪些配置呢?你可以在DevToolsPropertyDefaultsPostProcessor类找到对应的默认配置。

public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {

	static {
		Map<String, Object> properties = new HashMap<>();
		properties.put("spring.thymeleaf.cache", "false");
		properties.put("spring.freemarker.cache", "false");
		properties.put("spring.groovy.template.cache", "false");
		properties.put("spring.mustache.cache", "false");
		properties.put("server.servlet.session.persistent", "true");
		properties.put("spring.h2.console.enabled", "true");
		properties.put("spring.web.resources.cache.period", "0");
		properties.put("spring.web.resources.chain.cache", "false");
		properties.put("spring.template.provider.cache", "false");
		properties.put("spring.mvc.log-resolved-exception", "true");
		properties.put("server.error.include-binding-errors", "ALWAYS");
		properties.put("server.error.include-message", "ALWAYS");
		properties.put("server.error.include-stacktrace", "ALWAYS");
		properties.put("server.servlet.jsp.init-parameters.development", "true");
		properties.put("spring.reactor.debug", "true");
		PROPERTIES = Collections.unmodifiableMap(properties);
	}

当然如果你不想被应用属性被spring-boot-devtools默认设置, 可以通过spring.devtools.add-properties到false你application.yml中。

devtool是否可以给所有Springboot应用做全局的配置?

可以通过将spring-boot-devtools.yml文件添加到$HOME/.config/spring-boot目录来配置全局 devtools 设置

添加到这些文件的任何属性都适用于你机器上使用 devtools 的所有Spring Boot 应用程序。例如,要将重新启动配置为始终使用触发器文件,你需要将以下属性添加到你的spring-boot-devtools文件中:

spring:
  devtools:
    restart:
      trigger-file: ".reloadtrigger"

如果我不用devtool,还有什么选择?

如果我不用devtool,还有什么选择?

在实际的开发过程中,我也不会去使用devtool工具, 因为:

  • Devtool itself is based on the restart method, which is still not a real hot-replacement solution, JRebel is (it is charged)
  • The most important thing in development and debugging is a trade-off
    • If the overhead of automatic restart is not much different from manual restart, then it is better to restart manually (restart on demand)
    • In most cases, if it is an internal modification of a method or a modification of static resources , it can be hot-updated through Rebuild (Ctrl + Shift + F9) in IDEA

  • In addition, there is a tool spring loaded, which can realize hot deployment of modified class files. For details, please refer to the instructions on its github address .

Sample source code

github.com/realpdai/te…

Reference article

docs.spring.io/spring-boot…

liayun.blog.csdn.net/article/det…

more content

Say goodbye to fragmented learning, one-stop systematic learning without routines Back-end development: Java full-stack knowledge system (https://pdai.tech)

Guess you like

Origin juejin.im/post/7118676133318492167