15. 配置类

Spring Boot 支持基于 Java 的注解。尽管可以通过 XML 源调使用 SpringApplication,但我们通常建议你主要的源是一个 @Configuration 类。通常定义 main 方法的类也是 @Configuration 最好的候选者。

[Tip] Tip

网上已经发布了许多使用 XML 配置来进行 Spring 配置的例子。但要尽可能的尝试使用等价的 Java 注解。搜索 Enable* 注解是一个好的开端。

15.1 导入附加的配置类

你不必将所有的 @Configuration 放到一个单独的类中。可以使用 @Import 注解来导入附加的配置类。或者,你可以使用 @ComponentScan 来自动获得所有的 Spring 组件,包括 @Configuration 类。

15.2 导入 XML 配置

如果你绝对必须使用基于 XML 的配置,我们推荐你仍然从 @Configuration 类开始。然后你可以使用 @ImportResource 注解来加载 XML 配置文件。

16. 自动配置

Spring Boot 自动配置会基于你添加的 jar 依赖试图自动配置你的 Spring 应用。例如,如果 HSQLDB 在你的类路径中,并且你没有手动的配置任何数据库连接 bean,我们将会自动配置一个在内存中的数据库。

你需要通过选择添加一个 @EnableAutoConfiguration 或 @SpringBootApplication 注解到你的 @Configuration 类中来加入自动配置。

[Tip] Tip

你只需要添加一个 @SpringBootApplication 或 @EnableAutoConfiguration 注解。我们通常建议你将它添加到你主要的 @Configuration 类中。

16.1 逐步更换自动配置

自动配置是非侵入式的,在任何情况下,你都可以开始定义自己的配置来替换自动配置的特定部分。例如,如果你添加自己的 DataSource bean,默认的嵌入式数据库的支持将会退出。

如果你需要找出当前正在应用的自动配置和为什么,你可以使用 --debug 开关来启动你的应用。这将会使核心日志的输出级别变为 debug 级别并输出一个自动配置报告到控制台。

16.2 停止特定的自动配置类

如果你发现正在应用特定的你不想使用的自动配置类,你可以使用 @EnableAutoConfiguration 注解的 exclude 属性来禁用它们,如下面的示例所示:

import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}

如果这个类不在类路径中,你可以使用这个注解的 excludeName 属性并指定全限定名来代替。最后,你也可以通过 spring.autoconfigure.exclude 属性来排除,从而控制自动配置类的列表。

[Tip] Tip

你也可以在注解级别或使用属性来定义排除项。

17. Spring Bean 和依赖注入

你可以自由使用任何标准的 Spring Framework 技术来定义你的 bean 及其注入的依赖项。为了简单起见,我们常发现使用 @ComponentScan(来发现你的 bean) 和 @Autowired (构造函数注入)工作的很好。

如果你根据上述建议组织你的代码(将你的应用类放在根包中),你可以添加 @ComponentScan 注解而不需要任何参数。你所有的应用组件(@Component、@Service、@Repository、@Controller 等等)将会作为 Spring bean 进行自动注册。

下面的示例展示了一个 @Service Bean,它通过使用构造函数注入来获得 RiskAssessor bean:

package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DatabaseAccountService implements AccountService {

	private final RiskAssessor riskAssessor;

	@Autowired
	public DatabaseAccountService(RiskAssessor riskAssessor) {
		this.riskAssessor = riskAssessor;
	}

	// ...

}

如果一个 bean 有一个构造函数,可以省略 @Autowired,如下面示例所示:

@Service
public class DatabaseAccountService implements AccountService {

	private final RiskAssessor riskAssessor;

	public DatabaseAccountService(RiskAssessor riskAssessor) {
		this.riskAssessor = riskAssessor;
	}

	// ...

}
[Tip] Tip

注意使用构造函数注入允许 riskAssessor 字段标记为 final,意味着它接下来不能被修改。

18. 使用 @SpringBootApplication 注解

许多 Spring Boot 开发者喜欢在他们的应用上使用自动配置、组件扫描,并且能够在他们的 "application class" 上定义附加的配置。一个 @SpringBootApplication 注解可以用来启用这三个特性,即:

  • @EnableAutoConfiguration: 启用 Spring Boot 的自动配置机制
  • @ComponentScan: 在应用程序所在的包上启用 @Component 扫描 (参见 最佳实践)
  • @Configuration: 允许在上下文中注册额外的 bean 或导入附加的配置类

@SpringBootApplication 注解等价于使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 以及它们的默认属性,如下面的示例所示:

package com.example.myapplication;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

}
[Note] Note

@SpringBootApplication 也提供了别名来定制 @EnableAutoConfiguration 和 @ComponentScan 的特性。

[Note] Note

这些功能都不是强制性的,你可以通过一些可启用的特性来选择替换单个注解。例如,你可能不希望在应用程序中使用组件扫描:

package com.example.myapplication;

import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@EnableAutoConfiguration
@Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {

	public static void main(String[] args) {
			SpringApplication.run(Application.class, args);
	}

}

在这个例子中,Application 就像一些其它 Spring Boot 应用程序一样,除了 @Component 注解类不会自动发觉,用户指定的 bean 都会明确的导入 (参见 @Import)。

19. 运行应用

将应用打包成 jar 并使用嵌入式 HTTP 服务器的一个最大优势是你可以在任何地方运行你的应用。调试 Spring Boot 应用也很容易。你不必指定任何特定的 IDE 插件或扩展。

[Note] Note

这一节只包含基于 jar 的打包,如果你想选择将你的应用打包成 war 文件,你应该参考你的服务器和 IDE 文档。

19.1 从 IDE 中运行

你可以在 IDE 中像运行一个简单的 Java 应用一样运行一个 Spring Boot 应用。然而,首先你需要导入工程。导入步骤根据你的 IDE 和构建系统会有所变化。大多数 IDE 可以直接导入 Maven 工程,例如 Eclipse 用户可以选择从 File 菜单选择 Import…​ → Existing Maven Projects。

如果你不能直接将工程导入你的 IDE 中,你可以使用构建插件生成一个 IDE 元数据。Maven 中包含 Eclipse 和 IDEA 的插件;Gradle 有各种 IDE 的插件。

[Tip] Tip

如果你不小心的运行一个 web 应用两次,你会看到一个 “Port already in use” 错误。STS 用户可以使用 Relaunch 按钮而不是 Run 按钮以确保任何现有的实例被关闭。

19.2 运行打成包的应用

如果你使用 Spring Boot 的 Maven 或 Gradle 插件来创建一个可执行的 jar 包,你可以通过使用 java -jar 来运行你的应用。如下面的示例所示:

$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar

它也支持以远程调试模式运行一个打包的应用。这允许你在打包的应用中添加一个调试器,如下面的示例所示:

$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \
       -jar target/myapplication-0.0.1-SNAPSHOT.jar

19.3 使用 Maven 插件

Spring Boot Maven 插件包含一个 run 项,可以用来快速编译和运行你的应用。应用就像在 IDE 中一样快速运行。下面的示例展示了一个典型的 Maven 命令运行一个 Spring Boot 应用:

$ mvn spring-boot:run

你可能还想使用 MAVEN_OPTS 操作系统环境变量,如下面的示例所示:

$ export MAVEN_OPTS=-Xmx1024m

19.4 使用 Gradle 插件

Spring Boot Gradle 插件也包含一个 bootRun 任务,它可以用来以解压形式运行你的应用。当你应用 org.springframework.boot 和 java 插件时会添加 bootRun 任务,如下面的示例所示:

$ gradle bootRun

你可能还想使用 JAVA_OPTS 操作系统环境变量,如下面的示例所示:

$ export JAVA_OPTS=-Xmx1024m

19.5 热更新

由于 Spring Boot 应用只是普通的 Java 应用,JVM 热更新应该是开箱即用的。JVM 热更新可以替换的字节码有限制,一个更完整的解决方案是使用 JRebel。

spring-boot-devtools 模块也支持快速的应用重启。更多详细信息请查看下面的 章节 20, 开发者工具 和 热更新 “How-to”。

20. 开发者工具

Spring Boot 包含额外的工具集合,可以使应用开发的过程更方便一点。spring-boot-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")
}

[Note] Note

开发工具在运行一个完整打包的应用时会自动禁用。如果你的应用是通过 java -jar 启动的,或者是通过一个特定的类加载器启动的,那它会被当做一个“产品级应用”。在 Maven 中将依赖标记为 optional 或在 Gradle 使用 compileOnly 是阻止开发工具间接应用到使用你项目的其它模块的最佳实践。

[Tip] Tip

再打包文件默认不包含开发者工具。如果你想使用某些远程开发者工具特性,你需要让 excludeDevtools 构建属性包含它。Maven 和 Gradle 插件都支持这个属性。

20.1 默认属性

Spring Boot 支持的几个库使用缓存来提高性能。例如,模板引擎将缓存编译后的模板,以避免重复解析模板文件。另外,在服务静态资源时,Spring MVC 可以将 HTTP 缓存头添加到响应中。

虽然缓存在生产中非常有用,但它在开发过程中可能适得其反,阻止你看到刚才在应用中所做的更改。为此,spring-boot-devtools 将默认禁用这些缓存选项。

缓存选项通常通过你的 application.properties 文件中的设置来配置。例如,Thymeleaf 提供 spring.thymeleaf.cache 属性。spring-boot-devtools 模块会自动配置开发时配置,而不需要手动设置这些属性。

因为你在开发 Spring MVC 和 Spring WebFlux 应用时需要更多关于 web 请求的信息,所以开发者工具将为 Spring Framework web 基础设施启用 DEBUG 日志记录。这将为你提供关于传入请求、哪个处理程序正在处理它、响应结果等的信息。如果希望记录所有请求细节(包括潜在的敏感信息),则可以打开 spring.insights.web.log-request-details 配置属性。

[Note] Note

如果不希望应用属性的默认值,可以在 application.properties 中将 spring.devtools.add-properties 设置为 false。

[Tip] Tip

一个完整的属性的列表请查看 DevToolsPropertyDefaultsPostProcessor.

20.2 自动重启

当类路径中的文件修改时,使用 spring-boot-devtools 的应用会自动重启。当使用 IDE 开发时,这是一个很有用的功能,因为代码改变时它能快速的进行反馈。默认情况下,会监控类路径指向的文件夹中任何条目的变化。注意某些资源例如静态资源和视图模板不需要重启应用。

触发重启

作为 DevTools 监视器类路径中的资源,触发重启的唯一方式是更新类路径。引起类路径更新的方式取决于你使用的 IDE 。在 Eclipse 中,保存一个修改的文件将引起类路径更新并触发重启事件。在 IntelliJ IDEA 中,构建工程 (Build -> Make Project) 将会有同样的效果。

[Note] Note

只要启用了分支,你也可以通过支持的构建系统启动你的应用(例如,Maven 和 Gradle),因为 DevTools 需要一个独立的应用类加载器来进行正确操作。当 Gradle 和 Maven 在类路径中检测到 DevTools 时,它们默认的进行了那个工作。

[Tip] Tip

当使用实时重载时,自动重启能很好的工作。更多详细信息请查看实时重载部分。如果你使用 JRebel 进行自动重启,将不支持动态的类重加载。其它的开发者工具功能(例如 实时重载和属性覆写)仍能继续使用。

[Note] Note

DevTools 依赖应用上下文关闭钩子来进行重启期间的关闭。如果你禁用了关闭钩子它将不能正确工作(SpringApplication.setRegisterShutdownHook(false))。

[Note] Note

当决定类路径中输入引起的改变是否应该触发重启时,DevTools 会自动忽略命名为 spring-boot、spring-boot-devtools、spring-boot-autoconfigure、spring-boot-actuator 和 spring-boot-starter 的工程。

[Note] Note

DevTools 需要使用 ApplicationContext 定制 ResourceLoader。如果你的应用程序提供了一个,它将会覆盖。在 ApplicationContext 中的 getResource 方法是不支持直接覆盖的。

重启 vs 重载

Spring Boot 提供的重启功能是通过两个类加载器实现的。加载进基类加载器的类不能改变(例如,那些第三方 jar 包)。那些你正在开发的类加载进重启类加载器中。当应用重启时,丢弃旧的重启类加载器并创建一个新的。这种方法意味着应用重启时比冷启动更快,因为基类加载器已经存在并可用。

如果你觉得应用重启的还不够快,或者碰到了类加载问题,你可以考虑重载技术例如 ZeroTurnaround 的 JRebel。这些工作通过加载时类的重写使它们更适合重载。

20.2.1 更改状态评估中的日志

默认情况下,每次应用程序重新启动时,都会展示一个显示状态评估变化的报告。报告显示了在进行更改时应用程序自动配置进行的更改,如添加或删除 bean 和设置配置属性。

若要禁用报告的日志记录,请设置以下属性:

spring.devtools.restart.log-condition-evaluation-delta=false

20.2.2 不包含的资源

某些资源不一定需要在更改时触发重新启动。例如,Thymeleaf 模板可以就地编辑。默认情况下,/META-INF/maven、/META-INF/resources、/resources、/static、/public 或 /templates 中资源的改变不会触发重启,但会触发实时重载。如果你想定制这些排除项,你可以使用 spring.devtools.restart.exclude 属性。例如,仅排除 /static 和 /public,设置如下:

spring.devtools.restart.exclude=static/**,public/**
[Tip] Tip

如果你想保持这些默认,并添加额外的排除项,可以使用 spring.devtools.restart.additional-exclude 属性。

20.2.3 监听额外路径

当修改不在类路径中的文件时,你可能也想重启或重载你的应用。为了这样做,可以使用 spring.devtools.restart.additional-paths 属性来监控其它路径上的变化。你可以使用前面描述的 spring.devtools.restart.exclude 属性来控制其它路径上的变化是否会触发重启或仅触发实时重载。

20.2.4 禁用重启

如果你不想使用重启功能,你可以通过 spring.devtools.restart.enabled 属性禁用它。大多数情况下你可以在 application.properties 中设置它(这仍会初始化重启类加载器但它不会监控文件的变化)。

如果你需要完全禁用重启支持,例如,因为它不能与一个特定的库一起工作,你需要在调用 SpringApplication.run(…​) 之前设置一个 System 属性为 false。如下面的示例所示:

public static void main(String[] args) {
	System.setProperty("spring.devtools.restart.enabled", "false");
	SpringApplication.run(MyApp.class, args);
}

20.2.5 使用触发文件

如果你使用一个不断编译已更改文件的 IDE,你可能只希望在特定的时间触发重启。为此,你可以使用一个触发文件,这是一个特殊的文件,在实际触发重新启动检查时进行修改。修改这个文件只触发重启检查,只有 Devtools 检测到它必须要做些事情时,重启才会发生。触发文件应该进行手动更新,或通过 IDE 插件更新。

为了使用触发文件,设置 spring.devtools.restart.trigger-file 属性为触发文件的路径。

[Tip] Tip

为了使你所有的工程都表现一样,你需要将 spring.devtools.restart.trigger-file 设置为全局设置。

20.2.6 自定义重启类加载

像前面的的重启与重载比较部分所讲的那样,重启功能是通过两个类加载器实现的。对于大多数应用来说这个方法能很好的工作,但是有时候它也会引起一些类加载问题。

默认情况下,IDE 中的任何开放的工程都会使用重启类加载器进行加载,任何规范的 .jar 文件都会使用基类类加载器进行加载。如果你在一个多模块工程中工作,不是每一个模块都导入到你的 IDE 中,你可能需要定制一些东西。为了实现这个功能你可以创建一个 META-INF/spring-devtools.properties 文件。

spring-devtools.properties 文件可以包含 restart.exclude. 和 restart.include. 前缀属性。include 元素应该拉进重启类加载器中,exclude 元素应该推入到基类加载器中。该属性值是应用到类路径中的一个正则表达式,如下面的示例所示:

restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
[Note] Note

所有属性的键必须是唯一的。只要 restart.include. 或 restart.exclude. 作为一个属性启动,它就被应用。

[Tip] Tip

类路径中的所有 META-INF/spring-devtools.properties 都会被加载。你可以将文件打包进你的工程中,或这个工程使用的库中。

20.2.7 已知限制

当存在使用标准 ObjectInputStream 进行反序列化的对象时,重启功能不能很好的工作。如果你需要反序列化数据,你可能需要结合使用 Spring 的 ConfigurableObjectInputStream 和 Thread.currentThread().getContextClassLoader()。

遗憾的是,一些第三方库反序列化时没有考虑上下文类加载器。如果你发现这样的问题,你需要请求原作者进行修正。

20.3 实时重载

spring-boot-devtools 模块包含一个嵌入式实时重载服务器,当资源改变时可以用来触发浏览器重新刷新。实时重载浏览器扩展对于 livereload.com 中的 Chrome、Firefox 和 Safari 而言是可用的。

如果当你的应用运行时不想启动实时重载服务器,可以将 spring.devtools.livereload.enabled 属性设为 false。

[Note] Note

你一次只能运行一个实时重载服务器。在启动你的应用之前,确保没有其它的实时重载服务器在运行。如果你从 IDE 中启动多个应用,只有第一个应用有实时重载支持。

20.4 全局设置

通过添加一个名为 .spring-boot-devtools.properties 的文件到你的 $HOME 文件夹中(注意文件名以 “.” 开头),你可以配置全局开发者工具设置。任何添加到这个文件的属性都将应用到你机器上所有使用开发者工具的 Spring Boot 应用中。例如,为了配置重启时总是使用一个触发文件,你需要添加如下内容:

~/.spring-boot-devtools.properties. 

spring.devtools.reload.trigger-file=.reloadtrigger

20.5 远程应用程序

Spring Boot 开发者工具是不受本地环境限制的,在运行远程应用时你也可以使用一些功能。远程支持是选择性加入的,为了使它起作用你需要确保 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
[Warning] Warning

使 spring-boot-devtools 在远程应用上起作用是有安全风险的。你不应该在生产部署上启用支持。

远程 devtools 需要两部分提供支持:一个接收连接的服务器端,一个运行在 IDE 中的客户端应用。当设置 spring.devtools.remote.secret 属性时,服务器组件会自动起作用。客户端组件必须手动启动。

20.5.1 运行远程连接应用程序

远程客户端应用设计为从 IDE 中运行。你需要运行 org.springframework.boot.devtools.RemoteSpringApplication,与要连接的远程工程要使用同样的类路径。传给应用的非可选参数应该是你要连接的远程 URL。

例如,如果你在使用 Eclipse 或 STS,你有一个名为 my-app 的工程,它已经部署到了云计算中,你需要按照下面的步骤去做:

  • 从 Run 菜单中选择 Run Configurations…​。
  • 创建一个新的 Java Application “launch configuration”。
  • 浏览 my-app 工程。
  • 使用 org.springframework.boot.devtools.RemoteSpringApplication 做为主类。
  • 添加 https://myapp.cfapps.io 到 Program arguments (或无论你的远程 URL 是什么).

一个运行的远程客户端如下:

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

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)
[Note] Note

因为远程客户端与真实的应用使用的类路径相同,因此它可以直接读取应用属性。这就是 spring.devtools.remote.secret 属性是怎样读取并传递给服务器进行授权的。

[Tip] Tip

为了传输可以加密,密码不能被解析,建议总是使用 https:// 作为连接协议。

[Tip] Tip

如果你需要使用代理访问远程应用,配置 spring.devtools.remote.proxy.host 和 spring.devtools.remote.proxy.port 属性。

20.5.2 远程更新

远程客户端会像本地重启那样监控你应用的类路径的变化。任何资源的更新都会推送到远程应用并触发重启(如果需要的话)。如果你在迭代一个没有本地的使用云服务的功能,它是非常有帮助的。通常更新和重启比整个重新构建部署更快。

[Note] Note

只有在远程客户端运行时才监视文件。如果在启动远程客户端之前更改了文件,则不会将其推送到远程服务器。

21. 打包生产环境运行包

可执行 jar 可以用来进行生产部署。当它们是自包含时,理想情况下也是适合云部署的。

对于其它的生产就绪功能,例如健康、审计和监控 REST 或 JMX 端点,考虑添加 spring-boot-actuator。更多详细信息请参阅 章节 V, “Spring Boot 执行器: 生产就绪特性”。

22. 后续内容

现在你应该对怎么使用 Spring Boot 以及应该循序的一些最佳实践有了很好的理解。你可以继续深入学习具体的 Spring Boot 特性,或者你可以跳过这些,直接阅读 Spring Boot “生产就绪” 方面的内容。