# 核心功能
这一节深入讨论了 Spring 引导的细节。在这里,你可以了解你可能想要使用和自定义的关键功能。如果你还没有这样做,你可能希望阅读“[getting-started.html](getting-started.html#getting-started)”和“[using.html](using.html#using)”部分,以便你有一个良好的基础知识。
## 1. SpringApplication
`SpringApplication`类提供了一种方便的方式来引导从`main()`方法启动的 Spring 应用程序。在许多情况下,可以委托给静态`SpringApplication.run`方法,如以下示例所示:
```
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
```
当应用程序启动时,你应该会看到类似于以下输出的内容:
```
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: v2.6.4
2021-02-03 10:33:25.224 INFO 17321 --- [ main] o.s.b.d.s.s.SpringApplicationExample : Starting SpringApplicationExample using Java 1.8.0_232 on mycomputer with PID 17321 (/apps/myjar.jar started by pwebb)
2021-02-03 10:33:25.226 INFO 17900 --- [ main] o.s.b.d.s.s.SpringApplicationExample : No active profile set, falling back to default profiles: default
2021-02-03 10:33:26.046 INFO 17321 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-02-03 10:33:26.054 INFO 17900 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-02-03 10:33:26.055 INFO 17900 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-02-03 10:33:26.097 INFO 17900 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-02-03 10:33:26.097 INFO 17900 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 821 ms
2021-02-03 10:33:26.144 INFO 17900 --- [ main] s.tomcat.SampleTomcatApplication : ServletContext initialized
2021-02-03 10:33:26.376 INFO 17900 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-02-03 10:33:26.384 INFO 17900 --- [ main] o.s.b.d.s.s.SpringApplicationExample : Started SampleTomcatApplication in 1.514 seconds (JVM running for 1.823)
```
默认情况下,将显示`INFO`日志消息,包括一些相关的启动细节,例如启动应用程序的用户。如果需要`INFO`以外的日志级别,则可以设置它,如[Log Levels](#features.logging.log-levels)中所述。应用程序版本是使用主应用程序类的包中的实现版本来确定的。可以通过将`spring.main.log-startup-info`设置为`false`来关闭启动信息日志。这也将关闭应用程序活动配置文件的日志记录。
| |要在启动期间添加额外的日志记录,可以在`SpringApplication`的子类中覆盖`logStartupInfo(boolean)`。|
|---|--------------------------------------------------------------------------------------------------------------------------|
### 1.1.启动失败
如果你的应用程序无法启动,注册`FailureAnalyzers`将有机会提供一个专用的错误消息和具体的操作来解决问题。例如,如果你在端口`8080`上启动了一个 Web 应用程序,并且该端口已经在使用中,那么你应该会看到类似于以下消息的内容:
```
***************************
APPLICATION FAILED TO START
***************************
Description:
Embedded servlet container failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.
```
| |Spring Boot 提供了许多`FailureAnalyzer`实现,并且可以[添加你自己的](howto.html#howto.application.failure-analyzer)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------|
如果没有故障分析器能够处理异常,则仍然可以显示完整的条件报告,以更好地了解出了什么问题。为此,你需要[启用`debug`属性]或[启用`DEBUG`日志](#features.logging.log-levels)来实现`org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener`。
例如,如果你正在使用`java -jar`运行你的应用程序,那么你可以启用`debug`属性,如下所示:
```
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
```
### 1.2.惰性初始化
`SpringApplication`允许应用程序进行惰性初始化。当启用惰性初始化时,将根据需要而不是在应用程序启动期间创建 bean。因此,启用延迟初始化可以减少启动应用程序所需的时间。在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 bean 在收到 HTTP 请求之前不会被初始化。
延迟初始化的一个缺点是,它可能会延迟应用程序问题的发现。如果延迟初始化配置错误的 Bean,则在启动过程中将不再发生故障,并且只有在初始化 Bean 时,问题才会变得明显。还必须注意确保 JVM 有足够的内存来容纳所有应用程序的 bean,而不仅仅是那些在启动期间初始化的 bean。由于这些原因,默认情况下不启用惰性初始化,建议在启用惰性初始化之前对 JVM 的堆大小进行微调。
可以使用`SpringApplicationBuilder`上的`lazyInitialization`方法或`setLazyInitialization`上的`setLazyInitialization`方法以编程方式启用惰性初始化。或者,可以使用`spring.main.lazy-initialization`属性启用它,如以下示例所示:
属性
```
spring.main.lazy-initialization=true
```
Yaml
```
spring:
main:
lazy-initialization: true
```
| |如果你希望在对应用程序的其余部分使用惰性初始化的同时禁用某些 bean 的惰性初始化,那么可以使用`@Lazy(false)`注释显式地将它们的惰性属性设置为 false。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 1.3.自定义横幅
可以通过将`banner.txt`文件添加到 Classpath 或将`spring.banner.location`属性设置为此类文件的位置来更改在启动时打印的横幅。如果文件的编码不是 UTF-8,则可以设置`spring.banner.charset`。除了文本文件,还可以在 Classpath 中添加`banner.gif`、`banner.jpg`或`banner.png`图像文件,或者设置`spring.banner.image.location`属性。图像被转换为 ASCII 艺术表现,并打印在任何文本横幅之上。
在`banner.txt`文件中,你可以使用`Environment`中可用的任何键以及以下任何占位符:
| Variable |说明|
|--------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `${application.version}` |应用程序的版本号,如`MANIFEST.MF`中声明的。
例如,`Implementation-Version: 1.0`打印为`1.0`。|
| `${application.formatted-version}` |应用程序的版本号,如在`MANIFEST.MF`中声明的那样,并已格式化以供显示(周围用括号包围,前缀为`v`)。
例如`(v1.0)`。|
| `${spring-boot.version}` |你正在使用的 Spring 引导版本。
例如`2.6.4`。|
| `${spring-boot.formatted-version}` |你正在使用的 Spring 引导版本,格式为显示(周围用括号和前缀`v`)。
例如`(v2.6.4)`。|
|`${Ansi.NAME}` (or `${AnsiColor.NAME}`, `${AnsiBackground.NAME}`, `${AnsiStyle.NAME}`)|其中`NAME`是一个 ANSI 逃逸代码的名称。
详见[`AnsiPropertySource`](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot/SRC/main/java/org/springframework/boot/ansi/ansipropertysource.java)。|
| `${application.title}` |在`MANIFEST.MF`中声明的应用程序的标题。
例如`Implementation-Title: MyApp`打印为`MyApp`。|
| |如果你想以编程方式生成横幅,可以使用`SpringApplication.setBanner(…)`方法。
使用`org.springframework.boot.Banner`接口并实现你自己的`printBanner()`方法。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
你还可以使用`spring.main.banner-mode`属性来确定横幅是否必须在`System.out`(`console`)上打印、发送到配置的记录器(`log`),或者根本不产生(`off`)。
打印的横幅以单件 Bean 的形式注册,其名称如下:`springBootBanner`。
| |只有当你使用 Spring 引导启动器时,`${application.version}`和`${application.formatted-version}`属性才可用。
这些值将不会被解析如果你正在运行一个 Unpacked jar 并以`java -cp `启动它。
这就是为什么我们建议你总是使用`java org.springframework.boot.loader.JarLauncher`启动 Unpacked JAR。
这将在构建 Classpath 并启动应用程序之前初始化`application.*`横幅变量。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 1.4.自定义 SpringApplication
如果`SpringApplication`默认值不符合你的喜好,那么你可以创建一个本地实例并对其进行自定义。例如,要关闭横幅,你可以写:
```
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
```
| |传递给`SpringApplication`的构造函数参数是 Spring bean 的配置源。
在大多数情况下,这些参数是对`@Configuration`类的引用,但它们也可以是直接引用`@Component`类。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
也可以通过使用`application.properties`文件配置`SpringApplication`。详见 *[外部化配置](#features.external-config)*。
有关配置选项的完整列表,请参见[`SpringApplication`Javadoc](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/springapplication.html)。
### 1.5.Fluent Builder API
如果你需要构建`ApplicationContext`层次结构(具有父/子关系的多个上下文),或者如果你更喜欢使用“Fluent”Builder API,则可以使用`SpringApplicationBuilder`。
`SpringApplicationBuilder`允许你将多个方法调用链接在一起,并且包括`parent`和`child`方法,这些方法允许你创建一个层次结构,如以下示例所示:
```
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
```
| |在创建`ApplicationContext`层次结构时会有一些限制。
例如,Web 组件**必须**被包含在子上下文中,并且相同的`Environment`同时用于父上下文和子上下文。
参见[`SpringApplicationBuilder`Javadoc](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/org/applicationframework/applicbuilder/applicbuilder/applicbuilder/applatform/applicform/applicbuilder)以获取全部详细信息。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 1.6.应用程序可用性
当部署在平台上时,应用程序可以使用[Kubernetes 探测器](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)之类的基础设施向平台提供有关其可用性的信息。 Spring 启动包括对常用的“活性”和“就绪”可用性状态的开箱即用支持。如果你使用 Spring Boot 的“actuator”支持,那么这些状态将作为健康端点组公开。
此外,你还可以通过将`ApplicationAvailability`接口注入你自己的 bean 来获得可用性状态。
#### 1.6.1.活性状态
应用程序的“活性”状态表示其内部状态是否允许其正确工作,或者在当前出现故障时是否允许其自行恢复。中断的“活性”状态意味着应用程序处于无法恢复的状态,基础结构应该重新启动应用程序。
| |通常,“活性”状态不应该基于外部检查,例如[健康检查](actuator.html#actuator.endpoints.health)。
如果是这样,失败的外部系统(数据库、Web API、外部缓存)将触发大规模重启和跨平台的级联故障。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
Spring 引导应用程序的内部状态主要由 Spring `ApplicationContext`表示。 Spring 如果应用程序上下文已成功启动,则启动假定应用程序处于有效状态。一旦上下文被刷新,应用程序就被认为是实时的,请参见[Spring Boot application lifecycle and related Application Events](#features.spring-application.application-events-and-listeners)。
#### 1.6.2.准备状态
应用程序的“就绪”状态表明该应用程序是否已准备好处理流量。一个失败的“就绪”状态告诉平台,它现在不应该将流量路由到应用程序。这通常发生在启动过程中,而`CommandLineRunner`和`ApplicationRunner`组件正在被处理,或者在任何时候,如果应用程序决定它太忙而不能进行额外的流量。
一旦调用了应用程序和命令行运行器,就认为应用程序已准备就绪,请参见[Spring Boot application lifecycle and related Application Events](#features.spring-application.application-events-and-listeners)。
| |期望在启动期间运行的任务应该由`CommandLineRunner`和`ApplicationRunner`组件执行,而不是使用 Spring 组件生命周期回调,例如`@PostConstruct`。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 1.6.3.管理应用程序可用性状态
通过注入`ApplicationAvailability`接口并在其上调用方法,应用程序组件可以随时检索当前的可用性状态。更常见的情况是,应用程序希望侦听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的“就绪”状态导出到一个文件中,以便 Kubernetes 的“Exec Probe”可以查看该文件:
```
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyReadinessStateExporter {
@EventListener
public void onStateChange(AvailabilityChangeEvent event) {
switch (event.getState()) {
case ACCEPTING_TRAFFIC:
// create file /tmp/healthy
break;
case REFUSING_TRAFFIC:
// remove file /tmp/healthy
break;
}
}
}
```
当应用程序中断且无法恢复时,我们还可以更新应用程序的状态:
```
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class MyLocalCacheVerifier {
private final ApplicationEventPublisher eventPublisher;
public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void checkLocalCache() {
try {
// ...
}
catch (CacheCompletelyBrokenException ex) {
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
}
}
}
```
Spring Boot 提供[Kubernetes HTTP 探测与执行器健康端点的“活性”和“准备”](actuator.html#actuator.endpoints.kubernetes-probes)。你可以获得更多关于[deploying Spring Boot applications on Kubernetes in the dedicated section](deployment.html#deployment.cloud.kubernetes)的指导。
### 1.7.应用程序事件和监听器
除了通常的 Spring 框架事件,如[`ContextRefreshedEvent`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/context/event/contextrefreshedevent.html),a`SpringApplication`发送一些额外的应用程序事件。
| |有些事件实际上是在`ApplicationContext`被创建之前被触发的,所以你不能在这些事件上注册一个侦听器作为`@Bean`。
你可以用`SpringApplication.addListeners(…)`方法或`SpringApplicationBuilder.listeners(…)`方法来注册它们,
如果你想要自动注册这些侦听器,无论创建应用程序的方式如何,你都可以通过使用`org.springframework.context.ApplicationListener`键向项目添加`META-INF/spring.factories`文件并引用侦听器,如以下示例所示:
```
org.springframework.context.ApplicationListener=com.example.project.MyListener
```|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
在应用程序运行时,应用程序事件按以下顺序发送:
1. 一个`ApplicationStartingEvent`在运行开始时但在任何处理之前发送,除了注册侦听器和初始化器。
2. 当要在上下文中使用的`Environment`是已知的但在创建上下文之前,则发送`ApplicationEnvironmentPreparedEvent`。
3. 当`ApplicationContext`准备好并且在加载任何 Bean 定义之前调用了应用上下文初始化器时,发送`ApplicationContextInitializedEvent`。
4. 一个`ApplicationPreparedEvent`在刷新开始之前但是在 Bean 定义加载之后被发送。
5. 在刷新上下文之后,但在调用任何应用程序和命令行运行器之前,将发送`ApplicationStartedEvent`。
6. 在使用`LivenessState.CORRECT`之后立即发送`AvailabilityChangeEvent`,以表明该应用程序被认为是动态的。
7. 在调用任何[应用程序和命令行运行器](#features.spring-application.command-line-runner)后发送`ApplicationReadyEvent`。
8. 在使用`ReadinessState.ACCEPTING_TRAFFIC`之后立即发送`AvailabilityChangeEvent`,以表明应用程序已准备好为请求提供服务。
9. 如果启动时有异常,则发送`ApplicationFailedEvent`。
上面的列表只包含与`SpringApplication`绑定的`SpringApplicationEvent`。除了这些,以下事件还发布在`ApplicationPreparedEvent`之后和`ApplicationStartedEvent`之前:
* 在`WebServer`准备好之后发送`WebServerInitializedEvent`。`ServletWebServerInitializedEvent`和`ReactiveWebServerInitializedEvent`分别是 Servlet 和活性变量。
* 当刷新`ApplicationContext`时,将发送`ContextRefreshedEvent`。
| |你通常不需要使用应用程序事件,但是知道它们的存在是很方便的。
在内部, Spring boot 使用事件来处理各种任务。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |默认情况下,事件侦听器在同一个线程中执行时,不应该运行可能很长的任务。
考虑使用[应用程序和命令行运行器](#features.spring-application.command-line-runner)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
应用程序事件通过使用 Spring Framework 的事件发布机制发送。该机制的一部分确保在子上下文中发布给侦听器的事件也在任何祖先上下文中发布给侦听器。因此,如果应用程序使用`SpringApplication`实例的层次结构,则侦听器可能会接收同一类型的应用程序事件的多个实例。
为了允许侦听器区分其上下文的事件和子代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。上下文可以通过实现`ApplicationContextAware`来注入,或者,如果侦听器是 Bean,则可以通过使用`@Autowired`来注入。
### 1.8.网络环境
a`SpringApplication`试图代表你创建`ApplicationContext`的正确类型。用于确定`WebApplicationType`的算法如下:
* 如果存在 Spring MVC,则使用`AnnotationConfigServletWebServerApplicationContext`
* 如果 Spring MVC 不存在而 Spring WebFlux 存在,则使用`AnnotationConfigReactiveWebServerApplicationContext`
* 否则,使用`AnnotationConfigApplicationContext`
这意味着,如果你在同一应用程序中使用 Spring MVC 和 Spring WebFlux 中的新`WebClient`,则默认情况下将使用 Spring MVC。你可以通过调用`setWebApplicationType(WebApplicationType)`轻松地重写它。
也可以通过调用`setApplicationContextClass(…)`来完全控制`ApplicationContext`类型。
| |在 JUnit 测试中使用`SpringApplication`时,通常需要调用`setWebApplicationType(WebApplicationType.NONE)`。|
|---|----------------------------------------------------------------------------------------------------------------------------------|
### 1.9.访问应用程序参数
如果需要访问传递给`SpringApplication.run(…)`的应用程序参数,则可以插入`org.springframework.boot.ApplicationArguments` Bean。`ApplicationArguments`接口提供对 RAW`String[]`参数以及解析的`option`和`non-option`参数的访问,如以下示例所示:
```
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
```
| |Spring Boot 还使用 Spring `Environment`注册了`CommandLinePropertySource`。
这也允许你通过使用`@Value`注释来注入单个应用程序参数。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 1.10.使用 ApplicationRunner 或 CommandLineRunner
如果在`SpringApplication`启动后需要运行某些特定的代码,则可以实现`ApplicationRunner`或`CommandLineRunner`接口。这两个接口以相同的方式工作,并提供一个`run`方法,该方法在`SpringApplication.run(…)`完成之前被调用。
| |此契约非常适合在应用程序启动后但在它开始接受流量之前运行的任务。|
|---|------------------------------------------------------------------------------------------------------------------------|
`CommandLineRunner`接口以字符串数组的形式提供对应用程序参数的访问,而`ApplicationRunner`接口使用前面讨论的`ApplicationArguments`接口。下面的示例显示了带有`run`方法的`CommandLineRunner`:
```
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}
```
如果定义了几个`CommandLineRunner`或`ApplicationRunner`bean,必须以特定的顺序进行调用,则可以另外实现`org.springframework.core.Ordered`接口或使用`org.springframework.core.annotation.Order`注释。
### 1.11.应用程序退出
每个`SpringApplication`都向 JVM 注册一个关闭钩子,以确保`ApplicationContext`在退出时优雅地关闭。可以使用所有标准的 Spring 生命周期回调(例如`DisposableBean`接口或`@PreDestroy`注释)。
此外,如果希望在调用`SpringApplication.exit()`时返回特定的退出代码,则 bean 可以实现`org.springframework.boot.ExitCodeGenerator`接口。然后可以将此退出代码传递给`System.exit()`,以将其作为状态代码返回,如下例所示:
```
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApplication {
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args)));
}
}
```
此外,`ExitCodeGenerator`接口可以通过异常来实现。当遇到这样的异常时, Spring 引导返回由实现的`getExitCode()`方法提供的退出代码。
### 1.12.管理功能
通过指定`spring.application.admin.enabled`属性,可以为应用程序启用与管理相关的特性。这暴露了平台上的[`SpringApplicationAdminMXBean`](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot/SRC/main/java/org/springframework/boot/admin/applicationadmxbean.java)`MBeanServer`。你可以使用此功能远程管理你的 Spring 启动应用程序。这个特性对于任何服务包装器实现都是有用的。
| |如果你想知道应用程序在哪个 HTTP 端口上运行,请使用`local.server.port`的键获取该属性。|
|---|----------------------------------------------------------------------------------------------------------------------|
### 1.13.应用程序启动跟踪
在应用程序启动期间,`SpringApplication`和`ApplicationContext`执行许多与应用程序生命周期、bean 生命周期甚至处理应用程序事件有关的任务。通过[`ApplicationStartup`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/core/metrics/applicationstartup.html), Spring Framework[允许你使用`StartupStep`对象跟踪应用程序启动序列](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/ception/core-funicity.html#context-function-收集这些数据可以用于分析目的,或者仅仅是为了更好地了解应用程序启动过程。
在设置`SpringApplication`实例时,可以选择`ApplicationStartup`实现。例如,要使用`BufferingApplicationStartup`,你可以写:
```
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
}
}
```
第一个可用的实现方式,`FlightRecorderApplicationStartup`是由 Spring 框架提供的。它将 Spring 特定的启动事件添加到 Java 飞行记录器会话中,用于分析应用程序,并将其 Spring 上下文生命周期与 JVM 事件(例如分配、GCS、类加载……)关联起来。一旦配置完成,你就可以通过启用飞行记录器来运行应用程序来记录数据:
```
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar
```
Spring boot 附带`BufferingApplicationStartup`变体;该实现用于缓冲启动步骤并将它们排入外部度量系统。应用程序可以在任何组件中请求类型`BufferingApplicationStartup`的 Bean。
Spring 启动还可以被配置为公开一个[`startup`端点](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/actuator-api/htmlsingle/#startup),它将此信息作为 JSON 文档提供。
## 2. 外部化配置
Spring 引导允许你外部化你的配置,以便你可以在不同的环境中使用相同的应用程序代码。你可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
可以使用`@Value`注释直接将属性值注入到 bean 中,该注释可通过 Spring 的`Environment`抽象访问,也可以通过[绑定到结构化对象](#features.external-config.typesafe-configuration-properties)通过`@Configuration属性`访问。
Spring 引导使用非常特殊的`PropertySource`顺序,该顺序被设计为允许合理地覆盖值。按以下顺序考虑属性(来自较低项的值覆盖了较早的项):
1. 默认属性(通过设置`SpringApplication.setDefault属性`指定)。
2. [`@PropertySource`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/context/annotation/propertysource.html)你的`@Configuration`类上的注释。请注意,在刷新应用程序上下文之前,不会将此类属性源添加到`Environment`中。现在配置某些属性(如`logging.*`和`spring.main.*`)已经太晚了,这些属性是在开始刷新之前读取的。
3. 配置数据(如`application.properties`文件)。
4. a`RandomValuePropertySource`仅在`random.*`中具有属性。
5. OS 环境变量。
6. Java 系统属性(`System.get属性()`)。
7. 来自`java:comp/env`的 JNDI 属性。
8. `ServletContext`init 参数。
9. `ServletConfig`init 参数。
10. 来自`SPRING_APPLICATION_JSON`的属性(嵌入在环境变量或系统属性中的内联 JSON)。
11. 命令行参数。
12. `properties`测试中的属性。
可在[`@SpringBootTest`](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/test/context/springbootttest.html)和[测试用于测试应用程序的特定部分的注释](#features.testing.spring-boot-applications.autoconfigured-tests)上查阅。
13. [`@TestPropertySource`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/test/context/testpropertysource.html)测试上的注释。
14. 当 devtools 处于活动状态时,[DevTools 全局设置属性](using.html#using.devtools.globalsettings)在`$HOME/.config/spring-boot`目录中。
配置数据文件按以下顺序考虑:
1. [应用程序属性](#features.external-config.files)封装在你的 jar 中(`application.properties`和 YAML 变体)。
2. [特定于配置文件的应用程序属性](#features.external-config.files.profile-specific)封装在你的 jar 中(`application-{profile}.properties`和 YAML 变体)。
3. [应用程序属性](#features.external-config.files)在你打包的 jar 之外(`application.properties`和 YAML 变体)。
4. [特定于配置文件的应用程序属性](#features.external-config.files.profile-specific)在你打包的 jar 之外(`application-{profile}.properties`和 YAML 变体)。
| |对于整个应用程序,建议使用一种格式。如果你的配置文件同时具有`.properties`和`.yml`两种格式,那么`.properties`将优先使用。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
为了提供一个具体的示例,假设你开发了一个`@Component`,它使用了`name`属性,如以下示例所示:
```
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
```
在你的应用程序 Classpath 上(例如,在你的 jar 内部),你可以拥有一个`application.properties`文件,该文件为`name`提供了一个合理的默认属性值。在新环境中运行时,可以在 jar 之外提供一个`application.properties`文件,该文件覆盖`name`。对于一次性测试,可以使用特定的命令行开关启动(例如,`java -jar app.jar --name="Spring"`)。
| |`env`和`configprops`端点可以用于确定属性为什么具有特定值。
你可以使用这两个端点来诊断意外的属性值。
有关详细信息,请参见“[生产就绪功能](actuator.html#actuator.endpoints)一节。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 2.1.访问命令行属性
默认情况下,`SpringApplication`将任何命令行选项参数(即以`--`开头的参数,例如`--server.port=9000`)转换为`property`,并将其添加到 Spring `Environment`。如前所述,命令行属性总是优先于基于文件的属性源。
如果不希望将命令行属性添加到`Environment`,则可以使用`SpringApplication.setAddCommandLine属性(false)`禁用它们。
### 2.2.JSON 应用程序属性
环境变量和系统属性通常有一些限制,这意味着一些属性名称不能使用。为了帮助实现这一点, Spring Boot 允许你将一组属性编码到一个 JSON 结构中。
当应用程序启动时,任何`spring.application.json`或`SPRING_APPLICATION_JSON`属性都将被解析并添加到`Environment`中。
例如,`SPRING_APPLICATION_JSON`属性可以在 un\*x shell 的命令行中作为环境变量提供:
```
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar
```
在前面的示例中,在 Spring `Environment`中得到`my.name=test`。
同样的 JSON 也可以作为系统属性提供:
```
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
```
或者,你可以通过使用一个命令行参数来提供 JSON:
```
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
```
如果要部署到经典的应用程序服务器,还可以使用名为`java:comp/env/spring.application.json`的 JNDI 变量。
| |虽然来自 JSON 的`null`值将被添加到结果属性源中,但`PropertySourcesPropertyResolver`将`null`属性视为缺失值。
这意味着 JSON 不能使用`null`值覆盖来自较低顺序属性源的属性。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 2.3.外部应用程序属性
Spring 当应用程序启动时,启动将自动从以下位置查找并加载`application.properties`和`application.yaml`文件:
1. 来自 Classpath
1. Classpath 根
2. Classpath `/config`包
2. 从当前目录
1. 当前目录
2. 当前目录中的`/config`子目录
3. `/config`子目录的直接子目录
该列表是按优先级排序的(来自较低项的值覆盖了较早的项)。来自加载的文件的文档以`PropertySources`的形式添加到 Spring `Environment`中。
如果不喜欢将`application`作为配置文件名,则可以通过指定`spring.config.name`环境属性切换到另一个文件名。例如,要查找`myproject.properties`和`myproject.yaml`文件,你可以按以下方式运行应用程序:
```
$ java -jar myproject.jar --spring.config.name=myproject
```
你还可以使用`spring.config.location`Environment 属性引用显式位置。此属性接受要检查的一个或多个位置的逗号分隔列表。
下面的示例展示了如何指定两个不同的文件:
```
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
```
| |如果[位置是可选的](#features.external-config.files.optional-prefix),则使用前缀`optional:`,如果它们不存在,则不介意。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| |`spring.config.name`、`spring.config.location`和`spring.config.additional-location`很早就被用于确定必须加载哪些文件。
它们必须被定义为一个环境属性(通常是一个 OS 环境变量、一个系统属性或一个命令行参数)。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果`spring.config.location`包含目录(与文件相反),则它们应该以`/`结尾。在运行时,在加载之前,将使用从`spring.config.name`生成的名称对它们进行追加。在`spring.config.location`中指定的文件是直接导入的。
| |还扩展了目录和文件位置值以检查[特定于配置文件的文件](#features.external-config.files.profile-specific)。
例如,如果你有`spring.config.location`的`classpath:myconfig.properties`,你还会发现适当的`classpath:myconfig-.properties`文件已加载。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
在大多数情况下,你添加的每个`spring.config.location`项都将引用一个文件或目录。位置是按照定义的顺序进行处理的,后面的位置可以覆盖前面的位置的值。
如果你有一个复杂的位置设置,并且你使用了特定于配置文件的配置文件,那么你可能需要提供进一步的提示,以便 Spring 引导知道应该如何对它们进行分组。位置组是所有位置都在同一级别上被考虑的位置的集合。例如,你可能希望对所有 Classpath 位置进行分组,然后对所有外部位置进行分组。位置组中的项应该用`;`分隔。有关更多详细信息,请参见“[配置文件特定的文件](#features.external-config.files.profile-specific)”小节中的示例。
使用`spring.config.location`配置的位置替换默认位置。例如,如果`spring.config.location`被配置为值`optional:classpath:/custom-config/,optional:file:./custom-config/`,则考虑的完整位置集为:
1. `optional:classpath:custom-config/`
2. `optional:file:./custom-config/`
如果你更喜欢添加其他位置,而不是替换它们,那么可以使用`spring.config.additional-location`。从其他位置加载的属性可以覆盖默认位置中的属性。例如,如果`spring.config.additional-location`被配置为值`optional:classpath:/custom-config/,optional:file:./custom-config/`,则考虑的完整位置集为:
1. `optional:classpath:/;optional:classpath:/config/`
2. `optional:file:./;optional:file:./config/;optional:file:./config/*/`
3. `optional:classpath:custom-config/`
4. `optional:file:./custom-config/`
这种搜索排序允许你在一个配置文件中指定默认值,然后有选择地重写另一个配置文件中的这些值。你可以在其中一个默认位置的`application.properties`(或你选择的任何其他带有`spring.config.name`的 basename)中为你的应用程序提供默认值。然后可以在运行时使用位于自定义位置之一中的不同文件重写这些默认值。
| |如果使用环境变量而不是系统属性,大多数操作系统都不允许使用周期分隔的键名,但可以使用下划线代替(例如,`SPRING_CONFIG_NAME`而不是`spring.config.name`)。
有关详细信息,请参见[来自环境变量的绑定](#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |如果你的应用程序运行在 Servlet 容器或应用程序服务器中,那么可以使用 JNDI 属性(在`java:comp/env`中)或 Servlet 上下文初始化参数来代替环境变量或系统属性。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.3.1.可选位置
默认情况下,当指定的配置数据位置不存在时, Spring 启动将抛出`ConfigDataLocationNotFoundException`,并且你的应用程序将不会启动。
如果你想指定一个位置,但并不介意它并不总是存在,那么可以使用`optional:`前缀。你可以在`spring.config.location`和`spring.config.additional-location`属性以及[`spring.config.import`](#features.external-config.files.importing)声明中使用此前缀。
例如,`spring.config.import`值`optional:file:./myconfig.properties`允许你的应用程序启动,即使缺少`myconfig.properties`文件。
如果要忽略所有`ConfigDataLocationNotFoundExceptions`并始终继续启动应用程序,则可以使用`spring.config.on-not-found`属性。使用`SpringApplication.setDefault属性(…)`或使用系统/环境变量将值设置为`ignore`。
#### 2.3.2.通配符位置
如果配置文件位置包含最后一个路径段的`*`字符,则将其视为通配符位置。在加载配置时,通配符将被展开,以便也检查直接的子目录。当存在多个配置属性源时,通配符位置在 Kubernetes 等环境中特别有用。
例如,如果你有一些 Redis 配置和一些 MySQL 配置,那么你可能希望将这两个配置分开,同时要求这两个配置都存在于`application.properties`文件中。这可能会导致两个单独的`application.properties`文件安装在不同的位置,例如`/config/redis/application.properties`和`/config/mysql/application.properties`。在这种情况下,具有通配符位置`config/*/`,将导致这两个文件都被处理。
默认情况下, Spring 引导在默认搜索位置中包括`config/*/`。这意味着将搜索 jar 之外`/config`目录的所有子目录。
你可以使用`spring.config.location`和`spring.config.additional-location`属性自己使用通配符位置。
| |通配符位置必须只包含一个`*`,并且对于目录搜索位置以`*/`结尾,对于文件搜索位置以`*/`结尾。
通配符位置根据文件名的绝对路径按字母顺序排序。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |通配符位置仅与外部目录一起工作。
在`classpath:`位置中不能使用通配符。|
|---|-----------------------------------------------------------------------------------------------------------------|
#### 2.3.3.配置文件特定的文件
除了`application`属性文件外, Spring 引导还将尝试使用命名约定`application-{profile}`加载配置文件特定的文件。例如,如果应用程序激活名为`prod`的配置文件并使用 YAML 文件,那么`application.yml`和`application-prod.yml`都将被考虑。
配置文件特定的属性是从与标准`application.properties`相同的位置加载的,配置文件特定的文件总是覆盖非特定的文件。如果指定了多个配置文件,则应用“最后胜出”策略。例如,如果配置文件`prod,live`由`spring.profiles.active`属性指定,则`application-prod.properties`中的值可以被`application-live.properties`中的值覆盖。
| |Last-wins 策略适用于[位置组](#features.external-config.files.location-groups)级别。
a`spring.config.location`of`classpath:/cfg/,classpath:/ext/`将不会具有与`classpath:/cfg/;classpath:/ext/`相同的覆盖规则。
例如,继续上面的`prod,live`示例,我们可能有以下文件:
```
/cfg
application-live.properties
/ext
application-live.properties
application-prod.properties
```
当我们有`spring.config.location`of`classpath:/cfg/,classpath:/ext/`时,我们在所有`/ext`文件之前处理所有
文件:
1.`/cfg/application-live.properties`
2。`/ext/application-prod.properties`
3。`/ext/application-live.properties`
当我们有`classpath:/cfg/;classpath:/ext/`代替(用`;`分隔符)时,我们处理`/cfg`和`/ext`在同一水平上:
1。`/ext/application-prod.properties`
2。`/cfg/application-live.properties`
3。`/ext/application-live.properties`|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
`Environment`具有一组默认配置文件(默认情况下,`[default]`),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有配置文件被显式激活,那么`application-default`中的属性将被考虑。
| |属性文件只加载一次。
如果你已经直接[imported](#features.external-config.files.importing)配置文件特定的属性文件,那么它将不会被导入第二次。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.3.4.导入附加数据
应用程序属性可以使用`spring.config.import`属性从其他位置导入更多配置数据。导入在被发现时进行处理,并被视为在声明导入的文档下面插入的附加文档。
例如,你的 Classpath `application.properties`文件中可能包含以下内容:
属性
```
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
```
Yaml
```
spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties"
```
这将触发当前目录中`dev.properties`文件的导入(如果存在这样的文件)。导入的`dev.properties`中的值将优先于触发导入的文件。在上面的示例中,`dev.properties`可以将`spring.application.name`重新定义为不同的值。
一次进口,无论申报了多少次,都只能进口一次。在 属性/YAML 文件中的单个文档中定义的导入顺序并不重要。例如,下面的两个示例产生相同的结果:
属性
```
spring.config.import=my.properties
my.property=value
```
Yaml
```
spring:
config:
import: "my.properties"
my:
property: "value"
```
属性
```
my.property=value
spring.config.import=my.properties
```
Yaml
```
my:
property: "value"
spring:
config:
import: "my.properties"
```
在上述两个示例中,来自`my.properties`文件的值将优先于触发其导入的文件。
可以在一个`spring.config.import`键下指定多个位置。位置将按照定义的顺序进行处理,以后的导入将优先处理。
| |在适当的情况下,[特定于配置文件的变体](#features.external-config.files.profile-specific)也被考虑用于导入。
上面的示例将导入`my.properties`以及任何`my-.properties`变体。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |Spring 引导包括可插入的 API,允许支持各种不同的位置地址。
默认情况下可以导入 Java 属性、YAML 和“[配置树](#features.external-config.files.configtree)”。
第三方 JAR 可以提供对额外技术的支持(不要求文件是本地的)。
例如,你可以想象配置数据来自外部商店,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。
如果你想支持自己的位置,请参阅`ConfigDataLocationResolver`和`ConfigDataLoader`包中的类。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.3.5.导入无扩展文件
一些云平台不能向卷安装的文件添加文件扩展名。要导入这些无扩展文件,你需要给 Spring 引导一个提示,以便它知道如何加载它们。你可以通过在方括号中添加扩展提示来实现此目的。
例如,假设你有一个`/etc/config/myconfig`文件,你希望将其导入为 YAML。你可以使用以下方法从你的`application.properties`导入它:
属性
```
spring.config.import=file:/etc/config/myconfig[.yaml]
```
Yaml
```
spring:
config:
import: "file:/etc/config/myconfig[.yaml]"
```
#### 2.3.6.使用配置树
在云平台(如 Kubernetes)上运行应用程序时,通常需要读取平台提供的配置值。为此目的使用环境变量并不少见,但这可能有缺点,特别是如果值应该保密的话。
作为环境变量的一种替代方法,许多云平台现在允许你将配置映射到已安装的数据卷中。例如,Kubernetes 可以同时挂载[`ConfigMaps`](https:/kubernetes.io/DOCS/tasks/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap)和[`Secrets`(https://kubernetes.io/DOCS/concepts/configuration/sects/secret/#using-secrets-as-files-from-a-pod)。
可以使用两种常见的卷安装模式:
1. 一个文件包含一组完整的属性(通常写为 YAML)。
2. 多个文件被写入目录树,文件名成为“键”,内容成为“值”。
对于第一种情况,可以使用`spring.config.import`直接导入 YAML 或属性文件,如[above](#features.external-config.files.importing)所述。对于第二种情况,你需要使用`configtree:`前缀,以便 Spring 引导知道需要将所有文件作为属性公开。
举个例子,让我们假设 Kubernetes 已经安装了以下卷:
```
etc/
config/
myapp/
username
password
```
`username`文件的内容将是一个配置值,而`password`的内容将是一个秘密。
要导入这些属性,可以将以下内容添加到`application.properties`或`application.yaml`文件中:
属性
```
spring.config.import=optional:configtree:/etc/config/
```
Yaml
```
spring:
config:
import: "optional:configtree:/etc/config/"
```
然后,你可以以通常的方式从`Environment`访问或注入`myapp.username`和`myapp.password`属性。
| |配置树下的文件夹形成属性名。
在上面的示例中,要访问属性为`username`和`password`,你可以将`spring.config.import`设置为`optional:configtree:/etc/config/myapp`。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |例如,在上面的示例中,一个名为`myapp.username`的文件在`/etc/config`中将在`Environment`中产生一个`myapp.username`属性。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |根据预期的内容,配置树值可以绑定到字符串`String`和`byte[]`两种类型。|
|---|---------------------------------------------------------------------------------------------------------------------|
如果有多个配置树要从同一个父文件夹导入,则可以使用通配符快捷方式。任何以`configtree:`结尾的`/*/`位置都将把所有直接的子节点作为配置树导入。
例如,给出以下卷:
```
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password
```
可以使用`configtree:/etc/config/*/`作为导入位置:
属性
```
spring.config.import=optional:configtree:/etc/config/*/
```
Yaml
```
spring:
config:
import: "optional:configtree:/etc/config/*/"
```
这将添加`db.username`,`db.password`,`mq.username`和`mq.password`属性。
| |使用通配符加载的目录是按字母顺序排序的。
如果需要不同的顺序,那么应该将每个位置作为单独的导入列表|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------|
配置树也可以用于 Docker 秘密。当 Docker Swarm 服务被授予对秘密的访问权限时,该秘密将被装载到容器中。例如,如果一个名为`db.password`的秘密被安装在位置`/run/secrets/`上,则可以使用以下方法使`db.password`环境对 Spring 环境可用:
属性
```
spring.config.import=optional:configtree:/run/secrets/
```
Yaml
```
spring:
config:
import: "optional:configtree:/run/secrets/"
```
#### 2.3.7.财产占位符
`application.properties`和`application.yml`中的值在使用时会通过现有的`Environment`进行过滤,因此你可以引用以前定义的值(例如,从系统属性)。标准的`${name}`属性占位符语法可以在值的任何地方使用。
例如,下面的文件将`app.description`设置为“MyApp 是一个 Spring 启动应用程序”:
属性
```
app.name=MyApp
app.description=${app.name} is a Spring Boot application
```
Yaml
```
app:
name: "MyApp"
description: "${app.name} is a Spring Boot application"
```
| |你也可以使用此技术来创建现有 Spring 引导属性的“短”变体。
有关详细信息,请参见 *[howto.html](howto.html#howto.properties-and-configuration.short-command-line-arguments)*how-to。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.3.8.使用多文档文件
Spring 启动允许将单个物理文件拆分成多个独立添加的逻辑文档。文件的处理顺序是从上到下的。后面的文档可以覆盖前面的文档中定义的属性。
对于`application.yml`文件,使用标准的 YAML 多文档语法。三个连续的连字符表示一个文档的结束,和下一个文档的开始。
例如,下面的文件有两个逻辑文档:
```
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
```
对于`application.properties`文件,使用特殊的`#---`注释来标记文档分割:
```
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
```
| |属性文件分隔符不能有任何前置空格,并且必须正好有三个连字符。
分隔符前后的行不得是注释。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |多文档属性文件通常与激活属性结合使用,例如`spring.config.activate.on-profile`。
有关详细信息,请参见[下一节](#features.external-config.files.activation-properties)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |使用`@PropertySource`或`@TestPropertySource`注释无法加载多文档属性文件。|
|---|-------------------------------------------------------------------------------------------------------------------|
#### 2.3.9.活化特性
有时,只有在满足某些条件时才激活给定的一组属性是有用的。例如,你可能拥有仅在特定配置文件处于活动状态时才相关的属性。
可以使用`spring.config.activate.*`有条件地激活属性文档。
以下激活属性可用:
| Property |注|
|-------------------|------------------------------------------------------------------------|
| `on-profile` |必须匹配才能使文档处于活动状态的配置文件表达式。|
|`on-cloud-platform`|要使文档处于活动状态,必须检测到的`CloudPlatform`。|
例如,下面指定了第二个文档只有在 Kubernetes 上运行时才处于活动状态,并且只有当“prod”或“staging”配置文件处于活动状态时才处于活动状态:
Properties
```
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
```
Yaml
```
myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set"
```
### 2.4.加密属性
Spring 引导不提供任何内置的对加密属性值的支持,但是,它确实提供了修改包含在 Spring `Environment`中的值所必需的钩点。`EnvironmentPostProcessor`接口允许你在应用程序启动之前操作`Environment`。详见[howto.html](howto.html#howto.application.customize-the-environment-or-application-context)。
如果你需要一种安全的方式来存储凭据和密码,[Spring Cloud Vault](https://cloud.spring.io/spring-cloud-vault/)项目提供了在[HashiCorp 保险库](https://www.vaultproject.io/)中存储外部化配置的支持。
### 2.5.与 YAML 合作
[YAML](https://yaml.org)是 JSON 的超集,因此,是用于指定分层配置数据的一种方便的格式。只要在 Classpath 上有[SnakeYAML](https://bitbucket.org/asomov/snakeyaml)库,`SpringApplication`类就会自动支持 YAML 作为属性的替代。
| |如果使用“starter”,那么 SnakeYAML 将由`spring-boot-starter`自动提供。|
|---|------------------------------------------------------------------------------------|
#### 2.5.1.将 YAML 映射到属性
YAML 文档需要从其层次结构格式转换为可与 Spring `Environment`一起使用的平面结构。例如,考虑以下 YAML 文档:
```
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App"
```
为了从`Environment`中访问这些属性,它们将按以下方式进行平坦化:
```
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
```
同样,YAML 的列表也需要扁平化。它们被表示为带有`[index]`dereferencers 的属性键。例如,考虑以下 YAML:
```
my:
servers:
- "dev.example.com"
- "another.example.com"
```
前面的示例将转换为以下属性:
```
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
```
| |使用`[index]`表示法的属性可以使用 Spring boot 的[类型安全配置属性](#features.external-config.typesafe-configuration-properties)对象绑定到 Java`Set`对象。`Binder`class.
有关更多详细信息,请参见下面的“[类型安全配置属性](#features.external-config.typesafe-configuration-properties)”一节。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |无法通过使用`@PropertySource`或`@TestPropertySource`注释来加载 YAML 文件。
因此,在需要以这种方式加载值的情况下,需要使用属性文件。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.5.2.直接加载 YAML
Spring Framework 提供了两个方便的类,它们可以用来加载 YAML 文档。`YamlPropertiesFactoryBean`将 YAML 加载为`Properties`,`YamlMapFactoryBean`将 YAML 加载为`Map`。
如果希望以 Spring `PropertySource`的形式加载 YAML,也可以使用`YamlPropertySourceLoader`类。
### 2.6.配置随机值
`RandomValuePropertySource`对于注入随机值(例如,注入到秘密或测试用例中)很有用。它可以产生整数、长、UUID 或字符串,如以下示例所示:
Properties
```
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}
```
Yaml
```
my:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}"
```
`random.int*`语法是`OPEN value (,max) CLOSE`,其中`OPEN,CLOSE`是任意字符,`value,max`是整数。如果提供了`max`,则`value`是最小值,`max`是最大值(不包含)。
### 2.7.配置系统环境属性
Spring 引导支持为环境属性设置前缀。如果系统环境由具有不同配置要求的多个 Spring 引导应用程序共享,则这是有用的。系统环境属性的前缀可以直接设置在`SpringApplication`上。
例如,如果你将前缀设置为`input`,那么在系统环境中,诸如`remote.timeout`之类的属性也将解析为`input.remote.timeout`。
### 2.8.类型安全配置属性
使用`@Value("${property}")`注释来注入配置属性有时会很麻烦,尤其是在使用多个属性或数据本质上是分层的情况下。 Spring 引导提供了一种处理属性的替代方法,该方法允许强类型 bean 控制和验证应用程序的配置。
| |另请参见[`@Value`和类型安全配置属性之间的区别]。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.8.1.JavaBean 属性绑定
可以绑定声明标准 JavaBean 属性的 Bean,如下例所示:
```
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
// getters / setters...
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private String username;
private String password;
private List roles = new ArrayList<>(Collections.singleton("USER"));
// getters / setters...
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public List getRoles() {
return this.roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
}
}
```
前面的 POJO 定义了以下属性:
* `my.service.enabled`,默认值为`false`。
* `my.service.remote-address`,其类型可以从`String`强制执行。
* `my.service.security.username`,带有嵌套的“security”对象,其名称由属性的名称确定。特别是,该类型在那里根本不使用,并且可能是`SecurityProperties`。
* `my.service.security.password`.
* `my.service.security.roles`,其集合`String`默认为`USER`。
| |映射到 Spring 引导中可用的`@ConfigurationProperties`类的属性(通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公共 API,但是类本身的访问器(getters/setters)并不是直接使用的。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |这种安排依赖于缺省的空构造函数,getter 和 setter 通常是强制性的,因为绑定是通过标准的 Java bean 属性描述符进行的,就像在 Spring MVC 中一样。
在以下情况下,可以省略 setter:
* 映射,只要它们被初始化,需要一个 getter,但不一定是 setter,因为它们可以通过绑定程序进行变异,
* 集合和数组可以通过索引(通常使用 YAML)或使用一个逗号分隔的值(属性)进行访问,
在后一种情况下,setter 是强制性的。
我们建议总是为这样的类型添加一个 setter。
如果你初始化一个集合,请确保它不是不可变的(如前面的示例),
* 如果初始化了嵌套的 POJO 属性(如前面示例中的`Security`字段),setter 不是必需的。
如果你想让 binder 使用其默认构造函数动态地创建实例,你需要一个 setter。
有些人使用 Project Lombok 来自动添加 getter 和 setter。
确保 Lombok 不会为这样的类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。
最后,只考虑标准的 Java Bean 属性,不支持对静态属性的绑定。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.8.2.构造函数绑定
上一节中的示例可以以一种不可更改的方式重写,如下例所示:
```
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {
// fields...
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
// getters...
public boolean isEnabled() {
return this.enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
// fields...
private final String username;
private final String password;
private final List roles;
public Security(String username, String password, @DefaultValue("USER") List roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
// getters...
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public List getRoles() {
return this.roles;
}
}
}
```
在此设置中,`@ConstructorBinding`注释用于指示应该使用构造函数绑定。这意味着绑定器将期望找到一个具有你希望绑定的参数的构造函数。如果你使用的是 Java16 或更高版本,那么构造函数绑定可以用于记录。在这种情况下,除非你的记录有多个构造函数,否则不需要使用`@ConstructorBinding`。
`@ConstructorBinding`类的嵌套成员(例如上面示例中的`Security`)也将通过其构造函数绑定。
可以使用`@DefaultValue`指定默认值,并且将应用相同的转换服务来强制将`String`值强制到丢失属性的目标类型。默认情况下,如果没有属性绑定到`Security`,则`MyProperties`实例将包含`null`值`security`。如果你希望返回`Security`的非空实例,即使没有属性绑定到它,也可以使用空的`@DefaultValue`注释来这样做:
```
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
```
| |要使用构造函数绑定,必须使用`@EnableConfigurationProperties`或配置属性扫描来启用类。
你不能使用构造函数绑定由常规 Spring 机制创建的 bean(例如`@Component`bean,使用`@Bean`方法创建的 bean 或使用`@Import`加载的 bean)|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |如果你的类有多个构造函数,那么你也可以在应该绑定的构造函数上直接使用`@ConstructorBinding`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------|
| |不推荐使用`java.util.Optional`和`@ConfigurationProperties`,因为它主要是作为返回类型使用的。
因此,它不适合配置属性注入。
用于与其他类型的属性保持一致,如果你确实声明了一个`Optional`属性并且它没有值,则将绑定`null`而不是空的`Optional`。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.8.3.启用 @configrationProperties-注释类型
Spring Boot 提供了用于绑定`@ConfigurationProperties`类型并将它们注册为 bean 的基础设施。你可以逐个类地启用配置属性,也可以启用与组件扫描工作方式类似的配置属性扫描。
有时,用`@ConfigurationProperties`注释的类可能不适合扫描,例如,如果你正在开发自己的自动配置,或者希望有条件地启用它们。在这些情况下,使用`@EnableConfigurationProperties`注释指定要处理的类型列表。这可以在任何`@Configuration`类上完成,如以下示例所示:
```
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
```
要使用配置属性扫描,请将`@ConfigurationPropertiesScan`注释添加到应用程序中。通常,它被添加到用`@SpringBootApplication`注释的主应用程序类中,但它可以添加到任何`@Configuration`类中。默认情况下,将从声明注释的类的包中进行扫描。如果你想要定义要扫描的特定包,可以按照以下示例中所示的方式进行:
```
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
```
| |当使用配置属性扫描或通过`@EnableConfigurationProperties`注册`@ConfigurationProperties` Bean 时, Bean 有一个常规名称:`-`,其中``是`@ConfigurationProperties`注释中指定的环境密钥前缀,``是 Bean 的完全限定名称。
如果注释不提供任何前缀,只使用 Bean 的完全限定名称。
上面示例中的 Bean 名称是`com.example.app-com.example.app.SomeProperties`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
我们建议`@ConfigurationProperties`只处理环境,尤其是不从上下文注入其他 bean。对于角的情况,可以使用 setter 注入或框架提供的任何`*Aware`接口(例如`EnvironmentAware`如果需要访问`Environment`)。如果你仍然希望使用构造函数注入其他 bean,那么配置属性 Bean 必须使用`@Component`进行注释,并使用基于 JavaBean 的属性绑定。
#### 2.8.4.使用 @configrationProperties 注释类型
这种配置风格在`SpringApplication`外部 YAML 配置中尤其适用,如以下示例所示:
```
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"
```
要使用`@ConfigurationProperties`bean,你可以以与任何其他 Bean 相同的方式注入它们,如以下示例所示:
```
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final SomeProperties properties;
public MyService(SomeProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
```
| |使用`@ConfigurationProperties`还可以生成元数据文件,IDE 可以使用这些文件为你自己的密钥提供自动补全功能。
有关详细信息,请参见[appendix](configuration-metadata.html#appendix.configuration-metadata)。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.8.5.第三方配置
除了使用`@ConfigurationProperties`对类进行注释外,还可以在 public`@Bean`方法上使用它。当你希望将属性绑定到你无法控制的第三方组件时,这样做会特别有用。
要从`Environment`属性配置 Bean,请将`@ConfigurationProperties`添加到其 Bean 注册中,如以下示例所示:
```
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties(prefix = "another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
```
任何用`another`前缀定义的 JavaBean 属性都以类似于前面的`SomeProperties`示例的方式映射到`AnotherComponent` Bean 上。
#### 2.8.6.松弛结合
Spring Boot 使用一些宽松的规则将`Environment`属性绑定到`@ConfigurationProperties`bean,因此不需要在`Environment`属性名称和 Bean 属性名称之间进行精确匹配。这很有用的常见示例包括 dash 分隔的环境属性(例如,`context-path`绑定到`contextPath`)和大写的环境属性(例如,`PORT`绑定到`port`)。
例如,考虑以下`@ConfigurationProperties`类:
```
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
```
使用前面的代码,可以使用以下属性名称:
| Property |注|
|-----------------------------------|----------------------------------------------------------------------------------------------|
|`my.main-project.person.first-name`|烤肉串的情况下,这是建议在`.properties`和`.yml`文件中使用。|
|`my.main-project.person.firstName` |标准的驼峰大小写语法。|
|`my.main-project.person.first_name`|下划线符号,这是在`.properties`和`.yml`文件中使用的一种替代格式。|
| `MY_MAINPROJECT_PERSON_FIRSTNAME` |大写格式,这是建议时,使用系统环境变量.|
| |注释`prefix`的值*必须*以烤肉串为例(小写并用`-`分隔,如`my.main-project.person`)。|
|---|---------------------------------------------------------------------------------------------------------------------------------|
| Property Source |简单| List |
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Properties Files |camel case、kebab case 或下划线符号| Standard list syntax using `[ ]` or comma-separated values |
| YAML Files |camel case、kebab case 或下划线符号| Standard YAML list syntax or comma-separated values |
|Environment Variables|大写格式,下划线作为分隔符(参见[来自环境变量的绑定](#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables))。|Numeric values surrounded by underscores (see [来自环境变量的绑定](#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables))|
| System properties |camel case、kebab case 或下划线符号| Standard list syntax using `[ ]` or comma-separated values |
| |我们建议在可能的情况下,将属性存储在小写字母的烤肉串格式中,例如`my.person.first-name=Rod`。|
|---|-----------------------------------------------------------------------------------------------------------------------|
##### 绑定地图 #
当绑定到`Map`属性时,你可能需要使用特殊的括号表示法,以便保留原来的`key`值。如果键不被`[]`包围,则删除所有不是 alpha-numeric,`-`或`.`的字符。
例如,考虑将以下属性绑定到`Map`:
Properties
```
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3
```
Yaml
```
my:
map:
"[/key1]": "value1"
"[/key2]": "value2"
"/key3": "value3"
```
| |对于 YAML 文件,括号中需要用引号包围,以便正确解析这些键。|
|---|------------------------------------------------------------------------------------------------|
上面的属性将绑定到`Map`,其中`/key1`、`/key2`和`key3`作为映射中的键。斜杠已从`key3`中删除,因为它没有被方括号包围。
如果你的`key`包含`.`并且绑定到非标量值,那么你有时也可能需要使用括号表示法。例如,将`a.b=c`绑定到`Map`将返回一个带有条目`{"a"={"b"="c"}}`的映射,而`[a.b]=c`将返回一个带有条目`{"a.b"="c"}`的映射。
##### Binding from Environment Variables
大多数操作系统都对可用于环境变量的名称施加了严格的规则。例如,Linux Shell 变量只能包含字母(`a`到`z`或`A`到`Z`)、数字(`0`到`9`)或下划线字符(`_`)。按照惯例,UNIX shell 变量的名称也将是大写的。
Spring Boot 的宽松的绑定规则尽可能地被设计为与这些命名限制兼容。
要将规范形式中的属性名转换为环境变量名,你可以遵循以下规则:
* 将点(`.`)替换为下划线(`_`)。
* 删除任何破折号(`-`)。
* 转换为大写。
例如,配置属性`spring.main.log-startup-info`将是一个名为`SPRING_MAIN_LOGSTARTUPINFO`的环境变量。
当绑定到对象列表时,也可以使用环境变量。要绑定到`List`,元素号应该在变量名中用下划线包围。
例如,配置属性`my.service[0].other`将使用一个名为`MY_SERVICE_0_OTHER`的环境变量。
#### 2.8.7.合并复杂类型
当在多个位置配置列表时,可以通过替换整个列表来覆盖该列表。
例如,假设一个`MyPojo`对象,其`name`和`description`属性默认为`null`。下面的示例公开了来自`MyProperties`的`MyPojo`对象的列表:
```
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final List list = new ArrayList<>();
public List getList() {
return this.list;
}
}
```
考虑以下配置:
Properties
```
my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
```
Yaml
```
my:
list:
- name: "my name"
description: "my description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name"
```
如果`dev`配置文件不是活动的,则`MyProperties.list`包含一个`MyPojo`条目,如前面定义的那样。但是,如果启用了`dev`配置文件,则`list`*Still*只包含一个条目(名称为`my another name`,描述为`null`)。此配置*不是*将第二个`MyPojo`实例添加到列表中,并且它不合并项。
当在多个配置文件中指定`List`时,将使用优先级最高的那个配置文件。考虑以下示例:
Properties
```
my.list[0].name=my name
my.list[0].description=my description
my.list[1].name=another name
my.list[1].description=another description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
```
Yaml
```
my:
list:
- name: "my name"
description: "my description"
- name: "another name"
description: "another description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name"
```
在前面的示例中,如果`dev`配置文件是活动的,则`MyProperties.list`包含*One*`MyPojo`条目(名称为`my another name`,描述为`null`)。对于 YAML,逗号分隔的列表和 YAML 列表都可以用于完全覆盖列表的内容。
对于`Map`属性,你可以绑定来自多个源的属性值。但是,对于多个源中的相同属性,使用具有最高优先级的属性。下面的示例公开了来自`MyProperties`的`Map`:
```
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final Map map = new LinkedHashMap<>();
public Map getMap() {
return this.map;
}
}
```
考虑以下配置:
Properties
```
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2
```
Yaml
```
my:
map:
key1:
name: "my name 1"
description: "my description 1"
---
spring:
config:
activate:
on-profile: "dev"
my:
map:
key1:
name: "dev name 1"
key2:
name: "dev name 2"
description: "dev description 2"
```
如果`dev`配置文件不是活动的,`MyProperties.map`包含一个键值`key1`的条目(名称为`my name 1`,描述为`my description 1`)。但是,如果启用了`dev`配置文件,则`map`包含两个带有键`key1`的条目(名称为`dev name 1`,描述为`my description 1`)和`key2`(名称为`dev name 2`,描述为`dev description 2`)。
| |前面的合并规则适用于来自所有属性源的属性,而不仅仅是文件。|
|---|----------------------------------------------------------------------------------------------|
#### 2.8.8.属性转换
Spring 当绑定到`@ConfigurationProperties`bean 时,引导尝试强制将外部应用程序属性转换为正确的类型。如果需要自定义类型转换,可以提供`ConversionService` Bean(带有 Bean 名为`conversionService`)或自定义属性编辑器(通过`CustomEditorConfigurer` Bean)或自定义`Converters`(带有 Bean 注释为`@ConfigurationPropertiesBinding`的定义)。
| |由于在应用程序生命周期的很早阶段就请求了这个 Bean,因此请确保限制你的`ConversionService`正在使用的依赖关系。
通常,你所需要的任何依赖项可能在创建时没有完全初始化。
如果配置键强制不需要它,并且仅依赖于用`@ConfigurationPropertiesBinding`限定的自定义转换器,那么你可能想要重命名自定义`ConversionService`。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
##### 转换持续时间
Spring Boot 具有用于表示持续时间的专用支持。如果你公开了`java.time.Duration`属性,那么应用程序属性中的以下格式是可用的:
* 一个常规的`long`表示(使用毫秒作为默认单位,除非指定了`@DurationUnit`)
* 标准的 ISO-8601 格式[由`java.time.Duration`使用](https://DOCS.oracle.com/javase/8/DOCS/api/java/time/duration.html#parse-java.lang.charsequence-)
* 一种更可读的格式,其中值和单位是耦合的(`10s`表示 10 秒)
考虑以下示例:
```
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
// getters / setters...
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
```
要指定 30 秒的会话超时,`30`、`PT30S`和`30s`都是等效的。可以以以下任何一种形式指定 500ms 的读取超时:`500`,`PT0.5S`和`500ms`。
你也可以使用任何支持的单位。这些是:
* `ns`为纳秒
* `us`微秒
* `ms`毫秒
* `s`秒
* `m`分钟
* `h`小时
* `d`天
默认的单位是毫秒,可以使用`@DurationUnit`重写,如上面的示例所示。
如果你更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示:
```
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
// fields...
private final Duration sessionTimeout;
private final Duration readTimeout;
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
// getters...
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
}
```
| |如果要升级`Long`属性,请确保在不是毫秒的情况下定义该单元(使用`@DurationUnit`)。
这样做可以提供透明的升级路径,同时支持更丰富的格式。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
##### 转换周期
除了持续时间, Spring 启动还可以使用`java.time.Period`类型。在应用程序属性中可以使用以下格式:
* 一个常规的`int`表示(使用天数作为默认单位,除非指定了`@PeriodUnit`)
* 标准的 ISO-8601 格式[由`java.time.Period`使用](https://DOCS.oracle.com/javase/8/DOCS/api/java/time/period.html#parse-java.lang.charsequence-)
* 一种更简单的格式,其中值和单位对是耦合的(`1y3d`表示 1 年零 3 天)
以下单元以简单格式提供支持:
* 多年来`y`
* `m`持续数月
* `w`周
* 天`d`
| |`java.time.Period`类型实际上不会存储周数,它是一种表示“7 天”的快捷方式。|
|---|------------------------------------------------------------------------------------------------------------|
##### 转换数据大小
Spring 框架具有`DataSize`值类型,该类型表示以字节为单位的大小。如果你公开了`DataSize`属性,那么应用程序属性中的以下格式是可用的:
* 一个常规的`long`表示(使用字节作为默认单位,除非指定了`@DataSizeUnit`)
* 一种更可读的格式,其中的值和单位是耦合的(`10MB`表示 10 兆)
考虑以下示例:
```
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
// getters/setters...
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
```
要指定 10MB 的缓冲区大小,`10`和`10MB`是等效的。256 字节的大小阈值可以指定为`256`或`256B`。
你也可以使用任何支持的单位。这些是:
* `B`表示字节
* `KB`表示千字节
* `MB`代表兆字节
* GB=“936”/>
* `TB`用于 TB
默认的单元是字节,可以使用`@DataSizeUnit`重写,如上面的示例所示。
如果你更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示:
```
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
// fields...
private final DataSize bufferSize;
private final DataSize sizeThreshold;
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
// getters...
public DataSize getBufferSize() {
return this.bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
}
```
| |如果你正在升级`Long`属性,请确保定义不是字节的单元(使用`@DataSizeUnit`)。
这样做可以提供一个透明的升级路径,同时支持更丰富的格式。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.8.9.@configrationProperties 验证
Spring 每当使用 Spring 的`@Validated`注释对类进行注释时,引导都会尝试验证`@ConfigurationProperties`类。你可以直接在你的配置类上使用 JSR-303`javax.validation`约束注释。要做到这一点,请确保在你的 Classpath 上有一个兼容的 JSR-303 实现,然后将约束注释添加到你的字段中,如以下示例所示:
```
import java.net.InetAddress;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
// getters/setters...
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
}
```
| |你还可以通过注释`@Bean`方法来触发验证,该方法使用`@Validated`创建配置属性。|
|---|-----------------------------------------------------------------------------------------------------------------------------|
要确保始终为嵌套属性触发验证,即使没有找到属性,也必须用`@Valid`注释相关字段。下面的示例以前面的`MyProperties`示例为基础:
```
import java.net.InetAddress;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// getters/setters...
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
@NotEmpty
private String username;
// getters/setters...
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
}
```
还可以通过创建一个名为`configurationPropertiesValidator`的 Bean 定义来添加自定义 Spring `Validator`。应该声明`@Bean`方法`static`。配置属性验证器是在应用程序生命周期的早期创建的,将`@Bean`方法声明为 static 可以使 Bean 在无需实例化`@Configuration`类的情况下被创建。这样做可以避免早期实例化可能带来的任何问题。
| |`spring-boot-actuator`模块包括一个公开所有`@ConfigurationProperties`bean 的端点。
将你的 Web 浏览器指向`/actuator/configprops`或使用等效的 JMX 端点。
有关详细信息,请参见“[应用程序属性文件](#features.external-config.files)一节。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 2.8.10.@configrationProperties vs.@value
`@Value`注释是一个核心容器特性,它不提供与类型安全配置属性相同的特性。下表总结了`@ConfigurationProperties`和`@Value`支持的功能:
| Feature |`@ConfigurationProperties`|`@Value`|
|----------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------|
|[Relaxed binding](#features.external-config.typesafe-configuration-properties.relaxed-binding)| Yes |Limited(见[note below](#features.external-config.typesafe-configuration-properties.vs-value-annotation.note))|
| [Meta-data support](configuration-metadata.html#appendix.configuration-metadata) | Yes |无|
| `SpEL` evaluation | No |是的|
| |如果你确实想使用`@Value`,我们建议你使用它们的规范形式(kebab-case 只使用小写字母)来引用属性名称,
这将允许 Spring 引导使用与放松绑定`@ConfigurationProperties`时相同的逻辑,例如,
,`@Value("{demo.item-price}")`将从`application.properties`文件中获取`demo.item-price`和`demo.itemPrice`窗体,以及从系统环境中获取`DEMO_ITEMPRICE`。
如果使用`@Value("{demo.itemPrice}")`代替,则不会考虑`demo.item-price`和`DEMO_ITEMPRICE`。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果你为自己的组件定义了一组配置键,那么我们建议你将它们分组到一个用`@ConfigurationProperties`注释的 POJO 中。这样做将为你提供结构化的、类型安全的对象,你可以将其注入到自己的 bean 中。
在解析这些文件和填充环境时,不会处理`SpEL`中的[应用程序属性文件](#features.external-config.files)表达式。但是,可以在`@Value`中写入`SpEL`表达式。如果来自应用程序属性文件的属性的值是`SpEL`表达式,则在通过`@Value`使用时将对其进行求值。
## 3. 配置文件
Spring 配置文件提供了一种方法来隔离应用程序配置的部分,并使其仅在某些环境中可用。任何`@Component`、`@Configuration`或`@ConfigurationProperties`都可以用`@Profile`标记,以在加载时进行限制,如以下示例所示:
```
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
```
| |如果`@Configuration属性`bean 是通过`@EnableConfiguration属性`注册的,而不是通过自动扫描,则`@Profile`注释需要在具有`@Configuration`注释的类上指定。
在`@Configuration属性`被扫描的情况下,`@Profile`可以在`@Configuration属性`类本身上指定。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
你可以使用`spring.profiles.active``Environment`属性来指定哪些配置文件是活动的。你可以用本章前面描述的任何一种方式指定属性。例如,你可以在`application.properties`中包含它,如下例所示:
属性
```
spring.profiles.active=dev,hsqldb
```
Yaml
```
spring:
profiles:
active: "dev,hsqldb"
```
你还可以通过使用以下开关在命令行中指定它:`--spring.profiles.active=dev,hsqldb`。
如果没有配置文件处于活动状态,则启用默认配置文件。默认配置文件的名称是`default`,可以使用`spring.profiles.default``Environment`属性对其进行调优,如以下示例所示:
属性
```
spring.profiles.default=none
```
Yaml
```
spring:
profiles:
default: "none"
```
### 3.1.添加活动配置文件
`PropertySource`属性遵循与其他属性相同的排序规则:最高的`PropertySource`属性获胜。这意味着你可以使用命令行开关在`application.properties`中指定活动配置文件,然后在**替换**中指定它们。
有时,将**添加**的属性用于活动配置文件而不是替换它们是很有用的。`SpringApplication`入口点有一个用于设置附加配置文件的 Java API(即,在那些由`spring.profiles.active`属性激活的配置文件之上)。参见[SpringApplication](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/SpringApplication.html)中的`setAdditionalProfiles()`方法。在[下一节](#features.profiles.groups)中描述的配置文件组也可以用于在给定的配置文件处于活动状态时添加活动配置文件。
### 3.2.配置文件组
有时,你在应用程序中定义和使用的配置文件粒度太细,使用起来很麻烦。例如,你可能有`proddb`和`prodmq`配置文件,用于独立启用数据库和消息传递功能。
为了帮助实现这一点, Spring Boot 允许你定义配置文件组。配置文件组允许你为相关的配置文件组定义逻辑名称。
例如,我们可以创建一个`production`组,该组由我们的`proddb`和`prodmq`配置文件组成。
属性
```
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
```
Yaml
```
spring:
profiles:
group:
production:
- "proddb"
- "prodmq"
```
我们的应用程序现在可以使用`--spring.profiles.active=production`来启动`production`、`proddb`和`prodmq`配置文件。
### 3.3.以编程方式设置配置文件
你可以通过在应用程序运行之前调用`SpringApplication.setAdditionalProfiles(…)`以编程方式设置活动配置文件。也可以通过使用 Spring 的`ConfigurableEnvironment`接口激活配置文件。
### 3.4.特定于配置文件的配置文件
`application.properties`(或`application.yml`)和通过`@Configuration属性`引用的文件的配置文件特定变体都被视为文件并加载。详见“[配置文件特定的文件](#features.external-config.files.profile-specific)”。
## 4. 伐木
Spring 启动对所有内部日志使用[共享日志记录](https://commons.apache.org/logging),但对底层日志实现保持开放。为[Java util 日志记录](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html)、[Log4J2](https://logging.apache.org/log4j/2.x/)和`@SpringBootApplication`提供了默认配置。在每种情况下,记录器都被预先配置为使用控制台输出,可选的文件输出也可用。
默认情况下,如果你使用“starters”,则会使用回录来进行日志记录。还包括适当的回登路由,以确保使用 Java util 日志、Commons 日志、log4j 或 SLF4j 的依赖库都能正确工作。
| |有很多日志框架可用于 Java。
如果上面的列表看起来令人困惑,请不要担心。
通常,你不需要更改日志依赖项,并且 Spring 引导默认值工作得很好。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |当你将应用程序部署到 Servlet 容器或应用程序服务器时,使用 Java util Logging API 执行的日志记录不会路由到应用程序的日志中。
这将防止由容器执行的日志或已部署到它的其他应用程序出现在应用程序的日志中。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 4.1.日志格式
Spring 引导的默认日志输出类似于以下示例:
```
2019-03-05 10:57:51.112 INFO 45469 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.52
2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1358 ms
2019-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2019-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
```
以下项目是输出:
* 日期和时间:毫秒精度和容易排序.
* 日志级别:`ERROR`,`WARN`,`INFO`,[下一节](#features.profiles.groups),或`TRACE`。
* 进程 ID。
* 一个`---`分隔符,用于区分实际日志消息的开始。
* 线程名称:括在方括号中(对于控制台输出,可能会被截断)。
* 日志程序名称:这通常是源类名称(通常是缩写)。
* 日志消息。
| |注销没有`FATAL`级别。
它被映射到`ERROR`。|
|---|-------------------------------------------------------------------|
### 4.2.控制台输出
默认的日志配置会在消息写入时将消息回传到控制台。默认情况下,将记录`ERROR`-level、`WARN`-level 和`INFO`-level 消息。你还可以通过使用`--debug`标志启动应用程序来启用“调试”模式。
```
$ java -jar myapp.jar --debug
```
| |你还可以在`application.properties`中指定`debug=true`。|
|---|-------------------------------------------------------------------|
当调试模式被启用时,核心记录器(嵌入式容器、 Hibernate 和 Spring 引导)的选择被配置为输出更多信息。启用调试模式将*不是*配置你的应用程序以`DEBUG`级别记录所有消息。
或者,你可以通过使用`--trace`标志(或`trace=true`中的`application.properties`)启动应用程序来启用“跟踪”模式。这样做可以实现对核心记录器(嵌入式容器、 Hibernate 模式生成和整个 Spring 投资组合)的选择的跟踪日志记录。
#### 4.2.1.颜色编码输出
如果你的终端支持 ANSI,则使用颜色输出来提高可读性。可以将`spring.output.ansi.enabled`设置为[支持的值](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/ansi/AnsiOutput.Enabled.html)以覆盖自动检测。
颜色编码是通过使用`%clr`转换字来配置的。在最简单的形式中,转换器根据日志级别对输出进行着色,如以下示例所示:
```
%clr(%5p)
```
下表描述了日志级别到颜色的映射:
|电平|Color |
|-------|------|
|`FATAL`| Red |
|`ERROR`| Red |
|`WARN`|Yellow|
|`INFO`|Green |
|`DEBUG`|Green |
|`TRACE`|Green |
或者,你可以通过将其作为转换的选项来指定应该使用的颜色或样式。例如,要使文本为黄色,请使用以下设置:
```
%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow}
```
支持以下颜色和样式:
* `blue`
* `cyan`
* `faint`
* `green`
* `magenta`
* `red`
* `yellow`
### 4.3.文件输出
默认情况下, Spring 只引导日志到控制台,不写日志文件。如果要在控制台输出之外写日志文件,则需要设置`logging.file.name`或`logging.file.path`属性(例如,在`application.properties`中)。
下表显示了如何一起使用`logging.*`属性:
|`logging.file.name`|`logging.file.path`| Example |说明|
|-------------------|-------------------|----------|------------------------------------------------------------------------------------------------------------------------|
| *(none)* | *(none)* | |仅用于控制台日志记录。|
| Specific file | *(none)* | `my.log` |写入指定的日志文件。
名称可以是确切的位置或相对于当前目录的位置。|
| *(none)* |Specific directory |`/var/log`|将`spring.log`写入指定的目录。
名称可以是确切的位置或相对于当前目录的位置。|
当日志文件达到 10MB 时会旋转,并且与控制台输出一样,`ERROR`-level,`WARN`-level 和`INFO`-level 消息在默认情况下会被记录。
| |日志记录属性独立于实际的日志基础设施。因此,特定的配置键(例如
用于回登)不受 Spring 引导的管理。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 4.4.文件旋转
如果你使用的是回录,那么可以使用`application.properties`或`application.yaml`文件来微调日志旋转设置。对于所有其他日志系统,你将需要直接自己配置旋转设置(例如,如果你使用 log4j2,那么你可以添加`application.yaml`或`log4j2-spring.xml`文件)。
支持以下旋转策略属性:
| Name |说明|
|------------------------------------------------------|----------------------------------------------------------------------|
| `logging.logback.rollingpolicy.file-name-pattern` |用于创建日志归档的文件名模式。|
|`logging.logback.rollingpolicy.clean-history-on-start`|如果日志归档清理应该在应用程序启动时进行。|
| `logging.logback.rollingpolicy.max-file-size` |存档前日志文件的最大大小。|
| `logging.logback.rollingpolicy.total-size-cap` |在被删除之前,日志档案可以占用的最大大小。|
| `logging.logback.rollingpolicy.max-history` |保存的归档日志文件的最大数量(默认为 7)。|
### 4.5.日志级别
通过使用`logging.level.=`,所有受支持的日志系统都可以在 Spring `Environment`(例如,在`application.properties`)中设置日志程序级别,其中`application.properties`是跟踪、调试、信息、警告、错误、致命或关闭。`root`记录器可以通过使用`logging.level.root`进行配置。
下面的示例在`application.properties`中显示了潜在的日志设置:
属性
```
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
```
Yaml
```
logging:
level:
root: "warn"
org.springframework.web: "debug"
org.hibernate: "error"
```
也可以使用环境变量设置日志记录级别。例如,`LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG`将`org.springframework.web`设置为`DEBUG`。
| |上述方法仅对包级日志记录有效。
由于放松绑定总是将环境变量转换为小写,因此不可能以这种方式为单个类配置日志记录。
如果需要为一个类配置日志记录,可以使用[the`SPRING_APPLICATION_JSON`]变量。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 4.6.日志组
能够将相关的记录器组合在一起,以便可以同时对它们进行配置,这通常是很有用的。例如,你通常可能会更改*全部* Tomcat 相关日志记录器的日志记录级别,但是你不能很容易地记住顶级包。
为了解决这个问题, Spring Boot 允许你在 Spring `Environment`中定义日志记录组。例如,下面是如何将“ Tomcat”组添加到`application.properties`中来定义该组的方法:
属性
```
logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat
```
Yaml
```
logging:
group:
tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat"
```
一旦定义好,你就可以用一行来改变组中所有记录器的级别:
Properties
```
logging.level.tomcat=trace
```
Yaml
```
logging:
level:
tomcat: "trace"
```
Spring 启动包括以下预定义的记录组,这些记录组可以开箱即用:
|Name|伐木工|
|----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|web |`org.springframework.core.codec`, `org.springframework.http`, `org.springframework.web`, `org.springframework.boot.actuate.endpoint.web`, `org.springframework.boot.web.servlet.ServletContextInitializerBeans`|
|sql |`org.springframework.jdbc.core`, `org.hibernate.SQL`, `org.jooq.tools.LoggerListener`|
### 4.7.使用日志关闭钩子
为了在应用程序终止时释放日志资源,提供了一个关闭钩子,该钩子将在 JVM 退出时触发日志系统清理。除非你的应用程序被部署为 WAR 文件,否则此关闭钩子将自动注册。如果你的应用程序具有复杂的上下文层次结构,那么关闭钩子可能无法满足你的需求。如果没有,请禁用关闭钩子,并调查底层日志系统直接提供的选项。例如,Logback 提供[上下文选择器](http://logback.qos.ch/manual/loggingSeparation.html),允许在自己的上下文中创建每个记录器。你可以使用`logging.register-shutdown-hook`属性禁用关闭钩子。将其设置为`false`将禁用注册。你可以在`false`或`application.yaml`文件中设置该属性:
Properties
```
logging.register-shutdown-hook=false
```
Yaml
```
logging:
register-shutdown-hook: false
```
### 4.8.自定义日志配置
各种日志记录系统可以通过在 Classpath 上包括适当的库来激活,并且可以通过在 Classpath 的根目录中或在由下面的 Spring 指定的位置中提供适当的配置文件来进一步定制属性:。
可以使用`org.springframework.boot.logging.LoggingSystem`系统属性强制 Spring 引导使用特定的日志系统。该值应该是`LoggingSystem`实现的完全限定类名称。还可以使用`@EnableScheduling`的值来完全禁用 Spring Boot 的日志配置。
| |由于日志是初始化的**在此之前**`ApplicationContext`已被创建,因此不可能在 Spring `@PropertySources`文件中从`@Configuration`文件中控制日志。**在此之前**改变日志系统或完全禁用它的唯一方法是通过系统属性。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
根据你的日志记录系统,将加载以下文件:
| Logging System |定制|
|-----------------------|---------------------------------------------------------------------------------|
| Logback |`logback-spring.xml`,`logback-spring.groovy`,`logback.xml`,或`logback.groovy`|
| Log4j2 |`log4j2-spring.xml`或`log4j2.xml`|
|JDK (Java Util Logging)|`logging.properties`|
| |如果可能,我们建议你在日志配置中使用`-spring`变量(例如,`logback-spring.xml`而不是`logback.xml`)。
如果使用标准配置位置, Spring 不能完全控制日志初始化。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |在从“可执行文件 jar”运行时,Java util 日志记录中存在已知的类加载问题,这些问题会导致问题。
如果可能的话,我们建议你在从“可执行文件 jar”运行时避免此类问题。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
为了帮助进行定制,将一些其他属性从 Spring `Environment`转移到系统属性,如下表所述:
| Spring Environment | System Property |评论|
|-----------------------------------|-------------------------------|-----------------------------------------------------------------------------------------------------------|
|`logging.exception-conversion-word`|`LOG_EXCEPTION_CONVERSION_WORD`|记录异常时使用的转换词.|
| `logging.file.name` | `LOG_FILE` |如果定义了,它将在默认的日志配置中使用。|
| `logging.file.path` | `LOG_PATH` |如果定义了,它将在默认的日志配置中使用。|
| `logging.pattern.console` | `CONSOLE_LOG_PATTERN` |在控制台上使用的日志模式。|
| `logging.pattern.dateformat` | `LOG_DATEFORMAT_PATTERN` |日志日期格式的附录模式。|
| `logging.charset.console` | `CONSOLE_LOG_CHARSET` |用于控制台日志记录的字符集。|
| `logging.pattern.file` | `FILE_LOG_PATTERN` |在文件中使用的日志模式(如果启用`LOG_FILE`)。|
| `logging.charset.file` | `FILE_LOG_CHARSET` |用于文件日志记录的字符集(如果启用`LOG_FILE`)。|
| `logging.pattern.level` | `LOG_LEVEL_PATTERN` |呈现日志级别时要使用的格式(默认`%5p`)。|
| `PID` | `PID` |当前的进程 ID(如果可能的话,在尚未定义为 OS 环境变量时发现)。|
如果使用注销,还会传输以下属性:
| Spring Environment | System Property |评论|
|------------------------------------------------------|----------------------------------------------|------------------------------------------------------------------------------------|
| `logging.logback.rollingpolicy.file-name-pattern` | `LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN` |滚转日志文件名的模式(默认`${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz`)。|
|`logging.logback.rollingpolicy.clean-history-on-start`|`LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START`|是否在启动时清除归档日志文件。|
| `logging.logback.rollingpolicy.max-file-size` | `LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE` |最大日志文件大小。|
| `logging.logback.rollingpolicy.total-size-cap` | `LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP` |要保留的日志备份的总大小。|
| `logging.logback.rollingpolicy.max-history` | `LOGBACK_ROLLINGPOLICY_MAX_HISTORY` |要保存的归档日志文件的最大数量。|
所有受支持的日志记录系统在解析其配置文件时都可以参考系统属性。有关示例,请参见`spring-boot.jar`中的默认配置:
* [Logback](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml)
* [Log4j 2](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml)
* [Java util 日志记录](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging-file.properties)
| |如果你想在日志属性中使用占位符,那么你应该使用[Spring Boot’s syntax](#features.external-config.files.property-placeholders),而不是底层框架的语法。
值得注意的是,如果你使用 logback,你应该使用`:`作为属性名与其默认值之间的分隔符,而不是使用`:-`。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |你可以通过只重写`LOG_LEVEL_PATTERN`(或`logging.pattern.level`带回日志),将 MDC 和其他临时内容添加到日志行中,
例如,如果你使用`logging.pattern.level=user:%X{user} %5p`,那么默认的日志格式包含一个“user”的 MDC 条目,如果它存在的话,如下面的示例所示。
```
2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.Controller
Handling authenticated request
```|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 4.9.回录扩展
Spring 启动包括许多对注销的扩展,这些扩展可以帮助进行高级配置。你可以在`logback-spring.xml`配置文件中使用这些扩展名。
| |因为标准的`logback.xml`配置文件加载得太早,所以不能在其中使用扩展名。
你需要使用`logback-spring.xml`或定义`logging.config`属性。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |这些扩展名不能与 Logback 的[配置扫描](https://logback.qos.ch/manual/configuration.html#autoScan)一起使用。
如果你试图这样做,对配置文件进行更改将导致类似于记录以下错误之一的错误:|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
```
ERROR in [email protected]:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]]
ERROR in ch.qos.logback.core.joran.spi.Interpret[email protected]:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
```
#### 4.9.1.特定于配置文件的配置
``标记允许你基于活动 Spring 配置文件可选地包括或排除配置的部分。在``元素中的任何地方都支持配置文件部分。使用`name`属性指定哪个配置文件接受配置。``标记可以包含配置文件名(例如`staging`)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如`production & (eu-central | eu-west)`。查看[参考指南](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/core.html#beans-definition-profiles-java)以获取更多详细信息。下面的清单显示了三个示例配置文件:
```
```
#### 4.9.2.环境属性
``标记允许你公开 Spring `Environment`中的属性,以便在回传过程中使用。如果你希望在你的回登配置中访问`application.properties`文件中的值,那么这样做会很有用。该标记的工作方式与 Logback 的标准``标记类似。但是,不是直接指定`value`,而是指定属性的`source`(来自`Environment`)。如果需要将属性存储在`local`范围以外的其他地方,则可以使用`scope`属性。如果你需要一个回退值(如果该属性未在`Environment`中设置),则可以使用`defaultValue`属性。下面的示例展示了如何公开属性以便在回登过程中使用:
```
${fluentHost}
...
```
| |`source`必须在烤肉串的情况下指定(例如`my.property-name`)。
但是,可以通过使用放松的规则将属性添加到`Environment`中。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
## 5. 国际化
Spring 启动支持本地化消息,以便你的应用程序能够迎合具有不同语言偏好的用户。默认情况下, Spring 引导在 Classpath 的根位置查找`messages`资源包的存在。
| |当配置的资源包的默认属性文件可用时(默认情况下`messages.properties`),自动配置将应用,
如果你的资源包只包含语言特定的属性文件,则需要添加默认的属性文件。
如果没有找到匹配任何配置的基名的属性文件,不会自动配置`messages`。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
可以使用`messages`命名空间来配置资源包的 basename 以及其他几个属性,如下例所示:
Properties
```
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false
```
Yaml
```
spring:
messages:
basename: "messages,config.i18n.messages"
fallback-to-system-locale: false
```
| |`spring.messages.basename`支持以逗号分隔的位置列表,可以是包限定符,也可以是从 Classpath 根目录解析的资源。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------|
参见[`MessageSourceProperties`](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/context/mentt/messagesourceproperties.java)以获得更多支持的选项。
## 6. JSON
Spring Boot 提供了与三个 JSON 映射库的集成:
* 格森
* Jackson
* JSON-B
Jackson 是首选的默认库。
### 6.1.Jackson
提供了用于 Jackson 的自动配置,并且 Jackson 是`messages`的一部分。当 Jackson 在 Classpath 上时,`ObjectMapper` Bean 被自动配置。为[自定义`ObjectMapper`](howto.html#howto. Spring-mvc.customize-Jackson-objectmapper)的配置提供了几个配置属性。
### 6.2.格森
提供了 GSON 的自动配置。当 GSON 在 Classpath 上时,`Gson` Bean 被自动配置。为定制配置提供了几个`spring.gson.*`配置属性。要获得更多的控制,可以使用一个或多个`GsonBuilderCustomizer`bean。
### 6.3.JSON-B
提供了 JSON-B 的自动配置。当 JSON-B API 和实现都在 Classpath 上时,`Jsonb` Bean 将被自动配置。首选的 JSON-B 实现是 Apache Johnzon,它提供了依赖管理。
## 7. 任务执行和调度
在上下文中没有`Executor` Bean 的情况下, Spring 引导自动配置具有合理默认值的`ThreadPoolTaskExecutor`,该默认值可以自动关联到异步任务执行(`@EnableAsync`)和 Spring MVC 异步请求处理。
| |如果你在上下文中定义了自定义的`@EnableAsync`,则常规的任务执行(即`@EnableAsync`)将透明地使用它,但是 Spring MVC 支持将不会被配置,因为它需要`AsyncTaskExecutor`实现(名为`applicationTaskExecutor`)。
取决于你的目标安排,你可以将你的`Executor`更改为`ThreadPoolTaskExecutor`,或者同时定义`ThreadPoolTaskExecutor`和`AsyncConfigurer`包装你的自定义`Executor`。
自动配置的`TaskExecutorBuilder`允许你轻松地创建实例,这些实例复制默认情况下自动配置的功能。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
线程池使用 8 个核心线程,这些线程可以根据负载的大小进行增长和收缩。可以使用`Executor`名称空间对这些默认设置进行微调,如下例所示:
Properties
```
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s
```
Yaml
```
spring:
task:
execution:
pool:
max-size: 16
queue-capacity: 100
keep-alive: "10s"
```
这将线程池更改为使用有界队列,以便当队列已满(100 个任务)时,线程池最多增加到 16 个线程。当线程空闲 10 秒(而不是默认的 60 秒)时,会回收线程,因此池的收缩会更剧烈。
如果需要与计划的任务执行相关联,`ThreadPoolTaskScheduler`也可以自动配置(例如使用`@EnableScheduling`)。线程池默认使用一个线程,其设置可以使用`spring.task.scheduling`名称空间进行微调,如下例所示:
Properties
```
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2
```
Yaml
```
spring:
task:
scheduling:
thread-name-prefix: "scheduling-"
pool:
size: 2
```
如果需要创建自定义执行器或调度程序,则在上下文中可以使用`TaskExecutorBuilder` Bean 和`TaskSchedulerBuilder` Bean。
## 8. 测试
Spring 引导提供了许多实用工具和注释,以在测试应用程序时提供帮助。测试支持由两个模块提供:`spring-boot-test`包含核心项,`spring-boot-test-autoconfigure`支持测试的自动配置。
大多数开发人员使用`spring-boot-starter-test`“starter”,它既导入 Spring 引导测试模块,也导入 JUnit Jupiter、AssertJ、Hamcrest 和许多其他有用的库。
| |如果你有使用 JUnit4 的测试,可以使用 JUnit5 的 Vintage 引擎来运行它们。`@EnableScheduling`要使用 Vintage 引擎,请在`junit-vintage-engine`上添加一个依赖项,如以下示例所示:
|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
`spring-boot-starter-test`被排除在外,而`org.hamcrest:hamcrest`是`spring-boot-starter-test`的一部分。
### 8.1.测试范围依赖项
`spring-boot-starter-test`“starter”(在`test``scope`中)包含以下提供的库:
* [JUnit 5](https://junit.org/junit5/):用于单元测试 Java 应用程序的事实标准。
* [Spring Test](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#integration-testing)& Spring 引导测试: Spring 引导应用程序的实用工具和集成测试支持。
* [AssertJ](https://assertj.github.io/doc/):一个流畅的断言程序库。
* [Hamcrest](https://github.com/hamcrest/JavaHamcrest):Matcher 对象(也称为约束或谓词)的库。
* [Mockito](https://site.mockito.org/):一个 Java 模拟框架。
* [JSONassert](https://github.com/skyscreamer/JSONassert):JSON 的断言程序库。
* [JsonPath](https://github.com/jayway/JsonPath):XPath for JSON。
在编写测试时,我们通常会发现这些公共库非常有用。如果这些库不适合你的需要,那么你可以添加自己的额外测试依赖项。
### 8.2.测试 Spring 应用程序
依赖注入的一个主要优点是,它应该使代码更容易进行单元测试。你可以使用`new`运算符实例化对象,甚至不需要涉及 Spring。你也可以使用[Java util 日志记录](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging-file.properties)来代替真正的依赖关系。
通常,你需要超越单元测试,开始集成测试(使用 Spring `ApplicationContext`)。能够在不需要部署应用程序或不需要连接到其他基础设施的情况下执行集成测试是非常有用的。
Spring 框架包括用于这种集成测试的专用测试模块。你可以直接将依赖项声明为`org.springframework:spring-test`,或者使用`spring-boot-starter-test`“starter”来传递地将其拉入。
如果你以前没有使用`spring-test`模块,那么你应该从阅读 Spring 框架参考文档的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testing)开始。
### 8.3.测试 Spring 引导应用程序
Spring 引导应用程序是 Spring `ApplicationContext`,因此,除了通常使用普通上下文 Spring 所做的事情外,无需做任何非常特殊的事情来测试它。
| |只有在使用`SpringApplication`创建它的情况下, Spring 引导的外部属性、日志记录和其他功能才默认安装在上下文中。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------|
Spring Boot 提供了一个`@SpringBootTest`注释,其可以作为标准`spring-test``@ContextConfiguration`注释的替代项,当需要 Spring 启动功能时。该注释的工作原理是[通过`SpringApplication`创建测试中使用的`ApplicationContext`](#features.testing. Spring-boot-applications.detecting-configuration)。除了`@SpringBootTest`之外,还为[测试更具体的切片](#features.testing.spring-boot-applications.autoconfigured-tests)的应用程序提供了许多其他注释。
| |如果你正在使用 JUnit4,请不要忘记在你的测试中也添加`@RunWith(SpringRunner.class)`,否则注释将被忽略。
如果你正在使用 JUnit5,没有必要将等价的`@ExtendWith(SpringExtension.class)`添加为`@SpringBootTest`,并且其他`@…Test`注释已经用它注释了。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
默认情况下,`@SpringBootTest`不会启动服务器。可以使用`@SpringBootTest`的`webEnvironment`属性来进一步细化测试的运行方式:
* `MOCK`(默认):加载 Web`ApplicationContext`并提供模拟 Web 环境。使用此注释时,嵌入式服务器不会启动。如果你的 Classpath 上没有可用的 Web 环境,则该模式可透明地返回到创建常规的非 Web`ApplicationContext`。它可以与[`@AutoConfigureMockMvc`或`@AutoConfigureWebTestClient`](#features.testing. Spring-boot-applications.with-mock-environment)结合使用,用于对 Web 应用程序进行基于模拟的测试。
* `RANDOM_PORT`:加载`WebServerApplicationContext`并提供一个真实的 Web 环境。嵌入式服务器在一个随机端口上启动和监听。
* `DEFINED_PORT`:加载`WebServerApplicationContext`,并提供一个真正的 Web 环境。嵌入式服务器在定义的端口(从`application.properties`)或默认端口`8080`上启动和侦听。
* `NONE`:通过使用`SpringApplication`加载`ApplicationContext`,但不提供*任何*Web 环境(模拟或其他方式)。
| |如果你的测试是`@Transactional`,那么默认情况下,它会在每个测试方法的末尾回滚事务。
但是,由于使用这种安排,`RANDOM_PORT`或`RANDOM_PORT`隐式地提供了一个真正的 Servlet 环境,HTTP 客户机和服务器在单独的线程中运行,因此,在单独的事务中。
在这种情况下,在服务器上发起的任何事务都不回滚。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |如果应用程序为管理服务器使用不同的端口,则`@SpringBootTest`with`webEnvironment = WebEnvironment.RANDOM_PORT`也将在单独的随机端口上启动管理服务器。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.1.检测 Web 应用程序类型
Spring 如果 MVC 是可用的,则配置一个常规的基于 MVC 的应用程序上下文。如果你只有 Spring 个 WebFlux,我们将检测到它并配置一个基于 WebFlux 的应用程序上下文。
如果两者都存在, Spring MVC 优先。如果要在此场景中测试一个反应式 Web 应用程序,则必须设置`spring.main.web-application-type`属性:
```
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
```
#### 8.3.2.检测测试配置
如果你熟悉 Spring 测试框架,那么你可能习惯于使用`@ContextConfiguration(classes=…)`来指定要加载哪个 Spring `@Configuration`。或者,你可能经常在测试中使用嵌套的`@Configuration`类。
在测试 Spring 引导应用程序时,这通常不是必需的。 Spring 只要不显式地定义一个配置,Boot 的`@*Test`注释就会自动搜索你的主要配置。
搜索算法从包含测试的包开始工作,直到找到一个用`@SpringBootApplication`或`@SpringBootConfiguration`注释的类。只要以一种合理的方式
,通常就可以找到你的主配置。
| |如果使用[测试注释以测试应用程序的一个更具体的部分](#features.testing.spring-boot-applications.autoconfigured-tests),你应该避免在[主方法的应用程序类](#features.testing.spring-boot-applications.user-configuration-and-slicing)上添加特定于特定区域的配置设置。
`@SpringBootApplication`的底层组件扫描配置定义了排除过滤器如果你在你的`@SpringBootApplication`-注释类上使用显式的`@ComponentScan`指令,请注意这些过滤器将被禁用。
如果你正在使用切片,则应再次定义它们。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果你想定制主配置,可以使用嵌套的`@TestConfiguration`类。不同于嵌套的`@Configuration`类,嵌套的`@TestConfiguration`类将被用来代替应用程序的主配置,在应用程序的主配置之外还使用了嵌套的`@TestConfiguration`类。
| |Spring 的测试框架在测试之间缓存应用程序上下文。
因此,只要你的测试共享相同的配置(无论如何发现),加载上下文的可能耗时的过程只会发生一次。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.3.不包括测试配置
如果你的应用程序使用组件扫描(例如,如果你使用`@SpringBootApplication`或`@ComponentScan`),你可能会发现只为特定测试创建的顶级配置类在任何地方都会意外地被选中。
正如我们[看过更早的](#features.testing.spring-boot-applications.detecting-configuration),`@TestConfiguration`可以用于对内部类的测试进行自定义的主要配置。当放置在顶级类上时,`@TestConfiguration`表示不应通过扫描来读取`src/test/java`中的类。然后,你可以在需要的地方显式地导入该类,如下面的示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
```
| |如果直接使用`@ComponentScan`(即不通过`@SpringBootApplication`),则需要用它注册`TypeExcludeFilter`。
详见[Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/context/TypeExcludeFilter.html)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.4.使用应用程序参数
如果应用程序期望`@SpringBootTest`,则可以使用`args`属性注入它们。
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
```
#### 8.3.5.在模拟环境中进行测试
默认情况下,`@SpringBootTest`不会启动服务器,而是设置一个模拟环境来测试 Web 端点。
使用 Spring MVC,我们可以使用[`MockMvc`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/testing.html# Spring-mvc-test-framework)或`WebTestClient`查询我们的 Web 端点,如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
```
| |如果你希望只关注 Web 层,而不是启动一个完整的`ApplicationContext`,请考虑[使用`@WebMvcTest`代替](#features.testing. Spring-boot-applications. Spring-mvc-tests)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
使用 Spring WebFlux 端点,可以使用[`WebTestClient`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/testing.html#WebTestClient-tests),如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
```
| |在模拟环境中进行测试通常比使用完整的 Servlet 容器运行更快。但是,由于模拟发生在 Spring MVC 层,因此依赖较低级别 Servlet 容器行为的代码不能直接使用 MockMVC 进行测试。例如,, Spring boot 的错误处理是基于 Servlet 容器提供的“错误页”支持。
这意味着,虽然你可以测试你的 MVC 层抛出和处理预期的异常,你无法直接测试呈现了特定的[自定义错误页面](web.html#web.servlet.spring-mvc.error-handling.error-pages)。
如果你需要测试这些较低级别的问题,你可以启动一个完全运行的服务器,如下一节所述。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.6.使用正在运行的服务器进行测试
如果你需要启动一个完整的正在运行的服务器,我们建议你使用随机端口。如果使用`@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)`,则每次测试运行时都会随机选择一个可用的端口。
`@LocalServerPort`注释可以用于[注入实际使用的端口](howto.html#howto.webserver.discover-port)到你的测试中。为了方便起见,需要对启动的服务器进行 REST 调用的测试还可以另外`@Autowire`a[`WebTestClient`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/testing.html#webtestclient-tests),它解析与运行中的服务器的相关链接,并附带用于验证响应的专用 API,如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
```
| |`WebTestClient`可以同时用于实时服务器和[模拟环境](#features.testing.spring-boot-applications.with-mock-environment)。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------|
此设置需要在 Classpath 上执行`spring-webflux`。如果不能或不会添加 WebFlux, Spring Boot 还提供了`TestRestTemplate`功能:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
```
#### 8.3.7.自定义 WebTestClient
要定制`WebTestClient` Bean,请配置`WebTestClientBuilderCustomizer` Bean。使用`WebTestClient.Builder`调用任何这样的 bean,该 bean 用于创建`WebTestClient`。
#### 8.3.8.使用 JMX
当测试上下文框架缓存上下文时,默认情况下禁用 JMX,以防止相同的组件在同一域上注册。如果这样的测试需要访问`MBeanServer`,也可以考虑将其标记为 dirty:
```
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() throws MalformedObjectNameException {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
```
#### 8.3.9.使用度量
无论你的 Classpath 是什么,除了内存备份之外,仪表注册中心在使用`@SpringBootTest`时不会自动配置。
如果作为集成测试的一部分,需要将指标导出到不同的后端,请用`@AutoConfigureMetrics`对其进行注释。
#### 8.3.10.嘲笑和窥探豆子
在运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,你可能在某个远程服务上有一个 facade,而在开发过程中它是不可用的。当你想要模拟在真实环境中可能很难触发的故障时,模拟也很有用。
Spring 引导包括一个`@MockBean`注释,该注释可用于在你的`ApplicationContext`中为 Bean 定义一个 mockito 模拟。你可以使用该注释来添加新的 bean 或替换单个现有的 Bean 定义。注释可以直接用于测试类、测试中的字段或`@Configuration`类和字段。当在字段上使用时,创建的模拟实例也会被注入。在每种测试方法之后,mock bean 都会自动重置。
| |如果你的测试使用 Spring boot 的测试注释之一(例如`@SpringBootTest`),则自动启用此功能。
要以不同的方式使用此功能,必须显式地添加监听器,如以下示例所示:
```
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@ContextConfiguration(classes = MyConfig.class)
@TestExecutionListeners({ MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class })
class MyTests {
// ...
}
```|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例用模拟实现替换现有的`RemoteService` Bean:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@SpringBootTest
class MyTests {
@Autowired
private Reverser reverser;
@MockBean
private RemoteService remoteService;
@Test
void exampleTest() {
given(this.remoteService.getValue()).willReturn("spring");
String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}
```
| |`@MockBean`不能用来模拟 Bean 在应用程序上下文刷新期间执行的行为。
在执行测试时,应用程序上下文刷新已经完成,现在配置模拟行为已经太晚了。
在这种情况下,我们建议使用`@Bean`方法来创建和配置模拟行为。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
此外,你可以使用`@SpyBean`用 mockito`spy`包装任何现有的 Bean。有关详细信息,请参见[Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/test/mock/mockito/SpyBean.html)。
| |CGLIB 代理,例如为作用域 bean 创建的代理,将 proxied 方法声明为`final`。
这将阻止 Mockito 正常运行,因为它无法在其默认配置中模拟或监视`final`方法。,
如果你想模拟或监视这样的 Bean,通过将`org.mockito:mockito-inline`添加到应用程序的测试依赖项,将 Mockito 配置为使用其内联 mock maker。
这允许 Mockito 模拟和监视`final`方法。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,而`@MockBean`或`@SpyBean`的使用会影响缓存键,这很可能会增加上下文的数量。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |如果你使用`@SpyBean`监视带有`@Cacheable`方法的 Bean,该方法通过名称引用参数,则你的应用程序必须使用`-parameters`进行编译。
这将确保在监视了 Bean 之后,参数名称对缓存基础设施是可用的。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |当你使用`@SpyBean`监视由 Spring 代理的 Bean 时,在某些情况下可能需要删除 Spring 的代理,例如在使用`given`或`when`设置期望时。
使用`AopTestUtils.getTargetObject(yourProxiedSpy)`来这样做。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.11.自动配置的测试
Spring Boot 的自动配置系统在应用程序中运行良好,但有时在测试中可能会有点太多。通常情况下,只加载测试应用程序“部分”所需的配置部分是有帮助的。例如,你可能希望测试 Spring MVC 控制器是否正确映射 URL,并且你不希望在这些测试中涉及数据库调用,或者你可能希望测试 JPA 实体,并且你对这些测试运行时的 Web 层不感兴趣。
`spring-boot-test-autoconfigure`模块包括许多注释,可用于自动配置此类“切片”。它们都以类似的方式工作,提供一个`@…Test`注释,用于加载`ApplicationContext`和一个或多个`@AutoConfigure…`注释,这些注释可用于自定义自动配置设置。
| |每个切片将组件扫描限制为适当的组件,并加载一组非常受限制的自动配置类。
如果需要排除其中之一,大多数`@…Test`注释都提供了`excludeAutoConfiguration`属性。
或者,你可以使用`@ImportAutoConfiguration#exclude`。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |不支持在一个测试中使用多个`@…Test`注释来包含多个“切片”。
如果需要多个“切片”,请选择其中一个`@…Test`注释,并手动包含其他“切片”的`@AutoConfigure…`注释。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |也可以使用带有标准`@SpringBootTest`注释的`@AutoConfigure…`注释。
如果你对应用程序“切片”不感兴趣,但希望使用一些自动配置的测试 bean,则可以使用此组合。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.12.自动配置的 JSON 测试
要测试对象 JSON 序列化和反序列化是否如预期的那样工作,你可以使用`@JsonTest`注释。`@JsonTest`自动配置可用的受支持的 JSON 映射器,它可以是以下库之一:
* Jackson`ObjectMapper`,任何`@JsonComponent`bean 和任何 Jackson`Module`s
* `Gson`
* `Jsonb`
| |由`@JsonTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果需要配置自动配置的元素,可以使用`@AutoConfigureJsonTesters`注释。
Spring 引导包括基于 AssertJ 的帮助器,这些帮助器与 JSONASSERT 和 JSONPATH 库一起工作,以检查 JSON 是否如预期的那样出现。`JacksonTester`、`GsonTester`、`JsonbTester`和`BasicJsonTester`类可以分别用于 Jackson、GSON、JSONB 和字符串。当使用`@JsonTest`时,测试类上的任何 helper 字段都可以是`@Autowired`。下面的示例展示了一个用于 Jackson 的测试类:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester json;
@Test
void serialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
}
@Test
void deserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
```
| |JSON helper 类也可以在标准单元测试中直接使用。
要这样做,如果不使用`@JsonTest`方法,请在你的`initFields`方法中调用该 helper 的`initFields`方法。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果使用 Spring boot 的基于 AssertJ 的助手在给定的 JSON 路径上断言一个数字值,则可能无法根据类型使用`isEqualTo`。相反,你可以使用 AssertJ 的`satisfies`来断言该值匹配给定的条件。例如,下面的示例断言,在`0.01`的偏移量内,实际数字是一个接近`0.15`的浮点值。
```
@Test
void someTest() throws Exception {
SomeObject value = new SomeObject(0.152f);
assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
```
#### 8.3.13.自动配置的 Spring MVC 测试
要测试 Spring MVC 控制器是否如预期的那样工作,请使用`@WebMvcTest`注释。`@WebMvcTest`自动配置 Spring MVC 基础架构,并将扫描的 bean 限制为`@Controller`,`@ControllerAdvice`,`Converter`,`GenericConverter`,`Filter`,`HandlerInterceptor`,<1440"/>,,和”1442">。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@WebMvcTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。
| |`@WebMvcTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |如果需要注册额外的组件,例如 Jackson`Module`,则可以在测试中使用`@Import`导入额外的配置类。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------|
通常,`@WebMvcTest`仅限于单个控制器,并与`@MockBean`组合使用,以为所需的协作者提供模拟实现。
`@WebMvcTest`还自动配置`MockMvc`。Mock MVC 提供了一种强大的方法,可以快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。
| |你还可以在非-`@WebMvcTest`(例如`@SpringBootTest`)中自动配置`MockMvc`(例如`@SpringBootTest`),方法是用`@AutoConfigureMockMvc`对其进行注释。
下面的示例使用`MockMvc`:|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
}
```
| |如果需要配置自动配置的元素(例如,当应用 Servlet 过滤器时),可以在`@AutoConfigureMockMvc`注释中使用属性。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果使用 htmlUnit 和 Selenium,Auto-Configuration 还提供了一个 htmlUnit Bean 和/或一个 Selenium Bean。以下示例使用了 htmlUnit:
```
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
```
| |默认情况下, Spring boot 将`WebDriver`bean 放在一个特殊的“范围”中,以确保每次测试后驱动程序退出,并且注入了一个新的实例。
如果你不想要这种行为,可以将`@Scope("singleton")`添加到你的`WebDriver`定义中。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |由 Spring 引导创建的`webDriver`作用域将替换任何用户定义的同名作用域。
如果你定义自己的`webDriver`作用域,则当你使用`@WebMvcTest`时,你可能会发现它停止工作。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果在 Classpath 上具有 Spring 安全性,则`@WebMvcTest`也将扫描`WebSecurityConfigurer`bean。你可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。关于如何使用 Spring Security 的`MockMvc`支持的更多详细信息,可以在这个 *[howto.html](howto.html#howto.testing.with-spring-security)*how-to 部分中找到。
| |有时编写 Spring MVC 测试是不够的; Spring 引导可以帮助你运行[使用实际服务器进行完整的端到端测试](#features.testing.spring-boot-applications.with-running-server)。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.14.自动配置的 Spring WebFlux 测试
要测试[Spring WebFlux](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html)控制器是否按预期工作,可以使用`@WebFluxTest`注释。`@WebFluxTest`自动配置 Spring WebFlux 基础设施,并将扫描的 bean 限制为`@Controller`,`@ControllerAdvice`,`Converter`,`GenericConverter`,`WebFilter`,以及`WebFluxConfigurer`。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@WebFluxTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。
| |由`@WebFluxTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |如果需要注册额外的组件,例如 Jackson`Module`,则可以在测试中使用`@Import`导入额外的配置类。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------|
通常,`@WebFluxTest`仅限于单个控制器,并与`@MockBean`注释结合使用,以为所需的协作者提供模拟实现。
`@WebFluxTest`还自动配置[`WebTestClient`](https://DOCS. Spring.io/ Spring-Framework/DOCS/5.3.16/Reference/html/testing.html#WebTestClient),它提供了一种强大的方式,可以快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。
| |你还可以在非-`@WebFluxTest`(例如`@SpringBootTest`)中通过使用`@AutoConfigureWebTestClient`对其进行注释来自动配置`WebTestClient`。
下面的示例显示了一个同时使用`@WebFluxTest`和`WebTestClient`的类:|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.mockito.BDDMockito.given;
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Honda Civic");
}
}
```
| |此设置仅由 WebFlux 应用程序支持,因为在模拟 Web 应用程序中使用`WebTestClient`目前仅与 WebFlux 一起工作。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------|
| |`@WebFluxTest`无法检测通过 Functional Web Framework 注册的路由。
对于在上下文中测试`RouterFunction`bean,请考虑通过使用`@Import`或通过使用`@SpringBootTest`来导入你的`RouterFunction`自己。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |`@WebFluxTest`无法检测到注册为`@Bean`类型的`SecurityWebFilterChain`的自定义安全配置。
要在测试中包含该配置,你将需要通过使用`@Import`或通过使用`@SpringBootTest`导入注册 Bean 的配置。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |有时编写 Spring WebFlux 测试是不够的; Spring Boot 可以帮助你运行[使用实际服务器进行完整的端到端测试](#features.testing.spring-boot-applications.with-running-server)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.15.自动配置的数据 Cassandra 测试
你可以使用`@DataCassandraTest`来测试 Cassandra 应用程序。默认情况下,它配置`CassandraTemplate`,扫描`@Table`类,并配置 Spring 数据 Cassandra 存储库。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataCassandraTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。(关于使用 Spring boot 的 Cassandra 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.cassandra)”。
| |`@DataCassandraTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例展示了在 Spring Boot 中使用 Cassandra 测试的典型设置:
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}
```
#### 8.3.16.自动配置的数据 JPA 测试
你可以使用`@DataJpaTest`注释来测试 JPA 应用程序。默认情况下,它扫描`@Entity`类并配置 Spring 数据 JPA 存储库。如果嵌入式数据库在 Classpath 上可用,那么它也会配置一个。默认情况下,通过将`spring.jpa.show-sql`属性设置为`true`来记录 SQL 查询。可以使用注释的`showSql()`属性禁用此功能。
常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataJpaTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。
| |`@DataJpaTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
默认情况下,数据 JPA 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,你可以禁用测试或整个类的事务管理,如下所示:
```
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
```
数据 JPA 测试还可能注入一个[`TestEntityManager`](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-test-autofigure/SRC/main/java/org/springframework/boot/test/autoform/ JPA/testentitymanager.java) Bean,它为专门为测试设计的标准 JPA 提供了一种替代方案。
| |`TestEntityManager`还可以通过添加`@AutoConfigureTestEntityManager`自动配置到你的任何基于 Spring 的测试类。
这样做时,请确保你的测试是在事务中运行的,例如,在你的测试类或方法上添加`@Transactional`。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
如果需要的话,也可以使用`JdbcTemplate`。下面的示例显示了使用中的`@DataJpaTest`注释:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
class MyRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
void testExample() throws Exception {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getEmployeeNumber()).isEqualTo("1234");
}
}
```
内存中的嵌入式数据库通常可以很好地用于测试,因为它们速度快且不需要任何安装。但是,如果你更喜欢对真实的数据库运行测试,则可以使用`@AutoConfigureTestDatabase`注释,如下例所示:
```
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
```
#### 8.3.17.自动配置的 JDBC 测试
`@JdbcTest`类似于`@DataJpaTest`,但用于仅需要`DataSource`且不使用 Spring 数据 JDBC 的测试。默认情况下,它配置内存中的嵌入式数据库和`JdbcTemplate`。当使用`@JdbcTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。
| |`@JdbcTest`启用的自动配置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
默认情况下,JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,那么你可以禁用测试或整个类的事务管理,如下所示:
```
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {
}
```
如果你希望你的测试在真实的数据库中运行,那么你可以使用`@AutoConfigureTestDatabase`注释,就像使用`DataJpaTest`注释一样。(见“[Auto-configured Data JPA Tests](#features.testing.spring-boot-applications.autoconfigured-spring-data-jpa)”。
#### 8.3.18.自动配置的数据 JDBC 测试
`@DataJdbcTest`类似于`@JdbcTest`,但用于使用 Spring 数据 JDBC 存储库的测试。默认情况下,它配置内存中的嵌入式数据库、`JdbcTemplate`和 Spring 数据 JDBC 存储库。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataJdbcTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。
| |由`@DataJdbcTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
默认情况下,数据 JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,那么你可以禁用测试或整个测试类的事务管理,如[在 JDBC 示例中显示](#features.testing.spring-boot-applications.autoconfigured-jdbc)。
如果你希望你的测试在真实的数据库中运行,那么你可以使用`@AutoConfigureTestDatabase`注释,就像使用`DataJpaTest`注释一样。(见“[Auto-configured Data JPA Tests](#features.testing.spring-boot-applications.autoconfigured-spring-data-jpa)”。
#### 8.3.19.自动配置的 Jooq 测试
你可以以与`@JdbcTest`类似的方式使用`@JooqTest`,但用于与 Jooq 相关的测试。由于 Jooq 在很大程度上依赖于与数据库模式相对应的基于 Java 的模式,因此使用了现有的`DataSource`。如果要用内存数据库替换它,可以使用`@AutoConfigureTestDatabase`来覆盖这些设置。(有关使用 Spring boot 使用 Jooq 的更多信息,请参见本章前面的“[data.html](data.html#data.sql.jooq)”。)常规`@Component`和`@ConfigurationProperties`bean 在使用`@JooqTest`注释时不会被扫描。`@EnableConfigurationProperties`bean 可用于包括`@ConfigurationProperties`bean。
| |由`@JooqTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
`@JooqTest`配置`DSLContext`。下面的示例显示了正在使用的`@JooqTest`注释:
```
import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
@JooqTest
class MyJooqTests {
@Autowired
private DSLContext dslContext;
// ...
}
```
在默认情况下,Jooq 测试是事务性的,并在每个测试结束时回滚。如果这不是你想要的,那么你可以禁用测试或整个测试类的事务管理,如[在 JDBC 示例中显示](#features.testing.spring-boot-applications.autoconfigured-jdbc)。
#### 8.3.20.自动配置的数据 MongoDB 测试
你可以使用`@DataMongoTest`来测试 MongoDB 应用程序。默认情况下,它配置内存中的嵌入式 MongoDB(如果可用),配置`MongoTemplate`,扫描`@Document`类,并配置 Spring 数据 MongoDB 存储库。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataMongoTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。(有关使用 Spring Boot 的 MongoDB 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.mongodb)”。
| |`@DataMongoTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的类显示了使用中的`@DataMongoTest`注释:
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;
@DataMongoTest
class MyDataMongoDbTests {
@Autowired
private MongoTemplate mongoTemplate;
// ...
}
```
内嵌入内存的 MongoDB 通常在测试中运行良好,因为它速度快,并且不需要任何开发人员安装。但是,如果你更喜欢在真正的 MongoDB 服务器上运行测试,那么你应该排除嵌入的 MongoDB 自动配置,如下例所示:
```
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class MyDataMongoDbTests {
// ...
}
```
#### 8.3.21.自动配置的数据 NEO4J 测试
你可以使用`@DataNeo4jTest`来测试 NEO4J 应用程序。默认情况下,它扫描`@Node`类,并配置 Spring 数据 NEO4j 存储库。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataNeo4jTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。(有关在 Spring 引导下使用 NEO4j 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.neo4j)”。
| |`@DataNeo4jTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例显示了在 Spring 引导中使用 NEO4J 测试的典型设置:
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
@DataNeo4jTest
class MyDataNeo4jTests {
@Autowired
private SomeRepository repository;
// ...
}
```
默认情况下,数据 NEO4J 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,那么你可以禁用测试或整个类的事务管理,如下所示:
```
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {
}
```
| |反应性访问不支持事务性测试。
如果使用这种样式,则必须如上所述配置`@DataNeo4jTest`测试。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.22.自动配置的数据 Redis 测试
你可以使用`@DataRedisTest`来测试 Redis 应用程序。默认情况下,它扫描`@RedisHash`类并配置 Spring 数据 Redis 存储库。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataRedisTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。(有关在 Spring 引导下使用 Redis 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.redis)”。
| |`@DataRedisTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例显示了使用中的`@DataRedisTest`注释:
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
```
#### 8.3.23.自动配置的数据 LDAP 测试
你可以使用`@DataLdapTest`来测试 LDAP 应用程序。默认情况下,它配置内存中的嵌入式 LDAP(如果可用),配置`LdapTemplate`,扫描`@Entry`类,并配置 Spring 数据 LDAP 存储库。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@DataLdapTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。(有关在 Spring boot 中使用 LDAP 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.ldap)”。
| |`@DataLdapTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例显示了使用中的`@DataLdapTest`注释:
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;
@DataLdapTest
class MyDataLdapTests {
@Autowired
private LdapTemplate ldapTemplate;
// ...
}
```
内嵌入内存的 LDAP 通常在测试中运行良好,因为它速度快且不需要任何开发人员安装。但是,如果你更喜欢在真实的 LDAP 服务器上运行测试,则应该排除嵌入的 LDAP 自动配置,如下例所示:
```
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {
// ...
}
```
#### 8.3.24.自动配置的 REST 客户机
你可以使用`@RestClientTest`注释来测试 REST 客户机。默认情况下,它会自动配置 Jackson、GSON 和 JSONB 支持,配置`RestTemplateBuilder`,并添加对`MockRestServiceServer`的支持。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@RestClientTest`注释时不进行扫描。`@EnableConfigurationProperties`可以用于包括`@ConfigurationProperties`bean。
| |`@RestClientTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
要测试的特定 bean 应该使用`value`或`components`的`components`属性来指定,如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() throws Exception {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
```
#### 8.3.25.自动配置的 Spring REST DOCS 测试
在使用模拟 MVC、Rest Assured 或 WebTestClient 的测试中,可以使用`@AutoConfigureRestDocs`注释来使用[Spring REST Docs](https://spring.io/projects/spring-restdocs)。它消除了对 Spring REST DOCS 中的 JUnit 扩展的需要。
`@AutoConfigureRestDocs`可以用来覆盖默认的输出目录(`target/generated-snippets`如果你正在使用 Maven 或`build/generated-snippets`如果你正在使用 Gradle)。它还可以用于配置出现在任何有文档的 URI 中的主机、方案和端口。
##### Spring 使用模拟 MVC 的 REST DOCS 测试的自动配置
`@AutoConfigureRestDocs`在测试基于 Servlet 的 Web 应用程序时定制`MockMvc` Bean 以使用 Spring REST DOCS。你可以通过使用`@Autowired`注入它,并在测试中使用它,就像在使用模拟 MVC 和 Spring REST DOCS 时通常使用的那样,如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Autowired
private MockMvc mvc;
@Test
void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}
}
```
如果需要对 Spring REST DOCS 配置的控制比`@AutoConfigureRestDocs`的属性提供的更多,则可以使用`RestDocsMockMvcConfigurationCustomizer` Bean,如以下示例所示:
```
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
```
如果希望利用 Spring REST DOCS 对参数化输出目录的支持,则可以创建`RestDocumentationResultHandler` Bean。自动配置使用此结果处理程序调用`alwaysDo`,从而使每个`MockMvc`调用自动生成缺省片段。下面的示例显示了正在定义的`RestDocumentationResultHandler`:
```
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
```
##### 使用 WebTestClient 自动配置的 Spring REST DOCS 测试
`@AutoConfigureRestDocs`在测试反应性 Web 应用程序时也可以与`WebTestClient`一起使用。你可以通过使用`@Autowired`注入它,并在测试中使用它,就像在使用`@WebFluxTest`和 Spring REST DOCS 时通常使用它一样,如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void listUsers() {
this.webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(document("list-users"));
}
}
```
如果你需要对 Spring REST DOCS 配置的控制超过由`@AutoConfigureRestDocs`的属性提供的控制,则可以使用`RestDocsWebTestClientConfigurationCustomizer` Bean,如以下示例所示:
```
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {
@Override
public void customize(WebTestClientRestDocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}
}
```
如果希望利用 Spring REST DOCS 对参数化输出目录的支持,则可以使用`WebTestClientBuilderCustomizer`来为每个实体交换结果配置消费者。下面的示例显示了正在定义的这样的`WebTestClientBuilderCustomizer`:
```
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
public WebTestClientBuilderCustomizer restDocumentation() {
return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
}
}
```
##### Spring 具有 REST 保证的 REST DOCS 测试的自动配置
`@AutoConfigureRestDocs`生成一个`RequestSpecification` Bean,预先配置为使用 Spring REST DOCS,可用于你的测试。你可以通过使用`@Autowired`注入它,并在测试中使用它,就像在使用 REST ASSURED 和 Spring REST DOCS 时通常使用它一样,如以下示例所示:
```
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Test
void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
given(documentationSpec)
.filter(document("list-users"))
.when()
.port(port)
.get("/")
.then().assertThat()
.statusCode(is(200));
}
}
```
如果需要对 Spring REST DOCS 配置的控制超过由`@AutoConfigureRestDocs`的属性提供的控制,则可以使用`RestDocsRestAssuredConfigurationCustomizer` Bean,如以下示例所示:
```
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
```
#### 8.3.26.自动配置的 Spring Web 服务测试
##### 自动配置的 Spring Web 服务客户端测试
你可以使用`@WebServiceClientTest`来测试使用 Spring Web 服务项目调用 Web 服务的应用程序。默认情况下,它配置一个模拟`WebServiceServer` Bean 并自动定制你的`WebServiceTemplateBuilder`。(有关使用 Spring boot 的 Web 服务的更多信息,请参见本章前面的“[io.html](io.html#io.webservices)”。
| |`@WebServiceClientTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例显示了使用中的`@WebServiceClientTest`注释:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.xml.transform.StringSource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;
@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {
@Autowired
private MockWebServiceServer server;
@Autowired
private SomeWebService someWebService;
@Test
void mockServerCall() {
this.server
.expect(payload(new StringSource("")))
.andRespond(withPayload(new StringSource("200")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}
}
```
##### 自动配置的 Spring Web 服务服务器测试
你可以使用`@WebServiceServerTest`来测试使用 Spring Web 服务项目实现 Web 服务的应用程序。默认情况下,它配置了一个`MockWebServiceClient` Bean,可用于调用你的 Web 服务端点。(有关使用 Spring 引导的 Web 服务的更多信息,请参见本章前面的“[io.html](io.html#io.webservices)”。
| |`@WebServiceServerTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
下面的示例显示了使用中的`@WebServiceServerTest`注释:
```
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest;
import org.springframework.ws.test.server.MockWebServiceClient;
import org.springframework.ws.test.server.RequestCreators;
import org.springframework.ws.test.server.ResponseMatchers;
import org.springframework.xml.transform.StringSource;
@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {
@Autowired
private MockWebServiceClient client;
@Test
void mockServerCall() {
this.client
.sendRequest(RequestCreators.withPayload(new StringSource("")))
.andExpect(ResponseMatchers.payload(new StringSource("42")));
}
}
```
#### 8.3.27.附加的自动配置和切片
每个切片提供一个或多个`@AutoConfigure…`注释,即定义了作为切片的一部分应该包含的自动配置。通过创建自定义`@AutoConfigure…`注释或在测试中添加`@ImportAutoConfiguration`,可以在逐个测试的基础上添加额外的自动配置,如下例所示:
```
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {
}
```
| |确保不要使用常规的`@Import`注释来导入自动配置,因为它们是由 Spring 引导以特定方式处理的。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------|
或者,可以通过在`META-INF/spring.factories`中注册它们,为任何使用切片注释的情况添加额外的自动配置,如以下示例所示:
```
org.springframework.boot.test.autoconfigure.jdbc.JdbcTest=com.example.IntegrationAutoConfiguration
```
| |切片或`@AutoConfigure…`注释可以通过这种方式进行定制,只要它是用`@ImportAutoConfiguration`进行元注释的。|
|---|------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.28.用户配置和切片
如果以一种合理的方式[构造你的代码](using.html#using.structuring-your-code),你的`@SpringBootApplication`类是[默认使用](#features.testing.spring-boot-applications.detecting-configuration)作为测试的配置。
因此,重要的是,不要在应用程序的主类中浪费特定于其特定功能领域的配置设置。
假设你正在使用 Spring 批处理,并且你依赖于它的自动配置。你可以将你的`@SpringBootApplication`定义如下:
```
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {
// ...
}
```
因为这个类是测试的源配置,所以任何切片测试实际上都会尝试启动 Spring 批处理,而这绝对不是你想要做的。推荐的方法是将该区域特定的配置移动到与应用程序处于同一级别的单独的`@Configuration`类,如下例所示:
```
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class MyBatchConfiguration {
// ...
}
```
| |根据应用程序的复杂性,你可以有一个用于自定义的`@Configuration`类,或者每个域区域有一个类。
后一种方法允许你在你的一个测试中启用它,如果需要的话,使用`@Import`注释。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
测试片从扫描中排除`@Configuration`类。例如,对于`@WebMvcTest`,以下配置将不包括在测试切片加载的应用程序上下文中给定的`WebMvcConfigurer` Bean:
```
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {
@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
// ...
};
}
}
```
但是,下面的配置将导致测试切片加载自定义`WebMvcConfigurer`。
```
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
// ...
}
```
另一个令人困惑的原因是扫描。假设,当你以一种合理的方式构造代码时,你需要扫描一个额外的包。你的应用程序可能类似于以下代码:
```
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
```
这样做有效地覆盖了默认的组件扫描指令,其副作用是无论你选择的是哪一片,都要扫描这两个包。例如,`@DataJpaTest`似乎会突然扫描应用程序的组件和用户配置。同样,将自定义指令移动到一个单独的类是解决此问题的一个好方法。
| |如果这不是你的一个选项,那么你可以在测试的层次结构中的某个地方创建一个`@SpringBootConfiguration`,以便使用它。
或者,你可以为你的测试指定一个源,这将禁用查找缺省源的行为。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.3.29.使用 Spock 测试 Spring 启动应用程序
Spock2.x 可用于测试 Spring 引导应用程序。为此,在应用程序的构建中添加对 Spock 的`spock-spring`模块的依赖关系。`spock-spring`将 Spring 的测试框架集成到 Spock 中。有关更多详细信息,请参见[the documentation for Spock’s Spring module](https://spockframework.org/spock/docs/2.0/modules.html#_spring_module)。
### 8.4.测试实用程序
在测试应用程序时通常有用的一些测试实用程序类被打包为`spring-boot`的一部分。
#### 8.4.1.ConfigDataApplicationContextInitializer
`ConfigDataApplicationContextInitializer`是一个`ApplicationContextInitializer`,你可以将其应用到测试中以加载 Spring boot`application.properties`文件。当你不需要`@SpringBootTest`提供的全套功能时,可以使用它,如下例所示:
```
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}
```
| |单独使用`ConfigDataApplicationContextInitializer`并不提供对`@Value("${…}")`注入的支持。
其唯一的工作是确保`application.properties`文件被加载到 Spring 的`Environment`中,
对于`@Value`的支持,你需要另外配置一个`PropertySourcesPlaceholderConfigurer`,或者使用`@SpringBootTest`,它为你自动配置了一个。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 8.4.2.测试 PropertyValues
`TestPropertyValues`可以让你快速地将属性添加到`ConfigurableEnvironment`或`ConfigurableApplicationContext`。你可以使用`key=value`字符串调用它,如下所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
class MyEnvironmentTests {
@Test
void testPropertySources() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
assertThat(environment.getProperty("name")).isEqualTo("Boot");
}
}
```
#### 8.4.3.输出捕获
`OutputCapture`是一个 JUnit`Extension`,你可以使用它来捕获`System.out`和`System.err`输出。要使用添加`@ExtendWith(OutputCaptureExtension.class)`并将`CapturedOutput`作为参数注入到你的测试类构造函数或测试方法中,方法如下:
```
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
```
#### 8.4.4.测试模板
`TestRestTemplate`是 Spring 的`RestTemplate`的一种方便的替代方法,在集成测试中很有用。你可以获得一个普通模板或一个发送基本 HTTP 身份验证的模板(带有用户名和密码)。在这两种情况下,模板都是容错的。这意味着它以一种测试友好的方式运行,不会在 4xx 和 5xx 错误上抛出异常。相反,可以通过返回的`ResponseEntity`及其状态代码来检测此类错误。
| |Spring Framework5.0 提供了一个新的`WebTestClient`,它对[WebFlux 集成测试](#features.testing.spring-boot-applications.spring-webflux-tests)和[WebFlux 和 MVC 端到端测试](#features.testing.spring-boot-applications.with-running-server)都有效。
它为断言提供了一个流畅的 API,而不像`TestRestTemplate`。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
建议使用 Apache HTTP 客户机(版本 4.3.2 或更好),但不是强制的。如果你的 Classpath 上有这样的配置,则`TestRestTemplate`通过适当地配置客户机来进行响应。如果你确实使用了 Apache 的 HTTP 客户机,则启用了一些额外的测试友好特性:
* 重定向不会被跟踪(因此你可以断言响应位置)。
* cookies 被忽略(因此模板是无状态的)。
`TestRestTemplate`可以在集成测试中直接实例化,如以下示例所示:
```
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
class MyTests {
private TestRestTemplate template = new TestRestTemplate();
@Test
void testRequest() throws Exception {
ResponseEntity headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}
}
```
或者,如果使用`@SpringBootTest`注释和`WebEnvironment.RANDOM_PORT`或`WebEnvironment.DEFINED_PORT`,则可以插入一个完全配置的`TestRestTemplate`并开始使用它。如果需要,可以通过`RestTemplateBuilder` Bean 应用额外的自定义。任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如以下示例所示:
```
import java.time.Duration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration(proxyBeanMethods = false)
static class RestTemplateBuilderConfiguration {
@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}
```
## 9. 创建自己的自动配置
如果你在一家开发共享库的公司工作,或者在一家开放源代码或商业库工作,那么你可能希望开发自己的自动配置。自动配置类可以捆绑在外部 JAR 中,并且仍然可以在 Spring 启动时被拾取。
自动配置可以与一个“启动器”相关联,该启动器提供自动配置代码以及你将与之一起使用的典型库。我们首先介绍构建自己的自动配置所需的知识,然后继续讨论[创建自定义启动器所需的典型步骤](#features.developing-auto-configuration.custom-starter)。
| |可以使用[演示项目](https://github.com/snicoll-demos/spring-boot-master-auto-configuration)来展示如何一步一步地创建一个启动程序。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 9.1.理解自动配置的 bean
在引擎盖下,自动配置是通过标准的`@Configuration`类实现的。附加的`@Conditional`注释用于在应用自动配置时进行约束。通常,自动配置类使用`@ConditionalOnClass`和`@ConditionalOnMissingBean`注释。这确保了自动配置仅在找到相关类以及尚未声明自己的`@Configuration`时才适用。
你可以浏览[`spring-boot-autoconfigure`](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/org/springframework/boot/autofigure/autofigure)的源代码,查看 Spring 提供的`@Configuration`类(参见[`META-INF/spring.factories`](https://github.com/ Spring-projects/[[[[tree]-tree/6.6]-
### 9.2.定位自动配置候选项
Spring 引导检查在你发布的 jar 中是否存在`META-INF/spring.factories`文件。该文件应该在`EnableAutoConfiguration`键下列出你的配置类,如下例所示:
```
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
```
| |自动配置必须以*只有*的方式加载。
确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。
此外,自动配置类不应使组件扫描能够找到其他组件。应该使用
特定的`@Import`s。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
你可以使用[`@AutoConfigureAfter`](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autconfigure/SRC/main/java/org/SpringFramework/boot/autoConfigure/autoConfigure/autoafter.java.java)或[`@AutoConfigureBefore`](https:///github.com/ Spring-projects/[[ Spring-boot/tree/tree/v2.6.4/例如,如果你提供了特定于 Web 的配置,那么你的类可能需要在`WebMvcAutoConfiguration`之后应用。
如果你希望订购某些不应相互有任何直接了解的自动配置,也可以使用`@AutoConfigureOrder`。该注释与常规的`@Order`注释具有相同的语义,但为自动配置类提供了专用的顺序。
与标准的`@Configuration`类一样,应用自动配置类的顺序只会影响其 bean 的定义顺序。随后创建这些 bean 的顺序不受影响,并且由每个 Bean 的依赖关系和任何`@DependsOn`关系决定。
### 9.3.条件注释
你几乎总是希望在自动配置类中包含一个或多个`@Conditional`注释。`@ConditionalOnMissingBean`注释是一个常见的示例,如果开发人员对你的默认值不满意,它将允许他们覆盖自动配置。
Spring 引导包括许多`@Conditional`注释,你可以通过注释`@Configuration`类或单独的`@Bean`方法在自己的代码中重用这些注释。这些注释包括:
* [类条件](#features.developing-auto-configuration.condition-annotations.class-conditions)
* [Bean Conditions](#features.developing-auto-configuration.condition-annotations.bean-conditions)
* [财产条件](#features.developing-auto-configuration.condition-annotations.property-conditions)
* [资源条件](#features.developing-auto-configuration.condition-annotations.resource-conditions)
* [Web 应用程序条件](#features.developing-auto-configuration.condition-annotations.web-application-conditions)
* [SPEL 表达条件](#features.developing-auto-configuration.condition-annotations.spel-conditions)
#### 9.3.1.类条件
`@ConditionalOnClass`和`@ConditionalOnMissingClass`注释让`@Configuration`类基于特定类的存在或不存在而被包括。由于批注元数据是通过使用[ASM](https://asm.ow2.io/)进行解析的,因此你可以使用`value`属性来引用真正的类,即使该类实际上可能不会出现在正在运行的应用程序上 Classpath。如果你希望通过使用`String`值来指定类名,也可以使用`name`属性。
此机制不以同样的方式应用于`@Bean`方法,其中返回类型通常是条件的目标:在方法的条件应用之前,JVM 将装载类和可能处理的方法引用,如果类不存在,这些引用将失败。
要处理此场景,可以使用一个单独的`@Configuration`类来隔离该条件,如以下示例所示:
```
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
// Some conditions ...
public class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
}
```
| |如果使用`@ConditionalOnClass`或`@ConditionalOnMissingClass`作为元注释的一部分来编写自己的合成注释,则必须使用`name`作为在这种情况下未处理的类的引用。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 9.3.2. Bean 条件
`@ConditionalOnBean`和`@ConditionalOnMissingBean`注释允许基于特定 bean 的存在或不存在来包含 Bean。可以使用`value`属性按类型指定 bean,也可以使用`name`属性按名称指定 bean。`search`属性允许你限制在搜索 bean 时应该考虑的`ApplicationContext`层次结构。
当放置在`@Bean`方法上时,目标类型默认为该方法的返回类型,如以下示例所示:
```
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
```
在前面的示例中,如果`SomeService`类型的 Bean 已经包含在`ApplicationContext`中,则将创建`someService` Bean。
| |你需要非常小心添加 Bean 定义的顺序,因为这些条件是基于到目前为止已处理的内容进行评估的。,由于这个原因,在类级别上使用这些条件与用注释标记每个包含的`@Bean`方法之间的唯一区别是如果条件不匹配,则前者阻止将`@Configuration`类注册为 Bean。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |在声明`@Bean`方法时,在方法的返回类型中提供尽可能多的类型信息,例如,
,如果 Bean 的 Concrete 类实现了一个接口,那么 Bean 方法的返回类型应该是 Concrete 类而不是接口。在方法中提供尽可能多的类型信息在使用 Bean 条件时尤其重要,因为它们的评估只能依赖于在方法签名中可用的类型信息。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 9.3.3.财产条件
`@ConditionalOnProperty`注释允许基于 Spring Environment 属性包含配置。使用`prefix`和`name`属性来指定应该检查的属性。默认情况下,任何存在且不等于`false`的属性都是匹配的。还可以使用`havingValue`和`matchIfMissing`属性创建更高级的检查。
#### 9.3.4.资源条件
`@ConditionalOnResource`注释仅允许在存在特定资源时包含配置。可以通过使用通常的 Spring 约定来指定资源,如以下示例所示:`file:/home/user/test.dat`。
#### 9.3.5.Web 应用程序条件
`@ConditionalOnWebApplication`和`@ConditionalOnNotWebApplication`注释允许根据应用程序是否为“Web 应用程序”而包括配置。基于 Servlet 的 Web 应用程序是任何使用 Spring `WebApplicationContext`、定义`session`作用域或具有`ConfigurableWebEnvironment`的应用程序。反应性 Web 应用程序是任何使用`ReactiveWebApplicationContext`或具有`ConfigurableReactiveWebEnvironment`的应用程序。
`@ConditionalOnWarDeployment`注释允许根据应用程序是否是部署到容器中的传统 WAR 应用程序来包含配置。对于使用嵌入式服务器运行的应用程序,此条件将不匹配。
#### 9.3.6.SPEL 表达条件
`@ConditionalOnExpression`注释允许基于[Spel 表达式](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/core.html#expressions)的结果包含配置。
| |在表达式中引用 Bean 将导致 Bean 在上下文刷新处理中很早就被初始化。
因此, Bean 将不符合后处理的条件(例如配置属性绑定),并且其状态可能不完整。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 9.4.测试你的自动配置
自动配置可能受到许多因素的影响:用户配置(`@Bean`定义和`Environment`定制)、条件评估(存在特定库),以及其他因素。具体地说,每个测试都应该创建一个定义良好的`ApplicationContext`,它代表了这些定制的组合。`ApplicationContextRunner`提供了一种很好的实现方法。
`ApplicationContextRunner`通常被定义为用于收集基础、公共配置的测试类的字段。下面的示例确保始终调用`MyServiceAutoConfiguration`:
```
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
```
| |如果必须定义多个自动配置,则不需要对它们的声明进行排序,因为它们的调用顺序与运行应用程序时的顺序完全相同。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
每个测试都可以使用 Runner 来表示特定的用例。例如,下面的示例调用一个用户配置(`UserConfiguration`),并检查自动配置是否正确备份。调用`run`提供了一个可以与`AssertJ`一起使用的回调上下文。
```
@Test
void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
MyService myCustomService() {
return new MyService("mine");
}
}
```
也可以方便地定制`Environment`,如以下示例所示:
```
@Test
void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
});
}
```
运行程序还可以用来显示`ConditionEvaluationReport`。报告可以在`INFO`或`DEBUG`级别打印。下面的示例展示了如何使用`ConditionEvaluationReportLoggingListener`在自动配置测试中打印报告。
```
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class MyConditionEvaluationReportingTests {
@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}
```
#### 9.4.1.模拟 Web 上下文
如果需要测试仅在 Servlet 或反应性 Web 应用程序上下文中操作的自动配置,则分别使用`WebApplicationContextRunner`或`ReactiveWebApplicationContextRunner`。
#### 9.4.2.超越 Classpath
也可以测试在运行时不存在特定类和/或包时会发生什么。 Spring 具有`FilteredClassLoader`的引导船,该引导船可以很容易地被跑步者使用。在下面的示例中,我们断言,如果`MyService`不存在,则自动配置将被正确禁用:
```
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
.run((context) -> assertThat(context).doesNotHaveBean("myService"));
}
```
### 9.5.创建自己的启动器
典型的引导启动器包含自动配置和自定义给定技术基础设施的代码,我们将其称为“Acme”。为了使其易于扩展,可以将专用名称空间中的许多配置键公开到环境中。最后,提供了一个单一的“启动”依赖项,以帮助用户尽可能轻松地启动。
具体地说,自定义启动器可以包含以下内容:
* `autoconfigure`模块,其中包含“acme”的自动配置代码。
* `starter`模块提供了对`autoconfigure`模块的依赖关系,以及“acme”和任何通常有用的附加依赖关系。简而言之,添加 starter 应该提供开始使用该库所需的一切。
这种在两个模块中的分离是绝对没有必要的。如果“Acme”有几种口味、选项或可选功能,那么最好分离自动配置,因为你可以清楚地表达这样一个事实,即某些功能是可选的。此外,你还可以创建一个启动器,提供有关这些可选依赖项的意见。同时,其他人只能依赖`autoconfigure`模块,并根据不同的意见制作自己的启动器。
如果自动配置相对简单,并且没有可选功能,那么合并启动器中的两个模块肯定是一个选项。
#### 9.5.1.命名
你应该确保为你的启动器提供一个正确的名称空间。即使使用不同的 Maven `groupId`,也不要以`spring-boot`开始模块名称。我们可能会在将来为你的自动配置提供官方支持。
根据经验,你应该以 starter 命名一个组合模块。例如,假设你正在为“acme”创建一个启动器,并且将自动配置模块命名为`acme-spring-boot`,并将启动器命名为`acme-spring-boot-starter`。如果只有一个模块合并了这两个模块,请将其命名为`acme-spring-boot-starter`。
#### 9.5.2.配置键
如果你的启动器提供了配置键,请为它们使用唯一的命名空间。特别是,不要在 Spring 引导使用的名称空间中包含你的键(例如`server`,`management`,`spring`,等等)。如果使用相同的名称空间,将来我们可能会以破坏模块的方式修改这些名称空间。作为一条经验法则,在所有的键前加上一个你拥有的名称空间(例如`acme`)。
通过为每个属性添加字段 Javadoc,确保配置键是有文档记录的,如下例所示:
```
import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("acme")
public class AcmeProperties {
/**
* Whether to check the location of acme resources.
*/
private boolean checkLocation = true;
/**
* Timeout for establishing a connection to the acme server.
*/
private Duration loginTimeout = Duration.ofSeconds(3);
// getters/setters ...
public boolean isCheckLocation() {
return this.checkLocation;
}
public void setCheckLocation(boolean checkLocation) {
this.checkLocation = checkLocation;
}
public Duration getLoginTimeout() {
return this.loginTimeout;
}
public void setLoginTimeout(Duration loginTimeout) {
this.loginTimeout = loginTimeout;
}
}
```
| |你应该只在`@ConfigurationProperties`字段 Javadoc 中使用纯文本,因为它们在被添加到 JSON 之前不会被处理。|
|---|------------------------------------------------------------------------------------------------------------------------------------------|
以下是我们内部遵循的一些规则,以确保描述一致:
* 不要以“the”或“a”开头描述。
* 对于`boolean`类型,以“是否”或“启用”开始描述。
* 对于基于集合的类型,以“逗号分隔的列表”开始描述。
* 使用`java.time.Duration`而不是`long`,并描述缺省单位,如果它与毫秒不同,例如“如果没有指定持续时间后缀,将使用秒”。
* 不要在描述中提供默认值,除非它必须在运行时确定。
请确保[触发元数据生成](configuration-metadata.html#appendix.configuration-metadata.annotation-processor),以便你的密钥也可以使用 IDE 辅助。你可能想要查看生成的元数据(`META-INF/spring-configuration-metadata.json`),以确保你的密钥被正确地记录。在兼容的 IDE 中使用自己的启动器也是验证元数据质量的一个好主意。
#### 9.5.3.“自动配置”模块
`autoconfigure`模块包含了启动该库所需的所有内容。它还可能包含配置键定义(例如`@ConfigurationProperties`)和任何回调接口,可用于进一步定制组件的初始化方式。
| |你应该将库的依赖关系标记为可选的,这样你就可以更容易地在项目中包含`autoconfigure`模块。
如果你这样做,那么库就不会被提供,并且在默认情况下, Spring back off。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
Spring 引导使用注释处理器来收集元数据文件(`META-INF/spring-autoconfigure-metadata.properties`)中关于自动配置的条件。如果该文件存在,它将被用来急切地过滤不匹配的自动配置,这将提高启动时间。建议在包含自动配置的模块中添加以下依赖项:
```
org.springframework.boot
spring-boot-autoconfigure-processor
true
```
如果直接在应用程序中定义了自动配置,请确保配置`spring-boot-maven-plugin`,以防止`repackage`目标将依赖项添加到 fat jar 中:
```
org.springframework.boot
spring-boot-maven-plugin
org.springframework.boot
spring-boot-autoconfigure-processor
```
对于 Gradle 4.5 或更早的版本,依赖关系应该在`compileOnly`配置中声明,如以下示例所示:
```
dependencies {
compileOnly "org.springframework.boot:spring-boot-autoconfigure-processor"
}
```
对于 Gradle 4.6 及更高版本,依赖关系应该在`annotationProcessor`配置中声明,如以下示例所示:
```
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
```
#### 9.5.4.启动器模块
起动器实际上是一个空的 jar。它的唯一目的是提供使用该库所需的依赖项。你可以把它看作是对开始工作需要什么的一种固执己见的看法。
不要对添加了启动器的项目进行假设。如果你正在自动配置的库通常需要其他启动器,那么也应该提及它们。如果可选依赖关系的数量很高,那么提供一组适当的*默认值*依赖关系可能会很困难,因为你应该避免包含对于库的典型使用来说不必要的依赖关系。换句话说,你不应该包括可选的依赖关系。
| |无论哪种方式,你的启动程序都必须直接或间接地引用核心 Spring 启动程序(`spring-boot-starter`)(如果你的启动程序依赖于另一个启动程序,则无需添加它),如果仅使用你的自定义启动程序创建项目,则
, Spring Boot 的核心功能将因核心启动器的出现而受到尊重。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## 10. Kotlin 支持
[Kotlin](https://kotlinlang.org)是一种针对 JVM(和其他平台)的静态类型语言,它允许编写简洁而优雅的代码,同时提供[互操作性](https://kotlinlang.org/docs/reference/java-interop.html)用 Java 编写的现有库。
Spring Boot 通过利用其他 Spring 项目中的支持来提供 Kotlin 支持,例如 Spring 框架、 Spring 数据和反应堆。有关更多信息,请参见[Spring Framework Kotlin support documentation](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/languages.html#kotlin)。
启动 Spring boot 和 Kotlin 的最简单方法是遵循[这个全面的教程](https://spring.io/guides/tutorials/spring-boot-kotlin/)。你可以使用[start.spring.io](https://start.spring.io/#!language=kotlin)创建新的 Kotlin 项目。如果你需要支持,可以随时加入[Kotlin Slack](https://slack.kotlinlang.org/)的 # Spring 频道,或者使用`spring`和`kotlin`标签在[堆栈溢出](https://stackoverflow.com/questions/tagged/spring+kotlin)上提问。
### 10.1.所需经费
Spring 启动需要至少 Kotlin 1.3.x 并且通过依赖管理管理管理管理合适的 Kotlin 版本。要使用 Kotlin,`org.jetbrains.kotlin:kotlin-stdlib`和`org.jetbrains.kotlin:kotlin-reflect`必须存在于 Classpath 上。也可以使用`kotlin-stdlib`变种`kotlin-stdlib-jdk7`和`kotlin-stdlib-jdk8`。
由于[Kotlin classes are final by default](https://discuss.kotlinlang.org/t/classes-final-by-default/166),你可能想要配置[kotlin-spring](https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support)插件,以便自动打开 Spring-注释的类,以便可以代理它们。
在 Kotlin 中,序列化/反序列化 JSON 数据需要[Jackson’s Kotlin module](https://github.com/FasterXML/jackson-module-kotlin)。当在 Classpath 上找到它时,它会自动注册。如果 Jackson 和 Kotlin 存在但 Jackson Kotlin 模块不存在,则记录警告消息。
| |默认情况下,如果在[start.spring.io](https://start.spring.io/#!language=kotlin)上引导一个 Kotlin 项目,就会提供这些依赖项和插件。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------|
### 10.2.零安全
Kotlin 的关键特征之一是[零安全](https://kotlinlang.org/docs/reference/null-safety.html)。它在编译时处理`null`值,而不是将问题推迟到运行时并遇到`NullPointerException`。这有助于消除常见的错误源,而无需支付`Optional`之类包装器的费用。 Kotlin 还允许使用如在此[comprehensive guide to null-safety in Kotlin](https://www.baeldung.com/kotlin-null-safety)中所描述的具有可空的值的函数构造。
虽然 Java 不允许在其类型系统中表示空安全,但 Spring Framework、 Spring Data 和 Reactor 现在通过对工具友好的注释为其 API 提供了空安全。默认情况下,来自 Kotlin 中使用的 Java API 的类型被识别为[平台类型](https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types),对其放松了空检查。[Kotlin’s support for JSR 305 annotations](https://kotlinlang.org/docs/reference/java-interop.html#jsr-305-support)与空性注释相结合,为 Kotlin 中相关的 Spring API 提供了空安全性。
可以通过添加带有以下选项的`-Xjsr305`编译器标志来配置 JSR305 检查:`-Xjsr305={strict|warn|ignore}`。默认行为与`-Xjsr305=warn`相同。在从 Spring API 推断出的 Kotlin 类型中,`strict`值必须考虑到空安全性,但在使用时应了解 Spring API 的无效性声明即使在较小的版本之间也可以发展,并且将来可能会添加更多的检查)。
| |还不支持泛型类型参数、varargs 和数组元素的可空性。
有关最新信息,请参见[SPR-15942](https://jira.spring.io/browse/SPR-15942)。
还请注意 Spring Boot 自己的 API 是[尚未注释](https://github.com/spring-projects/spring-boot/issues/10712)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 10.3. Kotlin 空气污染指数
#### 10.3.1.运行应用程序
Spring Boot 提供了一种惯用的方式来运行带有`runApplication(*args)`的应用程序,如以下示例所示:
```
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array) {
runApplication(*args)
}
```
这是`SpringApplication.run(MyApplication::class.java, *args)`的直接替换。它还允许定制应用程序,如以下示例所示:
```
runApplication(*args) {
setBannerMode(OFF)
}
```
#### 10.3.2.扩展
Kotlin [extensions](https://kotlinlang.org/docs/reference/extensions.html)提供了扩展具有附加功能的现有类的能力。 Spring 引导 Kotlin API 利用这些扩展为现有的 API 添加新的 Kotlin 特定的便利。
`TestRestTemplate`扩展,类似于 Spring 框架中为`RestOperations`提供的扩展,在 Spring 框架中。在其他事情中,这些扩展使得有可能利用 Kotlin 具体化的类型参数。
### 10.4.依赖管理
为了避免混合 Kotlin 依赖于 Classpath 的不同版本, Spring 引导导入 Kotlin BOM。
有了 Maven, Kotlin 版本可以通过设置`kotlin.version`属性来定制,并且为`kotlin-maven-plugin`提供插件管理。在 Gradle 中, Spring 引导插件自动将`kotlin.version`与 Kotlin 插件的版本对齐。
Spring Boot 还通过导入 Kotlin 协程 BOM 来管理协程依赖的版本。可以通过设置`kotlin-coroutines.version`属性来定制版本。
| |如果一个 Kotlin 项目至少对[start.spring.io](https://start.spring.io/#!language=kotlin)具有一个反应性依赖项,则默认情况下提供`org.jetbrains.kotlinx:kotlinx-coroutines-reactor`依赖项。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 10.5.@configrationProperties
`@ConfigurationProperties`与[`@ConstructorBinding`](#features.external-config.typesafe-configuration-properties.constructor-binding)结合使用时,支持具有不可变`val`属性的类,如以下示例所示:
```
@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {
data class MyService(
val apiToken: String,
val uri: URI
)
}
```
| |要使用注释处理器生成[你自己的元数据](configuration-metadata.html#appendix.configuration-metadata.annotation-processor),[`kapt`应该配置](https://kotlinlang.org/DOCS/reference/kapt.html)与`spring-boot-configuration-processor`依赖关系。
注意,由于 Kapt 提供的模型中的限制,一些功能(例如检测默认值或不推荐项)不起作用。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### 10.6.测试
虽然可以使用 JUnit4 来测试 Kotlin 代码,但 JUnit5 是默认提供的,并且是推荐的。JUnit5 允许一个测试类被实例化一次,并在类的所有测试中重用。这使得在非静态方法上使用`@BeforeAll`和`@AfterAll`注释成为可能,这非常适合 Kotlin。
要模拟 Kotlin 类,建议使用[MockK](https://mockk.io/)。如果你需要`Mockk`特定于 mockito 的[`@MockBean`和`@SpyBean`注释](#features.testing. Spring-boot-applications.mocking-beans)的等价物,则可以使用[斯普林莫克](https://github.com/Ninja-Squad/springmockk),它提供类似的`@MockkBean`和`@SpykBean`注释。
### 10.7.资源
#### 10.7.1.进一步阅读
* [Kotlin language reference](https://kotlinlang.org/docs/reference/)
* [Kotlin Slack](https://kotlinlang.slack.com/)(带专用 # Spring 频道)
* [Stackoverflow with`spring`and`kotlin`tags](https://stackoverflow.com/questions/tagged/ Spring + Kotlin)
* [Try Kotlin in your browser](https://try.kotlinlang.org/)
* [Kotlin blog](https://blog.jetbrains.com/kotlin/)
* [Awesome Kotlin](https://kotlin.link/)
* [Tutorial: building web applications with Spring Boot and Kotlin](https://spring.io/guides/tutorials/spring-boot-kotlin/)
* [Developing Spring Boot applications with Kotlin](https://spring.io/blog/2016/02/15/developing-spring-boot-applications-with-kotlin)
* [A Geospatial Messenger with Kotlin, Spring Boot and PostgreSQL](https://spring.io/blog/2016/03/20/a-geospatial-messenger-with-kotlin-spring-boot-and-postgresql)
* [Introducing Kotlin support in Spring Framework 5.0](https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0)
* [Spring Framework 5 Kotlin APIs, the functional way](https://spring.io/blog/2017/08/01/spring-framework-5-kotlin-apis-the-functional-way)
#### 10.7.2.例子
* [spring-boot-kotlin-demo](https://github.com/sdeleuze/spring-boot-kotlin-demo):常规 Spring 引导 + Spring 数据 JPA 项目
* [mixit](https://github.com/mixitconf/mixit): Spring Boot2+WebFlux+Reactive Spring Data MongoDB
* [spring-kotlin-fullstack](https://github.com/sdeleuze/spring-kotlin-fullstack):使用 Kotlin2js 代替 JavaScript 或 TypeScript 作为前端的 webflux Kotlin fullstack 示例
* [spring-petclinic-kotlin](https://github.com/spring-petclinic/spring-petclinic-kotlin): Spring PetClinic 样本应用程序的 Kotlin 版本
* [spring-kotlin-deepdive](https://github.com/sdeleuze/spring-kotlin-deepdive):将 Boot1.0+Java 逐步迁移到 Boot2.0+ Kotlin
* [spring-boot-coroutines-demo](https://github.com/sdeleuze/spring-boot-coroutines-demo):协程样本项目
## 11. 接下来要读什么?
如果你想了解更多关于本节中讨论的任何类的信息,请参见[Spring Boot API documentation](https://docs.spring.io/spring-boot/docs/2.6.4/api/),或者你也可以浏览[源代码直接](https://github.com/spring-projects/spring-boot/tree/v2.6.4)。如果你有具体的问题,请参阅[how-to](howto.html#howto)部分。
如果你对 Spring Boot 的核心功能感到满意,那么你可以继续阅读[可投入生产的功能](actuator.html#actuator)。