本节深入探讨 Spring Boot 的细节。在这里您可以了解您可能想要使用和自定义的关键功能。如果您还没有这样做,您可能需要阅读“入门”和“使用 Spring Boot 进行开发”部分,以便您有良好的基础知识。
1.Spring应用程序
该类SpringApplication
提供了一种方便的方法来引导从方法启动的 Spring 应用程序main()
。在许多情况下,您可以委托给静态SpringApplication.run
方法,如以下示例所示:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
当您的应用程序启动时,您应该看到类似于以下输出的内容:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.1.5) 2023-10-19T12:18:10.851Z INFO 37470 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 17.0.9 with PID 37470 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-10-19T12:18:10.856Z INFO 37470 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-10-19T12:18:12.277Z INFO 37470 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-10-19T12:18:12.298Z INFO 37470 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-10-19T12:18:12.298Z INFO 37470 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15] 2023-10-19T12:18:12.560Z INFO 37470 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-10-19T12:18:12.567Z INFO 37470 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1649 ms 2023-10-19T12:18:13.238Z INFO 37470 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-10-19T12:18:13.250Z INFO 37470 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.831 seconds (process running for 3.163)
默认情况下,INFO
会显示日志消息,包括一些相关的启动详细信息,例如启动应用程序的用户。如果您需要除 之外的日志级别INFO
,您可以设置它,如日志级别中所述。应用程序版本是使用主应用程序类包中的实现版本确定的。spring.main.log-startup-info
可以通过设置为来关闭启动信息记录false
。这也将关闭应用程序活动配置文件的日志记录。
要在启动期间添加额外的日志记录,您可以logStartupInfo(boolean) 在SpringApplication .
|
1.1. 启动失败
如果您的应用程序无法启动,注册者FailureAnalyzers
有机会提供专门的错误消息和解决问题的具体操作。例如,如果您在端口上启动 Web 应用程序8080
并且该端口已在使用中,您应该会看到类似于以下消息的内容:
*************************** 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 实现,您可以添加自己的.
|
如果没有故障分析器能够处理异常,您仍然可以显示完整的情况报告,以更好地了解出了什么问题。为此,您需要启用该debug
属性或启用DEBUG
.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 的堆大小。
可以使用lazyInitialization
on 方法SpringApplicationBuilder
或setLazyInitialization
on 方法以编程方式启用延迟初始化SpringApplication
。或者,可以使用spring.main.lazy-initialization
属性来启用它,如以下示例所示:
spring.main.lazy-initialization=true
spring:
main:
lazy-initialization: true
如果要禁用某些 bean 的延迟初始化,同时对应用程序的其余部分使用延迟初始化,则可以使用注释显式将其延迟属性设置为 false @Lazy(false) 。
|
1.3. 自定义横幅
banner.txt
可以通过将文件添加到类路径或将spring.banner.location
属性设置为此类文件的位置来更改启动时打印的横幅。如果文件的编码不是 UTF-8,您可以设置spring.banner.charset
.
在banner.txt
文件中,您可以使用 中可用的任何键Environment
以及以下任何占位符:
多变的 | 描述 |
---|---|
|
您的应用程序的版本号,如 中声明的那样 |
|
您的应用程序的版本号,如 中 声明的 |
|
您正在使用的 Spring Boot 版本。例如 |
|
您正在使用的 Spring Boot 版本,已格式化以供显示(用方括号括起来并以 为前缀 |
|
|
|
您的申请的标题,如 中声明的那样 |
SpringApplication.setBanner(…) 如果您想以编程方式生成横幅,则可以使用
该方法。使用该org.springframework.boot.Banner 接口并实现您自己的printBanner() 方法。
|
您还可以使用该spring.main.banner-mode
属性来确定是否必须打印横幅System.out
( console
)、发送到配置的记录器 ( log
),还是根本不生成横幅 ( off
)。
打印的横幅以以下名称注册为单例 bean:springBootBanner
。
仅当您使用 Spring Boot 启动器时, 这就是为什么我们建议您始终使用 启动未打包的 jar |
1.4. 定制 SpringApplication
如果SpringApplication
默认设置不符合您的口味,您可以创建一个本地实例并对其进行自定义。例如,要关闭横幅,您可以编写:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
}
}
传递给构造函数的参数SpringApplication 是 Spring bean 的配置源。在大多数情况下,这些是对@Configuration 类的引用,但它们也可以是直接引用@Component 类。
|
也可以SpringApplication
使用application.properties
文件进行配置。有关详细信息,请参阅外部化配置。
有关配置选项的完整列表,请参阅SpringApplication
Javadoc。
1.5. Fluent 构建器 API
如果您需要构建ApplicationContext
层次结构(具有父/子关系的多个上下文),或者如果您更喜欢使用“流畅”构建器 API,则可以使用SpringApplicationBuilder
.
允许SpringApplicationBuilder
您将多个方法调用链接在一起,并包含parent
可child
让您创建层次结构的方法,如以下示例所示:
new SpringApplicationBuilder().sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
SpringApplicationBuilder()
.sources(Parent::class.java)
.child(Application::class.java)
.bannerMode(Banner.Mode.OFF)
.run(*args)
创建层次结构时存在一些限制ApplicationContext 。例如,Web 组件必须包含在子上下文中,并且这同样Environment 适用于父上下文和子上下文。有关完整详细信息,
请参阅SpringApplicationBuilder Javadoc 。 |
1.6. 应用程序可用性
当部署在平台上时,应用程序可以使用Kubernetes Probes等基础设施向平台提供有关其可用性的信息。Spring Boot 包括对常用的“活跃”和“就绪”可用性状态的开箱即用支持。如果您使用 Spring Boot 的“执行器”支持,那么这些状态将作为运行状况端点组公开。
此外,您还可以通过将ApplicationAvailability
接口注入到您自己的bean中来获取可用性状态。
1.6.1. 活性状态
应用程序的“活跃”状态表明其内部状态是否允许其正常工作,或者在当前发生故障时自行恢复。损坏的“Liveness”状态意味着应用程序处于无法恢复的状态,基础设施应该重新启动应用程序。
一般来说,“Liveness”状态不应该基于外部检查,例如Health 检查。如果确实如此,发生故障的外部系统(数据库、Web API、外部缓存)将触发整个平台的大规模重启和级联故障。 |
Spring Boot 应用程序的内部状态主要由 Spring 表示ApplicationContext
。如果应用程序上下文已成功启动,Spring Boot 会假定应用程序处于有效状态。一旦上下文刷新,应用程序就被视为处于活动状态,请参阅Spring Boot 应用程序生命周期和相关应用程序事件。
1.6.2. 准备状态
应用程序的“就绪”状态表明应用程序是否已准备好处理流量。失败的“就绪”状态告诉平台它现在不应该将流量路由到应用程序。这通常发生在启动期间、处理组件时CommandLineRunner
,ApplicationRunner
或者在应用程序认为太忙而无法承受额外流量的任何时间。
一旦调用应用程序和命令行运行程序,应用程序就被视为准备就绪,请参阅Spring Boot 应用程序生命周期和相关应用程序事件。
预计在启动期间运行的任务应该由CommandLineRunner 和ApplicationRunner 组件执行,而不是使用 Spring 组件生命周期回调,例如@PostConstruct .
|
1.6.3. 管理应用程序可用性状态
ApplicationAvailability
应用程序组件可以通过注入接口并调用其方法来随时检索当前的可用性状态。更常见的是,应用程序希望监听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的“Readiness”状态导出到一个文件,以便 Kubernetes“exec Probe”可以查看该文件:
@Component
public class MyReadinessStateExporter {
@EventListener
public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
switch (event.getState()) {
case ACCEPTING_TRAFFIC:
// create file /tmp/healthy
break;
case REFUSING_TRAFFIC:
// remove file /tmp/healthy
break;
}
}
}
@Component
class MyReadinessStateExporter {
@EventListener
fun onStateChange(event: AvailabilityChangeEvent<ReadinessState?>) {
when (event.state) {
ReadinessState.ACCEPTING_TRAFFIC -> {
// create file /tmp/healthy
}
ReadinessState.REFUSING_TRAFFIC -> {
// remove file /tmp/healthy
}
else -> {
// ...
}
}
}
}
当应用程序中断且无法恢复时,我们还可以更新应用程序的状态:
@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);
}
}
}
@Component
class MyLocalCacheVerifier(private val eventPublisher: ApplicationEventPublisher) {
fun checkLocalCache() {
try {
// ...
} catch (ex: CacheCompletelyBrokenException) {
AvailabilityChangeEvent.publish(eventPublisher, ex, LivenessState.BROKEN)
}
}
}
Spring Boot 通过 Actuator Health Endpoints 提供Kubernetes HTTP 探测“Liveness”和“Readiness”。您可以在专门部分中获得有关在 Kubernetes 上部署 Spring Boot 应用程序的更多指导。
1.7. 应用程序事件和侦听器
除了常见的 Spring 框架事件(例如 )之外ContextRefreshedEvent
,aSpringApplication
还会发送一些其他应用程序事件。
有些事件实际上是在 如果您希望自动注册这些侦听器,无论应用程序的创建方式如何,您都可以将文件添加 org.springframework.context.ApplicationListener=com.example.project.MyListener |
当您的应用程序运行时,应用程序事件按以下顺序发送:
-
An
ApplicationStartingEvent
在运行开始时但在任何处理之前发送,监听器和初始化程序的注册除外。 -
当已知上下文中要使用的 时但在创建上下文之前,
ApplicationEnvironmentPreparedEvent
会发送An 。Environment
-
当准备好并且调用 ApplicationContextInitializers 时但在加载任何 bean 定义之前
ApplicationContextInitializedEvent
发送An 。ApplicationContext
-
An
ApplicationPreparedEvent
在刷新开始之前、bean 定义加载之后发送。 -
ApplicationStartedEvent
在刷新上下文之后但在调用任何应用程序和命令行运行程序之前发送An 。 -
AvailabilityChangeEvent
在 with 之后立即发送,LivenessState.CORRECT
表示该应用程序被视为有效。 -
在调用任何应用程序和命令行运行程序
ApplicationReadyEvent
后发送An 。 -
AvailabilityChangeEvent
紧随 with 之后发送,表示ReadinessState.ACCEPTING_TRAFFIC
应用程序已准备好处理请求。 -
ApplicationFailedEvent
如果启动时出现异常,则会发送一条消息。
上面的列表仅包含SpringApplicationEvent
与 绑定的 s SpringApplication
。除此之外,以下事件也在 之前ApplicationPreparedEvent
和之后发布ApplicationStartedEvent
:
-
A准备好
WebServerInitializedEvent
后发送WebServer
。ServletWebServerInitializedEvent
和ReactiveWebServerInitializedEvent
分别是 servlet 和响应式变体。 -
当 an刷新
ContextRefreshedEvent
时发送A。ApplicationContext
您通常不需要使用应用程序事件,但知道它们的存在会很方便。在内部,Spring Boot 使用事件来处理各种任务。 |
事件侦听器不应运行可能很长的任务,因为默认情况下它们在同一线程中执行。考虑使用应用程序和命令行运行程序。 |
应用程序事件是通过使用Spring框架的事件发布机制来发送的。此机制的一部分确保发布到子上下文中的侦听器的事件也发布到任何祖先上下文中的侦听器。因此,如果您的应用程序使用SpringApplication
实例层次结构,侦听器可能会接收同一类型应用程序事件的多个实例。
为了允许您的侦听器区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现注入上下文ApplicationContextAware
,或者如果侦听器是 bean,则可以使用@Autowired
.
1.8. 网络环境
A尝试代表您SpringApplication
创建正确的类型。ApplicationContext
用于确定 a 的算法WebApplicationType
如下:
-
如果存在 Spring MVC,则
AnnotationConfigServletWebServerApplicationContext
使用 -
如果 Spring MVC 不存在并且 Spring WebFlux 存在,则
AnnotationConfigReactiveWebServerApplicationContext
使用an -
否则,
AnnotationConfigApplicationContext
使用
这意味着,如果您WebClient
在同一应用程序中使用 Spring MVC 和 Spring WebFlux 中的新功能,则默认情况下将使用 Spring MVC。您可以通过调用轻松覆盖它setWebApplicationType(WebApplicationType)
。
还可以通过ApplicationContext
调用来完全控制所使用的类型setApplicationContextFactory(…)
。
在 JUnit 测试中
setWebApplicationType(WebApplicationType.NONE) 使用时
通常需要调用。SpringApplication |
1.9. 访问应用程序参数
如果您需要访问传递给 的应用程序参数SpringApplication.run(…)
,您可以注入一个org.springframework.boot.ApplicationArguments
bean。该ApplicationArguments
接口提供对原始String[]
参数以及解析后的option
参数的访问non-option
,如以下示例所示:
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
@Component
class MyBean(args: ApplicationArguments) {
init {
val debug = args.containsOption("debug")
val files = args.nonOptionArgs
if (debug) {
println(files)
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
Spring Boot 还CommandLinePropertySource 向 Spring注册了一个Environment . 这使您还可以使用@Value 注释注入单个应用程序参数。
|
1.10. 使用 ApplicationRunner 或 CommandLineRunner
如果您需要在SpringApplication
启动后运行某些特定代码,您可以实现ApplicationRunner
或CommandLineRunner
接口。两个接口以相同的方式工作,并提供一个run
方法,该方法在完成之前调用SpringApplication.run(…)
。
该合约非常适合应在应用程序启动后但开始接受流量之前运行的任务。 |
这些CommandLineRunner
接口以字符串数组的形式提供对应用程序参数的访问,而 则ApplicationRunner
使用ApplicationArguments
前面讨论的接口。以下示例显示了CommandLineRunner
withrun
方法:
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}
@Component
class MyCommandLineRunner : CommandLineRunner {
override fun run(vararg args: String) {
// Do something...
}
}
如果定义了多个CommandLineRunner
必须ApplicationRunner
按特定顺序调用的 bean,则可以另外实现该org.springframework.core.Ordered
接口或使用org.springframework.core.annotation.Order
注释。
1.11. 应用程序退出
每个进程SpringApplication
都会向 JVM 注册一个关闭钩子,以确保ApplicationContext
在退出时正常关闭。可以使用所有标准 Spring 生命周期回调(例如DisposableBean
接口或注释)。@PreDestroy
此外,org.springframework.boot.ExitCodeGenerator
如果 bean 希望在SpringApplication.exit()
调用时返回特定的退出代码,则可以实现该接口。然后可以传递此退出代码System.exit()
以将其作为状态代码返回,如以下示例所示:
@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)));
}
}
@SpringBootApplication
class MyApplication {
@Bean
fun exitCodeGenerator() = ExitCodeGenerator { 42 }
}
fun main(args: Array<String>) {
exitProcess(SpringApplication.exit(
runApplication<MyApplication>(*args)))
}
此外,该ExitCodeGenerator
接口可以通过异常来实现。当遇到此类异常时,Spring Boot 将返回所实现的getExitCode()
方法提供的退出代码。
如果有多个ExitCodeGenerator
,则使用生成的第一个非零退出代码。要控制生成器的调用顺序,请另外实现接口org.springframework.core.Ordered
或使用org.springframework.core.annotation.Order
注释。
1.12. 管理功能
可以通过指定属性来启用应用程序的管理相关功能spring.application.admin.enabled
。这暴露了SpringApplicationAdminMXBean
平台上的情况MBeanServer
。您可以使用此功能远程管理您的 Spring Boot 应用程序。此功能对于任何服务包装器实现也很有用。
如果您想知道应用程序在哪个 HTTP 端口上运行,请获取键为 的属性local.server.port 。
|
1.13。应用程序启动跟踪
在应用程序启动期间,SpringApplication
将ApplicationContext
执行许多与应用程序生命周期、bean 生命周期甚至处理应用程序事件相关的任务。通过ApplicationStartup
Spring Framework, 您可以使用对象跟踪应用程序启动顺序StartupStep
。收集这些数据可以用于分析目的,或者只是为了更好地了解应用程序启动过程。
ApplicationStartup
您可以在设置实例时选择实现SpringApplication
。例如,要使用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);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
applicationStartup = BufferingApplicationStartup(2048)
}
}
第一个可用的实现FlightRecorderApplicationStartup
是由 Spring 框架提供的。它将 Spring 特定的启动事件添加到 Java Flight Recorder 会话中,旨在分析应用程序并将其 Spring 上下文生命周期与 JVM 事件(例如分配、GC、类加载……)相关联。配置完成后,您可以通过在启用飞行记录器的情况下运行应用程序来记录数据:
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar
Spring Boot 附带了该BufferingApplicationStartup
变体;此实现旨在缓冲启动步骤并将其排入外部指标系统。应用程序可以请求BufferingApplicationStartup
任何组件中类型的 bean。
Spring Boot 还可以配置为公开一个startup
端点,该端点以 JSON 文档的形式提供此信息。
2. 外部化配置
Spring Boot 允许您外部化您的配置,以便您可以在不同的环境中使用相同的应用程序代码。您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
属性值可以通过使用注释直接注入到您的bean中@Value
,通过Spring的抽象访问,Environment
或者通过.@ConfigurationProperties
Spring Boot 使用一种非常特殊的PropertySource
顺序,旨在允许合理地覆盖值。后面的属性源可以覆盖前面定义的值。按以下顺序考虑来源:
-
默认属性(由设置指定
SpringApplication.setDefaultProperties
)。 -
@PropertySource
类上的注释@Configuration
。Environment
请注意,在刷新应用程序上下文之前,不会将此类属性源添加到中。现在配置某些属性为时已晚,例如在刷新开始之前读取的logging.*
和。spring.main.*
-
配置数据(例如
application.properties
文件)。 -
A
RandomValuePropertySource
仅在 中具有属性random.*
。 -
操作系统环境变量。
-
Java 系统属性 (
System.getProperties()
)。 -
JNDI 属性来自
java:comp/env
. -
ServletContext
初始化参数。 -
ServletConfig
初始化参数。 -
来自
SPRING_APPLICATION_JSON
(嵌入环境变量或系统属性中的内联 JSON)的属性。 -
命令行参数。
-
properties
测试的属性。可用于测试应用程序的特定部分的测试@SpringBootTest
注释。 -
@DynamicPropertySource
测试中的注释。 -
@TestPropertySource
对你的测试进行注释。 -
$HOME/.config/spring-boot
当 devtools 处于活动状态时目录中的Devtools全局设置属性。
配置数据文件按以下顺序考虑:
-
打包在 jar 内的应用程序属性
application.properties
(和 YAML 变体)。 -
特定于配置文件的应用程序属性打包在 jar(
application-{profile}.properties
和 YAML 变体)内。 -
打包的 jar(和 YAML 变体)之外的应用程序属性
application.properties
。 -
打包的 jar(和 YAML 变体)之外的特定于配置文件的应用程序属性
application-{profile}.properties
。
建议您的整个申请坚持使用一种格式。如果您.properties 在同一位置有同时具有 YAML 格式的配置文件,.properties 则优先。
|
如果您使用环境变量而不是系统属性,则大多数操作系统不允许使用句点分隔的键名称,但您可以使用下划线代替(例如,SPRING_CONFIG_NAME 代替spring.config.name )。有关详细信息,
请参阅从环境变量绑定。 |
如果您的应用程序在 servlet 容器或应用程序服务器中运行,则java:comp/env 可以使用 JNDI 属性(在 中)或 servlet 上下文初始化参数来代替环境变量或系统属性,或者与环境变量或系统属性一起使用。
|
为了提供一个具体的示例,假设您开发一个@Component
使用name
属性的应用程序,如以下示例所示:
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
@Component
class MyBean {
@Value("\${name}")
private val name: String? = null
// ...
}
在您的应用程序类路径上(例如,在您的 jar 内),您可以拥有一个application.properties
为name
. 在新环境中运行时,application.properties
可以在 jar 外部提供一个文件来覆盖name
. 对于一次性测试,您可以使用特定的命令行开关(例如java -jar app.jar --name="Spring"
)启动。
和端点可用于确定属性具有特定值的原因env 。configprops 您可以使用这两个端点来诊断意外的属性值。有关详细信息,请参阅“生产就绪功能”部分。
|
2.1. 访问命令行属性
默认情况下,SpringApplication
将任何命令行选项参数(即以 开头的参数--
,例如--server.port=9000
)转换为 aproperty
并将它们添加到 Spring Environment
。如前所述,命令行属性始终优先于基于文件的属性源。
如果您不希望将命令行属性添加到 中Environment
,可以使用 禁用它们SpringApplication.setAddCommandLineProperties(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
在前面的示例中,您最终会进入my.name=test
Spring Environment
。
还可以提供相同的 JSON 作为系统属性:
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
或者您可以使用命令行参数提供 JSON:
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
如果要部署到经典应用程序服务器,您还可以使用名为 的 JNDI 变量java:comp/env/spring.application.json
。
尽管null JSON 中的值将添加到生成的属性源中,但PropertySourcesPropertyResolver 会将null 属性视为缺失值。这意味着 JSON 无法使用值覆盖来自低阶属性源的属性null 。
|
2.3. 外部应用程序属性
当您的应用程序启动时,Spring Boot 将自动从以下位置查找并加载application.properties
文件:application.yaml
-
从类路径
-
类路径根
-
类路径
/config
包
-
-
从当前目录
-
当前目录
-
config/
当前目录下的子目录 -
config/
子目录的直接子目录
-
该列表按优先级排序(较低项目的值覆盖较早项目的值)。已加载文件中的文档将添加PropertySources
到 Spring 中Environment
。
如果您不喜欢application
配置文件名,可以通过指定spring.config.name
环境属性切换到另一个文件名。例如,要查找myproject.properties
和myproject.yaml
文件,您可以按如下方式运行应用程序:
$ java -jar myproject.jar --spring.config.name=myproject
您还可以使用环境属性来引用显式位置spring.config.location
。此属性接受以逗号分隔的一个或多个要检查位置的列表。
以下示例显示如何指定两个不同的文件:
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
optional: 如果位置是可选的并且您不介意它们不存在,
请使用前缀。 |
spring.config.name 、spring.config.location 、 和spring.config.additional-location 很早就被用来确定必须加载哪些文件。它们必须定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。
|
如果spring.config.location
包含目录(而不是文件),则它们应该以/
. 在运行时,它们将附加spring.config.name
加载之前生成的名称。中指定的文件spring.config.location
将直接导入。
目录和文件位置值也会扩展以检查特定于配置文件的文件。例如,如果您有spring.config.location of classpath:myconfig.properties ,您还会发现classpath:myconfig-<profile>.properties 加载了适当的文件。
|
在大多数情况下,spring.config.location
您添加的每个项目都将引用单个文件或目录。位置按照定义的顺序进行处理,后面的位置可以覆盖前面位置的值。
如果您有复杂的位置设置,并且使用特定于配置文件的配置文件,则可能需要提供进一步的提示,以便 Spring Boot 知道应如何对它们进行分组。位置组是所有被视为同一级别的位置的集合。例如,您可能想要对所有类路径位置进行分组,然后对所有外部位置进行分组。位置组内的项目应使用 分隔;
。有关更多详细信息,请参阅“配置文件特定文件”部分中的示例。
使用配置的位置spring.config.location
替换默认位置。例如,如果spring.config.location
配置了值optional:classpath:/custom-config/,optional:file:./custom-config/
,则考虑的完整位置集为:
-
optional:classpath:custom-config/
-
optional:file:./custom-config/
如果您希望添加其他位置而不是替换它们,则可以使用spring.config.additional-location
. 从其他位置加载的属性可以覆盖默认位置中的属性。例如,如果spring.config.additional-location
配置了值optional:classpath:/custom-config/,optional:file:./custom-config/
,则考虑的完整位置集为:
-
optional:classpath:/;optional:classpath:/config/
-
optional:file:./;optional:file:./config/;optional:file:./config/*/
-
optional:classpath:custom-config/
-
optional:file:./custom-config/
通过这种搜索顺序,您可以在一个配置文件中指定默认值,然后有选择地覆盖另一个配置文件中的这些值。您可以在默认位置之一为您的应用程序application.properties
(或您选择的任何其他基本名称)提供默认值。spring.config.name
然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。
2.3.1. 可选地点
默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出异常,ConfigDataLocationNotFoundException
并且您的应用程序将无法启动。
如果您想指定一个位置,但不介意它并不总是存在,则可以使用前缀optional:
。您可以将此前缀与spring.config.location
和spring.config.additional-location
属性以及spring.config.import
声明一起使用。
例如,即使文件丢失,spring.config.import
值 也允许您的应用程序启动。optional:file:./myconfig.properties
myconfig.properties
如果您想忽略所有ConfigDataLocationNotFoundExceptions
并始终继续启动您的应用程序,您可以使用该spring.config.on-not-found
属性。将值设置为ignore
使用SpringApplication.setDefaultProperties(…)
或与系统/环境变量一起使用。
2.3.2. 通配符位置
如果配置文件位置包含*
最后一个路径段的字符,则它被视为通配符位置。加载配置时会扩展通配符,以便还会检查直接子目录。当存在多个配置属性源时,通配符位置在 Kubernetes 等环境中特别有用。
例如,如果您有一些 Redis 配置和一些 MySQL 配置,您可能希望将这两个配置分开,同时要求这两个配置都存在于一个文件中application.properties
。这可能会导致两个单独的application.properties
文件安装在不同的位置,例如/config/redis/application.properties
和/config/mysql/application.properties
。在这种情况下,通配符位置config/*/
, 将导致两个文件都被处理。
默认情况下,Spring Boot 包含config/*/
在默认搜索位置中。这意味着/config
将搜索 jar 之外的目录的所有子目录。
您可以自行将通配符位置与spring.config.location
和spring.config.additional-location
属性一起使用。
对于目录搜索位置或文件搜索位置,
通配符位置必须仅包含一个* 并以结尾。带有通配符的位置根据文件名的绝对路径按字母顺序排序。
*/ */<filename> |
通配符位置仅适用于外部目录。您不能在某个位置使用通配符classpath: 。
|
2.3.3. 配置文件特定文件
除了application
属性文件之外,Spring Boot 还将尝试使用命名约定加载特定于配置文件的文件application-{profile}
。例如,如果您的应用程序激活名为prod
并使用 YAML 文件的配置文件,则 和application.yaml
都会application-prod.yaml
被考虑。
配置文件特定的属性从与标准相同的位置加载application.properties
,配置文件特定的文件始终覆盖非特定的文件。如果指定了多个配置文件,则应用最后获胜策略。例如,如果配置文件prod,live
由属性指定spring.profiles.active
,则 中的值application-prod.properties
可以被 中的值覆盖application-live.properties
。
最后获胜策略适用于地点组级别。A 例如,继续 /cfg application-live.properties /ext application-live.properties application-prod.properties 当我们有
当我们有
|
有Environment
一组默认配置文件(默认情况下为[default]
),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,则application-default
考虑来自的属性。
属性文件仅加载一次。如果您已经直接导入了配置文件特定的属性文件,则不会再次导入。 |
2.3.4. 导入附加数据
应用程序属性可以使用该属性从其他位置导入更多配置数据spring.config.import
。进口在发现时进行处理,并被视为紧邻插入进口申报文件下方的附加文件。
例如,您的类路径application.properties
文件中可能包含以下内容:
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties"
这将触发当前目录中文件的导入dev.properties
(如果存在这样的文件)。导入的值dev.properties
将优先于触发导入的文件。在上面的示例中,dev.properties
可以重新定义spring.application.name
为不同的值。
一次进口无论申报多少次,都只会进口一次。在properties/yaml 文件中的单个文档中定义导入的顺序并不重要。例如,下面的两个示例产生相同的结果:
spring.config.import=my.properties
my.property=value
spring:
config:
import: "my.properties"
my:
property: "value"
my.property=value
spring.config.import=my.properties
my:
property: "value"
spring:
config:
import: "my.properties"
在上述两个示例中,文件中的值my.properties
将优先于触发其导入的文件。
可以在一个键下指定多个位置spring.config.import
。位置将按照定义的顺序进行处理,稍后导入的优先。
在适当的情况下,还会考虑导入特定于配置文件的变体。上面的示例将导入两者my.properties 以及任何my-<profile>.properties 变体。
|
Spring Boot 包含可插入 API,允许支持各种不同的位置地址。默认情况下,您可以导入 Java Properties、YAML 和“配置树”。 第三方 jar 可以提供对其他技术的支持(不要求文件位于本地)。例如,您可以想象配置数据来自外部存储,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。 如果您想支持自己的位置,请参阅包中的 |
2.3.5。导入无扩展名文件
某些云平台无法为卷安装的文件添加文件扩展名。要导入这些无扩展名文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。您可以通过将扩展提示放在方括号中来完成此操作。
例如,假设您有一个/etc/config/myconfig
文件希望导入为 yaml。您可以application.properties
使用以下命令将其导入:
spring.config.import=file:/etc/config/myconfig[.yaml]
spring:
config:
import: "file:/etc/config/myconfig[.yaml]"
2.3.6。使用配置树
在云平台(例如 Kubernetes)上运行应用程序时,您通常需要读取平台提供的配置值。将环境变量用于此类目的并不罕见,但这可能有缺点,特别是如果该值应该保密的话。
作为环境变量的替代方案,许多云平台现在允许您将配置映射到已安装的数据卷中。例如,Kubernetes 可以对ConfigMaps
和进行卷挂载Secrets
。
可以使用两种常见的卷安装模式:
-
单个文件包含一组完整的属性(通常编写为 YAML)。
-
多个文件被写入目录树,文件名成为“键”,内容成为“值”。
对于第一种情况,您可以使用上述spring.config.import
方法直接导入 YAML 或 Properties 文件。对于第二种情况,您需要使用前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。configtree:
作为示例,我们假设 Kubernetes 已安装以下卷:
etc/ config/ myapp/ username password
文件的内容username
将是一个配置值,并且 的内容password
将是一个秘密。
要导入这些属性,您可以将以下内容添加到您的application.properties
或application.yaml
文件中:
spring.config.import=optional:configtree:/etc/config/
spring:
config:
import: "optional:configtree:/etc/config/"
然后,您可以以通常的方式访问或注入myapp.username
属性。myapp.password
Environment
配置树下的文件夹和文件的名称构成属性名称。在上面的示例中,要访问属性 和username ,password 您可以设置spring.config.import 为optional:configtree:/etc/config/myapp 。
|
带点符号的文件名也可以正确映射。例如,在上面的示例中,名为myapp.username in的文件/etc/config 将myapp.username 在Environment .
|
配置树值可以根据预期的内容
绑定到字符串String 和类型。byte[] |
如果您有多个配置树要从同一父文件夹导入,则可以使用通配符快捷方式。任何configtree:
以 结尾的位置都/*/
将导入所有直接子项作为配置树。与非通配符导入一样,每个配置树下的文件夹和文件的名称形成属性名称。
例如,给定以下体积:
etc/ config/ dbconfig/ db/ username password mqconfig/ mq/ username password
您可以用作configtree:/etc/config/*/
导入位置:
spring.config.import=optional:configtree:/etc/config/*/
spring:
config:
import: "optional:configtree:/etc/config/*/"
这将添加db.username
、db.password
和mq.username
属性mq.password
。
使用通配符加载的目录按字母顺序排序。如果您需要不同的订单,那么您应该将每个位置列为单独的导入 |
配置树也可用于 Docker 秘密。当 Docker swarm 服务被授予对机密的访问权限时,该机密就会被安装到容器中。例如,如果将名为 的机密db.password
安装在 location /run/secrets/
,您可以db.password
使用以下命令将其提供给 Spring 环境:
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
2.3.7. 属性占位符
application.properties
和中的值在使用时application.yaml
会通过现有值进行过滤Environment
,因此您可以引用以前定义的值(例如,来自系统属性或环境变量的值)。标准${name}
属性占位符语法可以在值内的任何位置使用。属性占位符还可以使用 a:
将默认值与属性名称分隔开来指定默认值,例如${name:default}
。
以下示例显示了带默认值和不带默认值的占位符的使用:
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
app:
name: "MyApp"
description: "${app.name} is a Spring Boot application written by ${username:Unknown}"
假设该username
属性尚未在其他地方设置,app.description
则值为MyApp is a Spring Boot application written by Unknown
。
您应该始终使用规范形式(仅使用小写字母的短横线大小写)来引用占位符中的属性名称。这将允许 Spring Boot 使用与宽松绑定 例如,将从文件以及系统环境中 |
您还可以使用此技术创建现有 Spring Boot 属性的“短”变体。有关详细信息,请参阅howto.html操作方法。 |
2.3.8. 处理多文档文件
Spring Boot 允许您将单个物理文件拆分为多个逻辑文档,每个逻辑文档都是独立添加的。文件按从上到下的顺序处理。后面的文档可以覆盖前面文档中定义的属性。
对于application.yaml
文件,使用标准 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 . 有关详细信息,请参阅下一节。
|
无法使用@PropertySource 或@TestPropertySource 注释加载多文档属性文件。
|
2.3.9. 激活属性
有时,仅在满足某些条件时激活一组给定的属性很有用。例如,您可能拥有仅在特定配置文件处于活动状态时才相关的属性。
您可以使用 有条件地激活属性文档spring.config.activate.*
。
以下激活属性可用:
财产 | 笔记 |
---|---|
|
必须匹配才能使文档处于活动状态的配置文件表达式。 |
|
|
例如,以下内容指定第二个文档仅在 Kubernetes 上运行时才处于活动状态,并且仅当“prod”或“staging”配置文件处于活动状态时:
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set"
2.4. 加密属性
Spring Boot 不提供任何对加密属性值的内置支持,但是,它确实提供了修改 Spring 中包含的值所需的挂钩点Environment
。该界面允许您在应用程序启动之前EnvironmentPostProcessor
进行操作。Environment
有关详细信息,请参阅howto.html。
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault项目提供了在HashiCorp Vault中存储外部化配置的支持。
2.5. 使用 YAML
YAML是 JSON 的超集,因此是指定分层配置数据的便捷格式。只要您的类路径上有SnakeYAML库,该类SpringApplication
就会自动支持 YAML 作为属性的替代方案。
如果您使用“Starters”,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]
解引用器的属性键。例如,考虑以下 YAML:
my:
servers:
- "dev.example.com"
- "another.example.com"
前面的示例将转换为以下属性:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
使用该表示法的属性可以使用 Spring Boot 的类[index] 绑定到 JavaList 或对象。有关更多详细信息,请参阅下面的“类型安全配置属性”部分。
Set Binder |
@PropertySource 无法使用或注释
加载 YAML 文件@TestPropertySource 。因此,如果您需要以这种方式加载值,则需要使用属性文件。
|
2.6。配置随机值
这RandomValuePropertySource
对于注入随机值(例如,注入秘密或测试用例)非常有用。它可以生成整数、长整型、uuid 或字符串,如以下示例所示:
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]}
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 Boot 支持为环境属性设置前缀。如果系统环境由具有不同配置要求的多个 Spring Boot 应用程序共享,这非常有用。系统环境属性的前缀可以直接在 上设置SpringApplication
。
例如,如果将前缀设置为input
,则诸如 之类的属性也将像在系统环境中remote.timeout
一样被解析。input.remote.timeout
2.8. 类型安全的配置属性
使用@Value("${property}")
注释来注入配置属性有时可能很麻烦,特别是当您正在使用多个属性或您的数据本质上是分层的时。Spring Boot 提供了另一种使用属性的方法,让强类型 bean 管理和验证应用程序的配置。
2.8.1. JavaBean 属性绑定
可以绑定声明标准 JavaBean 属性的 bean,如以下示例所示:
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
}
}
@ConfigurationProperties("my.service")
class MyProperties {
var isEnabled = false
var remoteAddress: InetAddress? = null
val security = Security()
class Security {
var username: String? = null
var password: String? = null
var roles: List<String> = ArrayList(setOf("USER"))
}
}
前面的 POJO 定义了以下属性:
-
my.service.enabled
,默认值为false
。 -
my.service.remote-address
,具有可以从 强制的类型String
。 -
my.service.security.username
,带有一个嵌套的“安全”对象,其名称由属性名称确定。特别是,该类型根本没有在那里使用,并且本来可以使用SecurityProperties
。 -
my.service.security.password
。 -
my.service.security.roles
,其中的集合String
默认为USER
.
映射到 Spring Boot 中可用的类的属性@ConfigurationProperties (通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公共 API,但类本身的访问器(getter/setter)并不意味着可以直接使用。
|
这种安排依赖于默认的空构造函数,并且 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。在以下情况下可以省略 setter:
有些人使用 Project Lombok 自动添加 getter 和 setter。确保 Lombok 不会为此类类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。 最后,仅考虑标准 Java Bean 属性,并且不支持静态属性的绑定。 |
2.8.2. 构造函数绑定
上一节中的示例可以以不可变的方式重写,如下例所示:
@ConfigurationProperties("my.service")
public class MyProperties {
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public static class Security {
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
}
}
@ConfigurationProperties("my.service")
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
val security: Security) {
class Security(val username: String, val password: String,
@param:DefaultValue("USER") val roles: List<String>)
}
在此设置中,单个参数化构造函数的存在意味着应使用构造函数绑定。这意味着绑定器将找到一个带有您希望绑定的参数的构造函数。如果您的类有多个构造函数,则@ConstructorBinding
可以使用注释来指定用于构造函数绑定的构造函数。要选择退出具有单个参数化构造函数的类的构造函数绑定,该构造函数必须使用 进行注释@Autowired
。构造函数绑定可以与记录一起使用。除非您的记录有多个构造函数,否则无需使用@ConstructorBinding
.
构造函数绑定类的嵌套成员(例如Security
上面的示例)也将通过其构造函数进行绑定。
可以使用构造函数参数和记录组件指定默认值@DefaultValue
。转换服务将用于将注释的String
值强制转换为缺失属性的目标类型。
参考前面的示例,如果没有属性绑定到Security
,则MyProperties
实例将包含null
的值security
。要使其包含非空实例,Security
即使没有绑定任何属性(使用 Kotlin 时,这将要求username
和password
参数Security
声明为可为空,因为它们没有默认值),请使用空注释@DefaultValue
:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
@DefaultValue val security: Security) {
class Security(val username: String?, val password: String?,
@param:DefaultValue("USER") val roles: List<String>)
}
要使用构造函数绑定,必须使用@EnableConfigurationProperties 或配置属性扫描来启用类。您不能将构造函数绑定与由常规 Spring 机制创建的 bean(例如@Component bean、使用@Bean 方法创建的 bean 或使用 using 加载的 bean @Import )
|
要在本机映像中使用构造函数绑定,必须使用-parameters . 如果您使用 Spring Boot 的 Gradle 插件或者使用 Maven 和spring-boot-starter-parent .
|
不建议使用java.util.Optional with @ConfigurationProperties ,因为它主要用作返回类型。因此,它不太适合配置属性注入。为了与其他类型的属性保持一致,如果您确实声明了一个Optional 属性并且它没有值,则将绑定
null 空值,而不是空值。Optional |
2.8.3. 启用@ConfigurationProperties注释的类型
Spring Boot 提供了绑定@ConfigurationProperties
类型并将它们注册为 bean 的基础设施。您可以逐类启用配置属性,也可以启用与组件扫描类似的配置属性扫描。
有时,带有注释的类@ConfigurationProperties
可能不适合扫描,例如,如果您正在开发自己的自动配置或想要有条件地启用它们。在这些情况下,请使用注释指定要处理的类型列表@EnableConfigurationProperties
。这可以在任何类上完成@Configuration
,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration
@ConfigurationProperties("some.properties")
public class SomeProperties {
}
@ConfigurationProperties("some.properties")
class SomeProperties
要使用配置属性扫描,请将@ConfigurationPropertiesScan
注释添加到您的应用程序中。通常,它被添加到带有注释的主应用程序类中,@SpringBootApplication
但它可以添加到任何@Configuration
类中。默认情况下,将从声明注释的类的包中进行扫描。如果要定义要扫描的特定包,可以按照以下示例所示进行操作:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication
当 假设在包中,上面例子 |
我们建议@ConfigurationProperties
只处理环境,特别是不要从上下文中注入其他 bean。*Aware
对于极端情况,可以使用 setter 注入或框架提供的任何接口(例如EnvironmentAware
,如果您需要访问Environment
)。如果您仍然想使用构造函数注入其他 bean,则配置属性 bean 必须使用@Component
基于 JavaBean 的属性绑定进行注释并使用。
2.8.4. 使用@ConfigurationProperties注释的类型
这种配置风格特别适用于SpringApplication
外部 YAML 配置,如以下示例所示:
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"
要使用@ConfigurationProperties
Bean,您可以按照与任何其他 Bean 相同的方式注入它们,如以下示例所示:
@Service
public class MyService {
private final MyProperties properties;
public MyService(MyProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
@Service
class MyService(val properties: MyProperties) {
fun openConnection() {
val server = Server(properties.remoteAddress)
server.start()
// ...
}
// ...
}
使用@ConfigurationProperties 还可以让您生成元数据文件,IDE 可以使用这些文件来为您自己的密钥提供自动完成功能。详细内容
请参见附录。 |
2.8.5。第三方配置
除了用于@ConfigurationProperties
注释类之外,您还可以在公共@Bean
方法上使用它。当您想要将属性绑定到您无法控制的第三方组件时,这样做特别有用。
要从Environment
属性配置 bean,请添加@ConfigurationProperties
到其 bean 注册中,如以下示例所示:
@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties(prefix = "another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
@Configuration(proxyBeanMethods = false)
class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties(prefix = "another")
fun anotherComponent(): AnotherComponent = AnotherComponent()
}
使用前缀定义的任何 JavaBean 属性都会以与前面的示例类似的方式another
映射到该bean 。AnotherComponent
SomeProperties
2.8.6。宽松的绑定
Spring Boot 使用一些宽松的规则将Environment
属性绑定到bean,因此属性名称和 bean 属性名称@ConfigurationProperties
之间不需要完全匹配。Environment
有用的常见示例包括用短划线分隔的环境属性(例如,context-path
绑定到contextPath
)和大写的环境属性(例如,PORT
绑定到port
)。
作为示例,请考虑以下@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;
}
}
@ConfigurationProperties(prefix = "my.main-project.person")
class MyPersonProperties {
var firstName: String? = null
}
通过上面的代码,可以使用以下属性名称:
财产 | 笔记 |
---|---|
|
Kebab case,建议在 |
|
标准驼峰式语法。 |
|
|
|
大写格式,在使用系统环境变量时建议使用大写格式。 |
prefix 注释的值
必须采用短横线大小写(小写并用 分隔- ,例如my.main-project.person )。
|
财产来源 | 简单的 | 列表 |
---|---|---|
属性文件 |
驼峰式大小写、烤肉串大小写或下划线表示法 |
使用 |
YAML 文件 |
驼峰式大小写、烤肉串大小写或下划线表示法 |
标准 YAML 列表语法或逗号分隔值 |
环境变量 |
大写格式,下划线作为分隔符(请参阅从环境变量绑定)。 |
用下划线包围的数值(请参阅从环境变量绑定) |
系统属性 |
驼峰式大小写、烤肉串大小写或下划线表示法 |
使用 |
我们建议,如果可能,属性以小写短横线格式存储,例如my.person.first-name=Rod .
|
绑定地图
绑定到Map
属性时,您可能需要使用特殊的括号表示法,以便key
保留原始值。如果键没有被 包围[]
,则任何非字母数字字符-
或.
都会被删除。
例如,考虑将以下属性绑定到 a Map<String,String>
:
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3
my:
map:
"[/key1]": "value1"
"[/key2]": "value2"
"/key3": "value3"
对于 YAML 文件,括号需要用引号括起来,以便正确解析键。 |
上面的属性将绑定到Map
,/key1
和/key2
作为key3
映射中的键。斜杠已被删除,key3
因为它没有被方括号包围。
绑定到标量值时,.
其中带有 的键不需要用 括起来[]
。标量值包括枚举和java.lang
包中除Object
. 绑定a.b=c
toMap<String, String>
将保留.
键中的 并返回带有该条目的 Map {"a.b"="c"}
。对于任何其他类型,如果您key
包含.
. 例如,绑定a.b=c
到Map<String, Object>
将返回一个带有条目的 Map {"a"={"b"="c"}}
,而[a.b]=c
将返回一个带有条目的 Map {"a.b"="c"}
。
从环境变量绑定
大多数操作系统对可用于环境变量的名称施加严格的规则。例如,Linux shell 变量只能包含字母 ( a
toz
或A
to Z
)、数字 ( 0
to 9
) 或下划线字符 ( _
)。按照惯例,Unix shell 变量的名称也采用大写形式。
Spring Boot 宽松的绑定规则旨在尽可能兼容这些命名限制。
要将规范形式的属性名称转换为环境变量名称,您可以遵循以下规则:
-
将点 (
.
) 替换为下划线 (_
)。 -
删除所有破折号 (
-
)。 -
转换为大写。
例如,配置属性spring.main.log-startup-info
将是名为 的环境变量SPRING_MAIN_LOGSTARTUPINFO
。
绑定到对象列表时也可以使用环境变量。要绑定到 a List
,变量名称中的元素编号应用下划线括起来。
例如,配置属性my.service[0].other
将使用名为 的环境变量MY_SERVICE_0_OTHER
。
2.8.7. 合并复杂类型
当在多个位置配置列表时,覆盖通过替换整个列表来进行。
例如,假设一个MyPojo
对象具有默认的name
和description
属性null
。MyPojo
以下示例公开了来自 的对象列表MyProperties
:
@ConfigurationProperties("my")
public class MyProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
@ConfigurationProperties("my")
class MyProperties {
val list: List<MyPojo> = ArrayList()
}
考虑以下配置:
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
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
仍仅包含一个条目(名称为my another name
,描述为null
)。此配置不会将第二个MyPojo
实例添加到列表中,也不会合并项目。
当在多个配置文件中指定 a 时List
,将使用具有最高优先级的配置文件(且仅使用该配置文件)。考虑以下示例:
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
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
则包含一个 MyPojo
条目(名称为my another name
,描述为null
)。对于 YAML,逗号分隔列表和 YAML 列表都可用于完全覆盖列表的内容。
对于Map
属性,您可以绑定从多个源获取的属性值。但是,对于多个源中的同一属性,将使用优先级最高的属性。以下示例公开了Map<String, MyPojo>
from MyProperties
:
@ConfigurationProperties("my")
public class MyProperties {
private final Map<String, MyPojo> map = new LinkedHashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
@ConfigurationProperties("my")
class MyProperties {
val map: Map<String, MyPojo> = LinkedHashMap()
}
考虑以下配置:
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
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 Boot 在绑定到@ConfigurationProperties
bean 时尝试将外部应用程序属性强制为正确的类型。如果您需要自定义类型转换,您可以提供一个ConversionService
bean(带有名为 的 bean conversionService
)或自定义属性编辑器(通过CustomEditorConfigurer
bean)或自定义Converters
(带有注释为 的 bean 定义@ConfigurationPropertiesBinding
)。
由于此 bean 在应用程序生命周期的早期就被请求,因此请确保限制您ConversionService 正在使用的依赖项。通常,您需要的任何依赖项在创建时可能不会完全初始化。ConversionService 如果配置键强制不需要并且仅依赖于 . 限定的自定义转换器,您可能需要重命名您的自定义转换器@ConfigurationPropertiesBinding 。
|
转换持续时间
Spring Boot 专门支持表达持续时间。如果您公开java.time.Duration
属性,则应用程序属性中的以下格式可用:
-
正则表示(除非指定了
long
a,否则使用毫秒作为默认单位)@DurationUnit
-
使用的标准 ISO-8601 格式
java.time.Duration
-
一种更易读的格式,其中值和单位耦合(
10s
表示 10 秒)
考虑以下示例:
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
}
@ConfigurationProperties("my")
class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
var sessionTimeout = Duration.ofSeconds(30)
var readTimeout = Duration.ofMillis(1000)
}
要指定 30 秒的会话超时,30
、PT30S
和30s
都是等效的。可以通过以下任意形式指定 500ms 的读取超时:500
、PT0.5S
和500ms
。
您还可以使用任何受支持的单位。这些都是:
-
ns
纳秒 -
us
微秒 -
ms
毫秒 -
s
几秒钟 -
m
几分钟 -
h
用了几个小时 -
d
好几天
默认单位是毫秒,可以使用@DurationUnit
上面示例中所示的方法进行覆盖。
如果您更喜欢使用构造函数绑定,则可以公开相同的属性,如以下示例所示:
@ConfigurationProperties("my")
public class MyProperties {
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
}
@ConfigurationProperties("my")
class MyProperties(@param:DurationUnit(ChronoUnit.SECONDS) @param:DefaultValue("30s") val sessionTimeout: Duration,
@param:DefaultValue("1000ms") val readTimeout: Duration)
如果您要升级Long 属性,请确保定义单位(使用@DurationUnit )(如果它不是毫秒)。这样做提供了透明的升级路径,同时支持更丰富的格式。
|
转换期间
除了持续时间之外,Spring Boot 还可以使用java.time.Period
类型。应用程序属性中可以使用以下格式:
-
常规
int
表示(使用天作为默认单位,除非@PeriodUnit
已指定 a) -
使用的标准 ISO-8601 格式
java.time.Period
-
一种更简单的格式,其中值和单位对耦合(
1y3d
表示 1 年零 3 天)
简单格式支持以下单位:
-
y
多年 -
m
几个月 -
w
数周 -
d
好几天
该java.time.Period 类型实际上从不存储周数,它是一个快捷方式,意思是“7 天”。
|
转换数据大小
Spring框架有一个以DataSize
字节为单位表示大小的值类型。如果您公开DataSize
属性,则应用程序属性中的以下格式可用:
-
正则表示(除非指定了
long
a,否则使用字节作为默认单位)@DataSizeUnit
-
一种更易读的格式,其中值和单位耦合(
10MB
表示 10 兆字节)
考虑以下示例:
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
}
@ConfigurationProperties("my")
class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
var bufferSize = DataSize.ofMegabytes(2)
var sizeThreshold = DataSize.ofBytes(512)
}
指定缓冲区大小为10兆字节,10
与10MB
是等价的。256 字节的大小阈值可以指定为256
或256B
。
您还可以使用任何受支持的单位。这些都是:
-
B
对于字节 -
KB
千字节 -
MB
兆字节 -
GB
千兆字节 -
TB
兆兆字节
默认单位是字节,可以使用@DataSizeUnit
上面示例中所示的方法进行覆盖。
如果您更喜欢使用构造函数绑定,则可以公开相同的属性,如以下示例所示:
@ConfigurationProperties("my")
public class MyProperties {
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
}
@ConfigurationProperties("my")
class MyProperties(@param:DataSizeUnit(DataUnit.MEGABYTES) @param:DefaultValue("2MB") val bufferSize: DataSize,
@param:DefaultValue("512B") val sizeThreshold: DataSize)
如果要升级Long 属性,请确保定义单位(使用@DataSizeUnit )(如果它不是字节)。这样做提供了透明的升级路径,同时支持更丰富的格式。
|
2.8.9。@ConfigurationProperties 验证
@ConfigurationProperties
每当使用 Spring 的注释对类进行注释时,Spring Boot 就会尝试验证类@Validated
。jakarta.validation
您可以直接在配置类上使用 JSR-303约束注释。为此,请确保您的类路径上有兼容的 JSR-303 实现,然后向您的字段添加约束注释,如以下示例所示:
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
}
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
}
@Bean 您还可以通过使用注释创建配置属性的方法
来触发验证@Validated 。
|
为了确保即使没有找到任何属性,也始终会触发嵌套属性的验证,关联字段必须使用 进行注释@Valid
。以下示例基于前面的MyProperties
示例:
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
public static class Security {
@NotEmpty
private String username;
}
}
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
@Valid
val security = Security()
class Security {
@NotEmpty
var username: String? = null
}
}
Validator
您还可以通过创建名为 的 bean 定义来添加自定义 Spring configurationPropertiesValidator
。该@Bean
方法应该被声明static
。配置属性验证器是在应用程序生命周期的早期创建的,并且将该@Bean
方法声明为静态可以创建 bean,而无需实例化该类@Configuration
。这样做可以避免早期实例化可能引起的任何问题。
该spring-boot-actuator 模块包括一个公开所有 bean 的端点@ConfigurationProperties 。将您的 Web 浏览器指向/actuator/configprops 或使用等效的 JMX 端点。有关详细信息,请参阅“生产就绪功能”部分。
|
2.8.10. @ConfigurationProperties 与 @Value
注解@Value
是核心容器功能,它不提供与类型安全配置属性相同的功能。@ConfigurationProperties
下表总结了和支持的功能@Value
:
特征 | @ConfigurationProperties |
@Value |
---|---|---|
是的 |
有限(见下面的注释) |
|
是的 |
不 |
|
|
不 |
是的 |
如果您确实想使用 例如,将从文件以及系统环境中 |
如果您为自己的组件定义一组配置键,我们建议您将它们分组在用 注释的 POJO 中@ConfigurationProperties
。这样做将为您提供结构化的、类型安全的对象,您可以将其注入到您自己的 bean 中。
SpEL
在解析这些文件并填充环境时,不会处理应用程序属性文件中的表达式 。但是,可以SpEL
在 中编写表达式@Value
。如果应用程序属性文件中的属性值是一个SpEL
表达式,则在通过@Value
.
3. 简介
Spring Profiles 提供了一种方法来隔离应用程序配置的各个部分,并使其仅在某些环境中可用。任何@Component
,@Configuration
或 都@ConfigurationProperties
可以标记为@Profile
限制加载时间,如下例所示:
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
@Configuration(proxyBeanMethods = false)
@Profile("production")
class ProductionConfiguration {
// ...
}
如果通过而不是自动扫描来@ConfigurationProperties 注册bean ,则需要在具有注释的类上指定注释。在扫描的情况下,可以在类本身上指定。
@EnableConfigurationProperties @Profile @Configuration @EnableConfigurationProperties @ConfigurationProperties @Profile @ConfigurationProperties |
您可以使用spring.profiles.active
Environment
属性来指定哪些配置文件处于活动状态。您可以通过本章前面描述的任何方式指定属性。例如,您可以将其包含在您的 中application.properties
,如以下示例所示:
spring.profiles.active=dev,hsqldb
spring:
profiles:
active: "dev,hsqldb"
您还可以使用以下开关在命令行上指定它:--spring.profiles.active=dev,hsqldb
。
如果没有活动的配置文件,则启用默认配置文件。默认配置文件的名称是default
,可以使用该spring.profiles.default
Environment
属性对其进行调整,如以下示例所示:
spring.profiles.default=none
spring:
profiles:
default: "none"
spring.profiles.active
并且spring.profiles.default
只能在非配置文件特定文档中使用。这意味着它们不能包含在由激活的配置文件特定文件或文档spring.config.activate.on-profile
中。
例如第二个文档配置无效:
# this document is valid
spring.profiles.active=prod
#---
# this document is invalid
spring.config.activate.on-profile=prod
spring.profiles.active=metrics
# this document is valid
spring:
profiles:
active: "prod"
---
# this document is invalid
spring:
config:
activate:
on-profile: "prod"
profiles:
active: "metrics"
3.1. 添加活动配置文件
该spring.profiles.active
属性遵循与其他属性相同的排序规则:最高者PropertySource
获胜。这意味着您可以指定活动配置文件application.properties
,然后使用命令行开关替换它们。
有时,将属性添加到活动配置文件而不是替换它们会很有用。该spring.profiles.include
属性可用于在该spring.profiles.active
属性激活的配置文件之上添加活动配置文件。入口SpringApplication
点还有一个用于设置其他配置文件的 Java API。请参阅SpringApplicationsetAdditionalProfiles()
中的方法。
例如,当运行具有以下属性的应用程序时,即使使用开关运行,公共配置文件和本地配置文件也将被激活--spring.profiles.active
:
spring.profiles.include[0]=common
spring.profiles.include[1]=local
spring:
profiles:
include:
- "common"
- "local"
与 类似spring.profiles.active ,spring.profiles.include 只能在非配置文件特定文档中使用。这意味着它不能包含在由激活的配置文件特定文件或文档spring.config.activate.on-profile 中。
|
如果给定配置文件处于活动状态,下一节中介绍的配置文件组也可用于添加活动配置文件。
3.2. 配置文件组
有时,您在应用程序中定义和使用的配置文件粒度太细,使用起来很麻烦。例如,您可能拥有proddb
用于prodmq
独立启用数据库和消息传递功能的配置文件。
为了帮助解决此问题,Spring Boot 允许您定义配置文件组。配置文件组允许您为相关配置文件组定义逻辑名称。
例如,我们可以创建一个由我们的和配置文件production
组成的组。proddb
prodmq
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
spring:
profiles:
group:
production:
- "proddb"
- "prodmq"
现在可以启动我们的应用程序,一键--spring.profiles.active=production
激活production
、proddb
和配置文件。prodmq
3.3. 以编程方式设置配置文件
SpringApplication.setAdditionalProfiles(…)
您可以通过在应用程序运行之前调用以编程方式设置活动配置文件。还可以使用 Spring 的ConfigurableEnvironment
接口来激活配置文件。
3.4. 特定于配置文件的配置文件
application.properties
(或application.yaml
) 和通过引用的文件的特定于配置文件的变体@ConfigurationProperties
被视为文件并加载。有关详细信息,请参阅“配置文件特定文件”。
4. 日志记录
Spring Boot 使用Commons Logging进行所有内部日志记录,但使底层日志实现保持开放状态。为Java Util Logging、Log4j2和Logback提供了默认配置。在每种情况下,记录器都预先配置为使用控制台输出,还可以使用可选的文件输出。
默认情况下,如果您使用“Starters”,则使用 Logback 进行日志记录。还包括适当的 Logback 路由,以确保使用 Java Util Logging、Commons Logging、Log4J 或 SLF4J 的依赖库都能正常工作。
有很多可用于 Java 的日志框架。如果上面的列表看起来令人困惑,请不要担心。一般来说,您不需要更改日志依赖项,Spring Boot 默认值就可以正常工作。 |
当您将应用程序部署到 servlet 容器或应用程序服务器时,使用 Java Util Logging API 执行的日志记录不会路由到应用程序的日志中。这可以防止容器或已部署到容器的其他应用程序执行的日志记录出现在应用程序的日志中。 |
4.1. 日志格式
Spring Boot 的默认日志输出类似于以下示例:
2023-10-19T12:17:32.235Z INFO 34306 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 17.0.9 with PID 34306 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-10-19T12:17:32.241Z INFO 34306 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-10-19T12:17:34.422Z INFO 34306 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-10-19T12:17:34.441Z INFO 34306 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-10-19T12:17:34.442Z INFO 34306 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15] 2023-10-19T12:17:34.599Z INFO 34306 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-10-19T12:17:34.601Z INFO 34306 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2199 ms 2023-10-19T12:17:35.149Z INFO 34306 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-10-19T12:17:35.165Z INFO 34306 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 3.789 seconds (process running for 4.297)
输出以下项目:
-
日期和时间:毫秒精度且易于排序。
-
日志级别:
ERROR
、WARN
、INFO
、DEBUG
、 或TRACE
。 -
进程 ID。
-
---
用于区分实际日志消息开始的分隔符。 -
线程名称:用方括号括起来(可能会被截断以用于控制台输出)。
-
记录器名称:这通常是源类名称(通常是缩写)。
-
日志消息。
Logback 没有FATAL 级别。它被映射到ERROR .
|
4.2. 控制台输出
默认日志配置会在写入消息时将消息回显到控制台。默认情况下,会记录ERROR
-level、WARN
-level 和-level 消息。INFO
您还可以通过使用标志启动应用程序来启用“调试”模式--debug
。
$ java -jar myapp.jar --debug
您也可以debug=true 在您的application.properties .
|
启用调试模式后,将配置一系列核心记录器(嵌入式容器、Hibernate 和 Spring Boot)来输出更多信息。启用调试模式不会将您的应用程序配置为记录所有DEBUG
级别消息。
--trace
或者,您可以通过使用标志(或trace=true
在您的 中application.properties
)启动应用程序来启用“跟踪”模式。这样做可以为选定的核心记录器(嵌入式容器、Hibernate 模式生成和整个 Spring 产品组合)启用跟踪日志记录。
4.2.1. 颜色编码输出
如果您的终端支持 ANSI,则会使用颜色输出来提高可读性。您可以设置spring.output.ansi.enabled
为支持的值来覆盖自动检测。
颜色编码是通过使用%clr
转换字来配置的。在最简单的形式中,转换器根据日志级别对输出进行着色,如以下示例所示:
%clr(%5p)
下表描述了日志级别到颜色的映射:
等级 | 颜色 |
---|---|
|
红色的 |
|
红色的 |
|
黄色的 |
|
绿色的 |
|
绿色的 |
|
绿色的 |
或者,您可以通过将其作为转换选项提供来指定应使用的颜色或样式。例如,要将文本设为黄色,请使用以下设置:
%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){yellow}
支持以下颜色和样式:
-
blue
-
cyan
-
faint
-
green
-
magenta
-
red
-
yellow
4.3. 文件输出
默认情况下,Spring Boot 只记录日志到控制台,不写入日志文件。如果除了控制台输出之外还想写入日志文件,则需要设置logging.file.name
或logging.file.path
属性(例如,在您的 中application.properties
)。
下表显示了如何logging.*
一起使用这些属性:
logging.file.name |
logging.file.path |
例子 | 描述 |
---|---|---|---|
(没有任何) |
(没有任何) |
仅控制台日志记录。 |
|
具体文件 |
(没有任何) |
|
写入指定的日志文件。名称可以是确切位置或相对于当前目录的位置。 |
(没有任何) |
具体目录 |
|
写入 |
日志文件在达到 10 MB 时轮换,并且与控制台输出一样,默认情况下会记录ERROR
-level、WARN
-level 和-level 消息。INFO
日志记录属性独立于实际的日志记录基础设施。因此,特定的配置键(例如logback.configurationFile Logback)不由 spring Boot 管理。
|
4.4. 文件轮换
如果您使用 Logback,则可以使用您的application.properties
或application.yaml
文件微调日志轮换设置。对于所有其他日志系统,您需要自己直接配置轮换设置(例如,如果您使用 Log4j2,则可以添加log4j2.xml
或log4j2-spring.xml
文件)。
支持以下轮换策略属性:
姓名 | 描述 |
---|---|
|
用于创建日志存档的文件名模式。 |
|
应用程序启动时是否应进行日志归档清理。 |
|
归档之前日志文件的最大大小。 |
|
日志归档在被删除之前可以占用的最大大小。 |
|
要保留的归档日志文件的最大数量(默认为 7)。 |
4.5. 日志级别
所有受支持的日志记录系统都可以通过使用TRACE、DEBUG、INFO、WARN、ERROR、FATAL 或 OFF 之一在Spring 中设置记录器级别Environment
(例如,在 中)。可以使用 来配置记录器。application.properties
logging.level.<logger-name>=<level>
level
root
logging.level.root
以下示例显示了 中潜在的日志记录设置application.properties
:
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
logging:
level:
root: "warn"
org.springframework.web: "debug"
org.hibernate: "error"
还可以使用环境变量设置日志记录级别。例如,LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG
将设置org.springframework.web
为DEBUG
。
上述方法仅适用于包级别日志记录。由于宽松绑定始终将环境变量转换为小写,因此不可能以这种方式为单个类配置日志记录。如果需要为类配置日志记录,可以使用该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
logging:
group:
tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat"
定义后,您可以使用一行更改组中所有记录器的级别:
logging.level.tomcat=trace
logging:
level:
tomcat: "trace"
Spring Boot 包含以下可以开箱即用的预定义日志记录组:
姓名 | 伐木工 |
---|---|
网络 |
|
sql |
|
4.7. 使用日志关闭挂钩
为了在应用程序终止时释放日志记录资源,提供了一个关闭挂钩,该挂钩将在 JVM 退出时触发日志系统清理。除非您的应用程序部署为 war 文件,否则此关闭挂钩会自动注册。如果您的应用程序具有复杂的上下文层次结构,则关闭挂钩可能无法满足您的需求。如果没有,请禁用关闭挂钩并调查底层日志系统直接提供的选项。例如,Logback 提供上下文选择器,允许在自己的上下文中创建每个 Logger。您可以使用该logging.register-shutdown-hook
属性来禁用关闭挂钩。将其设置为false
将禁用注册。application.properties
您可以在您的或文件中设置该属性application.yaml
:
logging.register-shutdown-hook=false
logging:
register-shutdown-hook: false
4.8. 自定义日志配置
Environment
可以通过在类路径上包含适当的库来激活各种日志记录系统,并且可以通过在类路径的根目录中或在以下 Spring属性指定的位置中提供适当的配置文件来进一步定制logging.config
:
您可以使用系统属性强制 Spring Boot 使用特定的日志系统org.springframework.boot.logging.LoggingSystem
。该值应该是实现的完全限定类名LoggingSystem
。您还可以使用 值完全禁用 Spring Boot 的日志记录配置none
。
由于日志记录是在创建之前初始化的,因此ApplicationContext 无法@PropertySources 在 Spring@Configuration 文件中控制日志记录。更改日志记录系统或完全禁用它的唯一方法是通过系统属性。
|
根据您的日志系统,将加载以下文件:
记录系统 | 定制化 |
---|---|
回溯 |
|
日志4j2 |
|
JDK(Java 实用程序日志记录) |
|
如果可能,我们建议您使用-spring 日志记录配置的变体(例如,logback-spring.xml 而不是logback.xml )。如果使用标准配置位置,Spring 无法完全控制日志初始化。
|
Java Util Logging 存在已知的类加载问题,这些问题在从“可执行 jar”运行时会导致问题。我们建议您在从“可执行 jar”运行时尽可能避免使用它。 |
为了帮助定制,一些其他属性从 Spring 转移Environment
到系统属性。这允许记录系统配置使用这些属性。例如,设置logging.file.name
为环境变量application.properties
或LOGGING_FILE_NAME
设置为环境变量将导致LOG_FILE
设置系统属性。下表描述了传输的属性:
春季环境 | 系统属性 | 评论 |
---|---|---|
|
|
记录异常时使用的转换字。 |
|
|
如果定义,它将在默认日志配置中使用。 |
|
|
如果定义,它将在默认日志配置中使用。 |
|
|
在控制台(stdout)上使用的日志模式。 |
|
|
日志日期格式的附加程序模式。 |
|
|
用于控制台日志记录的字符集。 |
|
|
用于控制台日志记录的日志级别阈值。 |
|
|
文件中使用的日志模式(如果 |
|
|
用于文件日志记录的字符集(如果 |
|
|
用于文件日志记录的日志级别阈值。 |
|
|
渲染日志级别时使用的格式(默认 |
|
|
当前进程 ID(如果可能且尚未定义为操作系统环境变量时发现)。 |
如果您使用 Logback,还会传输以下属性:
春季环境 | 系统属性 | 评论 |
---|---|---|
|
|
滚动日志文件名的模式(默认 |
|
|
是否在启动时清理归档日志文件。 |
|
|
最大日志文件大小。 |
|
|
要保留的日志备份的总大小。 |
|
|
要保留的归档日志文件的最大数量。 |
所有受支持的日志系统在解析其配置文件时都可以查阅系统属性。spring-boot.jar
请参阅示例中的默认配置:
如果要在日志记录属性中使用占位符,则应该使用Spring Boot 的语法,而不是底层框架的语法。值得注意的是,如果您使用 Logback,则应该使用 用作 |
2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.Controller Handling authenticated request |
4.9. 登录扩展
Spring Boot 包含许多 Logback 扩展,可以帮助进行高级配置。您可以在配置文件中使用这些扩展logback-spring.xml
。
由于标准logback.xml 配置文件加载得太早,因此无法在其中使用扩展。您需要使用logback-spring.xml 或定义一个logging.config 属性。
|
这些扩展不能与 Logback 的配置扫描 一起使用。如果您尝试这样做,对配置文件进行更改会导致记录类似于以下内容之一的错误: |
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]] ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
4.9.1. 特定于配置文件的配置
该<springProfile>
标签允许您根据活动的 Spring 配置文件选择包含或排除配置部分。元素内的任何位置都支持配置文件部分<configuration>
。使用该name
属性指定哪个配置文件接受配置。该<springProfile>
标签可以包含配置文件名称(例如staging
)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west)
。查看Spring 框架参考指南以获取更多详细信息。以下清单显示了三个示例配置文件:
<springProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
</springProfile>
<springProfile name="dev | staging">
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</springProfile>
<springProfile name="!production">
<!-- configuration to be enabled when the "production" profile is not active -->
</springProfile>
4.9.2. 环境属性
该<springProperty>
标签允许您公开来自 Spring 的属性Environment
以在 Logback 中使用。如果您想访问application.properties
Logback 配置中文件中的值,那么这样做会很有用。该标签的工作方式与 Logback 的标准标签类似<property>
。但是,您不是直接指定value
,而是指定source
属性的 (来自Environment
)。如果您需要将属性存储在local
范围之外的某个位置,则可以使用该scope
属性。如果您需要后备值(如果未在 中设置该属性Environment
),则可以使用该defaultValue
属性。以下示例展示了如何公开属性以在 Logback 中使用:
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<remoteHost>${fluentHost}</remoteHost>
...
</appender>
必须source 以 kebab 大小写指定(例如my.property-name )。Environment 但是,可以使用宽松的规则
将属性添加到 中。 |
4.10. Log4j2 扩展
Spring Boot 包含许多对 Log4j2 的扩展,可以帮助进行高级配置。您可以在任何配置文件中使用这些扩展log4j2-spring.xml
。
由于标准log4j2.xml 配置文件加载得太早,因此无法在其中使用扩展。您需要使用log4j2-spring.xml 或定义一个logging.config 属性。
|
这些扩展取代了Log4J 提供的Spring Boot 支持。您应该确保不要将该org.apache.logging.log4j:log4j-spring-boot 模块包含在您的构建中。
|
4.10.1. 特定于配置文件的配置
该<SpringProfile>
标签允许您根据活动的 Spring 配置文件选择包含或排除配置部分。元素内的任何位置都支持配置文件部分<Configuration>
。使用该name
属性指定哪个配置文件接受配置。该<SpringProfile>
标签可以包含配置文件名称(例如staging
)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west)
。查看Spring 框架参考指南以获取更多详细信息。以下清单显示了三个示例配置文件:
<SpringProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
</SpringProfile>
<SpringProfile name="dev | staging">
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</SpringProfile>
<SpringProfile name="!production">
<!-- configuration to be enabled when the "production" profile is not active -->
</SpringProfile>
4.10.2. 环境属性查找
Environment
如果您想在 Log4j2 配置中引用 Spring 的属性,您可以使用spring:
带前缀的查找。如果您想要访问application.properties
Log4j2 配置中的文件中的值,那么这样做会很有用。
以下示例显示如何设置从 SpringapplicationName
读取的Log4j2 属性:spring.application.name
Environment
<Properties>
<Property name="applicationName">${spring:spring.application.name}</Property>
</Properties>
查找键应以短横线大小写指定(例如my.property-name )。
|
4.10.3. Log4j2 系统属性
Log4j2 初始化后加载的所有系统属性都可以从 Spring 获取Environment
。例如,您可以添加log4j2.skipJansi=false
到application.properties
文件中以ConsoleAppender
在 Windows 上使用 Jansi。
Environment 仅当系统属性和操作系统环境变量不包含正在加载的值时才考虑
Spring 。 |
在早期 Log4j2 初始化期间加载的系统属性无法引用 Spring Environment 。例如,在 Spring 环境可用之前,使用 Log4j2 属性来允许选择默认的 Log4j2 实现。
|
5. 国际化
Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户。messages
默认情况下,Spring Boot在类路径的根目录中查找是否存在资源包。
当配置的资源包的默认属性文件可用时(messages.properties 默认情况下),将应用自动配置。如果您的资源包仅包含特定于语言的属性文件,则需要添加默认值。如果没有找到与任何配置的基本名称匹配的属性文件,则不会有自动配置的MessageSource .
|
资源包的基本名称以及其他几个属性可以使用spring.messages
命名空间进行配置,如以下示例所示:
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false
spring:
messages:
basename: "messages,config.i18n.messages"
fallback-to-system-locale: false
spring.messages.basename 支持逗号分隔的位置列表,可以是包限定符,也可以是从类路径根解析的资源。
|
请参阅MessageSourceProperties
参考资料 了解更多支持的选项。
6.JSON
Spring Boot 提供与三个 JSON 映射库的集成:
-
格森
-
杰克逊
-
JSON-B
Jackson 是首选和默认库。
6.1. 杰克逊
提供了 Jackson 的自动配置,并且 Jackson 是spring-boot-starter-json
. 当 Jackson 位于类路径上时,ObjectMapper
会自动配置一个 bean。提供了几个配置属性用于自定义ObjectMapper
.
6.1.1. 自定义序列化器和反序列化器
如果您使用 Jackson 来序列化和反序列化 JSON 数据,您可能需要编写自己的JsonSerializer
类JsonDeserializer
。自定义序列化器通常通过 module 向 Jackson 注册,但 Spring Boot 提供了另一种@JsonComponent
注释,可以更轻松地直接注册 Spring Bean。
您可以@JsonComponent
直接在JsonSerializer
,JsonDeserializer
或KeyDeserializer
实现上使用注释。您还可以在包含序列化器/反序列化器作为内部类的类上使用它,如以下示例所示:
@JsonComponent
public class MyJsonComponent {
public static class Serializer extends JsonSerializer<MyObject> {
@Override
public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
jgen.writeStartObject();
jgen.writeStringField("name", value.getName());
jgen.writeNumberField("age", value.getAge());
jgen.writeEndObject();
}
}
public static class Deserializer extends JsonDeserializer<MyObject> {
@Override
public MyObject deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = jsonParser.getCodec();
JsonNode tree = codec.readTree(jsonParser);
String name = tree.get("name").textValue();
int age = tree.get("age").intValue();
return new MyObject(name, age);
}
}
}
@JsonComponent
class MyJsonComponent {
class Serializer : JsonSerializer<MyObject>() {
@Throws(IOException::class)
override fun serialize(value: MyObject, jgen: JsonGenerator, serializers: SerializerProvider) {
jgen.writeStartObject()
jgen.writeStringField("name", value.name)
jgen.writeNumberField("age", value.age)
jgen.writeEndObject()
}
}
class Deserializer : JsonDeserializer<MyObject>() {
@Throws(IOException::class, JsonProcessingException::class)
override fun deserialize(jsonParser: JsonParser, ctxt: DeserializationContext): MyObject {
val codec = jsonParser.codec
val tree = codec.readTree<JsonNode>(jsonParser)
val name = tree["name"].textValue()
val age = tree["age"].intValue()
return MyObject(name, age)
}
}
}
@JsonComponent
中的所有beanApplicationContext
都会自动向 Jackson 注册。因为@JsonComponent
是用 元注释的@Component
,所以应用通常的组件扫描规则。
Spring Boot 还提供了JsonObjectSerializer
基JsonObjectDeserializer
类,这些基类在序列化对象时为标准 Jackson 版本提供有用的替代方案。有关详细信息,请参阅Javadoc 中的JsonObjectSerializer
和。JsonObjectDeserializer
上面的示例可以重写为使用JsonObjectSerializer
/ ,JsonObjectDeserializer
如下所示:
@JsonComponent
public class MyJsonComponent {
public static class Serializer extends JsonObjectSerializer<MyObject> {
@Override
protected void serializeObject(MyObject value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStringField("name", value.getName());
jgen.writeNumberField("age", value.getAge());
}
}
public static class Deserializer extends JsonObjectDeserializer<MyObject> {
@Override
protected MyObject deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec,
JsonNode tree) throws IOException {
String name = nullSafeValue(tree.get("name"), String.class);
int age = nullSafeValue(tree.get("age"), Integer.class);
return new MyObject(name, age);
}
}
}
`object`
@JsonComponent
class MyJsonComponent {
class Serializer : JsonObjectSerializer<MyObject>() {
@Throws(IOException::class)
override fun serializeObject(value: MyObject, jgen: JsonGenerator, provider: SerializerProvider) {
jgen.writeStringField("name", value.name)
jgen.writeNumberField("age", value.age)
}
}
class Deserializer : JsonObjectDeserializer<MyObject>() {
@Throws(IOException::class)
override fun deserializeObject(jsonParser: JsonParser, context: DeserializationContext,
codec: ObjectCodec, tree: JsonNode): MyObject {
val name = nullSafeValue(tree["name"], String::class.java)
val age = nullSafeValue(tree["age"], Int::class.java)
return MyObject(name, age)
}
}
}
7. 任务执行与调度
Executor
当上下文中没有bean 时,Spring Boot 会自动配置一个ThreadPoolTaskExecutor
合理的默认值,这些默认值可以自动关联到异步任务执行 ( @EnableAsync
) 和 Spring MVC 异步请求处理。
自动配置 |
线程池使用8个核心线程,可以根据负载增长和收缩。可以使用命名空间微调这些默认设置spring.task.execution
,如以下示例所示:
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s
spring:
task:
execution:
pool:
max-size: 16
queue-capacity: 100
keep-alive: "10s"
这会将线程池更改为使用有界队列,以便当队列已满(100 个任务)时,线程池会增加到最多 16 个线程。池的收缩更加积极,因为线程空闲 10 秒(而不是默认情况下 60 秒)时就会被回收。
如果需要与计划任务执行相关联(例如ThreadPoolTaskScheduler
使用),也可以自动配置A。@EnableScheduling
线程池默认使用一个线程,可以使用命名空间微调其设置spring.task.scheduling
,如下例所示:
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2
spring:
task:
scheduling:
thread-name-prefix: "scheduling-"
pool:
size: 2
TaskExecutorBuilder
如果需要创建自定义执行器或调度程序,则Bean 和Bean都TaskSchedulerBuilder
可以在上下文中使用。
8. 测试
Spring Boot 提供了许多实用程序和注释来帮助测试您的应用程序。测试支持由两个模块提供:spring-boot-test
包含核心项,并spring-boot-test-autoconfigure
支持测试的自动配置。
大多数开发人员使用spring-boot-starter-test
“Starter”,它导入 Spring Boot 测试模块以及 JUnit Jupiter、AssertJ、Hamcrest 和许多其他有用的库。
如果您有使用 JUnit 4 的测试,则可以使用 JUnit 5 的老式引擎来运行它们。要使用 vintage 引擎,请添加对 的依赖项
|
hamcrest-core
被排除在外,有利于org.hamcrest:hamcrest
是 的一部分spring-boot-starter-test
。
8.1. 测试范围依赖性
“ Starter spring-boot-starter-test
”(在 中test
scope
)包含以下提供的库:
-
JUnit 5:Java 应用程序单元测试的事实上的标准。
-
Spring Test和 Spring Boot Test:Spring Boot 应用程序的实用程序和集成测试支持。
-
AssertJ:一个流畅的断言库。
-
Hamcrest:匹配器对象库(也称为约束或谓词)。
-
Mockito:一个 Java 模拟框架。
-
JSONassert:JSON 的断言库。
-
JsonPath:JSON 的 XPath。
我们通常发现这些通用库在编写测试时很有用。如果这些库不能满足您的需求,您可以添加自己的其他测试依赖项。
8.2. 测试 Spring 应用程序
依赖注入的主要优点之一是它应该使您的代码更容易进行单元测试。您可以使用运算new
符实例化对象,甚至无需涉及 Spring。您还可以使用模拟对象而不是真正的依赖项。
通常,您需要超越单元测试并开始集成测试(使用 Spring ApplicationContext
)。无需部署应用程序或连接到其他基础设施即可执行集成测试,这非常有用。
Spring 框架包含一个用于此类集成测试的专用测试模块。您可以直接声明依赖项org.springframework:spring-test
或使用spring-boot-starter-test
“Starter”以传递方式将其拉入。
如果您之前没有使用过该spring-test
模块,则应该首先阅读Spring 框架参考文档的相关部分。
8.3. 测试 Spring Boot 应用程序
Spring Boot 应用程序是一个 Spring ApplicationContext
,因此除了通常使用普通 Spring 上下文进行的测试之外,无需执行任何特殊操作即可对其进行测试。
SpringApplication 仅当您用于创建
Spring Boot 的外部属性、日志记录和其他功能时,默认情况下才会将其安装在上下文中。 |
Spring Boot 提供了一个@SpringBootTest
注解,当您需要 Spring Boot 功能时,可以使用该注解来替代标准spring-test
@ContextConfiguration
注解。该注释的工作原理是通过创建ApplicationContext
在测试中使用的SpringApplication
注释。除了@SpringBootTest
许多其他注释之外,还提供了用于测试应用程序的更具体部分的注释。
如果您使用 JUnit 4,请不要忘记也添加@RunWith(SpringRunner.class) 到您的测试中,否则注释将被忽略。如果您使用的是 JUnit 5,则无需添加等效的@ExtendWith(SpringExtension.class) as @SpringBootTest ,并且其他@…Test 注释已经用它进行了注释。
|
默认情况下,@SpringBootTest
不会启动服务器。您可以使用webEnvironment
属性@SpringBootTest
来进一步优化测试的运行方式:
-
MOCK
(Default) : Loads a webApplicationContext
and provides a mock web environment. Embedded servers are not started when using this annotation. If a web environment is not available on your classpath, this mode transparently falls back to creating a regular non-webApplicationContext
. It can be used in conjunction with@AutoConfigureMockMvc
or@AutoConfigureWebTestClient
for mock-based testing of your web application. -
RANDOM_PORT
: Loads aWebServerApplicationContext
and provides a real web environment. Embedded servers are started and listen on a random port. -
DEFINED_PORT
: Loads aWebServerApplicationContext
and provides a real web environment. Embedded servers are started and listen on a defined port (from yourapplication.properties
) or on the default port of8080
. -
NONE
: Loads anApplicationContext
by usingSpringApplication
but does not provide any web environment (mock or otherwise).
If your test is @Transactional , it rolls back the transaction at the end of each test method by default.
However, as using this arrangement with either RANDOM_PORT or DEFINED_PORT implicitly provides a real servlet environment, the HTTP client and server run in separate threads and, thus, in separate transactions.
Any transaction initiated on the server does not roll back in this case.
|
@SpringBootTest with webEnvironment = WebEnvironment.RANDOM_PORT will also start the management server on a separate random port if your application uses a different port for the management server.
|
8.3.1. Detecting Web Application Type
If Spring MVC is available, a regular MVC-based application context is configured. If you have only Spring WebFlux, we will detect that and configure a WebFlux-based application context instead.
If both are present, Spring MVC takes precedence.
If you want to test a reactive web application in this scenario, you must set the spring.main.web-application-type
property:
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {
// ...
}
8.3.2. Detecting Test Configuration
If you are familiar with the Spring Test Framework, you may be used to using @ContextConfiguration(classes=…)
in order to specify which Spring @Configuration
to load.
Alternatively, you might have often used nested @Configuration
classes within your test.
When testing Spring Boot applications, this is often not required.
Spring Boot’s @*Test
annotations search for your primary configuration automatically whenever you do not explicitly define one.
The search algorithm works up from the package that contains the test until it finds a class annotated with @SpringBootApplication
or @SpringBootConfiguration
.
As long as you structured your code in a sensible way, your main configuration is usually found.
If you use a test annotation to test a more specific slice of your application, you should avoid adding configuration settings that are specific to a particular area on the main method’s application class. 的底层组件扫描配置 |
如果要自定义主要配置,可以使用嵌套@TestConfiguration
类。与将使用嵌套@Configuration
类代替应用程序的主要配置不同,嵌套@TestConfiguration
类除了应用程序的主要配置之外还使用。
Spring 的测试框架在测试之间缓存应用程序上下文。因此,只要您的测试共享相同的配置(无论如何发现),加载上下文的潜在耗时过程只会发生一次。 |
8.3.3. 使用测试配置主要方法
通常,发现的测试配置@SpringBootTest
将是您的 main @SpringBootApplication
. 在大多数结构良好的应用程序中,此配置类还将包括main
用于启动应用程序的方法。
例如,以下是典型 Spring Boot 应用程序的非常常见的代码模式:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
在上面的示例中,该main
方法除了委托给 之外不执行任何操作SpringApplication.run
。然而,可以有一个更复杂的main
方法,在调用之前应用自定义SpringApplication.run
。
例如,以下是一个更改横幅模式并设置其他配置文件的应用程序:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.setAdditionalProfiles("myprofile");
application.run(args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
setAdditionalProfiles("myprofile");
}
}
由于main
方法中的自定义可能会影响结果ApplicationContext
,因此您可能还想使用该main
方法来创建ApplicationContext
测试中使用的 。默认情况下,@SpringBootTest
不会调用您的main
方法,而是直接使用类本身来创建ApplicationContext
如果要更改此行为,可以更改to或useMainMethod
的属性。当设置为 时,如果找不到方法,测试将失败。当设置为该方法时,如果该方法可用,则将使用该方法,否则将使用标准加载机制。@SpringBootTest
UseMainMethod.ALWAYS
UseMainMethod.WHEN_AVAILABLE
ALWAYS
main
WHEN_AVAILABLE
main
例如,以下测试将调用main
方法MyApplication
来创建ApplicationContext
. 如果主要方法设置了其他配置文件,那么这些配置文件将在启动时处于活动状态ApplicationContext
。
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
void exampleTest() {
// ...
}
}
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
fun exampleTest() {
// ...
}
}
8.3.4. 排除测试配置
如果您的应用程序使用组件扫描(例如,如果您使用@SpringBootApplication
或@ComponentScan
),您可能会发现仅为特定测试创建的顶级配置类被意外地到处拾取。
正如我们之前所看到的,@TestConfiguration
可以在测试的内部类上使用来自定义主要配置。当放置在顶级类上时,指示不应通过扫描选取@TestConfiguration
中的类。src/test/java
然后,您可以在需要的地方显式导入该类,如以下示例所示:
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {
@Test
fun exampleTest() {
// ...
}
}
如果您直接使用@ComponentScan (即不通过@SpringBootApplication ),您需要向其注册TypeExcludeFilter 。有关详细信息,
请参阅Javadoc 。 |
8.3.5. 使用应用程序参数
如果您的应用程序需要参数,您可以@SpringBootTest
使用该属性注入它们args
。
@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");
}
}
@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {
@Test
fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
assertThat(args.optionNames).containsOnly("app.test")
assertThat(args.getOptionValues("app.test")).containsOnly("one")
}
}
8.3.6. 使用模拟环境进行测试
默认情况下,@SpringBootTest
不会启动服务器,而是设置一个模拟环境来测试 Web 端点。
MockMvc
使用 Spring MVC,我们可以使用或查询我们的 Web 端点WebTestClient
,如以下示例所示:
@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");
}
}
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
fun testWithMockMvc(@Autowired mvc: MockMvc) {
mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
如果您只想专注于 web 层而不开始完整ApplicationContext ,请考虑使用@WebMvcTest .
|
对于 Spring WebFlux 端点,您可以WebTestClient
按以下示例所示使用:
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
在模拟环境中进行测试通常比在完整的 servlet 容器中运行要快。但是,由于模拟发生在 Spring MVC 层,因此依赖于较低级别 servlet 容器行为的代码无法直接使用 MockMvc 进行测试。 例如,Spring Boot 的错误处理是基于 servlet 容器提供的“错误页面”支持。这意味着,虽然您可以测试 MVC 层是否按预期抛出和处理异常,但您无法直接测试是否呈现特定的自定义错误页面。如果您需要测试这些较低级别的问题,您可以启动一个完全运行的服务器,如下一节所述。 |
8.3.7. 使用正在运行的服务器进行测试
如果您需要启动完整运行的服务器,我们建议您使用随机端口。如果您使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
,则每次测试运行时都会随机选择一个可用端口。
该@LocalServerPort
注释可用于将实际使用的端口注入到您的测试中。为了方便起见,需要对启动的服务器进行 REST 调用的测试可以额外添加@Autowire
一个WebTestClient
,它解析到正在运行的服务器的相对链接,并附带一个用于验证响应的专用 API,如以下示例所示:
@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");
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
WebTestClient 可以用于实时服务器和模拟环境。
|
此设置需要spring-webflux
类路径。如果你不能或不会添加 webflux,Spring Boot 还提供了一个TestRestTemplate
工具:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
fun exampleTest(@Autowired restTemplate: TestRestTemplate) {
val body = restTemplate.getForObject("/", String::class.java)
assertThat(body).isEqualTo("Hello World")
}
}
8.3.8. 定制WebTestClient
要自定义WebTestClient
bean,请配置WebTestClientBuilderCustomizer
bean。任何此类 bean 都通过WebTestClient.Builder
用于创建 的进行调用WebTestClient
。
8.3.9. 使用 JMX
由于测试上下文框架会缓存上下文,因此默认情况下禁用 JMX,以防止相同的组件在同一域上注册。如果此类测试需要访问 an MBeanServer
,请考虑将其标记为脏:
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {
@Test
fun exampleTest() {
assertThat(mBeanServer.domains).contains("java.lang")
// ...
}
}
8.3.10. 使用指标
无论您的类路径如何,仪表注册表(内存中支持的除外)在使用时都不会自动配置@SpringBootTest
。
如果您需要将指标导出到不同的后端作为集成测试的一部分,请使用 进行注释@AutoConfigureObservability
。
8.3.11. 使用追踪
无论您的类路径如何,使用时都不会自动配置跟踪@SpringBootTest
。
如果您需要跟踪作为集成测试的一部分,请使用 进行注释@AutoConfigureObservability
。
8.3.12. 模拟和监视 Bean
运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,您可能有一些远程服务的外观,但在开发过程中不可用。当您想要模拟在真实环境中可能难以触发的故障时,模拟也很有用。
Spring Boot 包含一个@MockBean
注释,可用于为ApplicationContext
. 您可以使用注释来添加新的 bean 或替换单个现有的 bean 定义。该注释可以直接用在测试类、测试中的字段或@Configuration
类和字段上。当在字段上使用时,创建的模拟的实例也会被注入。模拟 bean 在每个测试方法之后会自动重置。
如果您的测试使用 Spring Boot 的测试注释之一(例如 Java
Kotlin
|
以下示例RemoteService
用模拟实现替换现有 bean:
@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");
}
}
@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {
@Test
fun exampleTest() {
given(remoteService.value).willReturn("spring")
val reverse = reverser.reverseValue // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps")
}
}
@MockBean 不能用于模拟在应用程序上下文刷新期间执行的 bean 的行为。执行测试时,应用程序上下文刷新已完成,此时配置模拟行为为时已晚。在这种情况下,我们建议使用一种@Bean 方法来创建和配置模拟。
|
此外,您可以使用@SpyBean
Mockito 来包装任何现有的 bean spy
。有关完整详细信息,请参阅Javadoc 。
虽然 Spring 的测试框架会在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但使用@MockBean 或@SpyBean 影响缓存键,这很可能会增加上下文的数量。
|
如果您使用通过名称引用参数的方法@SpyBean 来监视 bean @Cacheable ,则您的应用程序必须使用-parameters . 这确保了一旦 Bean 被监视,参数名称就可用于缓存基础设施。
|
当您用于@SpyBean 监视由 Spring 代理的 bean 时,在某些情况下您可能需要删除 Spring 的代理,例如使用given 或设置期望时when 。用来AopTestUtils.getTargetObject(yourProxiedSpy) 这样做。
|
8.3.13. 自动配置的测试
Spring Boot 的自动配置系统对于应用程序来说效果很好,但有时对于测试来说有点太多了。仅加载测试应用程序“片段”所需的配置部分通常会有所帮助。例如,您可能想要测试 Spring MVC 控制器是否正确映射 URL,并且您不想在这些测试中涉及数据库调用,或者您可能想要测试 JPA 实体,并且当这些实体出现时您对 Web 层不感兴趣。测试运行。
该spring-boot-test-autoconfigure
模块包含许多可用于自动配置此类“切片”的注释。它们中的每一个都以类似的方式工作,提供@…Test
加载的注释以及可用于自定义自动配置设置的ApplicationContext
一个或多个注释。@AutoConfigure…
每个切片将组件扫描限制为适当的组件,并加载一组非常有限的自动配置类。如果您需要排除其中之一,大多数@…Test 注释都会提供一个excludeAutoConfiguration 属性。或者,您可以使用@ImportAutoConfiguration#exclude .
|
@…Test 不支持在一个测试中
使用多个注释来包含多个“切片” 。如果您需要多个“切片”,请选择其中一个@…Test 注释并@AutoConfigure… 手动添加其他“切片”的注释。
|
也可以将@AutoConfigure… 注释与标准@SpringBootTest 注释一起使用。如果您对“切片”应用程序不感兴趣,但想要一些自动配置的测试 bean,则可以使用此组合。
|
8.3.14. 自动配置的 JSON 测试
要测试对象 JSON 序列化和反序列化是否按预期工作,您可以使用注释@JsonTest
。
@JsonTest
自动配置可用的支持的 JSON 映射器,它可以是以下库之一:
-
杰克逊
ObjectMapper
,任何@JsonComponent
豆子和任何杰克逊Module
的 -
Gson
-
Jsonb
@JsonTest 可以在附录中找到
启用的自动配置列表。
|
如果需要配置自动配置的元素,可以使用注解@AutoConfigureJsonTesters
。
Spring Boot 包含基于 AssertJ 的帮助程序,它们与 JSONAssert 和 JsonPath 库配合使用,以检查 JSON 是否按预期显示。、、和类可分别用于 Jackson、Gson、Jsonb 和 Strings JacksonTester
。测试类上的任何辅助字段都可以在使用时使用。以下示例显示了 Jackson 的测试类:GsonTester
JsonbTester
BasicJsonTester
@Autowired
@JsonTest
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> 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");
}
}
@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {
@Test
fun serialize() {
val details = VehicleDetails("Honda", "Civic")
// Assert against a `.json` file in the same package as the test
assertThat(json.write(details)).isEqualToJson("expected.json")
// Or use JSON path based assertions
assertThat(json.write(details)).hasJsonPathStringValue("@.make")
assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
}
@Test
fun deserialize() {
val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
assertThat(json.parseObject(content).make).isEqualTo("Ford")
}
}
JSON 帮助器类也可以直接在标准单元测试中使用。为此,如果您不使用,请initFields 在您的方法中调用帮助器的方法。
@Before @JsonTest |
如果您使用 Spring Boot 的基于 AssertJ 的帮助程序对给定 JSON 路径处的数字值进行断言,则可能无法使用,isEqualTo
具体取决于类型。相反,您可以使用 AssertJsatisfies
来断言该值与给定条件匹配。例如,以下示例断言实际数字是接近0.15
偏移量内的浮点值0.01
。
@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)));
}
@Test
fun someTest() {
val value = SomeObject(0.152f)
assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies(ThrowingConsumer { number ->
assertThat(number.toFloat()).isCloseTo(0.15f, within(0.01f))
})
}
8.3.15. 自动配置的 Spring MVC 测试
要测试 Spring MVC 控制器是否按预期工作,请使用@WebMvcTest
注释。
@WebMvcTest
自动配置 Spring MVC 基础结构并将扫描的 bean 限制为@Controller
、@ControllerAdvice
、@JsonComponent
、Converter
、GenericConverter
、Filter
、HandlerInterceptor
、WebMvcConfigurer
、WebMvcRegistrations
和HandlerMethodArgumentResolver
。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@WebMvcTest
@EnableConfigurationProperties
@ConfigurationProperties
@WebMvcTest 可以在附录中找到
启用的自动配置设置的列表。
|
如果您需要注册额外的组件,例如 Jackson Module ,您可以通过@Import 在测试中使用来导入其他配置类。
|
通常,@WebMvcTest
仅限于单个控制器,并与 结合使用,@MockBean
为所需的协作者提供模拟实现。
@WebMvcTest
还自动配置MockMvc
。Mock MVC 提供了一种强大的方法来快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。
您还可以通过使用 注释来
自动配置MockMvc 非@WebMvcTest (例如) 。以下示例使用:
@SpringBootTest @AutoConfigureMockMvc MockMvc |
@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"));
}
}
@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvc) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
mvc.perform(MockMvcRequestBuilders.get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().string("Honda Civic"))
}
}
如果您需要配置自动配置的元素(例如,何时应应用 servlet 过滤器),您可以在注释中使用属性@AutoConfigureMockMvc 。
|
如果您使用 HtmlUnit 和 Selenium,自动配置还会提供 HtmlUnit WebClient
bean 和/或 Selenium WebDriver
bean。以下示例使用 HtmlUnit:
@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");
}
}
@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
assertThat(page.body.textContent).isEqualTo("Honda Civic")
}
}
默认情况下,Spring Boot 将WebDriver bean 放入特殊的“范围”中,以确保驱动程序在每次测试后退出并注入新实例。如果您不希望出现这种行为,您可以添加@Scope("singleton") 到您的WebDriver @Bean 定义中。
|
Spring Boot 创建的作用域webDriver 将替换任何用户定义的同名作用域。如果您定义自己的webDriver 范围,您可能会发现当您使用 时它会停止工作@WebMvcTest 。
|
如果类路径上有 Spring Security,@WebMvcTest
也会扫描WebSecurityConfigurer
bean。您可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。有关如何使用 Spring SecurityMockMvc
支持的更多详细信息,请参阅此howto.html操作方法部分。
有时编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试。 |
8.3.16. 自动配置的 Spring WebFlux 测试
要测试Spring WebFlux控制器是否按预期工作,您可以使用@WebFluxTest
注释。
@WebFluxTest
自动配置 Spring WebFlux 基础设施并将扫描的 bean 限制为@Controller
、@ControllerAdvice
、@JsonComponent
、Converter
、GenericConverter
、WebFilter
和WebFluxConfigurer
。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@WebFluxTest
@EnableConfigurationProperties
@ConfigurationProperties
@WebFluxTest 可以在附录中找到
启用的自动配置列表。
|
如果您需要注册额外的组件,例如 Jackson Module ,您可以@Import 在测试中使用导入其他配置类。
|
通常,@WebFluxTest
仅限于单个控制器并与注释结合使用,@MockBean
为所需的协作者提供模拟实现。
@WebFluxTest
还有 auto-configures WebTestClient
,它提供了一种强大的方法来快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。
您还可以通过使用 注释来
自动配置WebTestClient 非@WebFluxTest (例如) 。以下示例显示了一个同时使用和 a 的类:
@SpringBootTest @AutoConfigureWebTestClient @WebFluxTest WebTestClient |
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() {
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");
}
}
@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Honda Civic")
}
}
此设置仅受 WebFlux 应用程序支持,因为WebTestClient 在模拟 Web 应用程序中使用目前仅适用于 WebFlux。
|
@WebFluxTest 无法检测通过功能性 Web 框架注册的路由。RouterFunction 要在上下文中测试bean,请考虑RouterFunction 使用@Import 或 使用来导入您自己@SpringBootTest 。
|
@WebFluxTest 无法检测注册为@Bean of type 的自定义安全配置SecurityWebFilterChain 。要将其包含在您的测试中,您需要使用@Import 或 使用 来导入注册 bean 的配置@SpringBootTest 。
|
有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试。 |
8.3.17. 自动配置的 Spring GraphQL 测试
Spring GraphQL 提供了专用的测试支持模块;您需要将其添加到您的项目中:
<dependencies>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Unless already present in the compile scope -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
dependencies {
testImplementation("org.springframework.graphql:spring-graphql-test")
// Unless already present in the implementation configuration
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
}
该测试模块附带GraphQlTester。测试仪在测试中被大量使用,所以一定要熟悉它的使用。有GraphQlTester
多种变体,Spring Boot 将根据测试类型自动配置它们:
-
在服务器端执行测试
ExecutionGraphQlServiceTester
,无需客户端或传输 -
使用连接到服务器的客户端执行测试
HttpGraphQlTester
,无论是否有实时服务器
Spring Boot 可帮助您使用注释测试Spring GraphQL 控制器@GraphQlTest
。
@GraphQlTest
自动配置 Spring GraphQL 基础设施,不涉及任何传输或服务器。这将扫描的bean 限制为@Controller
、RuntimeWiringConfigurer
、JsonComponent
、Converter
、GenericConverter
、DataFetcherExceptionResolver
和Instrumentation
。使用注释
时,不会扫描GraphQlSourceBuilderCustomizer
常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@GraphQlTest
@EnableConfigurationProperties
@ConfigurationProperties
@GraphQlTest 可以在附录中找到
启用的自动配置列表。
|
通常,@GraphQlTest
仅限于一组控制器,并与注释结合使用,@MockBean
为所需的协作者提供模拟实现。
@GraphQlTest(GreetingController.class)
class GreetingControllerTests {
@Autowired
private GraphQlTester graphQlTester;
@Test
void shouldGreetWithSpecificName() {
this.graphQlTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
@Test
void shouldGreetWithDefaultName() {
this.graphQlTester.document("{ greeting } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Spring!");
}
}
@GraphQlTest(GreetingController::class)
internal class GreetingControllerTests {
@Autowired
lateinit var graphQlTester: GraphQlTester
@Test
fun shouldGreetWithSpecificName() {
graphQlTester.document("{ greeting(name: \"Alice\") } ").execute().path("greeting").entity(String::class.java)
.isEqualTo("Hello, Alice!")
}
@Test
fun shouldGreetWithDefaultName() {
graphQlTester.document("{ greeting } ").execute().path("greeting").entity(String::class.java)
.isEqualTo("Hello, Spring!")
}
}
@SpringBootTest
测试是完整的集成测试,涉及整个应用程序。当使用随机或定义的端口时,会配置一个实时服务器并HttpGraphQlTester
自动提供一个 bean,以便您可以使用它来测试您的服务器。配置 MOCK 环境后,您还可以HttpGraphQlTester
通过使用以下注释来请求 bean @AutoConfigureHttpGraphQlTester
:
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
void shouldGreetWithSpecificName(@Autowired HttpGraphQlTester graphQlTester) {
HttpGraphQlTester authenticatedTester = graphQlTester.mutate()
.webTestClient((client) -> client.defaultHeaders((headers) -> headers.setBasicAuth("admin", "ilovespring")))
.build();
authenticatedTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
}
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
fun shouldGreetWithSpecificName(@Autowired graphQlTester: HttpGraphQlTester) {
val authenticatedTester = graphQlTester.mutate()
.webTestClient { client: WebTestClient.Builder ->
client.defaultHeaders { headers: HttpHeaders ->
headers.setBasicAuth("admin", "ilovespring")
}
}.build()
authenticatedTester.document("{ greeting(name: \"Alice\") } ").execute()
.path("greeting").entity(String::class.java).isEqualTo("Hello, Alice!")
}
}
8.3.18. 自动配置数据 Cassandra 测试
您可以用来@DataCassandraTest
测试 Cassandra 应用程序。默认情况下,它配置CassandraTemplate
、扫描类@Table
并配置 Spring Data Cassandra 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关将 Cassandra 与 Spring Boot 结合使用的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataCassandraTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataCassandraTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}
@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)
8.3.19. 自动配置的数据 Couchbase 测试
您可以用来@DataCouchbaseTest
测试 Couchbase 应用程序。默认情况下,它配置CouchbaseTemplate
或ReactiveCouchbaseTemplate
、扫描@Document
类并配置 Spring Data Couchbase 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关将 Couchbase 与 Spring Boot 结合使用的更多信息,请参阅本章前面的“ data.html ”。)@ConfigurationProperties
@DataCouchbaseTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataCouchbaseTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:
@DataCouchbaseTest
class MyDataCouchbaseTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.20. 自动配置数据 Elasticsearch 测试
您可以用来@DataElasticsearchTest
测试 Elasticsearch 应用程序。默认情况下,它配置ElasticsearchRestTemplate
、扫描@Document
类并配置 Spring Data Elasticsearch 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关将 Elasticsearch 与 Spring Boot 结合使用的更多信息,请参阅本章前面的“ data.html ”。)@ConfigurationProperties
@DataElasticsearchTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataElasticsearchTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:
@DataElasticsearchTest
class MyDataElasticsearchTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.21. 自动配置的数据 JPA 测试
您可以使用@DataJpaTest
注释来测试 JPA 应用程序。默认情况下,它扫描@Entity
类并配置 Spring Data JPA 存储库。如果类路径上有可用的嵌入式数据库,它也会配置一个。spring.jpa.show-sql
默认情况下,通过将该属性设置为 来记录 SQL 查询true
。showSql
可以使用注释的属性来禁用此功能。
使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@DataJpaTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataJpaTest 可以在附录中找到
启用的自动配置设置的列表。
|
默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅Spring 框架参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个类禁用事务管理,如下所示:
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
数据 JPA 测试还可以注入一个TestEntityManager
bean,它提供了专门为测试设计的标准 JPA 的替代方案EntityManager
。
TestEntityManager 还可以通过添加@AutoConfigureTestEntityManager . 执行此操作时,请确保您的测试在事务中运行,例如通过添加 @Transactional 测试类或方法。
|
JdbcTemplate
如果您需要的话,也可以使用A。以下示例显示了@DataJpaTest
正在使用的注释:
@DataJpaTest
class MyRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
void testExample() {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getEmployeeNumber()).isEqualTo("1234");
}
}
@DataJpaTest
class MyRepositoryTests(@Autowired val entityManager: TestEntityManager, @Autowired val repository: UserRepository) {
@Test
fun testExample() {
entityManager.persist(User("sboot", "1234"))
val user = repository.findByUsername("sboot")
assertThat(user?.username).isEqualTo("sboot")
assertThat(user?.employeeNumber).isEqualTo("1234")
}
}
内存嵌入式数据库通常非常适合测试,因为它们速度快并且不需要任何安装。但是,如果您更喜欢针对真实数据库运行测试,则可以使用注释@AutoConfigureTestDatabase
,如以下示例所示:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MyRepositoryTests {
// ...
}
8.3.22. 自动配置的 JDBC 测试
@JdbcTest
类似于,@DataJpaTest
但适用于仅需要DataSource
且不使用 Spring Data JDBC 的测试。默认情况下,它配置一个内存嵌入式数据库和一个JdbcTemplate
. 使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@JdbcTest
@EnableConfigurationProperties
@ConfigurationProperties
@JdbcTest 可以在附录中找到
启用的自动配置列表。
|
默认情况下,JDBC 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅Spring 框架参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个类禁用事务管理,如下所示:
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {
}
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests
如果您希望测试针对真实数据库运行,则可以按照@AutoConfigureTestDatabase
与@DataJpaTest
. (请参阅“自动配置数据 JPA 测试”。)
8.3.23. 自动配置的数据 JDBC 测试
@DataJdbcTest
类似于@JdbcTest
,但适用于使用 Spring Data JDBC 存储库的测试。默认情况下,它配置一个内存嵌入式数据库、一个JdbcTemplate
和 Spring Data JDBC 存储库。使用注解时只AbstractJdbcConfiguration
扫描子类,不扫描
常规和bean。可用于包含豆类。@DataJdbcTest
@Component
@ConfigurationProperties
@EnableConfigurationProperties
@ConfigurationProperties
@DataJdbcTest 可以在附录中找到
启用的自动配置列表。
|
默认情况下,数据 JDBC 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅Spring 框架参考文档中的相关部分。如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC 示例所示。
如果您希望测试针对真实数据库运行,则可以按照@AutoConfigureTestDatabase
与@DataJpaTest
. (请参阅“自动配置数据 JPA 测试”。)
8.3.24. 自动配置数据 R2DBC 测试
@DataR2dbcTest
类似于@DataJdbcTest
但适用于使用 Spring Data R2DBC 存储库的测试。默认情况下,它配置一个内存嵌入式数据库、一个R2dbcEntityTemplate
和 Spring Data R2DBC 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@DataR2dbcTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataR2dbcTest 可以在附录中找到
启用的自动配置列表。
|
默认情况下,数据 R2DBC 测试不是事务性的。
如果您希望测试针对真实数据库运行,则可以按照@AutoConfigureTestDatabase
与@DataJpaTest
. (请参阅“自动配置数据 JPA 测试”。)
8.3.25. 自动配置的 jOOQ 测试
您可以以与 jOOQ 相关测试@JooqTest
类似的方式使用。@JdbcTest
由于 jOOQ 严重依赖于与数据库模式相对应的基于 Java 的模式,因此DataSource
使用现有的模式。如果您想将其替换为内存数据库,您可以使用@AutoConfigureTestDatabase
覆盖这些设置。(有关在 Spring Boot 中使用 jOOQ 的更多信息,请参阅“ data.html ”。)使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@JooqTest
@EnableConfigurationProperties
@ConfigurationProperties
@JooqTest 可以在附录中找到
启用的自动配置列表。
|
@JooqTest
配置一个DSLContext
. 以下示例显示了@JooqTest
正在使用的注释:
@JooqTest
class MyJooqTests {
@Autowired
private DSLContext dslContext;
// ...
}
@JooqTest
class MyJooqTests(@Autowired val dslContext: DSLContext) {
// ...
}
JOOQ 测试是事务性的,默认情况下会在每次测试结束时回滚。如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC 示例所示。
8.3.26. 自动配置数据 MongoDB 测试
您可以用来@DataMongoTest
测试 MongoDB 应用程序。默认情况下,它配置MongoTemplate
、扫描@Document
类并配置 Spring Data MongoDB 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关将 MongoDB 与 Spring Boot 结合使用的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataMongoTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataMongoTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下类显示了@DataMongoTest
正在使用的注释:
@DataMongoTest
class MyDataMongoDbTests {
@Autowired
private MongoTemplate mongoTemplate;
// ...
}
@DataMongoTest
class MyDataMongoDbTests(@Autowired val mongoTemplate: MongoTemplate) {
// ...
}
8.3.27. 自动配置数据 Neo4j 测试
您可以用来@DataNeo4jTest
测试 Neo4j 应用程序。默认情况下,它扫描@Node
类并配置 Spring Data Neo4j 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关将 Neo4J 与 Spring Boot 结合使用的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataNeo4jTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataNeo4jTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了在 Spring Boot 中使用 Neo4J 测试的典型设置:
@DataNeo4jTest
class MyDataNeo4jTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataNeo4jTest
class MyDataNeo4jTests(@Autowired val repository: SomeRepository) {
// ...
}
默认情况下,Data Neo4j 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅Spring 框架参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个类禁用事务管理,如下所示:
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {
}
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests
反应式访问不支持事务测试。如果您使用此样式,则必须@DataNeo4jTest 如上所述配置测试。
|
8.3.28. 自动配置数据 Redis 测试
您可以用来@DataRedisTest
测试 Redis 应用程序。默认情况下,它扫描@RedisHash
类并配置 Spring Data Redis 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关将 Redis 与 Spring Boot 结合使用的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataRedisTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataRedisTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了@DataRedisTest
正在使用的注释:
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.29. 自动配置的数据 LDAP 测试
您可以用来@DataLdapTest
测试 LDAP 应用程序。默认情况下,它配置内存中的嵌入式 LDAP(如果可用),配置LdapTemplate
扫描@Entry
类,并配置 Spring Data LDAP 存储库。使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。(有关在 Spring Boot 中使用 LDAP 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataLdapTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataLdapTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了@DataLdapTest
正在使用的注释:
@DataLdapTest
class MyDataLdapTests {
@Autowired
private LdapTemplate ldapTemplate;
// ...
}
@DataLdapTest
class MyDataLdapTests(@Autowired val ldapTemplate: LdapTemplate) {
// ...
}
内存中嵌入式 LDAP 通常非常适合测试,因为它速度快并且不需要任何开发人员安装。但是,如果您希望针对真实 LDAP 服务器运行测试,则应排除嵌入式 LDAP 自动配置,如以下示例所示:
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {
// ...
}
@DataLdapTest(excludeAutoConfiguration = [EmbeddedLdapAutoConfiguration::class])
class MyDataLdapTests {
// ...
}
8.3.30。自动配置的 REST 客户端
您可以使用@RestClientTest
注释来测试 REST 客户端。默认情况下,它自动配置 Jackson、GSON 和 Jsonb 支持,配置RestTemplateBuilder
并添加对MockRestServiceServer
. 使用注释
时,不会扫描常规@Component
和beans。可用于包含豆类。@ConfigurationProperties
@RestClientTest
@EnableConfigurationProperties
@ConfigurationProperties
@RestClientTest 可以在附录中找到
启用的自动配置设置的列表。
|
value
应使用 的或components
属性指定要测试的特定 bean @RestClientTest
,如以下示例所示:
@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestClientTests(
@Autowired val service: RemoteVehicleDetailsService,
@Autowired val server: MockRestServiceServer) {
@Test
fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails(): Unit {
server.expect(MockRestRequestMatchers.requestTo("/greet/details"))
.andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
val greeting = service.callRestService()
assertThat(greeting).isEqualTo("hello")
}
}
8.3.31. 自动配置的 Spring REST 文档测试
您可以使用注释在 Mock MVC、REST Assured 或 WebTestClient 测试中@AutoConfigureRestDocs
使用Spring REST 文档。它消除了 Spring REST 文档中对 JUnit 扩展的需要。
@AutoConfigureRestDocs
可用于覆盖默认输出目录(target/generated-snippets
如果您使用 Maven 或build/generated-snippets
使用 Gradle)。它还可用于配置出现在任何记录的 URI 中的主机、方案和端口。
使用 Mock MVC 自动配置 Spring REST 文档测试
@AutoConfigureRestDocs
自定义MockMvc
bean 以在测试基于 servlet 的 Web 应用程序时使用 Spring REST 文档。您可以通过 using 来注入它@Autowired
,并在测试中使用它,就像使用 Mock MVC 和 Spring REST 文档时通常所做的那样,如以下示例所示:
@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"));
}
}
@WebMvcTest(UserController::class)
@AutoConfigureRestDocs
class MyUserDocumentationTests(@Autowired val mvc: MockMvc) {
@Test
fun listUsers() {
mvc.perform(MockMvcRequestBuilders.get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(MockMvcResultMatchers.status().isOk)
.andDo(MockMvcRestDocumentation.document("list-users"))
}
}
如果您需要对 Spring REST Docs 配置进行更多的控制,而不是 的属性所提供的控制@AutoConfigureRestDocs
,则可以使用RestDocsMockMvcConfigurationCustomizer
bean,如以下示例所示:
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsMockMvcConfigurationCustomizer {
override fun customize(configurer: MockMvcRestDocumentationConfigurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
}
}
如果您想利用 Spring REST Docs 对参数化输出目录的支持,您可以创建一个RestDocumentationResultHandler
bean。自动配置调用alwaysDo
此结果处理程序,从而导致每次MockMvc
调用自动生成默认片段。以下示例显示了RestDocumentationResultHandler
定义:
@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyResultHandlerConfiguration {
@Bean
fun restDocumentation(): RestDocumentationResultHandler {
return MockMvcRestDocumentation.document("{method-name}")
}
}
使用 WebTestClient 自动配置 Spring REST 文档测试
@AutoConfigureRestDocs
也可以在测试响应式 Web 应用程序时使用WebTestClient
。您可以通过使用来注入它,@Autowired
并在测试中使用它,就像使用@WebFluxTest
Spring REST 文档时通常所做的那样,如以下示例所示:
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void listUsers() {
this.webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(document("list-users"));
}
}
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests(@Autowired val webTestClient: WebTestClient) {
@Test
fun listUsers() {
webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk
.expectBody()
.consumeWith(WebTestClientRestDocumentation.document("list-users"))
}
}
如果您需要对 Spring REST Docs 配置进行更多的控制,而不是 的属性所提供的控制@AutoConfigureRestDocs
,则可以使用RestDocsWebTestClientConfigurationCustomizer
bean,如以下示例所示:
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {
@Override
public void customize(WebTestClientRestDocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsWebTestClientConfigurationCustomizer {
override fun customize(configurer: WebTestClientRestDocumentationConfigurer) {
configurer.snippets().withEncoding("UTF-8")
}
}
如果您想利用 Spring REST Docs 对参数化输出目录的支持,您可以使用 aWebTestClientBuilderCustomizer
为每个实体交换结果配置一个使用者。以下示例显示了这样的WebTestClientBuilderCustomizer
定义:
@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
public WebTestClientBuilderCustomizer restDocumentation() {
return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
fun restDocumentation(): WebTestClientBuilderCustomizer {
return WebTestClientBuilderCustomizer { builder: WebTestClient.Builder ->
builder.entityExchangeResultConsumer(
WebTestClientRestDocumentation.document("{method-name}")
)
}
}
}
自动配置的 Spring REST 文档测试,保证 REST
@AutoConfigureRestDocs
制作一个RequestSpecification
预配置为使用 Spring REST 文档的 bean,可供您的测试使用。您可以通过 using 来注入它@Autowired
,并在测试中使用它,就像使用 REST Assured 和 Spring REST Docs 时通常所做的那样,如以下示例所示:
@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));
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Test
fun listUsers(@Autowired documentationSpec: RequestSpecification?, @LocalServerPort port: Int) {
RestAssured.given(documentationSpec)
.filter(RestAssuredRestDocumentation.document("list-users"))
.`when`()
.port(port)["/"]
.then().assertThat()
.statusCode(Matchers.`is`(200))
}
}
如果您需要对 Spring REST Docs 配置进行更多的控制,而不是 的属性所提供的控制@AutoConfigureRestDocs
,则RestDocsRestAssuredConfigurationCustomizer
可以使用 bean,如以下示例所示:
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer {
override fun customize(configurer: RestAssuredRestDocumentationConfigurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
}
}
8.3.32. 自动配置的 Spring Web 服务测试
自动配置的 Spring Web 服务客户端测试
您可以@WebServiceClientTest
使用 Spring Web Services 项目来测试调用 Web 服务的应用程序。默认情况下,它配置一个模拟WebServiceServer
bean 并自动自定义您的WebServiceTemplateBuilder
. (有关使用 Spring Boot 的 Web 服务的更多信息,请参阅“ io.html ”。)
@WebServiceClientTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了@WebServiceClientTest
正在使用的注释:
@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {
@Autowired
private MockWebServiceServer server;
@Autowired
private SomeWebService someWebService;
@Test
void mockServerCall() {
this.server
.expect(payload(new StringSource("<request/>")))
.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}
}
@WebServiceClientTest(SomeWebService::class)
class MyWebServiceClientTests(@Autowired val server: MockWebServiceServer, @Autowired val someWebService: SomeWebService) {
@Test
fun mockServerCall() {
server
.expect(RequestMatchers.payload(StringSource("<request/>")))
.andRespond(ResponseCreators.withPayload(StringSource("<response><status>200</status></response>")))
assertThat(this.someWebService.test()).extracting(Response::status).isEqualTo(200)
}
}
自动配置的 Spring Web 服务服务器测试
您可以@WebServiceServerTest
使用 Spring Web Services 项目来测试实现 Web 服务的应用程序。默认情况下,它配置一个MockWebServiceClient
可用于调用 Web 服务端点的 bean。(有关使用 Spring Boot 的 Web 服务的更多信息,请参阅“ io.html ”。)
@WebServiceServerTest 可以在附录中找到
启用的自动配置设置的列表。
|
以下示例显示了@WebServiceServerTest
正在使用的注释:
@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {
@Autowired
private MockWebServiceClient client;
@Test
void mockServerCall() {
this.client
.sendRequest(RequestCreators.withPayload(new StringSource("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(new StringSource("<ExampleResponse>42</ExampleResponse>")));
}
}
@WebServiceServerTest(ExampleEndpoint::class)
class MyWebServiceServerTests(@Autowired val client: MockWebServiceClient) {
@Test
fun mockServerCall() {
client
.sendRequest(RequestCreators.withPayload(StringSource("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(StringSource("<ExampleResponse>42</ExampleResponse>")))
}
}
8.3.33. 额外的自动配置和切片
每个切片都提供一个或多个@AutoConfigure…
注释,即定义应包含在切片中的自动配置。@AutoConfigure…
可以通过创建自定义注释或添加到测试来逐个测试地添加其他自动配置,@ImportAutoConfiguration
如以下示例所示:
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {
}
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration::class)
class MyJdbcTests
确保不要使用常规@Import 注释来导入自动配置,因为 Spring Boot 以特定方式处理它们。
|
或者,可以通过将附加自动配置注册到存储的文件中来添加额外的自动配置,以用于切片注释的任何使用,META-INF/spring
如下例所示:
com.example.IntegrationAutoConfiguration
在此示例中,com.example.IntegrationAutoConfiguration
在每个用 注释的测试上启用@JdbcTest
。
# 您可以在此文件中
使用注释。 |
切片或@AutoConfigure… 注释可以通过这种方式自定义,只要它使用@ImportAutoConfiguration .
|
8.3.34. 用户配置和切片
然后,重要的是不要在应用程序的主类中乱扔特定于其功能的特定区域的配置设置。
假设您正在使用 Spring Data MongoDB,您依赖于它的自动配置,并且您已经启用了审计。您可以@SpringBootApplication
按如下方式定义您的:
@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {
// ...
}
@SpringBootApplication
@EnableMongoAuditing
class MyApplication {
// ...
}
因为此类是测试的源配置,所以任何切片测试实际上都会尝试启用 Mongo 审计,这绝对不是您想要做的。推荐的方法是将特定于区域的配置移至@Configuration
与应用程序同一级别的单独类,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {
// ...
}
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
class MyMongoConfiguration {
// ...
}
根据应用程序的复杂性,您可以使用单个@Configuration 类进行自定义,也可以为每个域区域使用一个类。后一种方法允许您在一个测试中启用它(如有必要),并使用注释@Import 。有关何时可能需要为切片测试启用特定类的更多详细信息,请参阅此操作方法部分@Configuration 。
|
测试切片@Configuration
从扫描中排除类。例如,对于 a @WebMvcTest
,以下配置将不会WebMvcConfigurer
在测试切片加载的应用程序上下文中包含给定的 bean:
@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {
@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
// ...
};
}
}
@Configuration(proxyBeanMethods = false)
class MyWebConfiguration {
@Bean
fun testConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
// ...
}
}
}
然而,下面的配置将导致WebMvcConfigurer
测试片加载自定义。
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
// ...
}
@Component
class MyWebMvcConfigurer : WebMvcConfigurer {
// ...
}
另一个混乱的来源是类路径扫描。假设,当您以合理的方式构建代码时,您需要扫描额外的包。您的应用程序可能类似于以下代码:
@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
@SpringBootApplication
@ComponentScan("com.example.app", "com.example.another")
class MyApplication {
// ...
}
这样做可以有效地覆盖默认的组件扫描指令,并具有扫描这两个包的副作用,无论您选择哪个切片。例如,a@DataJpaTest
似乎突然扫描应用程序的组件和用户配置。同样,将自定义指令移至单独的类是解决此问题的好方法。
如果这不适合您,您可以@SpringBootConfiguration 在测试层次结构中创建一个位置,以便使用它。或者,您可以为测试指定一个源,这会禁用查找默认源的行为。
|
8.3.35. 使用 Spock 测试 Spring Boot 应用程序
Spock 2.2 或更高版本可用于测试 Spring Boot 应用程序。为此,请将-groovy-4.0
Spockspock-spring
模块版本的依赖项添加到应用程序的构建中。
spock-spring
将Spring的测试框架集成到Spock中。有关更多详细信息,请参阅Spock 的 Spring 模块的文档。
8.4. 测试容器
Testcontainers库提供了一种管理 Docker 容器内运行的服务的方法。它与 JUnit 集成,允许您编写一个测试类,该测试类可以在任何测试运行之前启动容器。Testcontainers 对于编写与真实后端服务(例如 MySQL、MongoDB、Cassandra 等)通信的集成测试特别有用。
测试容器可以在 Spring Boot 测试中使用,如下所示:
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
val neo4j = Neo4jContainer("neo4j:5")
}
}
这将在运行任何测试之前启动一个运行 Neo4j 的 docker 容器(如果 Docker 在本地运行)。在大多数情况下,您需要配置应用程序以连接到容器中运行的服务。
8.4.1. 服务连接
服务连接是与任何远程服务的连接。Spring Boot 的自动配置可以使用服务连接的详细信息,并使用它们来建立与远程服务的连接。执行此操作时,连接详细信息优先于任何与连接相关的配置属性。
使用测试容器时,可以通过注释测试类中的容器字段来自动为容器中运行的服务创建连接详细信息。
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
@ServiceConnection
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
@ServiceConnection
val neo4j = Neo4jContainer("neo4j:5")
}
}
感谢@ServiceConnection
,上述配置允许应用程序中与 Neo4j 相关的 bean 与 Testcontainers 管理的 Docker 容器内运行的 Neo4j 进行通信。这是通过自动定义一个Neo4jConnectionDetails
bean 来完成的,然后由 Neo4j 自动配置使用该 bean,覆盖任何与连接相关的配置属性。
您需要将该spring-boot-testcontainers 模块添加为测试依赖项,以便将服务连接与测试容器一起使用。
|
ContainerConnectionDetailsFactory
服务连接注释由注册的类处理spring.factories
。A可以根据特定的子类或 Docker 镜像名称ContainerConnectionDetailsFactory
创建bean。ConnectionDetails
Container
jar中提供了以下服务连接工厂spring-boot-testcontainers
:
连接详情 | 匹配于 |
---|---|
|
容器类型 |
|
容器类型 |
|
容器类型 |
|
容器类型 |
|
容器类型 |
|
|
|
容器类型 |
|
容器类型 |
|
容器类型 |
|
|
|
容器类型 |
|
名为“redis”的容器 |
|
名为“openzipkin/zipkin”的容器 |
默认情况下,将为给定的 .xml 文件创建所有适用的连接详细信息 bean 如果您只想创建适用类型的子集,则可以使用 |
默认情况下Container.getDockerImageName()
用于获取用于查找连接详细信息的名称。如果您使用自定义 docker 镜像,则可以使用 属性name
来@ServiceConnection
覆盖它。
例如,如果您有一个GenericContainer
使用 Docker 映像registry.mycompany.com/mirror/myredis
,则可以使用它@ServiceConnection(name="redis")
来确保RedisConnectionDetails
已创建。
8.4.2. 动态属性
服务连接的一个稍微详细但也更灵活的替代方案是@DynamicPropertySource
。静态@DynamicPropertySource
方法允许向 Spring 环境添加动态属性值。
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
}
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
val neo4j = Neo4jContainer("neo4j:5")
@DynamicPropertySource
fun neo4jProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.neo4j.uri") { neo4j.boltUrl }
}
}
}
上述配置允许应用程序中与 Neo4j 相关的 bean 与 Testcontainers 管理的 Docker 容器内运行的 Neo4j 进行通信。
8.4.3. 在开发时使用测试容器
除了使用测试容器进行集成测试之外,还可以在开发时使用它们。这种方法允许开发人员快速启动应用程序所依赖的服务的容器,从而无需手动配置数据库服务器等内容。以这种方式使用 Testcontainers 提供与 Docker Compose 类似的功能,只不过您的容器配置采用 Java 而不是 YAML。
要在开发时使用测试容器,您需要使用“测试”类路径而不是“主”来启动应用程序。这将允许您访问所有声明的测试依赖项,并为您提供一个自然的位置来编写测试配置。
要创建应用程序的测试可启动版本,您应该在src/test
目录中创建一个“Application”类。例如,如果您的主应用程序位于src/main/java/com/example/MyApplication.java
,您应该创建src/test/java/com/example/TestMyApplication.java
该类TestMyApplication
可以使用该SpringApplication.from(…)
方法来启动真正的应用程序:
public class TestMyApplication {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main).run(args);
}
}
fun main(args: Array<String>) {
fromApplication<MyApplication>().run(*args)
}
您还需要定义Container
要与应用程序一起启动的实例。为此,您需要确保该spring-boot-testcontainers
模块已作为test
依赖项添加。完成后,您可以创建一个@TestConfiguration
类来声明@Bean
要启动的容器的方法。
您还可以使用 注解您的@Bean
方法@ServiceConnection
来创建ConnectionDetails
bean。有关支持的技术的详细信息,请参阅上面的服务连接部分。
典型的测试容器配置如下所示:
@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {
@Bean
@ServiceConnection
public Neo4jContainer<?> neo4jContainer() {
return new Neo4jContainer<>("neo4j:5");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {
@Bean
@ServiceConnection
fun neo4jContainer(): Neo4jContainer<*> {
return Neo4jContainer("neo4j:5")
}
}
bean的生命周期Container 由 Spring Boot 自动管理。容器将自动启动和停止。
|
定义测试配置后,您可以使用以下with(…)
方法将其附加到测试启动器:
public class TestMyApplication {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main).with(MyContainersConfiguration.class).run(args);
}
}
fun main(args: Array<String>) {
fromApplication<MyApplication>().with(MyContainersConfiguration::class).run(*args)
}
TestMyApplication
现在,您可以像启动任何常规 Javamain
方法应用程序一样启动您的应用程序及其需要运行的容器。
您可以使用 Maven 目标spring-boot:test-run 或 Gradle 任务bootTestRun 从命令行执行此操作。
|
在开发时贡献动态属性
如果您想在开发时从您的Container
@Bean
方法贡献动态属性,您可以通过注入DynamicPropertyRegistry
. 这与您可以在测试中使用的@DynamicPropertySource
注释的工作方式类似。它允许您添加容器启动后可用的属性。
典型的配置如下所示:
@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {
@Bean
public MongoDBContainer mongoDbContainer(DynamicPropertyRegistry properties) {
MongoDBContainer container = new MongoDBContainer("mongo:5.0");
properties.add("spring.data.mongodb.host", container::getHost);
properties.add("spring.data.mongodb.port", container::getFirstMappedPort);
return container;
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {
@Bean
fun monogDbContainer(properties: DynamicPropertyRegistry): MongoDBContainer {
var container = MongoDBContainer("mongo:5.0")
properties.add("spring.data.mongodb.host", container::getHost);
properties.add("spring.data.mongodb.port", container::getFirstMappedPort);
return container
}
}
建议尽可能使用 a @ServiceConnection ,但是,对于尚不支持的技术,动态属性可能是一个有用的后备方案@ServiceConnection 。
|
导入测试容器声明类
使用测试容器时的常见模式是将Container
实例声明为静态字段。通常这些字段直接在测试类上定义。它们也可以在父类或测试实现的接口上声明。
例如,以下MyContainers
接口声明mongo
和neo4j
容器:
public interface MyContainers {
@Container
MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");
@Container
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:5");
}
如果您已经以这种方式定义了容器,或者您只是喜欢这种样式,则可以导入这些声明类,而不是将容器定义为@Bean
方法。为此,请将@ImportTestcontainers
注释添加到您的测试配置类中:
@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers.class)
public class MyContainersConfiguration {
}
@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers::class)
class MyContainersConfiguration {
}
您可以使用字段@ServiceConnection 上的注释Container 来建立服务连接。您还可以将@DynamicPropertySource 带注释的方法添加到声明类中。
|
在开发时将 DevTools 与测试容器结合使用
使用 devtools 时,您可以使用 来注释 bean 和 bean 方法@RestartScope
。当开发工具重新启动应用程序时,不会重新创建此类 bean。这对于 Testcontainer Container
bean 特别有用,因为尽管应用程序重新启动,它们仍保持其状态。
@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {
@Bean
@RestartScope
public MongoDBContainer mongoDbContainer() {
return new MongoDBContainer("mongo:5.0");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {
@Bean
@RestartScope
fun monogDbContainer(): MongoDBContainer {
return MongoDBContainer("mongo:5.0")
}
}
如果您正在使用 Gradle 并希望使用此功能,则需要将spring-boot-devtools 依赖项的配置从更改developmentOnly 为testImplementation 。使用默认范围developmentOnly ,bootTestRun 任务将不会获取代码中的更改,因为开发工具未处于活动状态。
|
8.5。测试实用程序
测试应用程序时通常有用的一些测试实用程序类被打包为spring-boot
.
8.5.1. 配置数据应用程序上下文初始化器
ConfigDataApplicationContextInitializer
您可以将其ApplicationContextInitializer
应用于测试来加载 Spring Bootapplication.properties
文件。当您不需要 提供的全套功能时,可以使用它@SpringBootTest
,如下例所示:
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}
@ContextConfiguration(classes = [Config::class], initializers = [ConfigDataApplicationContextInitializer::class])
class MyConfigFileTests {
// ...
}
单独使用ConfigDataApplicationContextInitializer 不支持@Value("${…}") 注射。它唯一的工作是确保application.properties 文件加载到 Spring 的Environment . 为了获得@Value 支持,您需要额外配置一个PropertySourcesPlaceholderConfigurer 或使用@SpringBootTest ,它会为您自动配置一个。
|
8.5.2. 测试属性值
TestPropertyValues
让您快速添加属性到 aConfigurableEnvironment
或ConfigurableApplicationContext
。您可以使用key=value
字符串来调用它,如下所示:
class MyEnvironmentTests {
@Test
void testPropertySources() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
assertThat(environment.getProperty("name")).isEqualTo("Boot");
}
}
class MyEnvironmentTests {
@Test
fun testPropertySources() {
val environment = MockEnvironment()
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment)
assertThat(environment.getProperty("name")).isEqualTo("Boot")
}
}
8.5.3. 输出捕获
OutputCapture
是一个Extension
可用于捕获System.out
和System.err
输出的 JUnit。要使用它,请将其作为参数添加@ExtendWith(OutputCaptureExtension.class)
并注入到测试类构造函数或测试方法中,如下所示:CapturedOutput
@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
@ExtendWith(OutputCaptureExtension::class)
class MyOutputCaptureTests {
@Test
fun testName(output: CapturedOutput?) {
println("Hello World!")
assertThat(output).contains("World")
}
}
8.5.4. 测试休息模板
TestRestTemplate
是 Spring 的便捷替代方案RestTemplate
,在集成测试中很有用。您可以获得普通模板或发送基本 HTTP 身份验证(使用用户名和密码)的模板。无论哪种情况,模板都是容错的。这意味着它的行为方式对测试友好,不会在 4xx 和 5xx 错误上引发异常。ResponseEntity
相反,可以通过返回的值及其状态代码来检测此类错误。
WebTestClient Spring Framework 5.0 提供了适用于WebFlux 集成测试以及WebFlux 和 MVC 端到端测试的
新功能。与TestRestTemplate .
|
建议(但不是强制)使用 Apache HTTP 客户端(版本 5.1 或更高版本)。如果您的类路径上有该文件,则会TestRestTemplate
通过适当配置客户端来响应。如果您确实使用 Apache 的 HTTP 客户端,则会启用一些额外的测试友好功能:
-
不遵循重定向(因此您可以断言响应位置)。
-
Cookie 被忽略(因此模板是无状态的)。
TestRestTemplate
可以直接在集成测试中实例化,如下例所示:
class MyTests {
private final TestRestTemplate template = new TestRestTemplate();
@Test
void testRequest() {
ResponseEntity<String> headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}
}
class MyTests {
private val template = TestRestTemplate()
@Test
fun testRequest() {
val headers = template.getForEntity("https://myhost.example.com/example", String::class.java)
assertThat(headers.headers.location).hasHost("other.example.com")
}
}
或者,如果您将@SpringBootTest
注释与WebEnvironment.RANDOM_PORT
或 一起使用WebEnvironment.DEFINED_PORT
,则可以注入完全配置TestRestTemplate
并开始使用它。如有必要,可以通过 bean 应用其他自定义RestTemplateBuilder
。任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如下例所示:
@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));
}
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests(@Autowired val template: TestRestTemplate) {
@Test
fun testRequest() {
val headers = template.getForEntity("/example", String::class.java).headers
assertThat(headers.location).hasHost("other.example.com")
}
@TestConfiguration(proxyBeanMethods = false)
internal class RestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(): RestTemplateBuilder {
return RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1))
}
}
}
9.Docker Compose 支持
Docker Compose 是一种流行的技术,可用于定义和管理应用程序所需服务的多个容器。compose.yml
通常会在应用程序旁边创建一个文件,用于定义和配置服务容器。
Docker Compose 的典型工作流程是运行docker compose up
,处理您的应用程序并连接到已启动的服务,然后docker compose down
在完成后运行。
该spring-boot-docker-compose
模块可以包含在项目中,为使用 Docker Compose 处理容器提供支持。将模块依赖项添加到您的构建中,如以下 Maven 和 Gradle 清单所示:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
dependencies {
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
}
当此模块作为依赖项包含时,Spring Boot 将执行以下操作:
-
compose.yml
在应用程序目录中搜索 a和其他常见的撰写文件名 -
docker compose up
与被发现的人通话compose.yml
-
为每个受支持的容器创建服务连接 Bean
-
docker compose stop
当应用程序关闭时调用
或CLI 应用程序需要位于您的路径上,以便 Spring Boot 的支持正常工作
docker compose 。docker-compose |
默认情况下,Spring Boot 的 Docker Compose 支持在运行测试时处于禁用状态。要启用它,请设置spring.docker.compose.skip.in-tests 为false 。
|
9.1. 服务连接
服务连接是与任何远程服务的连接。Spring Boot 的自动配置可以使用服务连接的详细信息,并使用它们来建立与远程服务的连接。执行此操作时,连接详细信息优先于任何与连接相关的配置属性。
当使用 Spring Boot 的 Docker Compose 支持时,服务连接将建立到容器映射的端口。
Docker compose 通常的使用方式是将容器内的端口映射到计算机上的临时端口。例如,Postgres 服务器可能使用端口 5432 在容器内运行,但在本地映射到完全不同的端口。服务连接将始终发现并使用本地映射的端口。 |
通过容器的镜像名称建立服务连接。目前支持以下服务连接:
连接详情 | 匹配于 |
---|---|
|
名为“cassandra”的容器 |
|
名为“elasticsearch”的容器 |
|
名为“gvenzl/oracle-xe”、“mariadb”、“mssql/server”、“mysql”或“postgres”的容器 |
|
名为“mongo”的容器 |
|
名为“gvenzl/oracle-xe”、“mariadb”、“mssql/server”、“mysql”或“postgres”的容器 |
|
名为“rabbitmq”的容器 |
|
名为“redis”的容器 |
|
名为“openzipkin/zipkin”的容器。 |
9.2. 自定义图像
有时您可能需要使用您自己的图像版本来提供服务。您可以使用任何自定义图像,只要其行为方式与标准图像相同即可。具体来说,标准映像支持的任何环境变量也必须在您的自定义映像中使用。
如果您的映像使用不同的名称,您可以在文件中使用标签,compose.yml
以便 Spring Boot 可以提供服务连接。使用名为的标签org.springframework.boot.service-connection
来提供服务名称。
例如:
services:
redis:
image: 'mycompany/mycustomredis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.service-connection: redis
9.3. 跳过特定容器
如果您定义了一个容器映像,compose.yml
并且不想连接到您的应用程序,则可以使用标签来忽略它。任何带有 标签的容器org.springframework.boot.ignore
都会被 Spring Boot 忽略。
例如:
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.ignore: true
9.4. 使用特定的撰写文件
如果您的撰写文件与应用程序不在同一目录中,或者名称不同,则可以spring.docker.compose.file
在您的 中使用application.properties
或application.yaml
来指向不同的文件。属性可以定义为精确路径或与应用程序相关的路径。
例如:
spring.docker.compose.file=../my-compose.yml
spring:
docker:
compose:
file: "../my-compose.yml"
9.5。等待容器准备就绪
由 Docker Compose 启动的容器可能需要一些时间才能完全准备好。healthcheck
检查准备情况的建议方法是在文件中的服务定义下添加一个部分compose.yml
。
healthcheck
由于从文件中省略配置的情况并不罕见compose.yml
,因此 Spring Boot 还会直接检查服务准备情况。默认情况下,当可以建立到容器映射端口的 TCP/IP 连接时,容器被视为准备就绪。
org.springframework.boot.readiness-check.tcp.disable
您可以通过在文件中添加标签来针对每个容器禁用此功能compose.yml
。
例如:
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.readiness-check.tcp.disable: true
application.properties
您还可以更改或文件中的超时值application.yaml
:
spring.docker.compose.readiness.tcp.connect-timeout=10s
spring.docker.compose.readiness.tcp.read-timeout=5s
spring:
docker:
compose:
readiness:
tcp:
connect-timeout: 10s
read-timeout: 5s
总体超时可以使用配置spring.docker.compose.readiness.timeout
。
9.6. 控制 Docker Compose 生命周期
默认情况下,Spring Bootdocker compose up
在应用程序启动和docker compose stop
关闭时调用。如果您希望有不同的生命周期管理,您可以使用该spring.docker.compose.lifecycle-management
属性。
支持以下值:
-
none
- 不要启动或停止 Docker Compose -
start-only
- 在应用程序启动时启动 Docker Compose 并使其保持运行 -
start-and-stop
- 当应用程序启动时启动 Docker Compose,并在 JVM 退出时停止它
此外,您可以使用该spring.docker.compose.start.command
属性来更改是否使用docker compose up
或。docker compose start
允许spring.docker.compose.stop.command
您配置是否使用docker compose down
或。docker compose stop
以下示例显示了如何配置生命周期管理:
spring.docker.compose.lifecycle-management=start-and-stop
spring.docker.compose.start.command=start
spring.docker.compose.stop.command=down
spring.docker.compose.stop.timeout=1m
spring:
docker:
compose:
lifecycle-management: start-and-stop
start:
command: start
stop:
command: down
timeout: 1m
9.7. 激活 Docker Compose 配置文件
Docker Compose 配置文件与 Spring 配置文件类似,它们允许您针对特定环境调整 Docker Compose 配置。如果您想激活特定的 Docker Compose 配置文件,您可以使用spring.docker.compose.profiles.active
您的application.properties
或application.yaml
文件中的属性:
spring.docker.compose.profiles.active=myprofile
spring:
docker:
compose:
profiles:
active: "myprofile"
10. 创建您自己的自动配置
如果您在开发共享库的公司工作,或者如果您在开源或商业库上工作,您可能想要开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,并且仍然可以被 Spring Boot 拾取。
自动配置可以与提供自动配置代码以及与之一起使用的典型库的“启动器”相关联。我们首先介绍构建您自己的自动配置所需了解的内容,然后继续介绍创建自定义启动程序所需的典型步骤。
10.1. 了解自动配置 Bean
实现自动配置的类用 进行注释@AutoConfiguration
。该注释本身使用 进行元注释@Configuration
,使自动配置成为标准@Configuration
类。附加@Conditional
注释用于限制何时应用自动配置。通常,自动配置类使用@ConditionalOnClass
和@ConditionalOnMissingBean
注释。这确保仅当找到相关类并且您尚未声明自己的@Configuration
.
您可以浏览源代码spring-boot-autoconfigure
来查看@AutoConfiguration
Spring提供的类(参见META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件)。
10.2. 定位自动配置候选者
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Spring Boot 检查已发布的 jar 中是否存在文件。该文件应列出您的配置类,每行一个类名,如以下示例所示:
com.mycorp.libx.autoconfigure.LibXAutoConfiguration com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以使用该字符向导入文件添加注释# 。
|
自动配置必须仅通过在导入文件中命名来加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应启用组件扫描来查找其他组件。@Import 应使用
特定的注释来代替。 |
如果您的配置需要按特定顺序应用,您可以在注释或专用和注释上使用before
、beforeName
、after
和afterName
属性。例如,如果您提供特定于 Web 的配置,则您的类可能需要在.@AutoConfiguration
@AutoConfigureBefore
@AutoConfigureAfter
WebMvcAutoConfiguration
如果您想要订购某些彼此不应该有任何直接了解的自动配置,您也可以使用@AutoConfigureOrder
. 该注释与常规@Order
注释具有相同的语义,但为自动配置类提供了专用的顺序。
与标准@Configuration
类一样,自动配置类的应用顺序仅影响其 bean 的定义顺序。随后创建这些 Bean 的顺序不受影响,并且由每个 Bean 的依赖性和任何@DependsOn
关系决定。
10.3. 条件注释
@Conditional
您几乎总是希望在自动配置类上包含一个或多个注释。注释@ConditionalOnMissingBean
是一个常见的示例,用于允许开发人员在对默认设置不满意时覆盖自动配置。
Spring Boot 包含许多注释,您可以通过注释类或单个方法@Conditional
在自己的代码中重用它们。这些注释包括:@Configuration
@Bean
10.3.1. 上课条件
和注释允许根据特定类的存在或不存在来包含类@ConditionalOnClass
。由于注释元数据是使用ASM解析的,因此您可以使用该属性来引用真实的类,即使该类实际上可能不会出现在正在运行的应用程序类路径中。如果您希望使用值指定类名,也可以使用该属性。@ConditionalOnMissingClass
@Configuration
value
name
String
此机制不适用于@Bean
通常返回类型是条件目标的方法:在方法上的条件适用之前,JVM 将加载该类并可能处理方法引用,如果该类不适用,则方法引用将失败展示。
为了处理这种情况,@Configuration
可以使用单独的类来隔离该情况,如以下示例所示:
@AutoConfiguration
// 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();
}
}
}
@Configuration(proxyBeanMethods = false)
// Some conditions ...
class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService::class)
class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
fun someService(): SomeService {
return SomeService()
}
}
}
如果您使用@ConditionalOnClass 或@ConditionalOnMissingClass 作为元注释的一部分来组成您自己的组合注释,则必须使用name as 来引用该类,在这种情况下不会得到处理。
|
10.3.2. 豆子条件
和注释允许根据特定 bean 的存在或不存在来包含 bean @ConditionalOnBean
。@ConditionalOnMissingBean
您可以使用该value
属性按类型指定 Bean 或name
按名称指定 Bean。该search
属性允许您限制ApplicationContext
搜索 bean 时应考虑的层次结构。
当放置在@Bean
方法上时,目标类型默认为该方法的返回类型,如下例所示:
@AutoConfiguration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
fun someService(): SomeService {
return SomeService()
}
}
在前面的示例中,如果.xml 文件中尚未包含someService
任何类型的 bean,则将创建该 bean 。SomeService
ApplicationContext
您需要非常小心 bean 定义的添加顺序,因为这些条件是根据迄今为止已处理的内容进行评估的。因此,我们建议仅在自动配置类上
使用@ConditionalOnBean 和注释(因为可以保证在添加任何用户定义的 bean 定义后加载这些类)。@ConditionalOnMissingBean |
@ConditionalOnBean 并且@ConditionalOnMissingBean 不阻止@Configuration 创建类。在类级别使用这些条件与使用@Bean 注释标记每个包含的方法之间的唯一区别是,@Configuration 如果条件不匹配,前者会阻止将类注册为 Bean。
|
声明@Bean 方法时,在方法的返回类型中提供尽可能多的类型信息。例如,如果 bean 的具体类实现了一个接口,则 bean 方法的返回类型应该是具体类而不是接口。使用 bean 条件时,在方法中提供尽可能多的类型信息@Bean 尤其重要,因为它们的评估只能依赖于方法签名中可用的类型信息。
|
10.3.3。物业条件
该@ConditionalOnProperty
注释允许基于 Spring 环境属性包含配置。使用prefix
和name
属性来指定应检查的属性。false
默认情况下,匹配任何存在且不等于的属性。您还可以使用havingValue
和matchIfMissing
属性创建更高级的检查。
10.3.4。资源条件
该@ConditionalOnResource
注释允许仅当存在特定资源时才包含配置。可以使用常用的 Spring 约定来指定资源,如以下示例所示:file:/home/user/test.dat
。
10.3.5。网络申请条件
和注释允许根据应用程序是否是 Web 应用程序来包含配置@ConditionalOnWebApplication
。@ConditionalOnNotWebApplication
基于 servlet 的 Web 应用程序是任何使用 Spring WebApplicationContext
、定义session
范围或具有ConfigurableWebEnvironment
. 响应式 Web 应用程序是任何使用ReactiveWebApplicationContext
或具有ConfigurableReactiveWebEnvironment
.
和注释允许根据应用程序是否是部署到 servlet 容器的传统 WAR 应用程序来包含配置@ConditionalOnWarDeployment
。@ConditionalOnNotWarDeployment
此条件与使用嵌入式 Web 服务器运行的应用程序不匹配。
10.3.6。SpEL 表达条件
该@ConditionalOnExpression
注释允许根据SpEL 表达式的结果包含配置。
在表达式中引用 bean 将导致该 bean 在上下文刷新处理中很早就被初始化。因此,bean 将不适合进行后处理(例如配置属性绑定),并且其状态可能不完整。 |
10.4. 测试您的自动配置
自动配置可能会受到许多因素的影响:用户配置(@Bean
定义和Environment
定制)、条件评估(特定库的存在)等。具体来说,每个测试都应该创建一个明确定义的ApplicationContext
表示这些自定义的组合。
ApplicationContextRunner
提供了实现这一目标的好方法。
ApplicationContextRunner
通常定义为测试类的一个字段,用于收集基本的通用配置。以下示例确保MyServiceAutoConfiguration
始终被调用:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
val contextRunner = ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration::class.java))
如果必须定义多个自动配置,则无需对它们的声明进行排序,因为它们的调用顺序与运行应用程序时完全相同。 |
每个测试都可以使用运行器来代表特定的用例。例如,下面的示例调用用户配置 ( 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");
}
}
@Test
fun defaultServiceBacksOff() {
contextRunner.withUserConfiguration(UserConfiguration::class.java)
.run { context: AssertableApplicationContext ->
assertThat(context).hasSingleBean(MyService::class.java)
assertThat(context).getBean("myCustomService")
.isSameAs(context.getBean(MyService::class.java))
}
}
@Configuration(proxyBeanMethods = false)
internal class UserConfiguration {
@Bean
fun myCustomService(): MyService {
return 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");
});
}
@Test
fun serviceNameCanBeConfigured() {
contextRunner.withPropertyValues("user.name=test123").run { context: AssertableApplicationContext ->
assertThat(context).hasSingleBean(MyService::class.java)
assertThat(context.getBean(MyService::class.java).name).isEqualTo("test123")
}
}
跑步者也可用于显示ConditionEvaluationReport
. 报告可以按水平INFO
或DEBUG
水平打印。以下示例展示了如何ConditionEvaluationReportLoggingListener
在自动配置测试中使用 打印报告。
class MyConditionEvaluationReportingTests {
@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}
class MyConditionEvaluationReportingTests {
@Test
fun autoConfigTest() {
ApplicationContextRunner()
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
.run { context: AssertableApplicationContext? -> }
}
}
10.4.1. 模拟网络环境
如果您需要测试仅在 servlet 或反应式 Web 应用程序上下文中运行的自动配置,请分别使用WebApplicationContextRunner
或ReactiveWebApplicationContextRunner
。
10.4.2. 覆盖类路径
还可以测试当运行时不存在特定类和/或包时会发生什么。Spring Boot 附带了一个FilteredClassLoader
可供运行者轻松使用的工具。在以下示例中,我们断言如果MyService
不存在,则自动配置已正确禁用:
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
.run((context) -> assertThat(context).doesNotHaveBean("myService"));
}
@Test
fun serviceIsIgnoredIfLibraryIsNotPresent() {
contextRunner.withClassLoader(FilteredClassLoader(MyService::class.java))
.run { context: AssertableApplicationContext? ->
assertThat(context).doesNotHaveBean("myService")
}
}
10.5。创建您自己的启动器
典型的 Spring Boot 启动器包含用于自动配置和自定义给定技术的基础设施的代码,我们称之为“acme”。为了使其易于扩展,可以将专用命名空间中的许多配置键暴露给环境。最后,提供了一个“入门”依赖项来帮助用户尽可能轻松地入门。
具体来说,自定义启动器可以包含以下内容:
-
autoconfigure
包含“acme”自动配置代码的模块。 -
该
starter
模块提供对模块的依赖关系autoconfigure
以及“acme”以及通常有用的任何其他依赖关系。简而言之,添加启动器应该提供开始使用该库所需的一切。
这种分离为两个模块是没有必要的。如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的这一事实。此外,您还可以制作一个入门程序,提供有关这些可选依赖项的意见。同时,其他人也可以仅依赖该autoconfigure
模块并根据不同的意见制作自己的启动器。
如果自动配置相对简单并且没有可选功能,那么在启动器中合并两个模块绝对是一个选择。
10.5.1。命名
您应该确保为您的初学者提供正确的命名空间。spring-boot
即使您使用不同的 Maven,也不要以 开头模块名称groupId
。我们将来可能会为您自动配置的内容提供官方支持。
根据经验,您应该在启动器之后命名组合模块。例如,假设您正在为“acme”创建启动器,并且将自动配置模块acme-spring-boot
和启动器命名为acme-spring-boot-starter
。如果您只有一个将两者结合在一起的模块,请将其命名为acme-spring-boot-starter
.
10.5.2. 配置键
如果您的初学者提供配置密钥,请为它们使用唯一的命名空间。特别是,不要将您的密钥包含在 Spring Boot 使用的命名空间中(例如server
、management
、spring
等)。如果您使用相同的命名空间,我们将来可能会以破坏您的模块的方式修改这些命名空间。根据经验,请为所有键添加您拥有的命名空间前缀(例如acme
)。
确保通过为每个属性添加字段 javadoc 来记录配置键,如以下示例所示:
@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);
}
@ConfigurationProperties("acme")
class AcmeProperties(
/**
* Whether to check the location of acme resources.
*/
var isCheckLocation: Boolean = true,
/**
* Timeout for establishing a connection to the acme server.
*/
var loginTimeout:Duration = Duration.ofSeconds(3))
您应该仅使用带有字段 Javadoc 的纯文本@ConfigurationProperties ,因为它们在添加到 JSON 之前不会被处理。
|
以下是我们在内部遵循的一些规则,以确保描述的一致性:
-
不要以“The”或“A”开始描述。
-
对于
boolean
类型,以“是否”或“启用”开始描述。 -
对于基于集合的类型,以“逗号分隔列表”开始描述
-
如果默认单位与毫秒不同,则使用而
java.time.Duration
不是long
描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。 -
不要在描述中提供默认值,除非必须在运行时确定。
确保触发元数据生成,以便 IDE 帮助也可用于您的密钥。您可能需要检查生成的元数据 ( META-INF/spring-configuration-metadata.json
) 以确保您的密钥已正确记录。在兼容的 IDE 中使用您自己的启动器也是验证元数据质量的好主意。
10.5.3。“自动配置”模块
该autoconfigure
模块包含开始使用该库所需的所有内容。它还可能包含配置键定义(例如@ConfigurationProperties
)和任何可用于进一步自定义组件初始化方式的回调接口。
您应该将库的依赖项标记为可选,以便您可以autoconfigure 更轻松地将模块包含在项目中。如果您这样做,则不会提供该库,并且默认情况下 Spring Boot 会退出。
|
Spring Boot 使用注释处理器来收集元数据文件 ( ) 中的自动配置条件META-INF/spring-autoconfigure-metadata.properties
。如果该文件存在,它将用于急切地过滤不匹配的自动配置,这将缩短启动时间。
使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
如果您直接在应用程序中定义了自动配置,请确保配置spring-boot-maven-plugin
以防止repackage
目标将依赖项添加到 fat jar 中:
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
使用 Gradle,应在配置中声明依赖项annotationProcessor
,如以下示例所示:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
10.5.4。入门模块
启动器实际上是一个空罐子。它的唯一目的是提供使用该库所需的依赖项。您可以将其视为对开始所需内容的固执己见的观点。
不要对添加启动器的项目做出假设。如果您自动配置的库通常需要其他启动器,也请提及它们。如果可选依赖项的数量很多,那么提供一组适当的默认依赖项可能会很困难,因为您应该避免包含库的典型使用所不需要的依赖项。换句话说,您不应包含可选的依赖项。
无论哪种方式,您的启动器都必须直接或间接引用核心 Spring Boot 启动器 ( spring-boot-starter )(如果您的启动器依赖于另一个启动器,则无需添加它)。如果仅使用自定义启动器创建项目,则核心启动器的存在将体现 Spring Boot 的核心功能。
|
11.Kotlin 支持
Spring Boot 通过利用其他 Spring 项目(例如 Spring Framework、Spring Data 和 Reactor)中的支持来提供 Kotlin 支持。有关更多信息,请参阅Spring Framework Kotlin 支持文档。
开始使用 Spring Boot 和 Kotlin 的最简单方法是遵循这个综合教程。您可以使用start.spring.io创建新的 Kotlin 项目。如果您需要支持,请随时加入Kotlin Slack的 #spring 频道,或者使用Stack Overflow上的spring
和标签提问。kotlin
11.1. 要求
Spring Boot 至少需要 Kotlin 1.7.x,并通过依赖管理来管理合适的 Kotlin 版本。要使用 Kotlin,org.jetbrains.kotlin:kotlin-stdlib
并且org.jetbrains.kotlin:kotlin-reflect
必须存在于类路径中。也可以使用变kotlin-stdlib
体kotlin-stdlib-jdk7
和。kotlin-stdlib-jdk8
由于Kotlin 类默认是 Final 的,因此您可能需要配置kotlin-spring插件,以便自动打开 Spring 注解的类,以便可以代理它们。
在 Kotlin 中序列化/反序列化 JSON 数据需要Jackson 的 Kotlin 模块。当在类路径中找到它时,它会自动注册。如果 Jackson 和 Kotlin 存在但 Jackson Kotlin 模块不存在,则会记录一条警告消息。
如果在start.spring.io 上引导 Kotlin 项目,则默认提供这些依赖项和插件。 |
11.2. 零安全性
Kotlin 的关键功能之一是空安全。它在编译时处理null
值,而不是将问题推迟到运行时并遇到NullPointerException
. 这有助于消除常见的错误来源,而无需支付Optional
. Kotlin 还允许使用具有可为 null 值的函数构造,如这份Kotlin 空安全综合指南中所述。
尽管 Java 不允许在其类型系统中表达 null 安全性,但 Spring Framework、Spring Data 和 Reactor 现在通过工具友好的注释提供其 API 的 null 安全性。默认情况下,Kotlin 中使用的 Java API 中的类型被识别为放宽空检查的 平台类型。Kotlin 对 JSR 305 注释的支持与可空性注释相结合,为 Kotlin 中的相关 Spring API 提供了空安全性。
-Xjsr305
可以通过添加具有以下选项的编译器标志来配置 JSR 305 检查: -Xjsr305={strict|warn|ignore}
。默认行为与 相同-Xjsr305=warn
。strict
在从 Spring API 推断的 Kotlin 类型中,需要考虑该值的空安全性,但在使用时应了解 Spring API 空性声明甚至可能在次要版本之间演变,并且将来可能会添加更多检查)。
11.3。科特林 API
11.3.1。运行应用程序
Spring Boot 提供了一种惯用的方式来运行应用程序,runApplication<MyApplication>(*args)
如以下示例所示:
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
这是 的直接替代品SpringApplication.run(MyApplication::class.java, *args)
。它还允许自定义应用程序,如以下示例所示:
runApplication<MyApplication>(*args) {
setBannerMode(OFF)
}
11.3.2. 扩展
Kotlin扩展提供了使用附加功能扩展现有类的能力。Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利性。
TestRestTemplate
提供了与 Spring Framework 中提供的扩展类似的扩展RestOperations
。除此之外,这些扩展使得利用 Kotlin 具体化类型参数成为可能。
11.4。依赖管理
为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。
使用Maven,可以通过设置属性来定制Kotlin版本kotlin.version
,并为kotlin-maven-plugin
. 使用 Gradle,Spring Boot 插件会自动kotlin.version
与 Kotlin 插件的版本保持一致。
Spring Boot 还通过导入 Kotlin 协程 BOM 来管理协程依赖项的版本。可以通过设置属性来定制版本kotlin-coroutines.version
。
org.jetbrains.kotlinx:kotlinx-coroutines-reactor 如果引导一个 Kotlin 项目时至少有一个对start.spring.io 的反应性依赖,则默认提供依赖。
|
11.5。@配置属性
@ConfigurationProperties
与构造函数绑定结合使用时,支持具有不可变val
属性的类,如下例所示:
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {
data class MyService(
val apiToken: String,
val uri: URI
)
}
11.6。测试
虽然可以使用 JUnit 4 来测试 Kotlin 代码,但默认提供并推荐使用 JUnit 5。JUnit 5 允许测试类实例化一次并重用于该类的所有测试。这使得在非静态方法上使用@BeforeAll
和@AfterAll
注释成为可能,这非常适合 Kotlin。
要模拟 Kotlin 类,建议使用MockK 。如果您需要MockK
Mockito 特定的等效@MockBean
和@SpyBean
注释,您可以使用SpringMockK,它提供了类似的@MockkBean
和@SpykBean
注释。
11.7。资源
11.7.2。例子
-
spring-boot-kotlin-demo:常规 Spring Boot + Spring Data JPA 项目
-
mixit:Spring Boot 2 + WebFlux + 反应式 Spring Data MongoDB
-
spring-kotlin-fullstack:使用 Kotlin2js 作为前端而不是 JavaScript 或 TypeScript 的 WebFlux Kotlin 全栈示例
-
spring-petclinic-kotlin:Spring PetClinic 示例应用程序的 Kotlin 版本
-
spring-kotlin-deepdive:从 Boot 1.0 + Java 到 Boot 2.0 + Kotlin 的逐步迁移
-
spring-boot-coroutines-demo:协程示例项目
12.SSL
Spring Boot 提供了配置 SSL 信任材料的能力,该材料可应用于多种类型的连接以支持安全通信。带有前缀的配置属性spring.ssl.bundle
可用于指定信任材料和关联信息的命名集。
12.1。使用 Java KeyStore 文件配置 SSL
带有前缀的配置属性spring.ssl.bundle.jks
可用于配置使用 Javakeytool
实用程序创建并以 JKS 或 PKCS12 格式存储在 Java KeyStore 文件中的信任材料包。每个捆绑包都有一个用户提供的名称,可用于引用该捆绑包。
当用于保护嵌入式 Web 服务器时,keystore
通常配置一个包含证书和私钥的 Java KeyStore,如下例所示:
spring.ssl.bundle.jks.mybundle.key.alias=application
spring.ssl.bundle.jks.mybundle.keystore.location=classpath:application.p12
spring.ssl.bundle.jks.mybundle.keystore.password=secret
spring.ssl.bundle.jks.mybundle.keystore.type=PKCS12
spring:
ssl:
bundle:
jks:
mybundle:
key:
alias: "application"
keystore:
location: "classpath:application.p12"
password: "secret"
type: "PKCS12"
当用于保护客户端连接时,truststore
通常使用包含服务器证书的 Java KeyStore 进行配置,如下例所示:
spring.ssl.bundle.jks.mybundle.truststore.location=classpath:server.p12
spring.ssl.bundle.jks.mybundle.truststore.password=secret
spring:
ssl:
bundle:
jks:
mybundle:
truststore:
location: "classpath:server.p12"
password: "secret"
有关完整支持的属性集,请参阅JksSslBundleProperties 。
12.2. 使用 PEM 编码的证书配置 SSL
带有前缀的配置属性spring.ssl.bundle.pem
可用于以 PEM 编码文本的形式配置信任材料包。每个捆绑包都有一个用户提供的名称,可用于引用该捆绑包。
当用于保护嵌入式 Web 服务器时,keystore
通常会配置证书和私钥,如下例所示:
spring.ssl.bundle.pem.mybundle.keystore.certificate=classpath:application.crt
spring.ssl.bundle.pem.mybundle.keystore.private-key=classpath:application.key
spring:
ssl:
bundle:
pem:
mybundle:
keystore:
certificate: "classpath:application.crt"
private-key: "classpath:application.key"
当用于保护客户端连接时,truststore
通常使用服务器证书进行配置,如下例所示:
spring.ssl.bundle.pem.mybundle.truststore.certificate=classpath:server.crt
spring:
ssl:
bundle:
pem:
mybundle:
truststore:
certificate: "classpath:server.crt"
有关完整支持的属性集,请参阅PemSslBundleProperties 。
12.3。应用 SSL 捆绑包
使用属性进行配置后,可以在配置属性中通过名称引用 SSL 捆绑包,以用于 Spring Boot 自动配置的各种类型的连接。有关更多信息,请参阅有关嵌入式 Web 服务器、数据技术和REST 客户端的部分。
12.4。使用 SSL 捆绑包
Spring Boot 自动配置一个 bean 类型SslBundles
,该 bean 提供对使用属性配置的每个命名包的访问spring.ssl.bundle
。
可以SslBundle
从自动配置的SslBundles
bean 中检索并用于创建用于在客户端库中配置 SSL 连接的对象。提供SslBundle
了获取这些 SSL 对象的分层方法:
-
getStores()
提供对密钥存储和信任存储java.security.KeyStore
实例以及任何所需的密钥存储密码的访问。 -
getManagers()
提供对java.net.ssl.KeyManagerFactory
和java.net.ssl.TrustManagerFactory
实例以及它们创建的java.net.ssl.KeyManager
和数组的访问。java.net.ssl.TrustManager
-
createSslContext()
提供了一种获取新java.net.ssl.SSLContext
实例的便捷方法。
此外,还SslBundle
提供了有关正在使用的密钥、要使用的协议以及应应用于 SSL 引擎的任何选项的详细信息。
以下示例显示检索 anSslBundle
并使用它创建 an SSLContext
:
@Component
public class MyComponent {
public MyComponent(SslBundles sslBundles) {
SslBundle sslBundle = sslBundles.getBundle("mybundle");
SSLContext sslContext = sslBundle.createSslContext();
// do something with the created sslContext
}
}
@Component
class MyComponent(sslBundles: SslBundles) {
init {
val sslBundle = sslBundles.getBundle("mybundle")
val sslContext = sslBundle.createSslContext()
// do something with the created sslContext
}
}
13. 接下来读什么
如果您想了解有关本节中讨论的任何类的更多信息,请参阅Spring Boot API 文档或者可以直接浏览源代码。如果您有具体问题,请参阅操作方法部分。
如果您对 Spring Boot 的核心功能感到满意,您可以继续阅读有关生产就绪功能的信息。