0%

springboot中使用devtools实现热部署

在日常开发中,常常需要自己调试代码或者与终端同事进行联调,而在此过程中通常是需要修改代码的,那么为了改后的代码生效就需要重启服务,耗时且麻烦。在springboot的项目中,springboot-devtools帮助我们解决了这点,下面来说明一下在springboot项目中使用devtools实现热部署功能。

这里不讲远程热部署情况,可自行参考官方文档

依赖引入

要使用devtools,在pom文件中加入一下依赖即可。这里标记依赖为可选项是防止devtools传递应用于使用项目的其他模块。

1
2
3
4
5
6
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Copy

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

触发(triggering)热部署

这里以开发工具idea为例:

  • 每次修改完文件,手动触发,Build->Build Project
  • 自动触发,需要修改设置,Setting->Compile->Build project automatically

个人建议使用手动触发方式,这样可以自己控制类重新加载,而不是每改动一点过几秒就自动进行类重加载。

spring-boot-devtools实现热部署原理

Spring Boot提供的重启技术使用两个类加载器。一个基础的类加载器用来加载不更改的类(例如,来自第三方jar的类), 而我们开发的类用一个叫做重新启动的类加载器(RestartClassLoader)来加载。 每次更改代码后,触发类重新加载时,是将原来的RestartClassLoader抛弃,然后重新创建一个新的RestartClassLoader。这种方法意味着应用程序重新启动通常比“冷启动”快得多,因为基本类加载器已经可用并已填充。

spring-boot-devtools配置

在项目中引入devtools的依赖之后,便可以开始使用热部署功能了,因为其内部默认了一些配置信息。配置的类如下,这里去掉了碍眼的无用的代码(构造函数、get、set),因为此处关心的是配置信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@ConfigurationProperties(prefix = "spring.devtools")
public class DevToolsProperties {

private Restart restart = new Restart();

private Livereload livereload = new Livereload();

@NestedConfigurationProperty
private final RemoteDevToolsProperties remote = new RemoteDevToolsProperties();

/**
* Restart properties.
*/
public static class Restart {

private static final String DEFAULT_RESTART_EXCLUDES = "META-INF/maven/**,"
+ "META-INF/resources/**,resources/**,static/**,public/**,templates/**,"
+ "**/*Test.class,**/*Tests.class,git.properties,META-INF/build-info.properties";

/**
* Whether to enable automatic restart.
*/
private boolean enabled = true;

/**
* Patterns that should be excluded from triggering a full restart.
*/
private String exclude = DEFAULT_RESTART_EXCLUDES;

/**
* Additional patterns that should be excluded from triggering a full restart.
*/
private String additionalExclude;

/**
* Amount of time to wait between polling for classpath changes.
*/
private Duration pollInterval = Duration.ofSeconds(1);

/**
* Amount of quiet time required without any classpath changes before a restart is
* triggered.
*/
private Duration quietPeriod = Duration.ofMillis(400);

/**
* Name of a specific file that, when changed, triggers the restart check. If not
* specified, any classpath file change triggers the restart.
*/
private String triggerFile;

/**
* Additional paths to watch for changes.
*/
private List<File> additionalPaths = new ArrayList<>();

/**
* Whether to log the condition evaluation delta upon restart.
*/
private boolean logConditionEvaluationDelta = true;
}

/**
* LiveReload properties.
*/
public static class Livereload {

/**
* Whether to enable a livereload.com-compatible server.
*/
private boolean enabled = true;

/**
* Server port.
*/
private int port = 35729;

}

}
Copy

从源码中可以看见默认的参数有如下信息:

  • 使得自动重启生效(enabled = true)
  • 默认情况下不重加载的文件(exclude = DEFAULT_RESTART_EXCLUDES;)
  • 轮询类路径更改之间等待的时间(pollInterval = Duration.ofSeconds(1))
  • 重启之前的静待时间(quietPeriod = Duration.ofMillis(400))
  • 重新启动时记录条件评估增量(logConditionEvaluationDelta = true)
  • 热重载可用性及端口信息

在真实的使用中其实这些配置以及可以了,但也可以更改一下配置,或添加一下配置。如下所示,关掉评估增量日志,添加不冲加载的文件等

1
2
3
4
5
6
spring:
devtools:
restart:
enabled: true
log-condition-evaluation-delta: false
additional-exclude: "application.yml"
Copy

案例

这里创建一个简单的工程测验热部署功能,一个DevtoolsController类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.lazycece.sbac.devtools.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author lazycece
* @date 2019/2/22
*/
@RestController
@RequestMapping("/devtools")
public class DevtoolsController {

@Value(value = "${project.value}")
private String value;

@GetMapping("/clazz")
public Object clazz() {
return "this is clazz change: Hello, devtools !";
}

@GetMapping("/config")
public Object config() {
return "this is config change: " + value;
}
}
Copy

yml文件信息:

1
2
3
4
5
6
7
8
9
10
spring:
devtools:
restart:
enabled: true
log-condition-evaluation-delta: false
additional-exclude: "application.yml"
server:
port: 8080
project:
value: Hello, devtools !
Copy

案例测验信息如下:

  • 更改”/devtools/clazz”接口的返回信息测验类的重加载。
  • 通过设置”additional-exclude“是否包含”application.yml“文件,设置”project.value“值来查看”/devtools/config”接口的返回来测验”additional-exclude“设置生效问题。

案例源码

案例源码地址:https://github.com/lazycece/springboot-actual-combat/tree/master/springboot-ac-devtools

参考资料

官方文档:
https://docs.spring.io/spring-boot/docs/2.1.3.RELEASE/reference/htmlsingle/#using-boot-devtools