本节深入探讨 Spring Boot 的细节。在这里,您可以了解您可能想要使用和自定义的主要功能。如果您还没有这样做,您可能需要阅读“ getting-started.html ”和“ using.html ”部分,以便您有良好的基础知识。
1. SpringApplication
该类SpringApplication
提供了一种方便的方法来引导从main()
方法启动的 Spring 应用程序。在许多情况下,您可以委托给静态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 :: v2.7.1 2021-02-03 10:33:25.224 INFO 17321 --- [main] osbdssSpringApplicationExample:在 PID 17321 的 mycomputer 上使用 Java 1.8.0_232 启动 SpringApplicationExample(/apps/myjar.jar 由 pwebb 启动) 2021-02-03 10:33:25.226 INFO 17900 --- [main] osbdssSpringApplicationExample:没有活动配置文件集,回退到默认配置文件:默认 2021-02-03 10:33:26.046 INFO 17321 --- [main] osbwembedded.tomcat.TomcatWebServer:Tomcat 使用端口初始化:8080 (http) 2021-02-03 10:33:26.054 INFO 17900 --- [main] o.apache.catalina.core.StandardService:启动服务 [Tomcat] 2021-02-03 10:33:26.055 INFO 17900 --- [main] org.apache.catalina.core.StandardEngine:启动 Servlet 引擎:[Apache Tomcat/9.0.41] 2021-02-03 10:33:26.097 INFO 17900 --- [main] oaccC[Tomcat].[localhost].[/]:初始化 Spring 嵌入式 WebApplicationContext 2021-02-03 10:33:26.097 INFO 17900 --- [main] wscServletWebServerApplicationContext:Root WebApplicationContext:初始化在 821 毫秒内完成 2021-02-03 10:33:26.144 INFO 17900 --- [main] s.tomcat.SampleTomcatApplication:ServletContext 已初始化 2021-02-03 10:33:26.376 INFO 17900 --- [main] osbwembedded.tomcat.TomcatWebServer:Tomcat 在端口上启动:8080 (http),上下文路径为“” 2021-02-03 10:33:26.384 INFO 17900 --- [main] osbdssSpringApplicationExample:在 1.514 秒内启动 SampleTomcatApplication(JVM 运行 1.823)
默认情况下,INFO
会显示日志消息,包括一些相关的启动详细信息,例如启动应用程序的用户。如果您需要除 之外的日志级别INFO
,您可以设置它,如日志级别中所述。应用程序版本是使用主应用程序类包中的实现版本确定的。spring.main.log-startup-info
设置为可以关闭启动信息记录false
。这也将关闭应用程序活动配置文件的日志记录。
要在启动期间添加额外的日志记录,您可以logStartupInfo(boolean) 在SpringApplication .
|
1.1。启动失败
如果您的应用程序无法启动,注册FailureAnalyzers
有机会提供专门的错误消息和解决问题的具体措施。例如,如果您在端口上启动 Web 应用程序8080
并且该端口已在使用中,您应该会看到类似于以下消息的内容:
****************************** 应用程序无法启动 ****************************** 描述: 嵌入式 servlet 容器无法启动。端口 8080 已被使用。 行动: 识别并停止正在侦听端口 8080 的进程或将此应用程序配置为侦听另一个端口。
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
可以使用方法 onSpringApplicationBuilder
或setLazyInitialization
方法 on以编程方式启用延迟初始化SpringApplication
。或者,可以使用spring.main.lazy-initialization
以下示例中所示的属性启用它:
spring.main.lazy-initialization=true
spring:
main:
lazy-initialization: true
如果您想禁用某些 bean 的延迟初始化,同时对应用程序的其余部分使用延迟初始化,您可以使用@Lazy(false) 注解将其延迟属性显式设置为 false。
|
1.3. 自定义横幅
banner.txt
可以通过将文件添加到类路径或将spring.banner.location
属性设置为此类文件的位置来更改启动时打印的横幅。如果文件的编码不是 UTF-8,您可以设置spring.banner.charset
. 除了文本文件之外,您还可以将banner.gif
、banner.jpg
或banner.png
图像文件添加到您的类路径或设置spring.banner.image.location
属性。图像被转换为 ASCII 艺术表示并打印在任何文本横幅上方。
在您的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
:.
这就是为什么我们建议您始终使用 |
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。流畅的构建器 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 包括对常用“liveness”和“readiness”可用性状态的开箱即用支持。如果您使用 Spring Boot 的“执行器”支持,那么这些状态将作为健康端点组公开。
此外,您还可以通过将ApplicationAvailability
接口注入到自己的 bean 中来获取可用性状态。
1.6.1. 活跃状态
应用程序的“活跃度”状态表明其内部状态是否允许其正常工作,或者如果当前失败则自行恢复。损坏的“活动”状态意味着应用程序处于无法恢复的状态,基础设施应重新启动应用程序。
一般来说,“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)
}
}
}
1.7. 应用程序事件和监听器
除了通常的 Spring Framework 事件,例如ContextRefreshedEvent
, aSpringApplication
还发送一些额外的应用程序事件。
某些事件实际上 如果您希望自动注册这些侦听器,无论应用程序的创建方式如何,您都可以将 org.springframework.context.ApplicationListener=com.example.project.MyListener |
当您的应用程序运行时,应用程序事件按以下顺序发送:
-
An
ApplicationStartingEvent
在运行开始但在任何处理之前发送,除了侦听器和初始化程序的注册。 -
ApplicationEnvironmentPreparedEvent
当Environment
在上下文中使用的 已知但在创建上下文之前发送一个。 -
ApplicationContextInitializedEvent
当ApplicationContext
准备好并调用 ApplicationContextInitializers 但在加载任何 bean 定义之前发送一个。 -
An
ApplicationPreparedEvent
在刷新开始之前但在加载 bean 定义之后发送。 -
ApplicationStartedEvent
在刷新上下文之后但在调用任何应用程序和命令行运行程序之前发送一个。 -
AvailabilityChangeEvent
在 with 之后立即发送一个LivenessState.CORRECT
,表明应用程序被认为是活动的。 -
ApplicationReadyEvent
在调用任何应用程序和命令行运行程序后发送一个。 -
AvailabilityChangeEvent
在 with 之后立即发送一个,ReadinessState.ACCEPTING_TRAFFIC
以指示应用程序已准备好为请求提供服务。 -
如果
ApplicationFailedEvent
启动时出现异常,则发送一个。
上面的列表仅包括SpringApplicationEvent
与 a 绑定的 s SpringApplication
。除此之外,还发布了以下事件 afterApplicationPreparedEvent
和 before ApplicationStartedEvent
:
-
A在准备好
WebServerInitializedEvent
后发送。和分别是 servlet 和响应式变体。WebServer
ServletWebServerInitializedEvent
ReactiveWebServerInitializedEvent
-
A在刷新
ContextRefreshedEvent
时发送。ApplicationContext
您通常不需要使用应用程序事件,但知道它们的存在会很方便。在内部,Spring Boot 使用事件来处理各种任务。 |
默认情况下,事件侦听器不应运行可能冗长的任务,因为它们在同一线程中执行。考虑改用应用程序和命令行运行器。 |
应用程序事件是使用 Spring Framework 的事件发布机制发送的。此机制的一部分确保在子上下文中发布给侦听器的事件也发布给任何祖先上下文中的侦听器。因此,如果您的应用程序使用SpringApplication
实例层次结构,则侦听器可能会接收到相同类型的应用程序事件的多个实例。
为了让您的侦听器能够区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现来注入上下文,ApplicationContextAware
或者,如果侦听器是 bean,则可以使用@Autowired
.
1.8. 网络环境
ASpringApplication
尝试ApplicationContext
代表您创建正确的类型。用于确定 a 的算法WebApplicationType
如下:
-
如果存在 Spring MVC,
AnnotationConfigServletWebServerApplicationContext
则使用an -
如果 Spring MVC 不存在而 Spring WebFlux 存在,
AnnotationConfigReactiveWebServerApplicationContext
则使用an -
否则,
AnnotationConfigApplicationContext
使用
这意味着如果您WebClient
在同一个应用程序中使用 Spring MVC 和来自 Spring WebFlux 的 new,则默认情况下将使用 Spring MVC。您可以通过调用轻松覆盖它setWebApplicationType(WebApplicationType)
。
也可以ApplicationContext
通过调用来完全控制使用的类型setApplicationContextClass(…)
。
在 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"]
}
}
CommandLinePropertySource Spring Boot 还向 Spring
注册了 a Environment 。这使您还可以使用@Value 注释注入单个应用程序参数。
|
1.10。使用 ApplicationRunner 或 CommandLineRunner
如果您需要在启动后运行某些特定代码SpringApplication
,您可以实现ApplicationRunner
或CommandLineRunner
接口。两个接口以相同的方式工作,并提供一个run
方法,该方法在完成之前调用SpringApplication.run(…)
。
该合约非常适合应在应用程序启动后但开始接受流量之前运行的任务。 |
接口以字符串数组的CommandLineRunner
形式提供对应用程序参数的访问,而ApplicationRunner
使用ApplicationArguments
前面讨论的接口。以下示例显示了一个CommandLineRunner
带有方法的run
方法:
@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
or bean,您可以另外实现接口或使用注解。ApplicationRunner
org.springframework.core.Ordered
org.springframework.core.annotation.Order
1.11。应用程序退出
每个都SpringApplication
向 JVM 注册一个关闭钩子,以确保ApplicationContext
在退出时优雅地关闭。可以使用所有标准的 Spring 生命周期回调(例如DisposableBean
接口或@PreDestroy
注解)。
此外,如果 bean希望在被调用org.springframework.boot.ExitCodeGenerator
时返回特定的退出代码,它们可以实现该接口。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 端口上运行,请使用 .key 获取属性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 Framework 提供。它将 Spring 特定的启动事件添加到 Java Flight Recorder 会话中,用于分析应用程序并将它们的 Spring 上下文生命周期与 JVM 事件(例如分配、GC、类加载……)相关联。配置完成后,您可以通过运行启用了 Flight Recorder 的应用程序来记录数据:
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar
Spring Boot 附带该BufferingApplicationStartup
变体;此实现旨在缓冲启动步骤并将它们排入外部度量系统。BufferingApplicationStartup
应用程序可以在任何组件中请求类型的 bean 。
Spring Boot 也可以配置为公开一个以 JSON 文档形式提供此信息的startup
端点。
2. 外化配置
Spring Boot 允许您将配置外部化,以便您可以在不同的环境中使用相同的应用程序代码。您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
属性值可以通过使用注解直接注入到你的 bean 中@Value
,通过 Spring 的抽象Environment
访问,或者通过.@ConfigurationProperties
Spring Boot 使用一种非常特殊的PropertySource
顺序,旨在允许合理地覆盖值。属性按以下顺序考虑(较低项目的值覆盖较早的项目):
-
默认属性(由设置指定
SpringApplication.setDefaultProperties
)。 -
@PropertySource
@Configuration
您的课程上的注释。请注意,Environment
在刷新应用程序上下文之前,不会将此类属性源添加到 中。配置某些属性(例如在刷新开始之前读取的logging.*
和)为时已晚。spring.main.*
-
配置数据(例如
application.properties
文件)。 -
仅在中
RandomValuePropertySource
具有属性的 A。random.*
-
操作系统环境变量。
-
Java 系统属性 (
System.getProperties()
)。 -
JNDI 属性来自
java:comp/env
. -
ServletContext
初始化参数。 -
ServletConfig
初始化参数。 -
来自
SPRING_APPLICATION_JSON
(嵌入在环境变量或系统属性中的内联 JSON)的属性。 -
命令行参数。
-
properties
属性在你的测试。可用于测试应用程序的特定部分@SpringBootTest
的测试注释。 -
@TestPropertySource
测试上的注释。 -
$HOME/.config/spring-boot
当 devtools 处于活动状态时,目录中的Devtools 全局设置属性。
配置数据文件按以下顺序考虑:
-
打包在 jar 中的应用程序属性
application.properties
(和 YAML 变体)。 -
打包在您的 jar(
application-{profile}.properties
和 YAML 变体)中的特定于配置文件的应用程序属性。 -
打包 jar(
application.properties
和 YAML 变体)之外的应用程序属性。 -
打包 jar(
application-{profile}.properties
和 YAML 变体)之外的特定于配置文件的应用程序属性。
建议在整个应用程序中坚持使用一种格式。如果您在同一位置有同时具有.properties 和格式的配置文件,则优先。
.yml .properties |
举一个具体的例子,假设你开发了一个@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"}}'
如果要部署到经典应用程序服务器,还可以使用名为java:comp/env/spring.application.json
.
尽管null JSON 中的值将添加到生成的属性源中,但PropertySourcesPropertyResolver 将null 属性视为缺失值。这意味着 JSON 不能用值覆盖来自低阶属性源的属性null 。
|
2.3. 外部应用程序属性
当您的应用程序启动时,Spring Boot 将自动从以下位置查找并加载文件application.properties
:application.yaml
-
从类路径
-
类路径根
-
类路径
/config
包
-
-
从当前目录
-
当前目录
-
当前目录中的
/config
子目录 -
子目录的
/config
直接子目录
-
该列表按优先级排序(较低项目的值覆盖较早的项目)。加载文件中的文档将添加PropertySources
到 SpringEnvironment
中。
如果您不喜欢application
作为配置文件名,您可以通过指定spring.config.name
环境属性切换到另一个文件名。例如,要查找myproject.properties
和myproject.yaml
文件,您可以按如下方式运行应用程序:
$ java -jar myproject.jar --spring.config.name=myproject
您还可以使用spring.config.location
environment 属性来引用显式位置。此属性接受以逗号分隔的一个或多个要检查的位置列表。
以下示例显示如何指定两个不同的文件:
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
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
配置了 value optional:classpath:/custom-config/,optional:file:./custom-config/
,则考虑的完整位置集是:
-
optional:classpath:custom-config/
-
optional:file:./custom-config/
如果您喜欢添加其他位置,而不是替换它们,您可以使用spring.config.additional-location
. 从其他位置加载的属性可以覆盖默认位置中的属性。例如,如果spring.config.additional-location
配置了 value 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
然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。
如果您使用环境变量而不是系统属性,大多数操作系统不允许使用句点分隔的键名,但您可以使用下划线代替(例如,SPRING_CONFIG_NAME 代替spring.config.name )。有关详细信息,请参阅从环境变量绑定。
|
如果您的应用程序在 servlet 容器或应用程序服务器中运行,则可以使用 JNDI 属性(在 中java:comp/env )或 servlet 上下文初始化参数来代替环境变量或系统属性,或者也可以使用环境变量或系统属性。
|
2.3.1。可选位置
默认情况下,当指定的配置数据位置不存在时,Spring Boot 会抛出一个ConfigDataLocationNotFoundException
并且你的应用程序不会启动。
如果你想指定一个位置,但你不介意它不总是存在,你可以使用optional:
前缀。您可以将此前缀与spring.config.location
andspring.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
andspring.config.additional-location
属性一起使用。
对于作为目录的搜索位置或作为文件的搜索位置,
通配符位置必须仅包含一个* 并以结尾。带有通配符的位置根据文件名的绝对路径按字母顺序排序。
*/ */<filename> |
通配符位置仅适用于外部目录。您不能在classpath: 位置中使用通配符。
|
2.3.3。配置文件特定文件
除了application
属性文件,Spring Boot 还将尝试使用命名约定加载特定于配置文件的文件application-{profile}
。例如,如果您的应用程序激活了一个名为prod
并使用 YAML 文件的配置文件,那么两者application.yml
都会application-prod.yml
被考虑。
特定于配置文件的属性从与标准相同的位置加载,application.properties
特定于配置文件的文件始终覆盖非特定文件。如果指定了多个配置文件,则应用最后获胜策略。例如,如果配置文件prod,live
由spring.profiles.active
属性指定,则 中的值application-prod.properties
可以被 中的值覆盖application-live.properties
。
最后获胜的策略适用于营业地点组级别。A 例如,继续 /cfg 应用程序-live.properties /分机 应用程序-live.properties 应用程序-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 属性、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 或属性文件。对于第二种情况,您需要使用前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。configtree:
例如,假设 Kubernetes 挂载了以下卷:
ETC/ 配置/ 我的应用程序/ 用户名 密码
该username
文件的内容将是一个配置值,其内容password
将是一个秘密。
要导入这些属性,您可以将以下内容添加到您的application.properties
orapplication.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/ 配置/ 数据库配置/ D b/ 用户名 密码 MQ配置/ MQ/ 用户名 密码
您可以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 服务被授予对密钥的访问权限时,该密钥将被挂载到容器中。例如,如果一个名为的 secretdb.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.yml
通过现有值进行过滤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
。
您应该始终使用其规范形式(kebab-case 仅使用小写字母)引用占位符中的属性名称。这将允许 Spring Boot 使用与放松绑定 时相同的逻辑 例如,将从文件中以及从系统环境中 |
您还可以使用此技术创建现有 Spring Boot 属性的“短”变体。有关详细信息,请参阅howto.html 操作方法。 |
2.3.8。使用多文档文件
Spring Boot 允许您将单个物理文件拆分为多个独立添加的逻辑文档。文档从上到下按顺序处理。后面的文档可以覆盖早期文档中定义的属性。
对于application.yml
文件,使用标准的 YAML 多文档语法。三个连续的连字符代表一个文档的结束,以及下一个文档的开始。
例如,以下文件有两个逻辑文档:
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
对于application.properties
文件,特殊#---
注释用于标记文档拆分:
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
属性文件分隔符不能有任何前导空格,并且必须正好有三个连字符。分隔符前后的行不能是注释。 |
多文档属性文件通常与激活属性结合使用,例如spring.config.activate.on-profile . 有关详细信息,请参阅下一节。
|
不能使用@PropertySource or@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
如果你使用“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
使用该[index] 符号的属性可以绑定到 JavaList 或Set 使用 Spring Boot 的Binder 类的对象。有关更多详细信息,请参阅下面的“类型安全配置属性”部分。
|
YAML 文件不能使用@PropertySource 或@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
.
映射到@ConfigurationProperties Spring Boot 中可用的类的属性,通过属性文件、YAML 文件、环境变量和其他机制配置,是公共 API,但类本身的访问器(getter/setter)并不意味着直接使用.
|
这种安排依赖于默认的空构造函数,并且 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。在以下情况下可以省略 setter:
有些人使用 Project Lombok 来自动添加 getter 和 setter。确保 Lombok 不会为此类类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。 最后,只考虑标准 Java Bean 属性,不支持对静态属性的绑定。 |
2.8.2. 构造函数绑定
上一节中的示例可以以不可变的方式重写,如下例所示:
@ConstructorBinding
@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;
}
}
}
@ConstructorBinding
@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
注释用于指示应使用构造函数绑定。这意味着绑定器将期望找到具有您希望绑定的参数的构造函数。如果您使用的是 Java 16 或更高版本,则可以将构造函数绑定与记录一起使用。在这种情况下,除非您的记录有多个构造函数,否则无需使用@ConstructorBinding
.
类的嵌套成员@ConstructorBinding
(Security
例如上面的示例)也将通过其构造函数进行绑定。
可以使用@DefaultValue
构造函数参数指定默认值,或者在使用 Java 16 或更高版本时,使用记录组件指定默认值。将应用转换服务将String
值强制为缺失属性的目标类型。
参考前面的示例,如果没有属性绑定到Security
,则MyProperties
实例将包含 的null
值security
。Security
如果您希望即使没有绑定任何属性也返回一个非空实例,您可以使用空@DefaultValue
注释来执行此操作:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
要使用构造函数绑定,必须使用@EnableConfigurationProperties 或配置属性扫描启用类。您不能对由常规 Spring 机制创建的 bean 使用构造函数绑定(例如@Component ,bean、使用@Bean 方法创建的 bean 或使用加载的 bean @Import )
|
如果你的类有多个构造函数,你也可以@ConstructorBinding 直接在应该绑定的构造函数上使用。
|
不推荐使用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
要使用配置属性扫描,请将@ConfigurationPropertiesScan
注释添加到您的应用程序。通常,它被添加到带有注释的主应用程序类中,@SpringBootApplication
但它可以添加到任何@Configuration
类中。默认情况下,扫描将从声明注释的类的包中进行。如果要定义要扫描的特定包,可以按照以下示例进行操作:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication
当 上例中的 bean 名称是 |
我们建议@ConfigurationProperties
只处理环境,特别是不要从上下文中注入其他 bean。对于极端情况,可以使用 setter 注入或*Aware
框架提供的任何接口(例如,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 SomeProperties properties;
public MyService(SomeProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
@Service
class MyService(val properties: SomeProperties) {
fun openConnection() {
val server = Server(properties.remoteAddress)
server.start()
// ...
}
// ...
}
Using@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
}
使用上述代码,可以使用以下属性名称:
财产 | 笔记 |
---|---|
|
烤肉盒,推荐用于 |
|
标准驼峰式语法。 |
|
下划线表示法,这是在文件中使用的另一种 |
|
大写格式,在使用系统环境变量时推荐使用。 |
prefix 注释
的值必须是 kebab 大小写(小写并用 分隔- ,例如my.main-project.person )。
|
属性来源 | 简单的 | 列表 |
---|---|---|
属性文件 |
Camel case、kebab case 或下划线表示法 |
|
YAML 文件 |
Camel case、kebab case 或下划线表示法 |
标准 YAML 列表语法或逗号分隔值 |
环境变量 |
下划线作为分隔符的大写格式(请参阅从环境变量绑定)。 |
下划线包围的数值(请参阅从环境变量绑定) |
系统属性 |
Camel case、kebab case 或下划线表示法 |
|
我们建议尽可能以小写的 kebab 格式存储属性,例如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
with /key1
,/key2
并key3
作为映射中的键。斜线已被删除,key3
因为它没有被方括号包围。
当绑定到标量值时,.
其中的键不需要用[]
. 标量值包括枚举和java.lang
包中除Object
. 绑定a.b=c
到Map<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
实例添加到列表中,并且不会合并项目。
当List
在多个配置文件中指定 a 时,将使用具有最高优先级的一个(并且仅使用那个)。考虑以下示例:
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。属性转换
@ConfigurationProperties
Spring Boot 在绑定到bean时尝试将外部应用程序属性强制转换为正确的类型。如果您需要自定义类型转换,您可以提供一个ConversionService
bean(带有一个名为 的 bean conversionService
)或自定义属性编辑器(通过一个CustomEditorConfigurer
bean)或自定义Converters
(带有注释为 的 bean 定义@ConfigurationPropertiesBinding
)。
由于此 bean 在应用程序生命周期的早期被请求,因此请确保限制您ConversionService 正在使用的依赖项。通常,您需要的任何依赖项可能不会在创建时完全初始化。如果ConversionService 配置键强制不需要它并且仅依赖于使用@ConfigurationPropertiesBinding .
|
转换持续时间
Spring Boot 专门支持表达持续时间。如果公开java.time.Duration
属性,则应用程序属性中的以下格式可用:
-
常规
long
表示(使用毫秒作为默认单位,除非@DurationUnit
已指定 a) -
使用的标准 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")
@ConstructorBinding
public class MyProperties {
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
}
@ConfigurationProperties("my")
@ConstructorBinding
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 Framework 有一个DataSize
值类型,它以字节为单位表示大小。如果公开DataSize
属性,则应用程序属性中的以下格式可用:
-
常规
long
表示(使用字节作为默认单位,除非@DataSizeUnit
已指定 a) -
一种更易读的格式,其中值和单位是耦合的(
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")
@ConstructorBinding
public class MyProperties {
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
}
@ConfigurationProperties("my")
@ConstructorBinding
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
。javax.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
通过创建一个名为configurationPropertiesValidator
. @Bean
应该声明该方法static
。配置属性验证器是在应用程序生命周期的早期创建的,将@Bean
方法声明为静态允许创建 bean 而无需实例化@Configuration
类。这样做可以避免早期实例化可能导致的任何问题。
该spring-boot-actuator 模块包括一个公开所有@ConfigurationProperties bean 的端点。将您的 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
激活的配置文件之上添加活动配置文件。入口点还有一个用于设置附加配置文件的 Java API spring.profiles.active
。SpringApplication
请参阅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.yml
)和通过引用的文件的特定配置文件变体@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 的默认日志输出类似于以下示例:
2019-03-05 10:57:51.112 INFO 45469 --- [main] org.apache.catalina.core.StandardEngine:启动 Servlet 引擎:Apache Tomcat/7.0.52 2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] oaccC[Tomcat].[localhost].[/]:初始化 Spring 嵌入式 WebApplicationContext 2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] osweb.context.ContextLoader:根 WebApplicationContext:初始化在 1358 毫秒内完成 2019-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] osbceServletRegistrationBean:映射 servlet:'dispatcherServlet' 到 [/] 2019-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] osbcembedded.FilterRegistrationBean:映射过滤器:'hiddenHttpMethodFilter' 到:[/*]
输出以下项目:
-
日期和时间:毫秒精度,易于排序。
-
日志级别:
ERROR
、WARN
、INFO
、DEBUG
或TRACE
。 -
进程标识。
-
---
用于区分实际日志消息开始的分隔符。 -
线程名称:括在方括号中(可能会被截断以用于控制台输出)。
-
记录器名称:这通常是源类名称(通常缩写)。
-
日志消息。
Logback 没有FATAL 级别。它映射到ERROR .
|
4.2. 控制台输出
默认日志配置在写入时将消息回显到控制台。默认情况下,会记录ERROR
-level、WARN
-level 和INFO
-level 消息。--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 HH:mm:ss.SSS}){yellow}
支持以下颜色和样式:
-
blue
-
cyan
-
faint
-
green
-
magenta
-
red
-
yellow
4.3. 文件输出
默认情况下,Spring Boot 只记录到控制台,不写入日志文件。如果你想在控制台输出之外写日志文件,你需要设置一个logging.file.name
orlogging.file.path
属性(例如,在你的application.properties
.
下表显示了如何logging.*
一起使用这些属性:
logging.file.name |
logging.file.path |
例子 | 描述 |
---|---|---|---|
(没有任何) |
(没有任何) |
仅控制台日志记录。 |
|
具体文件 |
(没有任何) |
|
写入指定的日志文件。名称可以是确切的位置,也可以是相对于当前目录的位置。 |
(没有任何) |
具体目录 |
|
写入 |
日志文件在达到 10 MB 时会旋转,并且与控制台输出一样,默认情况下会记录ERROR
-level、WARN
-level 和INFO
-level 消息。
日志记录属性独立于实际的日志记录基础设施。因此,特定的配置键(例如logback.configurationFile 用于 Logback)不由 spring Boot 管理。
|
4.4. 文件轮换
application.properties
如果您使用的是 Logback,则可以使用您的orapplication.yaml
文件微调日志轮换设置。对于所有其他日志记录系统,您将需要自己直接配置轮换设置(例如,如果您使用 Log4J2,那么您可以添加一个log4j2.xml
或log4j2-spring.xml
文件)。
支持以下轮换策略属性:
姓名 | 描述 |
---|---|
|
用于创建日志存档的文件名模式。 |
|
如果应在应用程序启动时进行日志归档清理。 |
|
归档前日志文件的最大大小。 |
|
在被删除之前可以占用的最大大小的日志档案。 |
|
要保留的存档日志文件的最大数量(默认为 7)。 |
4.5. 日志级别
所有受支持的日志记录系统都可以在 Spring 中设置记录器级别Environment
(例如 in application.properties
),方法是使用logging.level.<logger-name>=<level>
TRACE level
、DEBUG、INFO、WARN、ERROR、FATAL 或 OFF 之一。记录器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
orapplication.yaml
文件中设置属性:
logging.register-shutdown-hook=false
logging:
register-shutdown-hook: false
4.8. 自定义日志配置
各种日志系统可以通过在类路径中包含适当的库来激活,并且可以通过在类路径的根目录或由以下 SpringEnvironment
属性指定的位置提供合适的配置文件来进一步定制logging.config
:
org.springframework.boot.logging.LoggingSystem
您可以使用system 属性强制 Spring Boot 使用特定的日志记录系统。该值应该是实现的完全限定类名LoggingSystem
。您还可以使用none
.
由于在创建之前
初始化了日志记录,ApplicationContext 因此无法从@PropertySources Spring@Configuration 文件中控制日志记录。更改日志系统或完全禁用它的唯一方法是通过系统属性。
|
根据您的日志记录系统,将加载以下文件:
测井系统 | 定制 |
---|---|
回溯 |
|
日志4j2 |
|
JDK(Java 实用程序日志记录) |
|
如果可能,我们建议您-spring 为日志配置使用变体(例如,logback-spring.xml 而不是logback.xml )。如果使用标准配置位置,Spring 无法完全控制日志初始化。
|
Java Util Logging 存在已知的类加载问题,这些问题在从“可执行 jar”运行时会导致问题。如果可能,我们建议您在从“可执行 jar”运行时避免使用它。 |
为了帮助进行自定义,一些其他属性从 Spring 转移Environment
到 System 属性,如下表所述:
Spring环境 | 系统属性 | 注释 |
---|---|---|
|
|
记录异常时使用的转换字。 |
|
|
如果已定义,它将在默认日志配置中使用。 |
|
|
如果已定义,它将在默认日志配置中使用。 |
|
|
在控制台上使用的日志模式 (stdout)。 |
|
|
日志日期格式的附加模式。 |
|
|
用于控制台日志记录的字符集。 |
|
|
在文件中使用的日志模式(如果 |
|
|
用于文件日志记录的字符集(如果 |
|
|
呈现日志级别时使用的格式(默认 |
|
|
当前进程 ID(如果可能且尚未定义为 OS 环境变量时发现)。 |
如果使用 Logback,还会传输以下属性:
Spring环境 | 系统属性 | 注释 |
---|---|---|
|
|
翻转日志文件名的模式(默认 |
|
|
是否在启动时清理归档日志文件。 |
|
|
最大日志文件大小。 |
|
|
要保留的日志备份的总大小。 |
|
|
要保留的存档日志文件的最大数量。 |
所有受支持的日志记录系统在解析其配置文件时都可以查阅系统属性。spring-boot.jar
有关示例,请参见中的默认配置:
如果你想在日志属性中使用占位符,你应该使用Spring Boot 的语法,而不是底层框架的语法。值得注意的是,如果你使用 Logback,你应该使用 |
2019-08-30 12:30:04.031 用户:某人 INFO 22174 --- [nio-8080-exec-0] demo.Controller 处理经过身份验证的请求 |
4.9. Logback 扩展
Spring Boot 包含许多对 Logback 的扩展,可以帮助进行高级配置。您可以在logback-spring.xml
配置文件中使用这些扩展。
因为标准logback.xml 配置文件加载得太早,所以不能在里面使用扩展。您需要使用logback-spring.xml 或定义logging.config 属性。
|
扩展不能与 Logback 的配置扫描一起使用。如果您尝试这样做,则对配置文件进行更改会导致记录类似于以下内容之一的错误: |
[电子邮件保护]中的错误:71 - [springProperty] 没有适用的操作,当前 ElementPath 是 [[configuration][springProperty]][email protected] 中的错误:71 - [springProfile] 没有适用的操作,当前 ElementPath 是 [[configuration][springProfile]]
4.9.1。配置文件特定的配置
该<springProfile>
标签允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。元素内的任何位置都支持配置文件部分<configuration>
。使用该name
属性来指定哪个配置文件接受配置。标签可以包含配置<springProfile>
文件名称(例如staging
)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west)
. 查看参考指南以获取更多详细信息。以下清单显示了三个示例配置文件:
<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>
必须在 kebab case中source 指定(例如my.property-name )。Environment 但是,可以使用宽松的规则
将属性添加到 中。 |
5.国际化
Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户。默认情况下,Spring Bootmessages
在类路径的根目录中查找资源包的存在。
当配置的资源包的默认属性文件可用时(messages.properties 默认情况下),自动配置适用。如果您的资源包仅包含特定于语言的属性文件,则需要添加默认值。如果没有找到与任何配置的基本名称匹配的属性文件,则不会有 auto-configured 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
类。自定义序列化程序通常通过一个模块向 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
bean 都会ApplicationContext
自动向 Jackson 注册。因为@JsonComponent
是用 元注释的@Component
,所以通常的组件扫描规则适用。
Spring Boot 还提供了JsonObjectSerializer
基JsonObjectDeserializer
类,它们在序列化对象时提供了标准 Jackson 版本的有用替代方案。JsonObjectSerializer
有关详细信息,请参阅JsonObjectDeserializer
Javadoc。
上面的例子可以重写为使用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
如果需要与计划的任务执行相关联(@EnableScheduling
例如使用),也可以自动配置A。线程池默认使用一个线程,可以使用命名空间微调其设置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 的老式引擎来运行它们。要使用老式引擎,请添加对 的依赖项
|
hamcrest-core
被排除在外,赞成org.hamcrest:hamcrest
那是 的一部分spring-boot-starter-test
。
8.1。测试范围依赖
“ spring-boot-starter-test
Starter”(在 中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 Framework 参考文档的相关部分。
8.3. 测试 Spring Boot 应用程序
Spring Boot 应用程序是一个 Spring ApplicationContext
,因此除了您通常使用普通 Spring 上下文所做的之外,无需做任何特别的事情来测试它。
默认情况下,Spring Boot 的外部属性、日志记录和其他功能仅在您用于SpringApplication 创建上下文时安装在上下文中。
|
Spring Boot 提供了注解,当您需要 Spring Boot 特性时@SpringBootTest
,可以将其用作标准注解的替代方案。spring-test
@ContextConfiguration
注释通过在您的测试中创建ApplicationContext
SpringApplication
使用的. 除了@SpringBootTest
许多其他注释之外,还提供了用于测试应用程序更具体的切片。
如果您使用的是 JUnit 4,请不要忘记也添加@RunWith(SpringRunner.class) 到您的测试中,否则注释将被忽略。如果您使用的是 JUnit 5,则无需添加等效@ExtendWith(SpringExtension.class) 的 as@SpringBootTest 并且其他@…Test 注释已经用它进行了注释。
|
默认情况下,@SpringBootTest
不会启动服务器。您可以使用 的webEnvironment
属性@SpringBootTest
来进一步优化测试的运行方式:
-
MOCK
(默认):加载网络ApplicationContext
并提供模拟网络环境。使用此注释时不启动嵌入式服务器。如果您的类路径上没有可用的 Web 环境,则此模式会透明地回退到创建常规的 non-webApplicationContext
。它可以与您的 Web 应用程序结合使用@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
用于基于模拟的测试。 -
RANDOM_PORT
: 加载WebServerApplicationContext
并提供一个真实的网络环境。嵌入式服务器启动并侦听随机端口。 -
DEFINED_PORT
: 加载WebServerApplicationContext
并提供一个真实的网络环境。嵌入式服务器启动并侦听定义的端口(来自您的application.properties
)或8080
. -
NONE
ApplicationContext
:通过使用加载一个SpringApplication
但不提供任何Web 环境(模拟或其他)。
如果您的测试是@Transactional ,默认情况下,它会在每个测试方法结束时回滚事务。然而,由于使用这种安排RANDOM_PORT 或DEFINED_PORT 隐含地提供了一个真正的 servlet 环境,HTTP 客户端和服务器在单独的线程中运行,因此在单独的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。
|
@SpringBootTest 如果您的应用程序为管理服务器使用不同的端口, withwebEnvironment = WebEnvironment.RANDOM_PORT 还将在单独的随机端口上启动管理服务器。
|
8.3.1. 检测 Web 应用程序类型
如果 Spring MVC 可用,则配置常规的基于 MVC 的应用程序上下文。如果您只有 Spring WebFlux,我们将检测并配置一个基于 WebFlux 的应用程序上下文。
如果两者都存在,则 Spring MVC 优先。如果要在这种情况下测试响应式 Web 应用程序,则必须设置spring.main.web-application-type
属性:
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {
// ...
}
8.3.2. 检测测试配置
如果您熟悉 Spring Test Framework,您可能习惯于使用@ContextConfiguration(classes=…)
以指定@Configuration
要加载的 Spring。或者,您可能经常@Configuration
在测试中使用嵌套类。
在测试 Spring Boot 应用程序时,通常不需要这样做。@*Test
只要您没有明确定义,Spring Boot 的注释就会自动搜索您的主要配置。
搜索算法从包含测试的包开始,直到找到一个用@SpringBootApplication
or注释的类@SpringBootConfiguration
。只要您以合理的方式构建代码,通常就会找到您的主要配置。
如果您使用测试注释来测试应用程序的更具体部分,则应避免在主方法的应用程序类上添加特定于特定区域的配置设置。
|
如果要自定义主要配置,可以使用嵌套@TestConfiguration
类。与将@Configuration
使用嵌套类代替应用程序的主要配置不同,嵌套@TestConfiguration
类是在应用程序的主要配置之外使用的。
Spring 的测试框架在测试之间缓存应用程序上下文。因此,只要您的测试共享相同的配置(无论它是如何被发现的),加载上下文的潜在耗时过程只会发生一次。 |
8.3.3. 排除测试配置
如果您的应用程序使用组件扫描(例如,如果您使用@SpringBootApplication
or @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.4. 使用应用程序参数
如果您的应用程序需要arguments,您可以使用属性@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.5。使用模拟环境进行测试
默认情况下,@SpringBootTest
不会启动服务器,而是设置一个模拟环境来测试 Web 端点。
使用 Spring MVC,我们可以使用MockMvc
or查询我们的 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 容器运行更快。但是,由于 mocking 发生在 Spring MVC 层,依赖于较低级别 servlet 容器行为的代码不能直接使用 MockMvc 进行测试。 例如,Spring Boot 的错误处理是基于 servlet 容器提供的“错误页面”支持。这意味着,虽然您可以按预期测试您的 MVC 层抛出和处理异常,但您不能直接测试是否呈现了特定的自定义错误页面。如果您需要测试这些较低级别的问题,您可以启动一个完全运行的服务器,如下一节所述。 |
8.3.6. 使用正在运行的服务器进行测试
如果您需要启动一个完整运行的服务器,我们建议您使用随机端口。如果您使用@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.7. 自定义 WebTestClient
要自定义WebTestClient
bean,请配置一个WebTestClientBuilderCustomizer
bean。任何此类 bean 都WebTestClient.Builder
使用用于创建WebTestClient
.
8.3.8. 使用 JMX
由于测试上下文框架会缓存上下文,因此默认情况下禁用 JMX,以防止相同的组件在同一域上注册。如果此类测试需要访问MBeanServer
,请考虑将其标记为脏:
@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
@ExtendWith(SpringExtension::class)
@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。模拟和窥探 Bean
运行测试时,有时需要在应用程序上下文中模拟某些组件。例如,您可能有一些在开发期间不可用的远程服务的外观。当您想要模拟在真实环境中可能难以触发的故障时,模拟也很有用。
Spring Boot 包含一个@MockBean
注解,可用于为您的ApplicationContext
. 您可以使用注解添加新 bean 或替换单个现有 bean 定义。注释可以直接用于测试类、测试中的字段或@Configuration
类和字段。在字段上使用时,创建的模拟实例也会被注入。在每个测试方法之后,模拟 bean 都会自动重置。
如果您的测试使用 Spring Boot 的测试注解之一(例如 java
科特林
|
以下示例将现有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。
CGLib 代理,例如为作用域 bean 创建的代理,将代理方法声明为final . 这会阻止 Mockito 正常运行,因为它无法final 在其默认配置中模拟或监视方法。如果您想模拟或监视这样的 bean,请将 Mockito 配置为通过添加org.mockito:mockito-inline 到应用程序的测试依赖项来使用其内联模拟生成器。这允许 Mockito 模拟和监视final 方法。
|
虽然 Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但使用@MockBean 或@SpyBean 影响缓存键,这很可能会增加上下文的数量。
|
如果您使用通过名称引用参数的方法@SpyBean 来监视 bean @Cacheable ,则您的应用程序必须使用-parameters . 这确保了一旦 bean 被监视,参数名称对缓存基础设施可用。
|
当您@SpyBean 用于监视由 Spring 代理的 bean 时,您可能需要在某些情况下删除 Spring 的代理,例如在使用given or设置期望时when 。用于AopTestUtils.getTargetObject(yourProxiedSpy) 这样做。
|
8.3.11。自动配置的测试
Spring Boot 的自动配置系统适用于应用程序,但有时对于测试来说有点太多了。它通常有助于仅加载测试应用程序“切片”所需的配置部分。例如,您可能想要测试 Spring MVC 控制器是否正确映射 URL,并且您不想在这些测试中涉及数据库调用,或者您可能想要测试 JPA 实体,而当这些测试运行。
该spring-boot-test-autoconfigure
模块包含许多注释,可用于自动配置此类“切片”。它们中的每一个都以类似的方式工作,提供一个@…Test
可以加载的注释ApplicationContext
以及一个或多个@AutoConfigure…
可用于自定义自动配置设置的注释。
每个切片将组件扫描限制到适当的组件并加载一组非常有限的自动配置类。如果您需要排除其中一个,大多数@…Test 注释都提供了一个excludeAutoConfiguration 属性。或者,您可以使用@ImportAutoConfiguration#exclude .
|
@…Test 不支持通过在一个测试中
使用多个注释来包含多个“切片” 。如果您需要多个“切片”,请选择其中一个@…Test 注释并@AutoConfigure… 手动包含其他“切片”的注释。
|
也可以将@AutoConfigure… 注释与标准@SpringBootTest 注释一起使用。如果您对“切片”您的应用程序不感兴趣,但您想要一些自动配置的测试 bean,您可以使用这种组合。
|
8.3.12。自动配置的 JSON 测试
要测试对象 JSON 序列化和反序列化是否按预期工作,您可以使用@JsonTest
注释。
@JsonTest
自动配置可用的支持 JSON 映射器,它可以是以下库之一:
-
杰克逊
ObjectMapper
,任何@JsonComponent
豆子和任何杰克逊Module
的 -
Gson
-
Jsonb
@JsonTest 可以在附录中找到
启用的自动配置列表。
|
如果需要配置自动配置的元素,可以使用@AutoConfigureJsonTesters
注解。
Spring Boot 包括基于 AssertJ 的帮助程序,它们与 JSONAssert 和 JsonPath 库一起检查 JSON 是否按预期显示。、JacksonTester
、GsonTester
和类可分别用于 Jackson、Gson、Jsonb 和 Strings JsonbTester
。BasicJsonTester
测试类上的任何辅助字段都可以@Autowired
在使用@JsonTest
. 以下示例显示了 Jackson 的测试类:
@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.13。自动配置的 Spring MVC 测试
要测试 Spring MVC 控制器是否按预期工作,请使用@WebMvcTest
注解。
@WebMvcTest
自动配置 Spring MVC 基础结构并将扫描的 bean 限制为@Controller
, @ControllerAdvice
, @JsonComponent
, Converter
, GenericConverter
, Filter
, HandlerInterceptor
, WebMvcConfigurer
,WebMvcRegistrations
和HandlerMethodArgumentResolver
. 使用注解
时不会扫描常规@Component
和bean。可用于包含bean。@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 how-to 部分。
有时编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您使用实际的服务器运行完整的端到端测试。 |
8.3.14。自动配置的 Spring WebFlux 测试
要测试Spring WebFlux控制器是否按预期工作,您可以使用@WebFluxTest
注释。
@WebFluxTest
自动配置 Spring WebFlux 基础结构并将扫描的 bean 限制为@Controller
、@ControllerAdvice
、@JsonComponent
、Converter
、GenericConverter
、WebFilter
和WebFluxConfigurer
. 使用注解
时不会扫描常规@Component
和bean。可用于包含bean。@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,请考虑通过 using或 using导入您RouterFunction 自己。
@Import @SpringBootTest |
@WebFluxTest 无法检测注册为@Bean 类型的自定义安全配置SecurityWebFilterChain 。要将其包含在您的测试中,您需要通过 using@Import 或 using导入注册 bean 的配置@SpringBootTest 。
|
有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际的服务器运行完整的端到端测试。 |
8.3.15。自动配置的 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
和。使用注解
时不会扫描常规和bean。可用于包含bean。Instrumentation
GraphQlSourceBuilderCustomizer
@Component
@ConfigurationProperties
@GraphQlTest
@EnableConfigurationProperties
@ConfigurationProperties
@GraphQlTest 可以在附录中找到
启用的自动配置列表。
|
如果您需要注册额外的组件,例如 Jackson ,您可以在您的测试中
Module 使用导入额外的配置类。@Import |
通常,@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.16。自动配置的数据 Cassandra 测试
您可以使用它@DataCassandraTest
来测试 Cassandra 应用程序。默认情况下,它配置一个CassandraTemplate
,扫描@Table
类,并配置 Spring Data Cassandra 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 Spring Boot 中使用 Cassandra 的更多信息,请参阅“ 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.17。自动配置的 Data Couchbase 测试
您可以@DataCouchbaseTest
用来测试 Couchbase 应用程序。默认情况下,它配置一个CouchbaseTemplate
or ReactiveCouchbaseTemplate
,扫描@Document
类,并配置 Spring Data Couchbase 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 Spring Boot 中使用 Couchbase 的更多信息,请参阅本章前面的“ 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.18。自动配置的数据 Elasticsearch 测试
您可以使用它@DataElasticsearchTest
来测试 Elasticsearch 应用程序。默认情况下,它配置一个ElasticsearchRestTemplate
,扫描@Document
类,并配置 Spring Data Elasticsearch 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 Spring Boot 中使用 Elasticsearch 的更多信息,请参阅本章前面的“ 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.19。自动配置的数据 JPA 测试
您可以使用@DataJpaTest
注释来测试 JPA 应用程序。默认情况下,它会扫描@Entity
类并配置 Spring Data JPA 存储库。如果类路径上有一个嵌入式数据库,它也会配置一个。默认情况下,通过将spring.jpa.show-sql
属性设置为 来记录 SQL 查询true
。这可以使用showSql()
注释的属性来禁用。
使用注解
时不会扫描常规@Component
和bean。可用于包含bean。@ConfigurationProperties
@DataJpaTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataJpaTest 可在附录中找到
启用的自动配置设置列表。
|
默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
数据 JPA 测试也可以注入一个bean,它提供了一个专门为测试设计TestEntityManager
的标准 JPA 的替代方案。EntityManager
TestEntityManager 也可以通过添加自动配置到任何基于 Spring 的测试类@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.20。自动配置的 JDBC 测试
@JdbcTest
类似于@DataJpaTest
但适用于只需要 aDataSource
且不使用 Spring Data JDBC 的测试。默认情况下,它配置一个内存嵌入式数据库和一个JdbcTemplate
. 使用注解
时不会扫描常规@Component
和bean。可用于包含bean。@ConfigurationProperties
@JdbcTest
@EnableConfigurationProperties
@ConfigurationProperties
@JdbcTest 可以在附录中找到
启用的自动配置列表。
|
默认情况下,JDBC 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {
}
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests
如果您希望您的测试针对真实数据库运行,您可以使用与@AutoConfigureTestDatabase
for 相同的方式使用注解DataJpaTest
。(请参阅“自动配置的数据 JPA 测试”。)
8.3.21。自动配置的数据 JDBC 测试
@DataJdbcTest
类似于@JdbcTest
但适用于使用 Spring Data JDBC 存储库的测试。默认情况下,它配置了内存中的嵌入式数据库、aJdbcTemplate
和 Spring Data JDBC 存储库。使用注解时只AbstractJdbcConfiguration
扫描子类@DataJdbcTest
,不扫描常规@Component
和@ConfigurationProperties
bean。
@EnableConfigurationProperties
可用于包含@ConfigurationProperties
bean。
@DataJdbcTest 可以在附录中找到
启用的自动配置列表。
|
默认情况下,Data JDBC 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC 示例中所示。
如果您希望您的测试针对真实数据库运行,您可以使用与@AutoConfigureTestDatabase
for 相同的方式使用注解DataJpaTest
。(请参阅“自动配置的数据 JPA 测试”。)
8.3.22。自动配置的 jOOQ 测试
您可以以与jOOQ 相关测试@JooqTest
类似的方式使用。@JdbcTest
由于 jOOQ 严重依赖与数据库模式相对应的基于 Java 的模式,因此DataSource
使用现有模式。如果您想用内存数据库替换它,您可以使用@AutoConfigureTestDatabase
覆盖这些设置。(有关在 Spring Boot 中使用 jOOQ 的更多信息,请参阅“ data.html ”。)使用注解
时不会扫描常规@Component
和bean。可用于包含bean。@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.23。自动配置数据 MongoDB 测试
您可以使用它@DataMongoTest
来测试 MongoDB 应用程序。默认情况下,它配置一个内存中的嵌入式 MongoDB(如果可用),配置一个MongoTemplate
,扫描@Document
类,并配置 Spring Data MongoDB 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 Spring Boot 中使用 MongoDB 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataMongoTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataMongoTest 可在附录中找到
启用的自动配置设置列表。
|
下面的类显示了@DataMongoTest
正在使用的注解:
@DataMongoTest
class MyDataMongoDbTests {
@Autowired
private MongoTemplate mongoTemplate;
// ...
}
@DataMongoTest
class MyDataMongoDbTests(@Autowired val mongoTemplate: MongoTemplate) {
// ...
}
内存中嵌入式 MongoDB 通常适用于测试,因为它速度快且不需要任何开发人员安装。但是,如果您更喜欢针对真正的 MongoDB 服务器运行测试,则应排除嵌入式 MongoDB 自动配置,如以下示例所示:
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class MyDataMongoDbTests {
// ...
}
@DataMongoTest(excludeAutoConfiguration = [EmbeddedMongoAutoConfiguration::class])
class MyDataMongoDbTests {
// ...
}
8.3.24。自动配置的数据 Neo4j 测试
您可以@DataNeo4jTest
用来测试 Neo4j 应用程序。默认情况下,它会扫描@Node
类并配置 Spring Data Neo4j 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 Spring Boot 中使用 Neo4J 的更多信息,请参阅“ 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 Framework 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {
}
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests
反应式访问不支持事务测试。如果您使用此样式,则必须@DataNeo4jTest 按上述方式配置测试。
|
8.3.25。自动配置的数据 Redis 测试
您可以@DataRedisTest
用来测试 Redis 应用程序。默认情况下,它会扫描@RedisHash
类并配置 Spring Data Redis 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 Spring Boot 中使用 Redis 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties
@DataRedisTest
@EnableConfigurationProperties
@ConfigurationProperties
@DataRedisTest 可在附录中找到
启用的自动配置设置列表。
|
以下示例显示了@DataRedisTest
正在使用的注释:
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.26。自动配置的数据 LDAP 测试
您可以使用它@DataLdapTest
来测试 LDAP 应用程序。默认情况下,它配置内存中的嵌入式 LDAP(如果可用)、配置LdapTemplate
、扫描@Entry
类并配置 Spring Data LDAP 存储库。使用注解
时不会扫描常规@Component
和bean。可用于包含bean。(有关在 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.27。自动配置的 REST 客户端
您可以使用@RestClientTest
注释来测试 REST 客户端。默认情况下,它会自动配置 Jackson、GSON 和 Jsonb 支持,配置 aRestTemplateBuilder
并添加对MockRestServiceServer
. 使用注解
时不会扫描常规@Component
和bean。可用于包含bean。@ConfigurationProperties
@RestClientTest
@EnableConfigurationProperties
@ConfigurationProperties
@RestClientTest 可在附录中找到
启用的自动配置设置列表。
|
您要测试的特定 bean 应使用 的value
orcomponents
属性指定@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.28。自动配置的 Spring REST 文档测试
您可以使用@AutoConfigureRestDocs
注解在您的测试中使用Spring REST Docs与 Mock MVC、REST Assured 或 WebTestClient。它消除了 Spring REST Docs 中对 JUnit 扩展的需求。
@AutoConfigureRestDocs
可用于覆盖默认输出目录(target/generated-snippets
如果您使用 Maven 或build/generated-snippets
使用 Gradle)。它还可用于配置出现在任何记录的 URI 中的主机、方案和端口。
使用 Mock MVC 自动配置 Spring REST 文档测试
@AutoConfigureRestDocs
在测试基于 servlet 的 Web 应用程序时自定义MockMvc
bean 以使用 Spring REST Docs。您可以@Autowired
像使用 Mock MVC 和 Spring REST Docs 时一样在测试中使用和使用它,如以下示例所示:
@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
也可以WebTestClient
在测试响应式 Web 应用程序时使用。您可以@Autowired
像使用 Spring REST Docs 时一样在测试中使用和使用它@WebFluxTest
,如以下示例所示:
@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}")
)
}
}
}
带有 REST Assured 的自动配置 Spring REST 文档测试
@AutoConfigureRestDocs
使RequestSpecification
预配置为使用 Spring REST Docs 的 bean 可用于您的测试。您可以@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.29。自动配置的 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.30。额外的自动配置和切片
每个切片提供一个或多个@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.31。用户配置和切片
因此,重要的是不要在应用程序的主类中乱扔特定于其功能特定区域的配置设置。
假设您使用的是 Spring Batch,并且您依赖于它的自动配置。您可以定义@SpringBootApplication
如下:
@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {
// ...
}
@SpringBootApplication
@EnableBatchProcessing
class MyApplication {
// ...
}
因为这个类是测试的源配置,所以任何切片测试实际上都会尝试启动 Spring Batch,这绝对不是你想要做的。推荐的方法是将特定于区域的配置移动到与您的应用程序处于同一级别的单独@Configuration
类中,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class MyBatchConfiguration {
// ...
}
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
class MyBatchConfiguration {
// ...
}
根据您的应用程序的复杂性,您可能有一个@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.32。使用 Spock 测试 Spring Boot 应用程序
Spock 2.x 可用于测试 Spring Boot 应用程序。为此,请将 Spockspock-spring
模块的依赖项添加到应用程序的构建中。
spock-spring
将 Spring 的测试框架集成到 Spock 中。有关详细信息,请参阅Spock 的 Spring 模块的文档。
8.4. 测试工具
测试应用程序时通常有用的一些测试实用程序类被打包为spring-boot
.
8.4.1. ConfigDataApplicationContextInitializer
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 支持,您需要另外配置 aPropertySourcesPlaceholderConfigurer 或 use @SpringBootTest ,它会为您自动配置一个。
|
8.4.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.4.3. 输出捕获
OutputCapture
Extension
是一个可用于捕获System.out
和System.err
输出的 JUnit 。要使用 add@ExtendWith(OutputCaptureExtension.class)
和 injectionCapturedOutput
作为测试类构造函数或测试方法的参数,如下所示:
@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.4.4. 测试休息模板
TestRestTemplate
是 Spring 的一种方便替代方案,RestTemplate
在集成测试中很有用。您可以获得一个普通模板或发送基本 HTTP 身份验证(带有用户名和密码)的模板。在任何一种情况下,模板都是容错的。这意味着它以一种测试友好的方式运行,不会在 4xx 和 5xx 错误上抛出异常。相反,可以通过返回ResponseEntity
的及其状态码检测此类错误。
Spring Framework 5.0 提供了一个新功能WebTestClient ,适用于WebFlux 集成测试以及WebFlux 和 MVC 端到端测试。与TestRestTemplate .
|
建议使用 Apache HTTP 客户端(版本 4.3.2 或更高版本),但不是强制性的。如果您的类路径中有它,则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
or一起使用WebEnvironment.DEFINED_PORT
,您可以注入一个完全配置的TestRestTemplate
并开始使用它。如有必要,可以通过RestTemplateBuilder
bean 应用额外的定制。任何未指定主机和端口的 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. 创建您自己的自动配置
如果您在开发共享库的公司工作,或者如果您在开源或商业库中工作,您可能希望开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,并且仍然可以被 Spring Boot 拾取。
自动配置可以与提供自动配置代码以及您将使用的典型库的“启动器”相关联。我们首先介绍了构建您自己的自动配置所需了解的内容,然后我们继续介绍创建自定义启动器所需的典型步骤。
一个演示项目可用于展示如何逐步创建启动器。 |
9.1。了解自动配置的 Bean
在幕后,自动配置是通过@AutoConfiguration
注释实现的。这个注释本身是用 元注释的@Configuration
,使自动配置成为标准@Configuration
类。附加@Conditional
注释用于限制何时应用自动配置。通常,自动配置类使用@ConditionalOnClass
和@ConditionalOnMissingBean
注释。这确保了自动配置仅在找到相关类并且您没有声明自己的类时适用@Configuration
。
您可以浏览源代码spring-boot-autoconfigure
以查看@Configuration
Spring 提供的类(参见META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件)。
9.2. 定位自动配置候选
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Spring Boot 检查发布的 jar中是否存在文件。该文件应列出您的配置类,如以下示例所示:
com.mycorp.libx.autoconfigure.LibXAutoConfiguration com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以通过# 在此文件中使用注释。
|
自动配置只能
以这种方式加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应启用组件扫描以查找其他组件。@Import 应该使用
特定的 s 来代替。 |
如果您的配置需要按特定顺序应用,您可以使用@AutoConfigureAfter
or注释。@AutoConfigureBefore
例如,如果您提供特定于 Web 的配置,您的类可能需要在WebMvcAutoConfiguration
.
如果您使用@AutoConfiguration 注解,则可以使用、 和before 属性beforeName 别名来代替专用注解。
after afterName |
如果您想订购某些彼此不应该有任何直接了解的自动配置,您也可以使用@AutoConfigureOrder
. 该注释与常规注释具有相同的语义,@Order
但为自动配置类提供了专用顺序。
与标准@Configuration
类一样,应用自动配置类的顺序只影响定义它们的 bean 的顺序。随后创建这些 bean 的顺序不受影响,由每个 bean 的依赖关系和任何@DependsOn
关系决定。
9.3. 条件注释
您几乎总是希望@Conditional
在您的自动配置类中包含一个或多个注释。注解是一个常见的@ConditionalOnMissingBean
例子,如果开发人员对你的默认设置不满意,它可以让他们覆盖自动配置。
Spring Boot 包含许多注释,您可以通过注释类或单个方法@Conditional
在自己的代码中重用它们。这些注释包括:@Configuration
@Bean
9.3.1。班级条件
@ConditionalOnClass
和@ConditionalOnMissingClass
注释允许根据@Configuration
特定类的存在与否来包含类。由于注释元数据是使用ASM解析的,因此您可以使用该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 引用类。
|
9.3.2. 豆条件
@ConditionalOnBean
和注释允许根据@ConditionalOnMissingBean
特定 bean 的存在或不存在来包含 bean。您可以使用该value
属性按类型name
指定bean 或按名称指定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()
}
}
在前面的示例中,someService
如果.SomeService
ApplicationContext
您需要非常小心添加 bean 定义的顺序,因为这些条件是根据到目前为止已处理的内容进行评估的。出于这个原因,我们建议只在自动配置类上使用@ConditionalOnBean 和@ConditionalOnMissingBean 注解(因为保证在添加任何用户定义的 bean 定义后加载这些注解)。
|
@ConditionalOnBean 并且@ConditionalOnMissingBean 不要阻止@Configuration 创建类。在类级别使用这些条件和使用注解标记每个包含的方法之间的唯一区别是,如果条件不匹配
@Bean ,前者会阻止将类注册为 bean。@Configuration |
声明@Bean 方法时,在方法的返回类型中提供尽可能多的类型信息。例如,如果你的 bean 的具体类实现了一个接口,那么 bean 方法的返回类型应该是具体类而不是接口。在使用 bean 条件时,在方法中提供尽可能多的类型信息@Bean 尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息。
|
9.3.3. 物业条件
@ConditionalOnProperty
注释允许基于 Spring Environment 属性包含配置。使用prefix
和name
属性指定应检查的属性。默认情况下,false
匹配任何存在但不等于的属性。您还可以使用havingValue
和matchIfMissing
属性创建更高级的检查。
9.3.4。资源条件
@ConditionalOnResource
注释允许仅在存在特定资源时才包含配置。可以使用通常的 Spring 约定来指定资源,如下例所示file:/home/user/test.dat
:
9.3.5。网络申请条件
@ConditionalOnWebApplication
和注释允许根据@ConditionalOnNotWebApplication
应用程序是否为“Web 应用程序”来包含配置。基于 servlet 的 Web 应用程序是任何使用 Spring WebApplicationContext
、定义session
范围或具有ConfigurableWebEnvironment
. 反应式 Web 应用程序是任何使用ReactiveWebApplicationContext
或具有ConfigurableReactiveWebEnvironment
.
注释允许根据@ConditionalOnWarDeployment
应用程序是否是部署到容器的传统 WAR 应用程序来包含配置。对于使用嵌入式服务器运行的应用程序,此条件将不匹配。
9.3.6。SpEL 表达条件
@ConditionalOnExpression
注释允许基于SpEL 表达式的结果包含配置。
在表达式中引用 bean 将导致该 bean 在上下文刷新处理中很早就被初始化。结果,bean 将不适合进行后处理(例如配置属性绑定),并且其状态可能不完整。 |
9.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
) 并检查自动配置是否正确退出。Invokingrun
提供了一个回调上下文,可以与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(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}
class MyConditionEvaluationReportingTests {
@Test
fun autoConfigTest() {
ApplicationContextRunner()
.withInitializer(ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run { context: AssertableApplicationContext? -> }
}
}
9.4.1。模拟 Web 上下文
如果您需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请分别使用WebApplicationContextRunner
或ReactiveWebApplicationContextRunner
。
9.4.2. 覆盖类路径
还可以测试在运行时不存在特定类和/或包时会发生什么。Spring Boot 附带一个FilteredClassLoader
runner 可以轻松使用的。在以下示例中,我们断言如果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")
}
}
9.5。创建自己的启动器
一个典型的 Spring Boot 启动器包含自动配置和自定义给定技术的基础设施的代码,我们称之为“acme”。为了使其易于扩展,可以将专用命名空间中的许多配置键暴露给环境。最后,提供了一个“starter”依赖项来帮助用户尽可能轻松地开始。
具体来说,自定义启动器可以包含以下内容:
-
autoconfigure
包含“acme”的自动配置代码的模块。 -
提供对模块
starter
的依赖关系的autoconfigure
模块以及“acme”和通常有用的任何其他依赖关系。简而言之,添加启动器应该提供开始使用该库所需的一切。
两个模块中的这种分离绝不是必要的。如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的事实。此外,您还可以制作一个启动器来提供有关这些可选依赖项的意见。同时,其他人只能依靠autoconfigure
模块,制作自己的不同意见的starter。
如果自动配置相对简单并且没有可选功能,那么在启动器中合并两个模块绝对是一种选择。
9.5.1。命名
您应该确保为您的启动器提供适当的命名空间。不要以 . 开头的模块名称spring-boot
,即使您使用不同的 Maven groupId
。我们将来可能会为您自动配置的内容提供官方支持。
根据经验,您应该在启动器之后命名组合模块。例如,假设您正在为“acme”创建一个启动器,并且您命名自动配置模块acme-spring-boot
和启动器acme-spring-boot-starter
。如果您只有一个模块将两者结合起来,请将其命名为acme-spring-boot-starter
.
9.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 中使用您自己的启动器也是验证元数据质量的好主意。
9.5.3。“自动配置”模块
该autoconfigure
模块包含开始使用该库所需的一切。它还可能包含配置键定义(例如@ConfigurationProperties
)和任何回调接口,可用于进一步自定义组件的初始化方式。
您应该将库的依赖项标记为可选,以便您可以autoconfigure 更轻松地将模块包含在项目中。如果您这样做,则不会提供该库,并且默认情况下,Spring Boot 会退出。
|
Spring Boot 使用注解处理器来收集元数据文件 ( META-INF/spring-autoconfigure-metadata.properties
) 中的自动配置条件。如果该文件存在,它将用于急切地过滤不匹配的自动配置,这将缩短启动时间。建议在包含自动配置的模块中添加以下依赖项:
<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 4.5 及更早版本,应在compileOnly
配置中声明依赖项,如以下示例所示:
dependencies {
compileOnly "org.springframework.boot:spring-boot-autoconfigure-processor"
}
对于 Gradle 4.6 及更高版本,应在annotationProcessor
配置中声明依赖项,如下例所示:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
9.5.4。入门模块
启动器实际上是一个空罐子。它的唯一目的是提供必要的依赖项以使用该库。您可以将其视为对入门所需内容的固执己见。
不要对添加启动器的项目做出假设。如果您要自动配置的库通常需要其他启动器,请同时提及它们。如果可选依赖项的数量很高,则提供一组适当的默认依赖项可能会很困难,因为您应该避免包含对于库的典型使用而言不必要的依赖项。换句话说,您不应该包含可选依赖项。
无论哪种方式,您的 starter 都必须直接或间接引用核心 Spring Boot starter ( spring-boot-starter )(如果您的 starter 依赖于另一个 starter,则无需添加它)。如果仅使用您的自定义启动器创建项目,则 Spring Boot 的核心功能将因核心启动器的存在而受到尊重。
|
10. Kotlin 支持
Spring Boot 通过利用其他 Spring 项目(如 Spring Framework、Spring Data 和 Reactor)中的支持来提供 Kotlin 支持。有关更多信息,请参阅Spring Framework Kotlin 支持文档。
开始使用 Spring Boot 和 Kotlin 的最简单方法是遵循这个综合教程。您可以使用start.spring.io创建新的 Kotlin 项目。如果您需要支持,请随时加入Kotlin Slack的#spring 频道,或者在Stack Overflowspring
上使用和kotlin
标签提出问题。
10.1。要求
Spring Boot 至少需要 Kotlin 1.3.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 项目,则默认提供这些依赖项和插件。 |
10.2. 零安全
Kotlin 的主要功能之一是null-safety。它在编译时处理null
值,而不是将问题推迟到运行时并遇到NullPointerException
. 这有助于消除常见的错误来源,而无需支付Optional
. Kotlin 还允许使用具有可为空值的函数构造,如本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
。在从 Spring API 推断出的 Kotlin 类型中,该strict
值需要考虑空值安全性,但应该在知道 Spring API 可空性声明甚至在次要版本之间演变并且将来可能会添加更多检查的情况下使用该值)。
尚不支持泛型类型参数、可变参数和数组元素可空性。有关最新信息,请参阅SPR-15942 。另请注意,Spring Boot 自己的 API尚未注释。 |
10.3. Kotlin API
10.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)
}
10.3.2. 扩展
Kotlin扩展提供了使用附加功能扩展现有类的能力。Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利。
TestRestTemplate
提供了类似于 Spring Framework 为RestOperations
Spring Framework 提供的扩展。除其他外,这些扩展使利用 Kotlin 实体化类型参数成为可能。
10.4. 依赖管理
为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。
使用 Maven,可以通过设置kotlin.version
属性来自定义 Kotlin 版本,并为kotlin-maven-plugin
. 使用 Gradle,Spring Boot 插件会自动与kotlin.version
Kotlin 插件的版本对齐。
Spring Boot 还通过导入 Kotlin Coroutines BOM 来管理 Coroutines 依赖项的版本。可以通过设置kotlin-coroutines.version
属性来自定义版本。
org.jetbrains.kotlinx:kotlinx-coroutines-reactor 如果一个 Kotlin 项目在start.spring.io上至少有一个反应性依赖项,则默认情况下会提供依赖项。
|
10.5。@ConfigurationProperties
@ConfigurationProperties
当与@ConstructorBinding
具有不可变属性的支持类结合使用时val
,如下例所示:
@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {
data class MyService(
val apiToken: String,
val uri: URI
)
}
10.6。测试
虽然可以使用 JUnit 4 来测试 Kotlin 代码,但 JUnit 5 默认提供并推荐使用。JUnit 5 允许一个测试类被实例化一次,并被重用于该类的所有测试。这使得在非静态方法上使用@BeforeAll
和@AfterAll
注释成为可能,这非常适合 Kotlin。
要模拟 Kotlin 类,建议使用MockK。如果您需要Mockk
等效的 Mockito 特定@MockBean
和@SpyBean
注释,您可以使用提供类似和注释的SpringMockK。@MockkBean
@SpykBean
10.7。资源
10.7.2. 例子
-
spring-boot-kotlin-demo: regular Spring Boot + Spring Data JPA project
-
mixit: Spring Boot 2 + WebFlux + Reactive Spring Data MongoDB
-
spring-kotlin-fullstack: WebFlux Kotlin fullstack example with Kotlin2js for frontend instead of JavaScript or TypeScript
-
spring-petclinic-kotlin: Kotlin version of the Spring PetClinic Sample Application
-
spring-kotlin-deepdive: a step by step migration for Boot 1.0 + Java to Boot 2.0 + Kotlin
-
spring-boot-coroutines-demo: Coroutines sample project
11. What to Read Next
如果您想了解有关本节中讨论的任何类的更多信息,请参阅Spring Boot API 文档,或者您可以直接浏览源代码。如果您有具体问题,请参阅操作方法部分。
如果您对 Spring Boot 的核心功能感到满意,您可以继续阅读有关生产就绪功能的信息。