Spring Boot包括一组额外的工具,这些工具可以使应用程序开发体验变得更加愉快。 spring-boot-devtools
模块可以包含在任何项目中,以提供额外的开发时特性。如果要想支持devtools,要将模块依赖项添加到构建中,如以下Maven和Gradle清单所示:
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")
}
在运行完全打包的应用程序时,会自动禁用开发人员工具。如果您的应用程序是从 java-jar
启动的,或者是从一个特殊的类加载器开始的,那么它就被认为是一个“生产应用程序”。将Maven中的依赖项的 optional
设置为true或使用 compileOnly
在Gradle中,这样可以防止devtools被传递到其他使用的项目的模块中。
重新打包的archives默认不包含devtools。如果我们想使用某个远程devtools功能,我们需要禁用 excludeDevtools
的build属性以包含它。Maven和Gradle插件都支持这个属性。
Spring Boot支持的几个库使用缓存来提高性能。例如,模板引擎缓存已编译的模板,以避免重复解析模板文件。此外,Spring MVC可以在服务静态资源时向响应添加HTTP缓存头。
虽然缓存在生产中非常有用,但在开发过程中它可能会产生反作用,防止我们看到我们在应用程序中所做的更改。出于这个原因,spring-boot-devtools默认禁用缓存选项。
缓存选项通常由 application.properties
文件配置。例如,Thymeleaf提供了 spring.thymeleaf.cache
属性。 spring-boot-devtools
模块不需要手动设置这些属性,而是自动应用合理的开发时配置。
有关devtools应用的属性的完整列表(默认配置):
@Order(Ordered.LOWEST_PRECEDENCE)
public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {
private static final Map<String, Object> PROPERTIES;
static {
Map<String, Object> devToolsProperties = new HashMap<>();
devToolsProperties.put("spring.thymeleaf.cache", "false");
devToolsProperties.put("spring.freemarker.cache", "false");
devToolsProperties.put("spring.groovy.template.cache", "false");
devToolsProperties.put("spring.mustache.cache", "false");
devToolsProperties.put("server.servlet.session.persistent", "true");
devToolsProperties.put("spring.h2.console.enabled", "true");
devToolsProperties.put("spring.resources.cache.period", "0");
devToolsProperties.put("spring.resources.chain.cache", "false");
devToolsProperties.put("spring.template.provider.cache", "false");
devToolsProperties.put("spring.mvc.log-resolved-exception", "true");
devToolsProperties.put("server.servlet.jsp.init-parameters.development", "true");
devToolsProperties.put("spring.reactor.stacktrace-mode.enabled", "true");
PROPERTIES = Collections.unmodifiableMap(devToolsProperties);
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (isLocalApplication(environment) && canAddProperties(environment)) {
PropertySource<?> propertySource = new MapPropertySource("refresh",
PROPERTIES);
environment.getPropertySources().addLast(propertySource);
}
}
private boolean isLocalApplication(ConfigurableEnvironment environment) {
return environment.getPropertySources().get("remoteUrl") == null;
}
private boolean canAddProperties(Environment environment) {
return isRestarterInitialized() || isRemoteRestartEnabled(environment);
}
private boolean isRestarterInitialized() {
try {
Restarter restarter = Restarter.getInstance();
return (restarter != null && restarter.getInitialUrls() != null);
}
catch (Exception ex) {
return false;
}
}
private boolean isRemoteRestartEnabled(Environment environment) {
return environment.containsProperty("spring.devtools.remote.secret");
}
}
使用 spring-boot-devtools
的应用程序在类路径上的文件发生更改时自动重新启动。在IDE中工作时,这可能是一个有用的特性,因为它为代码更改提供了一个非常快速的反馈循环。默认情况下,指向文件夹的类路径中的任何条目都将受到监视,以查看是否有更改。注意,某些资源(如静态资源和视图模板)不需要重新启动应用程序。
由于DevTools监控类路径资源,触发重新启动的惟一方法是更新类路径。使类路径更新的方式取决于您使用的IDE。在Eclipse中,保存修改后的文件会更新类路径并触发重新启动。在IntelliJ IDEA中,构建项目(Build -> Build project)也有着同样的效果。
只要启用了fork,您还可以通过使用受支持的构建插件(Maven和Gradle)启动应用程序,因为DevTools需要一个独立的应用程序类加载程序来正常运行。默认情况下,Gradle和Maven在类路径上检测DevTools时是这样做的。
自动重新启动在使用 LiveReload
时非常有效。如果您使用JRebel,自动重启被禁用,以支持动态类重载。其他devtools特性(如LiveReload和property overrides)仍然可以使用。
DevTools依赖于应用程序上下文的shutdown hook在重新启动时关闭它。如果禁用了shutdown hook(SpringApplication.setRegisterShutdownHook(false)),那么它就不能正常工作。
当决定类路径上的条目是否应该在更改时触发重新启动时,DevTools会自动忽略名为 spring-boot
、 spring-boot-devtools
、 spring-boot-autoconfigure
、 spring-boot-actuator
和 spring-boot-starter
的项目。
DevTools需要自定义 ApplicationContext
使用的 ResourceLoader
。如果您的应用程序已经提供了一个,那么它将被包装。不支持在 ApplicationContext
上直接覆盖 getResource
方法。
热加载和热部署
Spring Boot提供的热部署技术使用两个类加载器。不改变的类(例如,来自第三方jar的类)被加载到一个基类加载器中。正在积极开发的类被加载到restart classloader中。当重新启动应用程序时,会丢弃restart classloader,并创建一个新的。这种方法意味着应用程序重新启动通常比“冷启动”快得多,因为基类加载程序已经可用并填充。
如果发现热部署对应用程序来说不够快,或者遇到了类加载问题,可以考虑热加载技术,如零周转期的JRebel。这些工作通过重写类,使它们更适合热加载。
PS:后面会出一期单章说明热加载和热部署的区别,以及在IntelliJ IDEA中的使用方式。
默认情况下,每次应用程序重新启动时,都会记录显示变化的报告。报告显示了应用程序自动配置的更改,如添加或删除bean和设置配置属性。
要禁用报告的日志记录,设置以下属性:
spring.devtools.restart.log-condition-evaluation-delta=false
某些资源在更改时不一定需要触发重新启动。例如,可以就地编辑Thymeleaf模板。默认情况下,在/ META-INF/maven
、 /META-INF/resources
、 /resources
、 /static
、 /public
或 /template
中更改资源不会触发重新启动,但会触发实时重新加载。如果想自定义把这些排除,可以使用spring.devtools.restart.exclude属性。例如,仅排除/静态和/public,我们可以设置以下属性:
spring.devtools.restart.exclude=static/**,public/**
如果您想保留这些默认值并添加额外的排除,可以使用 spring.devtools.restart.additional-rejection
属性。
当我们对不在类路径上的文件进行更改时,我们可能希望应用程序重新启动或重新加载。为此,使用 spring.devtools.restart.additional-paths
属性配置其他路径以监视更改。我们可以使用前面描述的 spring.devtools.restart.exclude
属性来控制附加路径下的更改是触发完全重新启动还是实时重新加载。
如果不想使用重启功能,可以使用 spring.devtools.restart.enabled
属性禁用它。在大多数情况下,我们可以在 application.properties
设置此属性(这样做仍然初始化重启类加载器,但它不注意文件的更改)。 如果需要完全禁用重新启动支持(例如,因为它不能使用特定的库),您需要在调用 SpringApplication.run(…)
之前将 spring.devtools.restart.enabled
System
属性设置为false,如下面的示例所示:
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApp.class, args);
}
如果我们使用的IDE不断地编译已更改的文件,但是我们可能更希望只在特定的时间触发重新启动。为此,我们可以使用“trigger file”,这是一个特殊的文件,当我们希望实际触发重新启动检查时,必须对其进行修改。修改文件只会触发检查,只有在Devtools检测到它必须做一些事情时才会重新启动。触发器文件可以手动更新或使用IDE插件更新。
要使用触发器文件,请将 spring.devtools.restart.trigger-file
属性设置为触发器文件的路径。
如前所述,在Restart vs Reload部分中,使用两个classloaders实现了Restart功能。对于大多数应用程序来说,这种方法运行良好。但是,它有时会导致类加载问题。 默认情况下,IDE中的任何打开的项目都装载“重启”类加载器,任何常规的.jar文件都装载“基”类加载器。如果您正在处理一个多模块项目,并且不是每个模块都被导入到您的IDE中,那么您可能需要自定义一些东西。为此,您可以创建一个 META-INF/spring-devtools.properties
文件。
spring-devtools.properties
文件可以包含以 restart.exclude
和 restart.include
为前缀的属性。include元素是应该被拉到“重启”类加载器中的项,而exclude元素则是应该被推入“基”类加载器的项。属性的值是应用于类路径的regex模式,如下面的示例所示:
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
所有属性键必须是唯一的。它被认为是只要一个属性从 restart.include
或 restart.exclude
开始。
所有META-INF/spring-devtools.properties加载自类路径。我们可以在项目内部或项目所使用的库中打包文件。
通过使用标准的 ObjectInputStream
反序列化的对象,重新启动功能不能很好地工作。如果需要反序列化数据,可能需要结合使用Spring的 ConfigurableObjectInputStream
和 Thread.currentThread().getcontextclassloader()
。 不幸的是,一些第三方库在不考虑上下文类加载器的情况下反序列化。如果发现这样的问题,我们需要向原始作者请求修复。
spring-boot-devtools模块包含一个嵌入式LiveReload服务器,当资源发生更改时,该服务器可用于触发浏览器刷新。LiveReload可以从livereload.com免费获得Chrome、Firefox和Safari浏览器扩展。
如果不希望在应用程序运行时启动LiveReload服务器,可以将 spring.devtools.livereload.enabled
属性设置为false。
一次只能运行一个LiveReload服务器。在启动应用程序之前,请确保没有其他LiveReload服务器正在运行。如果我们从IDE启动多个应用程序,只有第一个具有LiveReload支持。
您可以通过添加名为.spring-boot-devtools.properties的文件来配置全局devtools设置到$HOME文件夹(注意文件名以“.”开头)。任何添加到这个文件的属性都适用于使用devtools的计算机上的所有Spring Boot应用程序。例如,要配置restart始终使用触发器文件,需要添加以下属性:
~/.spring-boot-devtools.properties.
spring.devtools.reload.trigger-file=.reloadtrigger
Spring Boot developer工具不仅限于本地开发。在远程运行应用程序时,还可以使用几个特性。远程支持是可选的。要启用它,需要确保 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
在远程应用程序上启用spring-boot-devtools存在安全风险。我们不应该在生产部署中启用支持。
远程devtools支持分为两部分:接受连接的服务器端端点和在IDE中运行的客户端应用程序。在设置好 spring.devtools.remote.secret
属性后,服务器组件将自动启用。必须手动启动客户端组件。
远程客户端应用程序设计为在IDE中运行。我们需要运行 org.springframe.boot.devtools.Remotespringapplication
,其类路径与连接到的远程项目相同。应用程序的唯一必需参数是它连接的远程URL。
例如,如果我们正在使用Eclipse或STS,并且我们已经将一个名为 my-app
的项目部署到Cloud Foundry,那么您将执行以下操作:
org.springframe.boot.devtools.remotespringapplication
作为主类。https://myapp.cfapps.io
到程序参数(或任何远程URL)。正在运行的远程客户端可能类似以下清单:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: 2.0.3.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.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)
因为远程客户端使用与实际应用程序相同的类路径,所以可以直接读取应用程序属性。这就是 spring.devtools.remote.secret
属性的读取方式,并将其传递给服务器进行身份验证。
通常建议使用https://作为连接协议,这样就可以加密通信,无法截获密码。
如果需要使用代理访问远程应用程序,请配置spring.devtools.remote.proxy.host和spring.devtools.remote.proxy.port属性。
远程客户端以与本地重启相同的方式监视应用程序类路径的更改。将任何更新的资源推到远程应用程序,并(如果需要)触发重新启动。如果您对使用本地没有的云服务的特性进行迭代,这将非常有用。通常,远程更新和重新启动要比完整的重新构建和部署周期快得多。
只有在远程客户端运行时才对文件进行监视。如果在启动远程客户端之前更改文件,则不会将其推到远程服务器。