如果应用程序使用了 spring-boot-devtools 工具,则只要 classpath 下的文件有变动,它就会自动重启。
这在使用 IDE 时非常有用,因为可以很快得到代码改变后的反馈。默认情况下,classpath 下任何文件夹或文件都会被监控。
注意:一些资源的修改(比如:静态资源)是不需要重启应用。
触发重启是由于 spring-boot-devtools 监控了 classpath 下的资源,所以唯一触发重启的方式就是更新 classpath。
引起 classpath 更新的方式依赖于你使用的 IDE,在 Eclipse 里,保存一个修改的文件将引起 classpath 更新,并触发重启。在 IntelliJ IDEA 中,构建工程 (Build → Make Project)有同样效果。
注意:你也可以通过支持的构建工具(比如,Maven 和 Gradle)启动应用,只要开启 fork 功能,因为 spring-boot-devtools 需要一个隔离的应用类加载器执行正确的操作。Gradle 默认支持该行为,按照以下配置可强制 Maven 插件支持 fork 进程:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> </configuration> </plugin>
自动重启跟 LiveReload 可以一起很好的工作。如果你使用 JRebel,自动重启将禁用以支持动态类加载,其他 spring-boot-devtools 特性,比如:LiveReload,属性覆盖仍旧可以使用。
spring-boot-devtools 依赖应用上下文的 shutdown 钩子来关闭处于重启过程的应用,如果禁用 shutdown 钩子( SpringApplication.setRegisterShutdownHook(false) ), 它将不能正常工作。
注意:当判定 classpath 下实体的改变是否会触发重启时,spring-boot-devtools 将自动忽略以下工程:spring-boot,spring-boot-devtools,spring-boot-autoconfigure,spring-boot-actuator 和 spring-boot-starter 。
Spring Boot 提供的重启技术是通过使用两个类加载器实现的。
没有变化的类(比如那些第三方 jars)会加载进一个基础(basic)classloader,正在开发的类会加载进一个重启(restart)classloader。当应用重启时,restart 类加载器会被丢弃,并创建一个新的。这种方式意味着应用重启通常比冷启动(cold starts)快很多,因为基础类加载器已经可用。
如果发现重启对于你的应用来说不够快,或遇到类加载的问题,那你可以考虑 reload 技术,比如 JRebel,这些技术是通过重写它们加载过的类实现的。然而,Spring Loaded 提供了另一种选择,然而很多框架不支持它,也得不到商业支持。
某些资源的变化没必要触发重启,比如: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/**
注意:如果你想保留默认属性,并添加其他的排除规则,可以使用 spring.devtools.restart.additional-exclude 属性作为代替。
如果想让应用程序在改变了没有位于 classpath 下的文件时也会重启或重新加载,你可以使用 spring.devtools.restart.additional-paths 属性来配置监控变化的额外路径。
你可以使用上面描述过的 spring.devtools.restart.exclude 属性去控制额外路径下的变化是否触发一个完整重启或只是一个实时重新加载。
如果不想使用重启特性,你可以通过 spring.devtools.restart.enabled 属性来禁用它,通常情况下可以在 application.properties 或 application.yml 文件中设置。但是,重启类加载器依旧会被初始化,但它不会监控文件变化。
如果需要彻底禁用重启支持,比如:不能跟某个特殊库一块工作,你需要在调用 SpringApplication.run(…) 之前设置一个系统属性,如下:
@SpringBootApplication public class MyApp { public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false" ); SpringApplication.run(MyApp.class, args); } }
如果使用一个IDE连续不断地编译变化的文件,你可能倾向于只在特定时间触发重启,触发器文件可以帮你实现该功能。
触发器文件是一个特殊的文件,只有修改它才能实际触发一个重启检测。改变该文件只会触发检测,实际的重启只会在 spring-boot-devtools 发现它必须这样做的时候,才去执行重启。
触发器文件可以手动更新,也可以通过IDE插件更新。
使用 spring.devtools.restart.trigger-file 属性可以指定触发器文件。
注意:你可能想将 spring.devtools.restart.trigger-file 属性设置为全局设置,这样所有的工程表现都会相同。
在 $HOME 文件夹下添加一个 .spring-boot-devtools.properties 的文件可以用来配置全局的 devtools 设置(注意:文件名以 "." 开头),添加进该文件的任何属性都会应用到你机器上使用该 devtools 的 Spring Boot 应用。
例如:想使用触发器文件进行重启,可以添加如下配置:
spring.devtools.reload.trigger-file=.trigger-file.txt
正如以上 Restart vs Reload 章节讨论的,重启功能是通过两个类加载器实现的。对于大部分应用来说是没问题的,但有时候它可能导致类加载问题。
默认情况,在IDE里打开的项目会通过 'restart' 类加载器加载,其他常规的 .jar 文件会使用 'basic' 类加载器加载。
如果你工作在一个多模块的项目下,并且不是每个模块都导入到 IDE 里,你可能需要自定义一些东西。你需要创建一个 META- INF/spring-devtools.properties 文件, spring-devtools.properties 文件可以包含 restart.exclude.、restart.include. 前缀的属性。
include 元素定义了那些需要加载进 'restart' 类加载器中的实体,exclude 元素定义了那些需要加载进 'basic' 类加载器中的实体,这些属性的值是一个将应用到 classpath 的正则表达式。 例如:
restart.include.companycommonlibs=/mycorp-common-[\\w-]+\.jar restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
注意:所有属性的 keys 必须唯一,只要以 restart.include. 或 restart.exclude. 开头都会考虑进去。所有来自 classpath 的 META-INF/spring-devtools.properties 都会被加载,你可以将文件打包进工程或工程使用的库里。
重启功能不能跟使用标准 ObjectInputStream 反序列化的对象工作,如果需要反序列化数据,你可能需要使用 Spring 的 ConfigurableObjectInputStream,并结合 Thread.currentThread().getContextClassLoader() 。
不幸的是,一些第三方库反序列化时没有考虑上下文类加载器,如果发现这样的问 题,你需要请求第三方库原作者给处理下。