核心功能
1. 弹簧应用
这SpringApplication
class 提供了一种方便的方法来引导从main()
方法。
在许多情况下,您可以委托给静态SpringApplication.run
方法,如以下示例所示:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
当应用程序启动时,您应该会看到类似于以下输出的内容:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.7.18) 2023-11-23 07:23:23.238 INFO 28579 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 1.8.0_392 on myhost with PID 28579 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-11-23 07:23:23.245 INFO 28579 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-11-23 07:23:24.552 INFO 28579 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-11-23 07:23:24.567 INFO 28579 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-11-23 07:23:24.567 INFO 28579 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.83] 2023-11-23 07:23:24.658 INFO 28579 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-11-23 07:23:24.659 INFO 28579 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1346 ms 2023-11-23 07:23:25.336 INFO 28579 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-11-23 07:23:25.355 INFO 28579 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.63 seconds (JVM running for 3.003)
默认情况下,INFO
将显示日志记录消息,包括一些相关的启动详细信息,例如启动应用程序的用户。
如果您需要的日志级别INFO
,您可以设置它,如日志级别中所述。
应用程序版本是使用主应用程序类包中的实现版本确定的。
可以通过设置spring.main.log-startup-info
自false
.
这也将关闭应用程序活动配置文件的日志记录。
若要在启动期间添加其他日志记录,可以覆盖logStartupInfo(boolean) 在SpringApplication . |
1.1. 启动失败
如果您的应用程序无法启动,请注册FailureAnalyzers
有机会提供专门的错误消息和解决问题的具体作。
例如,如果您在端口上启动一个 Web 应用程序8080
并且该端口已在使用中,您应该会看到类似于以下消息的内容:
*************************** APPLICATION FAILED TO START *************************** Description: Embedded servlet container failed to start. Port 8080 was already in use. Action: Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.
Spring Boot 提供了许多FailureAnalyzer 实现,您可以添加自己的实现。 |
如果没有故障分析器能够处理异常,您仍然可以显示完整的条件报告,以更好地了解出了什么问题。
为此,您需要启用debug
属性或使DEBUG
Logging为org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
.
例如,如果您使用java -jar
,您可以启用debug
属性如下:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
1.2. 延迟初始化
SpringApplication
允许延迟初始化应用程序。
启用延迟初始化后,将根据需要创建 Bean,而不是在应用程序启动期间创建 Bean。
因此,启用延迟初始化可以减少应用程序启动所需的时间。
在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 Bean在收到 HTTP 请求之前不会被初始化。
延迟初始化的一个缺点是它可能会延迟发现应用程序的问题。 如果延迟初始化了配置错误的 Bean,则在启动期间将不再发生故障,并且只有在初始化 Bean 时问题才会变得明显。 还必须注意确保 JVM 有足够的内存来容纳应用程序的所有 Bean,而不仅仅是那些在启动期间初始化的 Bean。 由于这些原因,默认情况下不启用延迟初始化,建议在启用延迟初始化之前对 JVM 的堆大小进行微调。
可以使用lazyInitialization
方法SpringApplicationBuilder
或setLazyInitialization
方法SpringApplication
.
或者,可以使用spring.main.lazy-initialization
属性,如以下示例所示:
spring.main.lazy-initialization=true
spring:
main:
lazy-initialization: true
如果要在对应用程序的其余部分使用延迟初始化时禁用某些 bean 的延迟初始化,则可以使用@Lazy(false) 注解。 |
1.3. 自定义横幅
启动时打印的横幅可以通过添加banner.txt
文件添加到您的类路径或通过将spring.banner.location
属性添加到此类文件的位置。
如果文件的编码不是 UTF-8,您可以将spring.banner.charset
.
除了文本文件之外,您还可以添加一个banner.gif
,banner.jpg
或banner.png
image 文件添加到您的类路径或将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. Fluent Builder 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 探针等基础设施向平台提供有关其可用性的信息。 Spring Boot 包括对常用的“活动”和“就绪”可用性状态的开箱即用支持。 如果您使用的是 Spring Boot 的“执行器”支持,则这些状态将作为运行状况端点组公开。
此外,您还可以通过注入ApplicationAvailability
接口到你自己的 bean 中。
1.6.1. 活体状态
应用程序的“活跃”状态告诉它是否允许其内部状态正常工作,或者如果当前出现故障,是否允许它自行恢复。 损坏的“活动”状态意味着应用程序处于无法恢复的状态,基础结构应重新启动应用程序。
通常,“活动”状态不应基于外部检查,例如运行状况检查。 如果发生这种情况,出现故障的外部系统(数据库、Web API、外部缓存)将触发大规模重启和跨平台的级联故障。 |
Spring Boot 应用程序的内部状态主要由 SpringApplicationContext
.
如果应用程序上下文已成功启动,Spring Boot 会假定应用程序处于有效状态。
一旦上下文刷新,应用程序就会被视为活动,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
1.6.2. 就绪状态
应用程序的“就绪”状态表明应用程序是否已准备好处理流量。
失败的“就绪”状态告诉平台,它目前不应将流量路由到应用程序。
这通常发生在启动期间,而CommandLineRunner
和ApplicationRunner
正在处理组件,或者如果应用程序认为它太忙而无法进行其他流量,则随时处理组件。
调用应用程序和命令行运行器后,应用程序就被视为就绪,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
预期在启动期间运行的任务应由CommandLineRunner 和ApplicationRunner 组件,而不是使用 Spring 组件生命周期回调,例如@PostConstruct . |
1.6.3. 管理应用程序可用性状态
应用程序组件可以随时检索当前可用性状态,方法是将ApplicationAvailability
接口和调用方法。
更常见的是,应用程序将希望侦听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的“就绪”状态导出到一个文件中,以便 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
一个SpringApplication
发送一些额外的应用程序事件。
有些事件实际上是在 如果您希望自动注册这些侦听器,无论应用程序的创建方式如何,都可以添加 org.springframework.context.ApplicationListener=com.example.project.MyListener |
应用程序事件在应用程序运行时按以下顺序发送:
-
一
ApplicationStartingEvent
在运行开始时但在任何处理之前发送,侦听器和初始值设定项的注册除外。 -
一
ApplicationEnvironmentPreparedEvent
在Environment
在上下文中使用是已知的,但在创建上下文之前。 -
一
ApplicationContextInitializedEvent
在ApplicationContext
已准备好,并且已调用 ApplicationContextInitializers,但在加载任何 bean 定义之前。 -
一
ApplicationPreparedEvent
在刷新开始之前但在加载 Bean 定义之后发送。 -
一
ApplicationStartedEvent
在刷新上下文之后,但在调用任何应用程序和命令行运行器之前发送。 -
一
AvailabilityChangeEvent
紧随其后发送LivenessState.CORRECT
以指示应用程序被视为活动。 -
一
ApplicationReadyEvent
在调用任何应用程序和命令行运行器后发送。 -
一
AvailabilityChangeEvent
紧随其后发送ReadinessState.ACCEPTING_TRAFFIC
以指示应用程序已准备好处理请求。 -
一
ApplicationFailedEvent
如果启动时出现异常,则发送。
以上列表仅包括SpringApplicationEvent
s 绑定到SpringApplication
.
除了这些之外,以下事件还发布在之后ApplicationPreparedEvent
和之前ApplicationStartedEvent
:
-
一个
WebServerInitializedEvent
在WebServer
准备好了。ServletWebServerInitializedEvent
和ReactiveWebServerInitializedEvent
分别是 servlet 和 reactive 变体。 -
一个
ContextRefreshedEvent
在ApplicationContext
刷新。
您通常不需要使用应用程序事件,但知道它们存在会很方便。 在内部,Spring Boot 使用事件来处理各种任务。 |
事件侦听器不应运行可能冗长的任务,因为它们默认在同一线程中执行。 请考虑改用应用程序和命令行运行器。 |
应用程序事件是使用 Spring Framework 的事件发布机制发送的。
此机制的一部分可确保在子上下文中发布到侦听器的事件也发布到任何祖先上下文中的侦听器。
因此,如果您的应用程序使用SpringApplication
实例,侦听器可能会接收相同类型应用程序事件的多个实例。
为了允许您的侦听器区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。
可以通过实现ApplicationContextAware
或者,如果侦听器是 bean,则使用@Autowired
.
1.8. Web 环境
一个SpringApplication
尝试创建正确类型的ApplicationContext
代表您。
用于确定WebApplicationType
如下:
-
如果存在 Spring MVC,则
AnnotationConfigServletWebServerApplicationContext
被使用 -
如果 Spring MVC 不存在,而 Spring WebFlux 存在,则
AnnotationConfigReactiveWebServerApplicationContext
被使用 -
否则
AnnotationConfigApplicationContext
被使用
这意味着,如果您使用的是 Spring MVC 和新的WebClient
来自同一应用程序中的 Spring WebFlux,默认情况下将使用 Spring MVC。
您可以通过调用setWebApplicationType(WebApplicationType)
.
也可以完全控制ApplicationContext
调用setApplicationContextFactory(…)
.
通常希望调用setWebApplicationType(WebApplicationType.NONE) 使用时SpringApplication 在 JUnit 测试中。 |
1.9. 访问应用程序参数
如果您需要访问传递给SpringApplication.run(…)
,您可以注入一个org.springframework.boot.ApplicationArguments
豆。
这ApplicationArguments
接口提供对原始String[]
参数以及解析的option
和non-option
参数,如以下示例所示:
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
@Component
class MyBean(args: ApplicationArguments) {
init {
val debug = args.containsOption("debug")
val files = args.nonOptionArgs
if (debug) {
println(files)
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
Spring Boot 还注册了一个CommandLinePropertySource 与Spring一起Environment .
这还允许您使用@Value 注解。 |
1.10. 使用 ApplicationRunner 或 CommandLineRunner
如果您需要运行一些特定的代码,那么一旦SpringApplication
已启动,您可以实现ApplicationRunner
或CommandLineRunner
接口。
两个接口的工作方式相同,并提供一个run
方法,该方法在SpringApplication.run(…)
完成。
此协定非常适合在应用程序启动后但在开始接受流量之前运行的任务。 |
这CommandLineRunner
接口以字符串数组的形式提供对应用程序参数的访问,而ApplicationRunner
使用ApplicationArguments
前面讨论的界面。
以下示例显示了CommandLineRunner
使用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
或ApplicationRunner
bean 定义了必须按特定顺序调用的 bean,您可以额外实现org.springframework.core.Ordered
接口或使用org.springframework.core.annotation.Order
注解。
1.11. 应用程序退出
每SpringApplication
向 JVM 注册一个 shutdown 钩子,以确保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 端口上运行,请获取键为local.server.port . |
1.13. 应用程序启动跟踪
在应用程序启动期间,SpringApplication
和ApplicationContext
执行许多与应用程序生命周期相关的任务,
Bean 生命周期甚至处理应用程序事件。
跟ApplicationStartup
, Spring 框架允许您使用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、类加载等)相关联。
配置完成后,您可以通过在启用飞行记录器的情况下运行应用程序来记录数据:
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar
Spring Boot 附带了BufferingApplicationStartup
变体;此实现旨在缓冲启动步骤并将它们清空到外部指标系统中。
应用程序可以请求类型为BufferingApplicationStartup
在任何组件中。
Spring Boot 也可以配置为公开startup
端点以 JSON 文档的形式提供此信息。
2. 外部化配置
Spring Boot 允许您将配置外部化,以便您可以在不同的环境中使用相同的应用程序代码。 您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
属性值可以通过使用@Value
注释,通过 Spring 的Environment
抽象,或者通过@ConfigurationProperties
.
Spring Boot 使用非常特殊的PropertySource
order 的命令,旨在允许合理地覆盖值。
较高的属性源可以覆盖较早的属性源中定义的值。
按以下顺序考虑来源:
-
默认属性(通过设置
SpringApplication.setDefaultProperties
). -
@PropertySource
注释@Configuration
类。 请注意,此类属性源不会添加到Environment
直到刷新应用程序上下文。 这为时已晚,无法配置某些属性,例如logging.*
和spring.main.*
在刷新开始之前读取。 -
配置数据(例如
application.properties
文件)。 -
一个
RandomValuePropertySource
仅在random.*
. -
作系统环境变量。
-
Java 系统属性 (
System.getProperties()
). -
JNDI 属性
java:comp/env
. -
ServletContext
init 参数。 -
ServletConfig
init 参数。 -
属性来自
SPRING_APPLICATION_JSON
(嵌入在环境变量或系统属性中的内联 JSON)。 -
命令行参数。
-
properties
属性。 适用于@SpringBootTest
以及用于测试应用程序特定切片的测试注释。 -
@DynamicPropertySource
测试中的注释。 -
@TestPropertySource
测试上的注释。 -
Devtools 全局设置属性中的
$HOME/.config/spring-boot
目录。
配置文件按以下顺序考虑:
-
打包在 jar 中的应用程序属性 (
application.properties
和 YAML 变体)。 -
打包在jar中的特定于配置文件的应用程序属性(
application-{profile}.properties
和 YAML 变体)。 -
打包 jar 外部的应用程序属性 (
application.properties
和 YAML 变体)。 -
打包的 jar 之外的特定于配置文件的应用程序属性 (
application-{profile}.properties
和 YAML 变体)。
建议对整个应用程序坚持使用一种格式。
如果您有包含两者的配置文件.properties 和 YAML 格式在同一位置,.properties 优先。 |
如果使用环境变量而不是系统属性,则大多数作系统不允许使用以句点分隔的键名,但可以改用下划线(例如,SPRING_CONFIG_NAME 而不是spring.config.name ).
有关详细信息,请参阅从环境变量绑定。 |
如果您的应用程序在 Servlet 容器或应用程序服务器中运行,那么 JNDI 属性(在java:comp/env ) 或 servlet 上下文初始化参数可以代替环境变量或系统属性,或者与环境变量或系统属性一起使用。 |
为了提供一个具体的例子,假设您开发了一个@Component
使用name
属性,如以下示例所示:
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
@Component
class MyBean {
@Value("\${name}")
private val name: String? = null
// ...
}
在您的应用程序类路径上(例如,在 jar 中),您可以有一个application.properties
文件,该文件为name
.
在新环境中运行时,application.properties
可以在 jar 外部提供文件,以覆盖name
.
对于一次性测试,您可以使用特定的命令行开关(例如java -jar app.jar --name="Spring"
).
这env 和configprops 终结点可用于确定属性具有特定值的原因。
可以使用这两个终结点来诊断意外的属性值。
有关详细信息,请参阅“生产就绪功能”部分。 |
2.1. 访问命令行属性
默认情况下,SpringApplication
转换任何命令行选项参数(即以 开头的参数,例如--
--server.port=9000
) 设置为property
并将它们添加到 SpringEnvironment
.
如前所述,命令行属性始终优先于基于文件的属性源。
如果您不希望将命令行属性添加到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
在SpringEnvironment
.
也可以将相同的 JSON 作为系统属性提供:
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
或者,您可以使用命令行参数提供 JSON:
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
如果您要部署到经典的 Application Server,您还可以使用名为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
environment 属性。
例如,要查找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 之classpath:myconfig.properties ,您还会发现合适的classpath:myconfig-<profile>.properties 文件已加载。 |
在大多数情况下,每个spring.config.location
您添加的项将引用单个文件或目录。
位置按定义的顺序进行处理,后面的位置可以覆盖前面位置的值。
如果您有复杂的位置设置,并且使用特定于配置文件的配置文件,则可能需要提供进一步的提示,以便 Spring Boot 知道应该如何对它们进行分组。
位置组是在同一级别考虑的所有位置的集合。
例如,您可能希望对所有类路径位置进行分组,然后对所有外部位置进行分组。
位置组中的项目应用 分隔。
有关更多详细信息,请参阅“配置文件特定文件”部分中的示例。;
使用spring.config.location
替换默认位置。
例如,如果spring.config.location
配置为optional:classpath:/custom-config/,optional:file:./custom-config/
,考虑的完整位置集为:
-
optional:classpath:custom-config/
-
optional:file:./custom-config/
如果您更喜欢添加其他位置而不是替换它们,您可以使用spring.config.additional-location
.
从其他位置加载的属性可以覆盖默认位置中的属性。
例如,如果spring.config.additional-location
配置为optional:classpath:/custom-config/,optional:file:./custom-config/
,考虑的完整位置集为:
-
optional:classpath:/;optional:classpath:/config/
-
optional:file:./;optional:file:./config/;optional:file:./config/*/
-
optional:classpath:custom-config/
-
optional:file:./custom-config/
通过此搜索排序,您可以在一个配置文件中指定默认值,然后有选择地覆盖另一个配置文件中的这些值。
您可以在application.properties
(或您选择的任何其他基本名称spring.config.name
) 在默认位置之一。
然后,可以在运行时使用位于其中一个自定义位置的不同文件覆盖这些默认值。
2.3.1. 可选位置
默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出一个ConfigDataLocationNotFoundException
并且您的应用程序将不会启动。
如果您想指定一个位置,但您不介意它是否总是存在,您可以使用optional:
前缀。
您可以将此前缀与spring.config.location
和spring.config.additional-location
属性,以及spring.config.import
声明。
例如,一个spring.config.import
值optional:file:./myconfig.properties
允许您的应用程序启动,即使myconfig.properties
文件丢失。
如果您想忽略所有ConfigDataLocationNotFoundExceptions
并始终继续启动您的应用程序,您可以使用spring.config.on-not-found
财产。
将值设置为ignore
用SpringApplication.setDefaultProperties(…)
或使用系统/环境变量。
2.3.2. 通配符位置
如果配置文件位置包含最后一个路径段的字符,则该位置被视为通配符位置。
加载配置时,通配符会展开,以便也会检查直接子目录。
当配置属性有多个来源时,通配符位置在 Kubernetes 等环境中特别有用。*
例如,如果你有一些 Redis 配置和一些 MySQL 配置,你可能希望将这两部分配置分开,同时要求这两部分都存在于application.properties
文件。
这可能会导致两个单独的application.properties
挂载在不同位置的文件,例如/config/redis/application.properties
和/config/mysql/application.properties
.
在这种情况下,通配符位置为config/*/
,将导致两个文件都被处理。
默认情况下,Spring Boot 包括config/*/
在默认搜索位置中。
这意味着/config
将搜索 jar 外部的目录。
您可以自己使用通配符位置,并使用spring.config.location
和spring.config.additional-location
性能。
通配符位置必须仅包含一个并以 结尾的搜索位置,即目录或* */ */<filename> 用于文件搜索位置。
带有通配符的位置根据文件名的绝对路径按字母顺序排序。 |
通配符位置仅适用于外部目录。
不能在classpath: 位置。 |
2.3.3. 配置文件特定文件
以及application
属性文件,Spring Boot 还将尝试使用命名约定加载特定于配置文件的文件application-{profile}
.
例如,如果您的应用程序激活名为prod
并使用 YAML 文件,则同时使用application.yaml
和application-prod.yaml
将被考虑。
特定于配置文件的属性从与标准相同的位置加载application.properties
,特定于配置文件的文件始终覆盖非特定文件。
如果指定了多个配置文件,则应用后赢策略。
例如,如果配置文件prod,live
由spring.profiles.active
属性,值application-prod.properties
可以被那些application-live.properties
.
后赢策略适用于位置组级别。
一个 例如,继续我们的 /cfg application-live.properties /ext application-live.properties application-prod.properties 当我们有一个
当我们有
|
这Environment
具有一组默认配置文件(默认情况下,[default]
),如果未设置活动配置文件,则使用该配置文件。
换句话说,如果没有显式激活配置文件,则属性application-default
被考虑。
属性文件仅加载一次。 如果您已经直接导入了配置文件特定的属性文件,则不会再次导入该文件。 |
2.3.4. 导入附加数据
应用程序属性可以使用spring.config.import
财产。
导入在发现时进行处理,并被视为插入紧邻声明导入的文档下方的附加文档。
例如,您的类路径中可能有以下内容application.properties
文件:
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties"
这将触发导入dev.properties
当前目录中的文件(如果存在此类文件)。
导入的dev.properties
将优先于触发导入的文件。
在上面的示例中,dev.properties
可以重新定义spring.application.name
到不同的值。
无论声明多少次,导入都只会导入一次。 在 properties/yaml 文件中的单个文档中定义导入的顺序无关紧要。 例如,下面的两个示例产生相同的结果:
spring.config.import=my.properties
my.property=value
spring:
config:
import: "my.properties"
my:
property: "value"
my.property=value
spring.config.import=my.properties
my:
property: "value"
spring:
config:
import: "my.properties"
在上述两个示例中,来自my.properties
文件将优先于触发其导入的文件。
可以在单个spring.config.import
钥匙。
位置将按照定义的顺序进行处理,以后导入优先。
在适当的情况下,还会考虑导入特定于配置文件的变体。
上面的示例将导入my.properties 以及任何my-<profile>.properties 变种。 |
Spring Boot 包括可插拔的 API,允许支持各种不同的位置地址。 默认情况下,您可以导入 Java 属性、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
如上所述。
对于第二种情况,您需要使用configtree:
前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。
例如,假设 Kubernetes 已挂载以下卷:
etc/ config/ myapp/ username password
的内容username
file 将是一个配置值,而password
将是一个秘密。
要导入这些属性,您可以将以下内容添加到application.properties
或application.yaml
文件:
spring.config.import=optional:configtree:/etc/config/
spring:
config:
import: "optional:configtree:/etc/config/"
然后,您可以访问或注入myapp.username
和myapp.password
属性Environment
以通常的方式。
配置树下的文件夹和文件的名称构成属性名称。
在上面的示例中,要访问属性username 和password ,您可以设置spring.config.import 自optional:configtree:/etc/config/myapp . |
带有点表示法的文件名也会正确映射。
例如,在上面的示例中,名为myapp.username 在/etc/config 将导致myapp.username 属性中的Environment . |
配置树值可以绑定到两个字符串String 和byte[] 类型取决于预期的内容。 |
如果要从同一父文件夹导入多个配置树,则可以使用通配符快捷方式。
任何configtree:
location 结尾为 将所有直接子项导入为配置树。
与非通配符导入一样,每个配置树下的文件夹和文件的名称构成属性名称。/*/
例如,给定以下卷:
etc/ config/ dbconfig/ db/ username password mqconfig/ mq/ username password
您可以使用configtree:/etc/config/*/
作为导入位置:
spring.config.import=optional:configtree:/etc/config/*/
spring:
config:
import: "optional:configtree:/etc/config/*/"
这将添加db.username
,db.password
,mq.username
和mq.password
性能。
使用通配符加载的目录按字母顺序排序。 如果您需要不同的订单,则应将每个位置列为单独的导入 |
配置树也可用于 Docker 密钥。
当 Docker swarm 服务被授予对机密的访问权限时,该机密将装载到容器中。
例如,如果名为db.password
安装在位置/run/secrets/
,您可以制作db.password
使用以下命令提供给 Spring 环境:
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
2.3.7. 属性占位符
中的值application.properties
和application.yaml
通过现有的Environment
使用它们时,您可以返回以前定义的值(例如,从系统属性或环境变量)。
标准${name}
属性占位符语法可以在值内的任何位置使用。
属性占位符还可以使用:
将默认值与属性名称分开,例如${name:default}
.
以下示例显示了带默认值和不带默认值的占位符的使用:
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
app:
name: "MyApp"
description: "${app.name} is a Spring Boot application written by ${username:Unknown}"
假设username
属性未在其他地方设置,app.description
将具有价值MyApp is a Spring Boot application written by Unknown
.
您应该始终使用占位符中的规范形式(仅使用小写字母的烤肉串大小写)来引用占位符中的属性名称。
这将允许 Spring Boot 使用与松绑定时相同的逻辑 例如 |
您还可以使用此技术创建现有 Spring Boot 属性的“短”变体。 有关详细信息,请参阅howto.html作方法。 |
2.3.8. 使用多文档文件
Spring Boot 允许您将单个物理文件拆分为多个逻辑文档,每个文档都是独立添加的。 文档按从上到下的顺序处理。 较后的文档可以覆盖较早的文档中定义的属性。
为application.yaml
文件中,使用标准的 YAML 多文档语法。
三个连续的连字符表示一个文档的结束,以及下一个文档的开始。
例如,以下文件有两个逻辑文档:
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
为application.properties
提交特殊或#---
!---
comment 用于标记文档拆分:
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
属性文件分隔符不得有任何前导空格,并且必须恰好具有三个连字符。 分隔符前后的行不得是相同的注释前缀。 |
多文档属性文件通常与激活属性(例如spring.config.activate.on-profile .
有关详细信息,请参阅下一节。 |
无法使用@PropertySource 或@TestPropertySource 附注。 |
2.3.9. 激活属性
有时,仅在满足某些条件时激活一组给定的属性很有用。 例如,您可能具有仅在特定配置文件处于活动状态时才相关的属性。
您可以使用spring.config.activate.*
.
以下激活属性可用:
属性 | 注意 |
---|---|
|
必须匹配才能使文档处于活动状态的配置文件表达式。 |
|
这 |
例如,以下内容指定第二个文档仅在 Kubernetes 上运行时处于活动状态,并且仅当“prod”或“staging”配置文件处于活动状态时才处于活动状态:
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set"
2.4. 加密属性
Spring Boot 不提供任何对加密属性值的内置支持,但是,它确实提供了修改 Spring 中包含的值所需的钩子点Environment
.
这EnvironmentPostProcessor
接口允许您作Environment
在应用程序启动之前。
有关详细信息,请参阅howto.html。
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目支持在 HashiCorp Vault 中存储外部化配置。
2.5. 使用 YAML
YAML 是 JSON 的超集,因此是指定分层配置数据的便捷格式。
这SpringApplication
每当你的类路径上有 SnakeYAML 库时,class 就会自动支持 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 类。
有关更多详细信息,请参阅下面的“类型安全配置属性”部分。 |
无法使用@PropertySource 或@TestPropertySource 附注。
因此,如果您需要以这种方式加载值,则需要使用属性文件。 |
2.6. 配置随机值
这RandomValuePropertySource
对于注入随机值(例如,注入机密或测试用例)很有用。
它可以生成整数、long、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 管理和验证应用程序的配置。
另请参阅差异@Value 和类型安全配置属性. |
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 中可用的类是公共 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
value 设置为缺失属性的目标类型。
参考前面的例子,如果没有属性绑定到Security
这MyProperties
实例将包含一个null
值security
.
要使其包含Security
即使没有属性绑定到它(使用 Kotlin 时,这也需要username
和password
参数Security
声明为可为 null,因为它们没有默认值),请使用空的@DefaultValue
注解:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
@DefaultValue val security: Security) {
class Security(val username: String?, val password: String?,
@param:DefaultValue("USER") val roles: List<String>)
}
要使用构造函数绑定,必须使用@EnableConfigurationProperties 或配置属性扫描。
您不能将构造函数绑定与由常规 Spring 机制创建的 bean(例如@Component beans,使用@Bean 方法或使用@Import )
|
如果你的类有多个构造函数,你也可以使用@ConstructorBinding 直接在应该绑定的构造函数上。 |
使用java.util.Optional 跟@ConfigurationProperties 不推荐,因为它主要用作返回类型。
因此,它不太适合配置属性注入。
为了与其他类型的属性保持一致,如果您确实声明了Optional 属性,它没有价值,null 而不是空的Optional 将被绑定。 |
2.8.3. 启用@ConfigurationProperties注解的类型
Spring Boot 提供了绑定的基础设施@ConfigurationProperties
类型并将它们注册为 bean。
您可以逐类启用配置属性,也可以启用与组件扫描类似的配置属性扫描。
有时,用@ConfigurationProperties
可能不适合扫描,例如,如果您正在开发自己的自动配置或想要有条件地启用它们。
在这些情况下,请使用@EnableConfigurationProperties
注解。
这可以在任何@Configuration
类,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration
@ConfigurationProperties("some.properties")
public class SomeProperties {
}
@ConfigurationProperties("some.properties")
class SomeProperties
要使用配置属性扫描,请将@ConfigurationPropertiesScan
注释到您的应用程序。
通常,它被添加到标注为@SpringBootApplication
但它可以添加到任何@Configuration
类。
默认情况下,将从声明注释的类的包进行扫描。
如果要定义要扫描的特定包,可以按照以下示例所示进行作:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication
当 假设它在 |
我们建议@ConfigurationProperties
只处理环境,特别是不从上下文中注入其他 bean。
对于极端情况,可以使用setter注入或任何*Aware
框架提供的接口(例如EnvironmentAware
如果您需要访问Environment
).
如果您仍然想使用构造函数注入其他 bean,则必须使用@Component
并使用基于 JavaBean 的属性绑定。
2.8.4. 使用@ConfigurationProperties注释类型
这种配置风格特别适用于SpringApplication
外部 YAML 配置,如以下示例所示:
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"
与@ConfigurationProperties
bean,您可以像任何其他 bean 一样注入它们,如以下示例所示:
@Service
public class MyService {
private final MyProperties properties;
public MyService(MyProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
@Service
class MyService(val properties: MyProperties) {
fun openConnection() {
val server = Server(properties.remoteAddress)
server.start()
// ...
}
// ...
}
用@ConfigurationProperties 还允许您生成元数据文件,IDE 可以使用这些文件为您自己的键提供自动完成功能。
有关详细信息,请参阅附录。 |
2.8.5. 第三方配置
以及使用@ConfigurationProperties
要注释类,您也可以在公共上使用它@Bean
方法。
当您想要将属性绑定到您无法控制的第三方组件时,这样做可能特别有用。
要从Environment
属性, 添加@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()
}
使用another
前缀映射到该AnotherComponent
bean 的方式与前面类似SomeProperties
例。
2.8.6. 松绑
Spring Boot 使用一些宽松的绑定规则Environment
属性设置为@ConfigurationProperties
bean,因此不需要Environment
属性名称和 Bean 属性名称。
这很有用的常见示例包括破折号分隔的环境属性(例如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 ). |
属性来源 | 简单 | 列表 |
---|---|---|
属性文件 |
驼峰式大小写、烤肉串大小写或下划线表示法 |
使用或逗号分隔值的标准列表语法 |
YAML 文件 |
驼峰式大小写、烤肉串大小写或下划线表示法 |
标准 YAML 列表语法或逗号分隔值 |
环境变量 |
大写格式,下划线作为分隔符(请参阅从环境变量绑定)。 |
用下划线括起来的数值(请参阅从环境变量绑定) |
系统属性 |
驼峰式大小写、烤肉串大小写或下划线表示法 |
使用或逗号分隔值的标准列表语法 |
我们建议,如果可能,将属性存储为小写烤肉串格式,例如my.person.first-name=Rod . |
绑定贴图
绑定到Map
属性,您可能需要使用特殊的括号表示法,以便原始的key
价值被保留。
如果键不被 、任何非字母数字字符包围,或[]
-
.
被删除。
例如,考虑将以下属性绑定到Map<String,String>
:
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3
my:
map:
"[/key1]": "value1"
"[/key2]": "value2"
"/key3": "value3"
对于 YAML 文件,需要用引号括起来才能正确解析键。 |
上述属性将绑定到Map
跟/key1
,/key2
和key3
作为地图中的按键。
斜杠已从key3
因为它没有被方括号包围。
绑定到标量值时,键.
在它们中不需要被 包围。
标量值包括枚举和[]
java.lang
包,除了Object
.
捆绑a.b=c
自Map<String, String>
将保留.
并返回带有条目的 Map{"a.b"="c"}
.
对于任何其他类型,如果您key
包含一个.
.
例如,绑定a.b=c
自Map<String, Object>
将返回一个带有条目的地图{"a"={"b"="c"}}
而[a.b]=c
将返回一个带有条目的地图{"a.b"="c"}
.
从环境变量绑定
大多数作系统对可用于环境变量的名称施加了严格的规则。
例如,Linux shell 变量只能包含字母 (a
自z
或A
自Z
)、数字 (0
自9
) 或下划线字符 ()。
按照惯例,Unix shell 变量的名称也将采用大写。_
Spring Boot 的宽松绑定规则尽可能地设计为与这些命名限制兼容。
要将规范表单中的属性名称转换为环境变量名称,您可以遵循以下规则:
-
替换点 (
.
) 带有下划线 ()。_
-
删除所有破折号 ()。
-
-
转换为大写。
例如,配置属性spring.main.log-startup-info
将是一个名为SPRING_MAIN_LOGSTARTUPINFO
.
绑定到对象列表时也可以使用环境变量。
要绑定到List
,则变量名称中的元素编号应用下划线括起来。
例如,配置属性my.service[0].other
将使用名为MY_SERVICE_0_OTHER
.
2.8.7. 合并复杂类型
当列表配置在多个位置时,覆盖的工作原理是替换整个列表。
例如,假设MyPojo
对象替换为name
和description
属性null
默认情况下。
以下示例公开了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
在多个配置文件中指定,则使用优先级最高的配置文件(并且仅使用该配置文件)。
请考虑以下示例:
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>
从MyProperties
:
@ConfigurationProperties("my")
public class MyProperties {
private final Map<String, MyPojo> map = new LinkedHashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
@ConfigurationProperties("my")
class MyProperties {
val map: Map<String, MyPojo> = LinkedHashMap()
}
请考虑以下配置:
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2
my:
map:
key1:
name: "my name 1"
description: "my description 1"
---
spring:
config:
activate:
on-profile: "dev"
my:
map:
key1:
name: "dev name 1"
key2:
name: "dev name 2"
description: "dev description 2"
如果dev
配置文件未激活,MyProperties.map
包含一个带有键的条目key1
(名称为my name 1
以及my description 1
).
如果dev
配置文件,但是,map
包含两个带有键的条目key1
(名称为dev name 1
以及my description 1
) 和key2
(名称为dev name 2
以及dev description 2
).
上述合并规则适用于所有属性源中的属性,而不仅仅是文件。 |
2.8.8. 属性转换
Spring Boot 在绑定到@ConfigurationProperties
豆。
如果您需要自定义类型转换,您可以提供ConversionService
bean(一个名为conversionService
)或自定义属性编辑器(通过CustomEditorConfigurer
bean)或自定义Converters
(bean 定义注释为@ConfigurationPropertiesBinding
).
由于此 bean 是在应用程序生命周期的早期请求的,因此请确保限制您的ConversionService 正在使用。通常,您需要的任何依赖项在创建时可能不会完全初始化。您可能需要重命名您的自定义ConversionService 如果配置密钥强制不需要它,并且仅依赖于符合@ConfigurationPropertiesBinding . |
转换持续时间
Spring Boot 专门支持表达持续时间。如果您公开java.time.Duration
属性,则应用程序属性中的以下格式可用:
-
一个常客
long
表示(使用毫秒作为默认单位,除非@DurationUnit
已指定) -
标准 ISO-8601 格式使用者
java.time.Duration
-
一种更易读的格式,其中值和单位耦合 (
10s
表示 10 秒)
请考虑以下示例:
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
}
@ConfigurationProperties("my")
class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
var sessionTimeout = Duration.ofSeconds(30)
var readTimeout = Duration.ofMillis(1000)
}
要将会话超时指定为 30 秒,30
,PT30S
和30s
都是等效的。可以以以下任何形式指定 500 毫秒的读取超时: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
已指定) -
标准 ISO-8601 格式使用者
java.time.Period
-
一种更简单的格式,其中值和单位对耦合(
1y3d
表示 1 年零 3 天)
简单格式支持以下单位:
-
y
多年来 -
m
几个月 -
w
数周 -
d
几天
这java.time.Period type 实际上从未存储周数,它是一个快捷方式,意思是“7 天”。 |
转换数据大小
Spring Framework 有一个DataSize
以字节为单位表示大小的值类型。
如果将DataSize
属性,则应用程序属性中的以下格式可用:
-
一个常客
long
表示(使用字节作为默认单位,除非@DataSizeUnit
已指定) -
一种更易读的格式,其中值和单位耦合 (
10MB
表示 10 兆字节)
请考虑以下示例:
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
}
@ConfigurationProperties("my")
class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
var bufferSize = DataSize.ofMegabytes(2)
var sizeThreshold = DataSize.ofBytes(512)
}
要指定 10 兆字节的缓冲区大小,10
和10MB
是等价的。
256 字节的大小阈值可以指定为256
或256B
.
您还可以使用任何受支持的单元。这些是:
-
B
对于字节 -
KB
对于千字节 -
MB
对于兆字节 -
GB
千兆字节 -
TB
对于 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验证
Spring Boot 尝试验证@ConfigurationProperties
类,每当它们使用 Spring 的@Validated
注解。
您可以使用 JSR-303javax.validation
约束注释。
为此,请确保您的类路径上有一个合规的 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
}
}
您还可以添加自定义 SpringValidator
通过创建一个名为configurationPropertiesValidator
.
这@Bean
方法应声明static
.
配置属性验证器是在应用程序生命周期的早期创建的,并声明@Bean
方法作为 static 可以创建 bean,而无需实例化@Configuration
类。
这样做可以避免早期实例化可能导致的任何问题。
这spring-boot-actuator 模块包含一个端点,该端点将所有@ConfigurationProperties 豆。
将 Web 浏览器指向/actuator/configprops 或使用等效的 JMX 端点。
有关详细信息,请参阅“生产就绪功能”部分。 |
2.8.10. @ConfigurationProperties与@Value
这@Value
注释是核心容器功能,它不提供与类型安全配置属性相同的功能。
下表汇总了@ConfigurationProperties
和@Value
:
特征 | @ConfigurationProperties |
@Value |
---|---|---|
是的 |
有限(见下面的注释) |
|
是的 |
不 |
|
|
不 |
是的 |
如果您确实想使用 例如 |
如果您为自己的组件定义了一组配置键,我们建议您将它们分组到一个 POJO 中,并用@ConfigurationProperties
.
这样做将为您提供结构化的、类型安全的对象,您可以将其注入到您自己的 bean 中。
SpEL
在分析这些文件并填充环境时,不会处理应用程序属性文件中的表达式。
但是,可以编写一个SpEL
表达式@Value
.
如果应用程序属性文件中的属性值是SpEL
expression,则通过@Value
.
3. 个人资料
Spring Profiles 提供了一种分离应用程序配置部分并使其仅在某些环境中可用的方法。
任何@Component
,@Configuration
或@ConfigurationProperties
可以标记为@Profile
以限制加载时间,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
@Configuration(proxyBeanMethods = false)
@Profile("production")
class ProductionConfiguration {
// ...
}
如果@ConfigurationProperties beans 通过@EnableConfigurationProperties 而不是自动扫描,@Profile 注释需要在@Configuration 具有@EnableConfigurationProperties 注解。
在以下情况下@ConfigurationProperties 被扫描,@Profile 可以在@ConfigurationProperties 类本身。 |
您可以使用spring.profiles.active
Environment
属性来指定哪些配置文件处于活动状态。
您可以通过本章前面所述的任何方式指定属性。
例如,您可以将其包含在application.properties
,如以下示例所示:
spring.profiles.active=dev,hsqldb
spring:
profiles:
active: "dev,hsqldb"
您也可以使用以下开关在命令行上指定它:--spring.profiles.active=dev,hsqldb
.
如果没有处于活动状态的配置文件,则启用默认配置文件。
默认配置文件的名称为default
并且可以使用spring.profiles.default
Environment
属性,如以下示例所示:
spring.profiles.default=none
spring:
profiles:
default: "none"
spring.profiles.active
和spring.profiles.default
只能用于非配置文件特定文档。
这意味着它们不能包含在配置文件特定文件或由spring.config.activate.on-profile
.
例如,第二个文档配置无效:
# this document is valid
spring.profiles.active=prod
#---
# this document is invalid
spring.config.activate.on-profile=prod
spring.profiles.active=metrics
# this document is valid
spring:
profiles:
active: "prod"
---
# this document is invalid
spring:
config:
activate:
on-profile: "prod"
profiles:
active: "metrics"
3.1. 添加活动配置文件
这spring.profiles.active
属性遵循与其他属性相同的排序规则:最高的PropertySource
获胜。
这意味着您可以在application.properties
,然后使用命令行开关替换它们。
有时,将属性添加到活动配置文件而不是替换它们会很有用。
这spring.profiles.include
属性可用于在激活的配置文件之上添加活动配置文件spring.profiles.active
财产。
这SpringApplication
入口点还有一个用于设置其他配置文件的 Java API。
请参阅setAdditionalProfiles()
方法。
例如,当运行具有以下属性的应用程序时,即使它使用 --spring.profiles.active 开关运行,也会激活公共和本地配置文件:
spring.profiles.include[0]=common
spring.profiles.include[1]=local
spring:
profiles:
include:
- "common"
- "local"
似spring.profiles.active ,spring.profiles.include 只能用于非配置文件特定文档。
这意味着它不能包含在配置文件特定文件或由spring.config.activate.on-profile . |
如果给定的配置文件处于活动状态,则下一节中将介绍的配置文件组也可用于添加活动配置文件。
3.2. 配置文件组
有时,您在应用程序中定义和使用的配置文件过于细粒度,使用起来很麻烦。
例如,您可能有proddb
和prodmq
用于独立启用数据库和消息传递功能的配置文件。
为了帮助解决这个问题,Spring Boot 允许您定义配置文件组。 配置文件组允许您为相关的配置文件组定义逻辑名称。
例如,我们可以创建一个production
由我们的proddb
和prodmq
配置 文件。
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
spring:
profiles:
group:
production:
- "proddb"
- "prodmq"
我们的应用程序现在可以使用--spring.profiles.active=production
激活production
,proddb
和prodmq
一次点击配置文件。
3.3. 以编程方式设置配置文件
您可以通过调用SpringApplication.setAdditionalProfiles(…)
在应用程序运行之前。
也可以使用 Spring 的ConfigurableEnvironment
接口。
3.4. 特定于配置文件的配置文件
两者的配置文件特定变体application.properties
(或application.yaml
)和通过@ConfigurationProperties
被视为文件并加载。
有关详细信息,请参阅“配置文件特定文件”。
4. 日志记录
Spring Boot 将 Commons Logging 用于所有内部日志记录,但使底层日志实现保持打开状态。 为 Java Util Logging、Log4J2 和 Logback 提供了默认配置。 在每种情况下,记录器都预先配置为使用控制台输出,也提供可选的文件输出。
默认情况下,如果您使用“Starters”,则使用 Logback 进行日志记录。 还包括适当的 Logback 路由,以确保使用 Java Util Logging、Commons Logging、Log4J 或 SLF4J 的依赖库都能正常工作。
有很多可用于 Java 的日志框架。 如果上面的列表看起来令人困惑,请不要担心。 通常,您不需要更改日志记录依赖项,并且 Spring Boot 默认值工作正常。 |
将应用程序部署到 Servlet 容器或应用程序服务器时,使用 Java Util Logging API 执行的日志记录不会路由到应用程序的日志中。 这可以防止容器或已部署到它的其他应用程序执行的日志记录出现在应用程序的日志中。 |
4.1. 日志格式
Spring Boot 的默认日志输出类似于以下示例:
2023-11-23 07:23:18.861 INFO 28351 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 1.8.0_392 on myhost with PID 28351 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-11-23 07:23:18.868 INFO 28351 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-11-23 07:23:20.194 INFO 28351 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-11-23 07:23:20.209 INFO 28351 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-11-23 07:23:20.209 INFO 28351 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.83] 2023-11-23 07:23:20.342 INFO 28351 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-11-23 07:23:20.342 INFO 28351 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1404 ms 2023-11-23 07:23:20.946 INFO 28351 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-11-23 07:23:20.962 INFO 28351 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.7 seconds (JVM running for 3.157)
输出以下项目:
-
日期和时间:毫秒级精度,易于排序。
-
日志级别:
ERROR
,WARN
,INFO
,DEBUG
或TRACE
. -
进程 ID。
-
用于区分实际日志消息的开头的分隔符。
---
-
线程名称:用方括号括起来(控制台输出可能会被截断)。
-
记录器名称:这通常是源类名称(通常缩写)。
-
日志消息。
Logback 没有FATAL 水平。
它映射到ERROR . |
4.2. 控制台输出
默认日志配置在写入消息时将消息回显到控制台。
默认情况下,ERROR
-水平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
或logging.file.path
属性(例如,在application.properties
).
下表显示了如何logging.*
属性可以一起使用:
logging.file.name |
logging.file.path |
示例 | 描述 |
---|---|---|---|
(无) |
(无) |
仅控制台日志记录。 |
|
特定文件 |
(无) |
|
写入指定的日志文件。 名称可以是确切的位置,也可以是相对于当前目录的名称。 |
(无) |
特定目录 |
|
写 |
日志文件在达到 10 MB 时轮换,与控制台输出一样,ERROR
-水平WARN
-level 和INFO
默认情况下,记录 -level 消息。
日志记录属性独立于实际日志记录基础结构。
因此,特定的配置键(例如logback.configurationFile 对于 Logback),不是由 spring Boot 管理。 |
4.4. 文件轮换
如果您使用的是 Logback,则可以使用application.properties
或application.yaml
文件。
对于所有其他日志系统,您需要直接自己配置轮换设置(例如,如果您使用 Log4J2,那么您可以添加一个log4j2.xml
或log4j2-spring.xml
文件)。
支持以下轮换策略属性:
名称 | 描述 |
---|---|
|
用于创建日志存档的文件名模式。 |
|
如果应用程序启动时应进行日志存档清理。 |
|
存档前日志文件的最大大小。 |
|
日志存档在被删除之前可以占用的最大大小。 |
|
要保留的最大存档日志文件数(默认为 7)。 |
4.5. 日志级别
所有受支持的日志记录系统都可以在 Spring 中设置记录器级别Environment
(例如,在application.properties
) 使用logging.level.<logger-name>=<level>
哪里level
是 TRACE、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 包括以下预定义的日志记录组,这些日志记录组可以开箱即用:
名称 | Logging |
---|---|
Web |
|
SQL的 |
|
4.7. 使用日志关闭钩子
为了在应用程序终止时释放日志资源,提供了一个关闭钩子,该钩子将在 JVM 退出时触发日志系统清理。
除非您的应用程序部署为 war 文件,否则此关闭挂钩会自动注册。
如果您的应用程序具有复杂的上下文层次结构,则关闭挂钩可能无法满足您的需求。
如果没有,请禁用关闭挂钩并调查底层日志记录系统直接提供的选项。
例如,Logback 提供了上下文选择器,允许在自己的上下文中创建每个 Logger。
您可以使用logging.register-shutdown-hook
属性来禁用关闭钩子。
将其设置为false
将禁用注册。
您可以在application.properties
或application.yaml
文件:
logging.register-shutdown-hook=false
logging:
register-shutdown-hook: false
4.8. 自定义日志配置
可以通过在类路径上包含适当的库来激活各种日志记录系统,并且可以通过在类路径的根目录或下一个 Spring 指定的位置提供合适的配置文件来进一步自定义Environment
财产:logging.config
.
您可以使用org.springframework.boot.logging.LoggingSystem
系统属性。
该值应是LoggingSystem
实现。
您还可以使用none
.
由于日志记录是在ApplicationContext 创建时,无法控制日志记录@PropertySources 在Spring@Configuration 文件。
更改日志记录系统或完全禁用它的唯一方法是通过系统属性。 |
根据日志记录系统,将加载以下文件:
测井系统 | 定制 |
---|---|
返回 |
|
日志4j2 |
|
JDK(Java Util 日志记录) |
|
如果可能,我们建议您使用-spring 变体(例如,logback-spring.xml 而不是logback.xml ).
如果使用标准配置位置,Spring 无法完全控制日志初始化。 |
Java Util Logging 存在已知的类加载问题,这些问题会导致从“可执行 jar”运行时出现问题。 如果可能的话,我们建议您在从“可执行 jar”运行时避免使用它。 |
为了帮助自定义,一些其他属性是从 Spring 转移的Environment
到系统属性。
这允许通过日志记录系统配置来使用属性。例如,将logging.file.name
在application.properties
或LOGGING_FILE_NAME
作为环境变量将导致LOG_FILE
系统属性。
下表介绍了转移的属性:
弹簧环境 | 系统属性 | 评论 |
---|---|---|
|
|
记录异常时使用的转换字。 |
|
|
如果已定义,则在默认日志配置中使用。 |
|
|
如果已定义,则在默认日志配置中使用。 |
|
|
要在控制台 (stdout) 上使用的日志模式。 |
|
|
日志日期格式的附加器模式。 |
|
|
用于控制台日志记录的字符集。 |
|
|
要在文件中使用的日志模式(如果 |
|
|
用于文件日志记录的字符集(如果 |
|
|
渲染日志级别时要使用的格式(默认 |
|
|
当前进程 ID(如果可能,则发现,并且尚未定义为 OS 环境变量)。 |
如果您使用 Logback,则还会传输以下属性:
弹簧环境 | 系统属性 | 评论 |
---|---|---|
|
|
滚动日志文件名的模式(默认 |
|
|
是否在启动时清理存档日志文件。 |
|
|
最大日志文件大小。 |
|
|
要保留的日志备份的总大小。 |
|
|
要保留的最大存档日志文件数。 |
所有受支持的日志记录系统都可以在解析其配置文件时查阅系统属性。
请参阅中的默认配置spring-boot.jar
例如:
如果要在日志记录属性中使用占位符,则应使用 Spring Boot 的语法,而不是底层框架的语法。
值得注意的是,如果您使用 Logback,您应该使用 |
您可以通过仅覆盖 2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.Controller Handling authenticated request |
4.9. Logback 扩展
Spring Boot 包括许多 Logback 扩展,可以帮助进行高级配置。
您可以在logback-spring.xml
配置文件。
因为标准logback.xml 配置文件加载过早,您无法在其中使用扩展。
您需要使用logback-spring.xml 或定义logging.config 财产。 |
这些扩展不能与 Logback 的配置扫描一起使用。 如果尝试这样做,对配置文件进行更改会导致类似于记录以下内容之一的错误: |
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]] ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
4.9.1. 特定于配置文件的配置
这<springProfile>
标签允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。
配置文件部分在<configuration>
元素。
使用name
属性来指定哪个配置文件接受配置。
这<springProfile>
标记可以包含配置文件名称(例如staging
) 或配置文件表达式。
配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west)
.
查看参考指南了解更多详细信息。
以下列表显示了三个示例配置文件:
<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 的标准类似<property>
标记。
但是,与其指定直接value
时,您可以指定source
属性的(从Environment
).
如果您需要将房产存储在其他地方,而不是local
范围,您可以使用scope
属性。
如果您需要一个回退值(如果该属性未在Environment
),您可以使用defaultValue
属性。
以下示例显示了如何公开属性以在 Logback 中使用:
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<remoteHost>${fluentHost}</remoteHost>
...
</appender>
这source 必须在烤肉串大小写中指定(例如my.property-name ).
但是,可以将属性添加到Environment 通过使用宽松的规则。 |
5. 国际化
Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户。
默认情况下,Spring Boot 会查找messages
资源包。
当已配置资源包的默认属性文件可用 (messages.properties 默认情况下)。
如果资源包仅包含特定于语言的属性文件,则需要添加默认值。
如果未找到与任何已配置的基本名称匹配的属性文件,则不会有自动配置MessageSource . |
资源包的基本名称以及其他几个属性可以使用spring.messages
命名空间,如以下示例所示:
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false
spring:
messages:
basename: "messages,config.i18n.messages"
fallback-to-system-locale: false
spring.messages.basename 支持以逗号分隔的位置列表,可以是包限定符,也可以是从类路径根解析的资源。 |
看MessageSourceProperties
以获取更多支持的选项。
6. 面向方面编程
Spring Boot 为面向方面编程 (AOP) 提供自动配置。 您可以在 Spring Framework 参考文档中了解有关 Spring 的 AOP 的更多信息。
默认情况下,Spring Boot 的自动配置将 Spring AOP 配置为使用 CGLib 代理。
要改用 JDK 代理,请将configprop:spring.aop.proxy-target-class
自false
.
如果 AspectJ 在类路径上,Spring Boot 的自动配置将自动启用 AspectJ 自动代理,以便@EnableAspectJAutoProxy
不是必需的。
7. JSON
Spring Boot 提供与三个 JSON 映射库的集成:
-
格森
-
Jackson
-
JSON-B
Jackson 是首选和默认库。
7.1. Jackson
提供了 Jackson 的自动配置,Jackson 是spring-boot-starter-json
.
当Jackson在班级路径上时,ObjectMapper
bean 是自动配置的。
提供了多个配置属性自定义ObjectMapper
.
7.1.1. 自定义序列化器和反序列化器
如果您使用 Jackson 序列化和反序列化 JSON 数据,您可能需要编写自己的 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)
}
}
}
8. 任务执行和调度
在没有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
例如)。
线程池默认使用一个线程,可以使用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 和TaskSchedulerBuilder
如果需要创建自定义执行器或调度程序,则在上下文中提供 bean。
9. 测试
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
.
9.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。
我们通常发现这些常用库在编写测试时很有用。 如果这些库不符合您的需求,您可以添加自己的其他测试依赖项。
9.2. 测试 Spring 应用程序
依赖注入的主要优点之一是它应该使您的代码更容易进行单元测试。
您可以使用new
运算符,甚至不涉及 Spring。
您还可以使用模拟对象而不是实际依赖项。
通常,您需要超越单元测试并开始集成测试(使用 SpringApplicationContext
).
无需部署应用程序或需要连接到其他基础结构即可执行集成测试非常有用。
Spring Framework 包括一个用于此类集成测试的专用测试模块。
您可以直接向org.springframework:spring-test
或使用spring-boot-starter-test
“Starter”以传递方式将其拉入。
如果您尚未使用spring-test
模块之前,您应该首先阅读 Spring Framework 参考文档的相关部分。
9.3. 测试 Spring Boot 应用程序
Spring Boot 应用程序是 SpringApplicationContext
,因此除了您通常使用普通 Spring 上下文所做的作之外,无需做任何非常特别的事情来测试它。
默认情况下,Spring Boot 的外部属性、日志记录和其他功能才会安装在上下文中,前提是使用SpringApplication 创建它。 |
Spring Boot 提供了一个@SpringBootTest
注释,可用作标准的替代方案spring-test
@ContextConfiguration
当您需要 Spring Boot 功能时,注释。
注释的工作原理是创建ApplicationContext
通过SpringApplication
.
除了@SpringBootTest
还提供了许多其他注释,用于测试应用程序的更具体切片。
如果您使用的是 JUnit 4,请不要忘记同时添加@RunWith(SpringRunner.class) 否则将忽略注释。
如果您使用的是 JUnit 5,则无需添加等效的@ExtendWith(SpringExtension.class) 如@SpringBootTest 和另一个@…Test 注释已经用它进行了注释。 |
默认情况下,@SpringBootTest
不会启动服务器。
您可以使用webEnvironment
属性@SpringBootTest
要进一步优化测试的运行方式,请执行以下作:
-
MOCK
(默认) :加载网页ApplicationContext
并提供模拟 Web 环境。 使用此注释时,不会启动嵌入式服务器。 如果您的类路径上没有 Web 环境,则此模式会透明地回退到创建常规的非 WebApplicationContext
. 它可以与@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
用于对 Web 应用程序进行基于模拟的测试。 -
RANDOM_PORT
:加载一个WebServerApplicationContext
并提供真实的 Web 环境。 嵌入式服务器在随机端口上启动并侦听。 -
DEFINED_PORT
:加载一个WebServerApplicationContext
并提供真实的 Web 环境。 嵌入式服务器启动并在定义的端口上监听(从application.properties
) 或默认端口8080
. -
NONE
:加载ApplicationContext
通过使用SpringApplication
但不提供任何 Web 环境(模拟或其他)。
如果您的测试是@Transactional ,默认情况下,它会在每个测试方法的末尾回滚事务。
但是,由于将这种安排与RANDOM_PORT 或DEFINED_PORT 隐式地提供了一个真正的 servlet 环境,HTTP 客户端和服务器在单独的线程中运行,因此在单独的事务中运行。
在这种情况下,在服务器上启动的任何事务都不会回滚。 |
@SpringBootTest 跟webEnvironment = WebEnvironment.RANDOM_PORT 如果应用程序为管理服务器使用不同的端口,则还将在单独的随机端口上启动管理服务器。 |
9.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 {
// ...
}
9.3.2. 检测测试配置
如果您熟悉 Spring Test Framework,您可能习惯于@ContextConfiguration(classes=…)
以指定哪个弹簧@Configuration
加载。或者,您可能经常使用 nested@Configuration
类。
在测试 Spring Boot 应用程序时,通常不需要这样做。
Spring Boot 的@*Test
注释会在未显式定义主配置时自动搜索主配置。
搜索算法从包含测试的包开始工作,直到找到一个用@SpringBootApplication
或@SpringBootConfiguration
.
只要您以合理的方式构建代码,通常会找到您的主要配置。
如果使用测试注释来测试应用程序的更具体切片,则应避免添加特定于主方法的应用程序类上特定区域的配置设置。 的底层组件扫描配置 |
如果要自定义主配置,可以使用嵌套的@TestConfiguration
类。
与嵌套的@Configuration
类,它将用于代替应用程序的主要配置,一个嵌套的@TestConfiguration
class 是除应用程序的主要配置之外使用的。
Spring 的测试框架在测试之间缓存应用程序上下文。 因此,只要您的测试共享相同的配置(无论它是如何发现的),加载上下文的潜在耗时过程只发生一次。 |
9.3.3. 排除测试配置
如果您的应用程序使用组件扫描(例如,如果您使用@SpringBootApplication
或@ComponentScan
),您可能会发现仅为特定测试创建的顶级配置类在任何地方都被意外地选中。
正如我们之前所看到的,@TestConfiguration
可用于测试的内部类,以自定义主配置。@TestConfiguration
也可以在顶级类上使用。这样做表示不应通过扫描来选取该类。
然后,您可以在需要的地方显式导入类,如以下示例所示:
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {
@Test
fun exampleTest() {
// ...
}
}
如果您直接使用@ComponentScan (也就是说,不是通过@SpringBootApplication ) 您需要注册TypeExcludeFilter 有了它。
有关详细信息,请参阅 Javadoc。 |
一个导入的@TestConfiguration 比内部类更早处理@TestConfiguration 和导入的@TestConfiguration 将在通过组件扫描找到任何配置之前进行处理。
一般来说,这种排序差异没有明显的影响,但如果您依赖 bean 覆盖,则需要注意这一点。 |
9.3.4. 使用应用程序参数
如果您的应用程序需要参数,您可以
有@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")
}
}
9.3.5. 使用模拟环境进行测试
默认情况下,@SpringBootTest
不启动服务器,而是设置一个模拟环境来测试 Web 端点。
使用 Spring MVC,我们可以使用MockMvc
或WebTestClient
,如以下示例所示:
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
fun testWithMockMvc(@Autowired mvc: MockMvc) {
mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
如果您只想关注 Web 图层而不开始完整的ApplicationContext 考虑用@WebMvcTest 相反. |
使用 Spring WebFlux 端点,您可以使用WebTestClient
如以下示例所示:
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
在模拟环境中进行测试通常比使用完整的 servlet 容器运行更快。 但是,由于模拟发生在 Spring MVC 层,因此无法直接使用 MockMvc 测试依赖于较低级别 servlet 容器行为的代码。 例如,Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。 这意味着,虽然您可以按预期测试 MVC 层引发和处理异常,但无法直接测试是否呈现了特定的自定义错误页。 如果需要测试这些较低级别的问题,可以启动完全运行的服务器,如下一节所述。 |
9.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 还可以与模拟环境一起使用,无需运行服务器,只需使用@AutoConfigureWebTestClient . |
此设置需要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")
}
}
9.3.7. 自定义WebTestClient
要自定义WebTestClient
bean,配置一个WebTestClientBuilderCustomizer
豆。
任何此类 bean 都使用WebTestClient.Builder
用于创建WebTestClient
.
9.3.8. 使用 JMX
由于测试上下文框架缓存上下文,因此默认情况下禁用 JMX 以防止相同的组件在同一域上注册。
如果此类测试需要访问MBeanServer
,也可以考虑将其标记为脏:
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {
@Test
fun exampleTest() {
assertThat(mBeanServer.domains).contains("java.lang")
// ...
}
}
9.3.10. 模拟和间谍 Beans
运行测试时,有时需要模拟应用程序上下文中的某些组件。 例如,您可能在某些远程服务上有一个在开发过程中不可用的外观。 当您想要模拟在真实环境中可能难以触发的故障时,模拟也很有用。
Spring Boot 包括一个@MockBean
注释,可用于为 bean 中的 bean 定义 Mockito 模拟ApplicationContext
.
您可以使用注释添加新 Bean 或替换单个现有 Bean 定义。
注释可以直接用于测试类、测试中的字段或@Configuration
类和字段。
在字段上使用时,还会注入创建的模拟的实例。
模拟 bean 在每个测试方法后自动重置。
如果你的测试使用 Spring Boot 的测试注解之一(例如 Java
Kotlin
|
以下示例将现有的RemoteService
bean 与模拟实现:
@SpringBootTest
class MyTests {
@Autowired
private Reverser reverser;
@MockBean
private RemoteService remoteService;
@Test
void exampleTest() {
given(this.remoteService.getValue()).willReturn("spring");
String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}
@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {
@Test
fun exampleTest() {
given(remoteService.value).willReturn("spring")
val reverse = reverser.reverseValue // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps")
}
}
@MockBean 不能用于模拟在应用程序上下文刷新期间执行的 Bean 的行为。
执行测试时,应用程序上下文刷新已完成,配置模拟行为为时已晚。
我们建议使用@Bean 在这种情况下创建和配置模拟的方法。 |
此外,您可以使用@SpyBean
用 Mockito 包装任何现有的 beanspy
.
有关完整详细信息,请参阅 Javadoc。
CGLib 代理(例如为作用域 Bean 创建的代理)将代理方法声明为final .
这会阻止 Mockito 正常运行,因为它无法模拟或监视final 方法。
如果你想模拟或监视这样的 bean,请配置 Mockito 以使用其内联模拟生成器,方法是将org.mockito:mockito-inline 到应用程序的测试依赖项。
这允许 Mockito 嘲笑和监视final 方法。 |
虽然 Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但使用@MockBean 或@SpyBean 影响缓存键,这很可能会增加上下文的数量。 |
如果您正在使用@SpyBean 用@Cacheable 按名称引用参数的方法,则应用程序必须使用-parameters .
这确保了一旦 bean 被监视,参数名称可供缓存基础设施使用。 |
当您使用@SpyBean 要监视由 Spring 代理的 bean,在某些情况下,您可能需要删除 Spring 的代理,例如在使用given 或when .
用AopTestUtils.getTargetObject(yourProxiedSpy) 这样做。 |
9.3.11. 自动配置的测试
Spring Boot 的自动配置系统适用于应用程序,但有时对于测试来说可能有点太多了。 仅加载测试应用程序的“切片”所需的配置部分通常会有所帮助。 例如,您可能想要测试 Spring MVC 控制器是否正确映射 URL,并且您不想在这些测试中涉及数据库调用,或者您可能想要测试 JPA 实体,并且在这些测试运行时您对 Web 层不感兴趣。
这spring-boot-test-autoconfigure
模块包含许多注释,可用于自动配置此类“切片”。
它们中的每一个都以类似的方式工作,提供一个@…Test
加载ApplicationContext
以及一个或多个@AutoConfigure…
可用于自定义自动配置设置的注释。
每个切片将组件扫描限制为适当的组件,并加载一组非常有限的自动配置类。
如果您需要排除其中之一,大多数@…Test 注释提供excludeAutoConfiguration 属性。
或者,您可以使用@ImportAutoConfiguration#exclude . |
使用多个@…Test 不支持在一个测试中使用注释。
如果您需要多个“切片”,请选择其中一个@…Test 注释,并包含@AutoConfigure… 手工注释其他“切片”。 |
也可以使用@AutoConfigure… 标注与标准@SpringBootTest 注解。
如果您对“切片”应用程序不感兴趣,但想要一些自动配置的测试 Bean,则可以使用此组合。 |
9.3.12. 自动配置的 JSON 测试
要测试对象 JSON 序列化和反序列化是否按预期工作,您可以使用@JsonTest
注解。@JsonTest
自动配置可用的受支持的 JSON 映射器,它可以是以下库之一:
-
Jackson
ObjectMapper
任何@JsonComponent
豆子和任何JacksonModule
s -
Gson
-
Jsonb
启用的自动配置列表@JsonTest 可以在附录中找到。 |
如果您需要配置自动配置的元素,可以使用@AutoConfigureJsonTesters
注解。
Spring Boot 包括基于 AssertJ 的帮助程序,可与 JSONAssert 和 JsonPath 库配合使用,以检查 JSON 是否按预期显示。
这JacksonTester
,GsonTester
,JsonbTester
和BasicJsonTester
类可以分别用于 Jackson、Gson、Jsonb 和 Strings。
测试类上的任何帮助程序字段都可以是@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
取决于类型。
相反,您可以使用 AssertJ 的satisfies
断言该值与给定条件匹配。
例如,以下示例断言实际数字是接近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))
})
}
9.3.13. 自动配置的 Spring MVC 测试
要测试 Spring MVC 控制器是否按预期工作,请使用@WebMvcTest
注解。@WebMvcTest
自动配置 Spring MVC 基础设施并将扫描的 bean 限制为@Controller
,@ControllerAdvice
,@JsonComponent
,Converter
,GenericConverter
,Filter
,HandlerInterceptor
,WebMvcConfigurer
,WebMvcRegistrations
和HandlerMethodArgumentResolver
.
定期@Component
和@ConfigurationProperties
当@WebMvcTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置设置列表@WebMvcTest 可以在附录中找到。 |
如果您需要注册额外的组件,例如 JacksonModule ,您可以使用以下命令导入其他配置类@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,则自动配置还会提供一个 HtmlUnitWebClient
bean 和/或 SeleniumWebDriver
豆。
以下示例使用 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 定义。 |
这webDriver Spring Boot 创建的范围将替换任何用户定义的同名范围。
如果您定义自己的webDriver scope 你可能会发现它在使用@WebMvcTest . |
如果类路径上有 Spring Security,@WebMvcTest
也会扫描WebSecurityConfigurer
豆。
您可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。
有关如何使用 Spring Security 的MockMvc
可以在此howto.html作方法部分找到支持。
有时编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试。 |
9.3.14. 自动配置的 Spring WebFlux 测试
要测试 Spring WebFlux 控制器是否按预期工作,您可以使用@WebFluxTest
注解。@WebFluxTest
自动配置 Spring WebFlux 基础设施并将扫描的 bean 限制为@Controller
,@ControllerAdvice
,@JsonComponent
,Converter
,GenericConverter
,WebFilter
和WebFluxConfigurer
.
定期@Component
和@ConfigurationProperties
当@WebFluxTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置列表@WebFluxTest 可以在附录中找到。 |
如果您需要注册额外的组件,例如 JacksonModule ,您可以使用以下命令导入其他配置类@Import 在你的测试中。 |
经常@WebFluxTest
仅限于单个控制器,并与@MockBean
注释,为所需的协作者提供模拟实现。
@WebFluxTest
还自动配置WebTestClient
,它提供了一种快速测试 WebFlux 控制器的强大方法,而无需启动完整的 HTTP 服务器。
您还可以自动配置WebTestClient 在非@WebFluxTest (例如@SpringBootTest ),通过用@AutoConfigureWebTestClient .
以下示例显示了同时使用@WebFluxTest 和WebTestClient : |
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Honda Civic");
}
}
@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Honda Civic")
}
}
此设置仅受 WebFlux 应用程序支持,因为使用WebTestClient 在模拟的 Web 应用程序中,目前仅适用于 WebFlux。 |
@WebFluxTest 无法检测通过功能 Web 框架注册的路由。
用于测试RouterFunction bean 中,请考虑导入RouterFunction 使用@Import 或通过使用@SpringBootTest . |
@WebFluxTest 无法检测注册为@Bean 类型SecurityWebFilterChain .
要将其包含在测试中,您需要使用@Import 或通过使用@SpringBootTest . |
有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试。 |
9.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 可帮助您使用@GraphQlTest
注解。@GraphQlTest
自动配置 Spring GraphQL 基础设施,不涉及任何传输或服务器。
这会将扫描的 bean 限制为@Controller
,RuntimeWiringConfigurer
,JsonComponent
,Converter
,GenericConverter
,DataFetcherExceptionResolver
,Instrumentation
和GraphQlSourceBuilderCustomizer
.
定期@Component
和@ConfigurationProperties
当@GraphQlTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置列表@GraphQlTest 可以在附录中找到。 |
如果您需要注册额外的组件,例如 JacksonModule ,您可以使用以下命令导入其他配置类@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!")
}
}
9.3.16. 自动配置的数据 Cassandra 测试
您可以使用@DataCassandraTest
以测试 Cassandra 应用程序。
默认情况下,它配置了一个CassandraTemplate
,扫描@Table
类,并配置 Spring Data Cassandra 存储库。
定期@Component
和@ConfigurationProperties
当@DataCassandraTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关将 Cassandra 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。
启用的自动配置设置列表@DataCassandraTest 可以在附录中找到。 |
以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}
@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)
9.3.17. 自动配置的 Data Couchbase 测试
您可以使用@DataCouchbaseTest
以测试 Couchbase 应用程序。
默认情况下,它配置了一个CouchbaseTemplate
或ReactiveCouchbaseTemplate
,扫描@Document
类,并配置 Spring Data Couchbase 存储库。
定期@Component
和@ConfigurationProperties
当@DataCouchbaseTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关将 Couchbase 与 Spring Boot 一起使用的更多信息,请参阅本章前面的“data.html”。
启用的自动配置设置列表@DataCouchbaseTest 可以在附录中找到。 |
以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:
@DataCouchbaseTest
class MyDataCouchbaseTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {
// ...
}
9.3.18. 自动配置的数据 Elasticsearch 测试
您可以使用@DataElasticsearchTest
以测试 Elasticsearch 应用程序。
默认情况下,它配置一个ElasticsearchRestTemplate
,扫描@Document
类,并配置 Spring Data Elasticsearch 存储库。
定期@Component
和@ConfigurationProperties
当@DataElasticsearchTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关将 Elasticsearch 与 Spring Boot 一起使用的更多信息,请参阅本章前面的“data.html”。
启用的自动配置设置列表@DataElasticsearchTest 可以在附录中找到。 |
以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:
@DataElasticsearchTest
class MyDataElasticsearchTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {
// ...
}
9.3.19. 自动配置的数据 JPA 测试
您可以使用@DataJpaTest
注释来测试 JPA 应用程序。
默认情况下,它会扫描@Entity
类并配置 Spring Data JPA 存储库。
如果类路径上有一个嵌入式数据库可用,它也会配置一个。
默认情况下,通过将spring.jpa.show-sql
属性设置为true
.
可以使用showSql()
属性。
定期@Component
和@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 测试还可以注入一个TestEntityManager
bean,它提供了标准 JPA 的替代方案EntityManager
这是专门为测试而设计的。
TestEntityManager 也可以通过添加@AutoConfigureTestEntityManager .
这样做时,请确保您的测试在事务中运行,例如通过添加@Transactional 在测试类或方法上。 |
一个JdbcTemplate
如果您需要,也可以使用。
以下示例显示了@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 {
// ...
}
9.3.20. 自动配置的 JDBC 测试
@JdbcTest
类似于@DataJpaTest
但适用于只需要DataSource
并且不要使用 Spring Data JDBC。
默认情况下,它配置内存中嵌入式数据库和JdbcTemplate
.
定期@Component
和@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
注释的方式与@DataJpaTest
.
(请参阅“自动配置的数据 JPA 测试”。
9.3.21. 自动配置的数据 JDBC 测试
@DataJdbcTest
类似于@JdbcTest
但适用于使用 Spring Data JDBC 存储库的测试。
默认情况下,它配置一个内存中嵌入式数据库,一个JdbcTemplate
和 Spring Data JDBC 存储库。
只AbstractJdbcConfiguration
当@DataJdbcTest
使用注释,常规@Component
和@ConfigurationProperties
不扫描豆子。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置列表@DataJdbcTest 可以在附录中找到。 |
默认情况下,数据 JDBC 测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。 如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如 JDBC 示例所示。
如果您希望测试针对真实数据库运行,则可以使用@AutoConfigureTestDatabase
注释的方式与@DataJpaTest
.
(请参阅“自动配置的数据 JPA 测试”。
9.3.22. 自动配置的数据 R2DBC 测试
@DataR2dbcTest
类似于@DataJdbcTest
但适用于使用 Spring Data R2DBC 存储库的测试。
默认情况下,它配置一个内存中嵌入式数据库,一个R2dbcEntityTemplate
和 Spring Data R2DBC 存储库。
定期@Component
和@ConfigurationProperties
当@DataR2dbcTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置列表@DataR2dbcTest 可以在附录中找到。 |
默认情况下,数据 R2DBC 测试不是事务性的。
如果您希望测试针对真实数据库运行,则可以使用@AutoConfigureTestDatabase
注释的方式与@DataJpaTest
.
(请参阅“自动配置的数据 JPA 测试”。
9.3.23. 自动配置的 jOOQ 测试
您可以使用@JooqTest
以类似于@JdbcTest
但用于与 jOOQ 相关的测试。
由于 jOOQ 严重依赖与数据库模式相对应的基于 Java 的模式,因此现有的DataSource
被使用。
如果要用内存数据库替换它,可以使用@AutoConfigureTestDatabase
以覆盖这些设置。
(有关将 jOOQ 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。
定期@Component
和@ConfigurationProperties
当@JooqTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置列表@JooqTest 可以在附录中找到。 |
@JooqTest
配置一个DSLContext
.
以下示例显示了@JooqTest
使用中的注释:
@JooqTest
class MyJooqTests {
@Autowired
private DSLContext dslContext;
// ...
}
@JooqTest
class MyJooqTests(@Autowired val dslContext: DSLContext) {
// ...
}
JOOQ 测试是事务性的,默认情况下在每个测试结束时回滚。 如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如 JDBC 示例所示。
9.3.24. 自动配置的数据MongoDB测试
您可以使用@DataMongoTest
测试 MongoDB 应用程序。
默认情况下,它配置一个内存中嵌入的 MongoDB(如果可用),配置一个MongoTemplate
,扫描@Document
类,并配置 Spring Data MongoDB 存储库。
定期@Component
和@ConfigurationProperties
当@DataMongoTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关将MongoDB与Spring Boot一起使用的更多信息,请参阅“data.html”。
启用的自动配置设置列表@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 {
// ...
}
9.3.25. 自动配置数据 Neo4j 测试
您可以使用@DataNeo4jTest
测试 Neo4j 应用程序。
默认情况下,它会扫描@Node
类,并配置 Spring Data Neo4j 存储库。
定期@Component
和@ConfigurationProperties
当@DataNeo4jTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关将 Neo4J 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。
启用的自动配置设置列表@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 如上所述的测试。 |
9.3.26. 自动配置的数据 Redis 测试
您可以使用@DataRedisTest
测试 Redis 应用程序。
默认情况下,它会扫描@RedisHash
类并配置 Spring Data Redis 存储库。
定期@Component
和@ConfigurationProperties
当@DataRedisTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关将 Redis 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。
启用的自动配置设置列表@DataRedisTest 可以在附录中找到。 |
以下示例显示了@DataRedisTest
使用中的注释:
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {
// ...
}
9.3.27. 自动配置的数据 LDAP 测试
您可以使用@DataLdapTest
以测试 LDAP 应用程序。
默认情况下,它配置内存中嵌入式 LDAP(如果可用),配置LdapTemplate
,扫描@Entry
类,并配置 Spring Data LDAP 存储库。
定期@Component
和@ConfigurationProperties
当@DataLdapTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
(有关在 Spring Boot 中使用 LDAP 的更多信息,请参阅“data.html”。
启用的自动配置设置列表@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 {
// ...
}
9.3.28. 自动配置的 REST 客户端
您可以使用@RestClientTest
注释来测试 REST 客户端。
默认情况下,它会自动配置 Jackson、GSON 和 Jsonb 支持,配置一个RestTemplateBuilder
,并添加了对MockRestServiceServer
.
定期@Component
和@ConfigurationProperties
当@RestClientTest
注释。@EnableConfigurationProperties
可用于包含@ConfigurationProperties
豆。
启用的自动配置设置列表@RestClientTest 可以在附录中找到。 |
要测试的特定 bean 应使用value
或components
属性@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")
}
}
9.3.29. 自动配置的 Spring REST Docs 测试
您可以使用@AutoConfigureRestDocs
注释以在测试中使用 Mock MVC、REST Assured 或 WebTestClient 的 Spring REST Docs。
它消除了对 Spring REST Docs 中 JUnit 扩展的需求。
@AutoConfigureRestDocs
可用于覆盖默认输出目录 (target/generated-snippets
如果您使用的是 Maven 或build/generated-snippets
如果您使用的是 Gradle)。
它还可用于配置任何记录的 URI 中显示的主机、方案和端口。
使用模拟 MVC 自动配置的 Spring REST Docs 测试
@AutoConfigureRestDocs
自定义MockMvc
bean 在测试基于 servlet 的 Web 应用程序时使用 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
豆。
自动配置调用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 Docs 测试
@AutoConfigureRestDocs
也可以与WebTestClient
在测试响应式 Web 应用程序时。
您可以使用以下命令来注入它@Autowired
并在测试中使用它,就像您通常使用@WebFluxTest
和 Spring REST Docs,如以下示例所示:
@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 对参数化输出目录的支持,您可以使用WebTestClientBuilderCustomizer
为每个实体交换结果配置一个使用者。以下示例显示了这样的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 Docs 测试
@AutoConfigureRestDocs
使RequestSpecification
bean,预配置为使用 Spring REST Docs,可用于您的测试。您可以使用@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())
}
}
9.3.30. 自动配置的 Spring Web Services 测试
自动配置的 Spring Web Services 客户端测试
您可以使用@WebServiceClientTest
测试使用 Spring Web Services 项目调用 Web 服务的应用程序。默认情况下,它配置了一个模拟WebServiceServer
bean 并自动自定义您的WebServiceTemplateBuilder
. (有关将 Web 服务与 Spring Boot 一起使用的更多信息,请参阅“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 Services 服务器测试
您可以使用@WebServiceServerTest
测试使用 Spring Web Services 项目实现 Web 服务的应用程序。默认情况下,它配置了一个MockWebServiceClient
可用于调用 Web 服务端点的 bean。(有关将 Web 服务与 Spring Boot 一起使用的更多信息,请参阅“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>")))
}
}
9.3.31. 其他自动配置和切片
每个切片提供一个或多个@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 . |
9.3.32. 用户配置和切片
然后,重要的是不要在应用程序的主类中乱扔特定于其特定功能区域的配置设置。
假设您正在使用 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 class 或每个域区域一个类。
后一种方法允许您在其中一个测试中启用它,如有必要,使用@Import 注解。
请参阅此作方法部分,了解有关何时可能想要启用特定@Configuration 用于切片测试的类。 |
测试切片排除@Configuration
类。
例如,对于@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 {
// ...
}
这样做会有效地覆盖默认组件扫描指令,无论您选择的切片如何,都会产生扫描这两个包的副作用。
例如,一个@DataJpaTest
似乎突然扫描了应用程序的组件和用户配置。
同样,将自定义指令移动到单独的类是解决此问题的好方法。
如果这不适合您,您可以创建一个@SpringBootConfiguration 在测试层次结构中的某个位置,以便使用它。
或者,您可以为测试指定源,这会禁用查找默认源的行为。 |
9.3.33. 使用 Spock 测试 Spring Boot 应用程序
Spock 2.x 可用于测试 Spring Boot 应用程序。
为此,请添加对 Spock 的spock-spring
模块添加到应用程序的构建中。spock-spring
将 Spring 的测试框架集成到 Spock 中。
有关更多详细信息,请参阅 Spock 的 Spring 模块的文档。
9.4. 测试实用程序
一些在测试应用程序时通常有用的测试实用程序类被打包为spring-boot
.
9.4.1. ConfigDataApplicationContext初始化器
ConfigDataApplicationContextInitializer
是一个ApplicationContextInitializer
您可以将其应用于测试以加载 Spring Bootapplication.properties
文件。
当不需要@SpringBootTest
,如以下示例所示:
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}
@ContextConfiguration(classes = [Config::class], initializers = [ConfigDataApplicationContextInitializer::class])
class MyConfigFileTests {
// ...
}
用ConfigDataApplicationContextInitializer 单独不提供支持@Value("${…}") 注射。
它唯一的工作是确保application.properties 文件被加载到 Spring 的Environment .
为@Value 支持,您需要额外配置一个PropertySourcesPlaceholderConfigurer 或使用@SpringBootTest ,它会自动为您配置一个。 |
9.4.2. TestProperty值
TestPropertyValues
允许您快速将属性添加到ConfigurableEnvironment
或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")
}
}
9.4.3. 输出捕获
OutputCapture
是 JUnitExtension
可用于捕获System.out
和System.err
输出。
使用添加@ExtendWith(OutputCaptureExtension.class)
并注入CapturedOutput
作为测试类构造函数或测试方法的参数,如下所示:
@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
@ExtendWith(OutputCaptureExtension::class)
class MyOutputCaptureTests {
@Test
fun testName(output: CapturedOutput?) {
println("Hello World!")
assertThat(output).contains("World")
}
}
9.4.4. TestRest模板
TestRestTemplate
是 Spring 的RestTemplate
这在集成测试中很有用。
您可以获得一个普通模板或发送基本 HTTP 身份验证(使用用户名和密码)的模板。
无论哪种情况,模板都是容错的。
这意味着它以测试友好的方式运行,不会在 4xx 和 5xx 错误上抛出异常。
相反,可以通过返回的ResponseEntity
及其状态代码。
Spring Framework 5.0 提供了一个新的WebTestClient 适用于 WebFlux 集成测试以及 WebFlux 和 MVC 端到端测试。
它为断言提供了一个流畅的 API,这与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
或WebEnvironment.DEFINED_PORT
,您可以注入完全配置的TestRestTemplate
并开始使用它。
如有必要,可以通过RestTemplateBuilder
豆。
任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如以下示例所示:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration(proxyBeanMethods = false)
static class RestTemplateBuilderConfiguration {
@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests(@Autowired val template: TestRestTemplate) {
@Test
fun testRequest() {
val headers = template.getForEntity("/example", String::class.java).headers
assertThat(headers.location).hasHost("other.example.com")
}
@TestConfiguration(proxyBeanMethods = false)
internal class RestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(): RestTemplateBuilder {
return RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1))
}
}
}
10. 创建您自己的自动配置
如果您在开发共享库的公司工作,或者如果您从事开源或商业库的工作,则可能需要开发自己的自动配置。 自动配置类可以捆绑在外部 jar 中,并且仍然被 Spring Boot 选取。
自动配置可以与提供自动配置代码以及您将与之一起使用的典型库的“Starters”相关联。 我们首先介绍构建自己的自动配置所需了解的内容,然后继续执行创建自定义Starters所需的典型步骤。
10.1. 了解自动配置的 Bean
实现自动配置的类用@AutoConfiguration
.
此注释本身是用@Configuration
,使自动配置成为标准@Configuration
类。
附加@Conditional
注释用于约束何时应用自动配置。
通常,自动配置类使用@ConditionalOnClass
和@ConditionalOnMissingBean
附注。
这确保了自动配置仅在找到相关类并且您尚未声明自己的类时才适用@Configuration
.
您可以浏览spring-boot-autoconfigure
查看@AutoConfiguration
Spring 提供的类(请参阅META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件)。
10.2. 查找自动配置候选者
Spring Boot 检查是否存在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件。
该文件应列出您的配置类,每行一个类名,如以下示例所示:
com.mycorp.libx.autoconfigure.LibXAutoConfiguration com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以使用该字符向导入文件添加注释。# |
自动配置只能通过在导入文件中命名来加载。
确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。
此外,自动配置类不应启用组件扫描以查找其他组件。
特定@Import 应改用 s。 |
如果您的配置需要按特定顺序应用,您可以使用before
,beforeName
,after
和afterName
属性@AutoConfiguration
注释或专用@AutoConfigureBefore
和@AutoConfigureAfter
附注。
例如,如果您提供特定于 Web 的配置,则可能需要在WebMvcAutoConfiguration
.
如果您想订购某些不应相互直接了解的自动配置,您还可以使用@AutoConfigureOrder
.
该注解与常规@Order
注释,但为自动配置类提供专用顺序。
与标准一样@Configuration
classes,自动配置类的应用顺序仅影响其 bean 的定义顺序。
随后创建这些 Bean 的顺序不受影响,由每个 Bean 的依赖关系和任何@DependsOn
关系。
10.3. 条件注释
您几乎总是希望包含一个或多个@Conditional
自动配置类上的注释。
这@ConditionalOnMissingBean
注释是一个常见的示例,用于允许开发人员在对默认值不满意时覆盖自动配置。
Spring Boot 包括许多@Conditional
可以通过注释在自己的代码中重用的注释@Configuration
班级或个人@Bean
方法。
这些注释包括:
10.3.1. 类条件
这@ConditionalOnClass
和@ConditionalOnMissingClass
注释让@Configuration
根据特定类的存在与否来包含类。
由于注释元数据是使用 ASM 解析的,因此您可以使用value
属性来引用真实的类,即使该类实际上可能不会出现在正在运行的应用程序类路径上。
您还可以使用name
属性,如果您更喜欢使用String
价值。
此机制不以同样的方式应用于@Bean
通常返回类型是条件目标的方法:在方法上的条件应用之前,JVM 将加载类并可能处理方法引用,如果类不存在,这些引用将失败。
为了处理这种情况,请单独的@Configuration
class 可用于隔离条件,如以下示例所示:
@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 因为在这种情况下引用类不会被处理。 |
10.3.2. Bean 条件
这@ConditionalOnBean
和@ConditionalOnMissingBean
注释允许根据特定 Bean 的存在与否包含 Bean。
您可以使用value
属性按类型指定 bean 或name
按名称指定 bean。
这search
属性允许您限制ApplicationContext
搜索 bean 时应考虑的层次结构。
当放置在@Bean
方法,目标类型默认为方法的返回类型,如下例所示:
@AutoConfiguration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
fun someService(): SomeService {
return SomeService()
}
}
在前面的示例中,someService
如果没有类型的 bean,则将创建 beanSomeService
已包含在ApplicationContext
.
您需要非常小心添加 bean 定义的顺序,因为这些条件是根据到目前为止处理的内容进行评估的。
因此,我们建议仅使用@ConditionalOnBean 和@ConditionalOnMissingBean 自动配置类上的注释(因为保证在添加任何用户定义的 Bean 定义后加载这些注释)。 |
@ConditionalOnBean 和@ConditionalOnMissingBean 不阻止@Configuration 类的创建。
在类级别使用这些条件与标记每个包含@Bean 方法,前者阻止注册@Configuration class 作为 bean 的 be。 |
当声明@Bean 方法,在方法的返回类型中提供尽可能多的类型信息。
例如,如果 bean 的具体类实现了一个接口,则 bean 方法的返回类型应该是具体类而不是接口。
在@Bean 在使用 bean 条件时,方法尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息。 |
10.3.3. 属性条件
这@ConditionalOnProperty
注释允许根据 Spring Environment 属性包含配置。
使用prefix
和name
属性来指定应检查的属性。
默认情况下,存在且不等于false
匹配。
您还可以使用havingValue
和matchIfMissing
属性。
10.3.4. 资源条件
这@ConditionalOnResource
注释允许仅在存在特定资源时包含配置。
可以使用通常的 Spring 约定来指定资源,如以下示例所示:file:/home/user/test.dat
.
10.3.5. Web 应用程序条件
这@ConditionalOnWebApplication
和@ConditionalOnNotWebApplication
注释允许根据应用程序是否是 Web 应用程序包含配置。
基于 servlet 的 Web 应用程序是使用 Spring 的任何应用程序WebApplicationContext
,定义一个session
scope,或者具有ConfigurableWebEnvironment
.
响应式 Web 应用程序是使用ReactiveWebApplicationContext
,或者具有ConfigurableReactiveWebEnvironment
.
这@ConditionalOnWarDeployment
和@ConditionalOnNotWarDeployment
注释允许根据应用程序是否是部署到 Servlet 容器的传统 WAR 应用程序来包含配置。
此条件与使用嵌入式 Web 服务器运行的应用程序不匹配。
10.3.6. SpEL表达条件
这@ConditionalOnExpression
注释允许根据 SpEL 表达式的结果包含配置。
在表达式中引用 Bean 将导致该 Bean 在上下文刷新处理中非常早地初始化。 因此,Bean 将不符合后处理条件(例如配置属性绑定),并且其状态可能不完整。 |
10.4. 测试您的自动配置
自动配置可能受到许多因素的影响:用户配置 (@Bean
definition 和Environment
定制)、条件评估(是否存在特定库)等。
具体来说,每个测试都应创建一个定义明确的ApplicationContext
这代表了这些自定义的组合。ApplicationContextRunner
提供了实现这一目标的好方法。
ApplicationContextRunner
通常定义为测试类的一个字段,用于收集基本的通用配置。
以下示例确保MyServiceAutoConfiguration
总是被调用:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
val contextRunner = ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration::class.java))
如果必须定义多个自动配置,则无需对其声明进行排序,因为它们的调用顺序与运行应用程序时完全相同。 |
每个测试都可以使用运行器来表示特定的用例。
例如,下面的示例调用用户配置 (UserConfiguration
) 并检查自动配置是否正确回退。
调用run
提供了一个回调上下文,可以与AssertJ
.
@Test
void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
MyService myCustomService() {
return new MyService("mine");
}
}
@Test
fun defaultServiceBacksOff() {
contextRunner.withUserConfiguration(UserConfiguration::class.java)
.run { context: AssertableApplicationContext ->
assertThat(context).hasSingleBean(MyService::class.java)
assertThat(context).getBean("myCustomService")
.isSameAs(context.getBean(MyService::class.java))
}
}
@Configuration(proxyBeanMethods = false)
internal class UserConfiguration {
@Bean
fun myCustomService(): MyService {
return MyService("mine")
}
}
也可以轻松自定义Environment
,如以下示例所示:
@Test
void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
});
}
@Test
fun serviceNameCanBeConfigured() {
contextRunner.withPropertyValues("user.name=test123").run { context: AssertableApplicationContext ->
assertThat(context).hasSingleBean(MyService::class.java)
assertThat(context.getBean(MyService::class.java).name).isEqualTo("test123")
}
}
运行器还可用于显示ConditionEvaluationReport
.
报告可打印至INFO
或DEBUG
水平。
以下示例演示如何使用ConditionEvaluationReportLoggingListener
在自动配置测试中打印报告。
class MyConditionEvaluationReportingTests {
@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}
class MyConditionEvaluationReportingTests {
@Test
fun autoConfigTest() {
ApplicationContextRunner()
.withInitializer(ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run { context: AssertableApplicationContext? -> }
}
}
10.4.1. 模拟 Web 上下文
如果您需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请使用WebApplicationContextRunner
或ReactiveWebApplicationContextRunner
分别。
10.4.2. 覆盖类路径
还可以测试当特定类和/或包在运行时不存在时会发生什么。
Spring Boot 附带了一个FilteredClassLoader
跑步者可以轻松使用。
在下面的示例中,我们断言如果MyService
不存在,则自动配置已正确禁用:
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
.run((context) -> assertThat(context).doesNotHaveBean("myService"));
}
@Test
fun serviceIsIgnoredIfLibraryIsNotPresent() {
contextRunner.withClassLoader(FilteredClassLoader(MyService::class.java))
.run { context: AssertableApplicationContext? ->
assertThat(context).doesNotHaveBean("myService")
}
}
10.5. 创建您自己的Starters
一个典型的 Spring Boot Starters包含用于自动配置和自定义给定技术的基础设施的代码,我们称之为“acme”。 为了使其易于扩展,可以向环境公开专用命名空间中的许多配置键。 最后,提供了一个“入门”依赖项,以帮助用户尽可能轻松地入门。
具体来说,自定义Starters可以包含以下内容:
-
这
autoconfigure
包含“acme”的自动配置代码的模块。 -
这
starter
模块,该模块为autoconfigure
模块以及“acme”和任何其他通常有用的依赖项。 简而言之,添加Starters应该提供开始使用该库所需的一切。
这种分为两个模块的分离绝没有必要。
如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的事实。
此外,您还可以制作一个Starters来提供有关这些可选依赖项的意见。
同时,其他人只能依靠autoconfigure
模块并制作自己的入门,并提出不同的意见。
如果自动配置相对简单且没有可选功能,那么合并Starters中的两个模块绝对是一种选择。
10.5.1. 命名
您应该确保为您的Starters提供适当的命名空间。
不要以spring-boot
,即使您使用不同的 MavengroupId
.
我们可能会在未来为您自动配置的内容提供官方支持。
根据经验,您应该以Starters命名组合模块。
例如,假设您正在为“acme”创建一个Starters,并将自动配置模块命名为acme-spring-boot
和Startersacme-spring-boot-starter
.
如果只有一个模块将两者组合在一起,请将其命名为acme-spring-boot-starter
.
10.5.2. 配置键
如果您的Starters提供配置键,请为它们使用唯一的命名空间。
特别是,不要在 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))
您应该仅将纯文本与@ConfigurationProperties 字段 Javadoc,因为它们在添加到 JSON 之前不会被处理。 |
以下是我们在内部遵循的一些规则,以确保描述一致:
-
不要以“The”或“A”开头描述。
-
为
boolean
类型,以“Frether”或“Enable”开头描述。 -
对于基于集合的类型,以“逗号分隔列表”开头描述
-
用
java.time.Duration
而不是long
如果默认单位与毫秒不同,则描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。 -
不要在描述中提供默认值,除非必须在运行时确定它。
确保触发元数据生成,以便 IDE 帮助也可用于您的密钥。
您可能需要查看生成的元数据 (META-INF/spring-configuration-metadata.json
) 以确保您的密钥已正确记录。
在兼容的 IDE 中使用自己的Starters也是验证元数据质量的好主意。
10.5.3. “autoconfigure”模块
这autoconfigure
模块包含开始使用库所需的一切。
它还可能包含配置键定义(例如@ConfigurationProperties
)和任何可用于进一步自定义组件初始化方式的回调接口。
您应该将库的依赖项标记为可选,以便可以包含autoconfigure 模块。
如果以这种方式执行此作,则不会提供库,并且默认情况下,Spring Boot 会退后。 |
Spring Boot 使用注释处理器在元数据文件 (META-INF/spring-autoconfigure-metadata.properties
).
如果存在该文件,则用于急切过滤不匹配的自动配置,这将缩短启动时间。
使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
如果您已直接在应用程序中定义了自动配置,请确保将spring-boot-maven-plugin
以防止repackage
目标,将依赖项添加到 fat jar 中:
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
对于 Gradle,依赖项应在annotationProcessor
配置,如以下示例所示:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
10.5.4. 入门模块
发酵剂真的是一个空罐子。 它的唯一目的是提供使用库所需的依赖项。 您可以将其视为对开始所需内容的固执己见。
不要对添加Starters的项目做出假设。 如果您自动配置的库通常需要其他Starters,请同时提及它们。 如果可选依赖项的数量较多,则提供一组适当的默认依赖项可能会很困难,因为您应该避免包含对库的典型使用不需要的依赖项。 换句话说,您不应包含可选的依赖项。
无论哪种方式,您的Starters都必须引用核心 Spring Boot Starters(spring-boot-starter )直接或间接(如果您的Starters依赖于另一个Starters,则无需添加它)。
如果仅使用自定义Starters创建项目,则 Spring Boot 的核心功能将因核心Starters的存在而得到尊重。 |
11. Kotlin 支持
Spring Boot 通过利用其他 Spring 项目(例如 Spring Framework、Spring Data 和 Reactor)中的支持来提供 Kotlin 支持。 有关更多信息,请参阅 Spring Framework Kotlin 支持文档。
开始使用 Spring Boot 和 Kotlin 的最简单方法是遵循这个综合教程。
您可以使用 start.spring.io 创建新的 Kotlin 项目。
请随时加入 Kotlin Slack 的 #spring 频道或使用spring
和kotlin
标签,如果您需要支持。
11.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 类默认是最终的,因此您可能希望配置 kotlin-spring 插件,以便自动打开带 Spring 注释的类,以便可以代理它们。
在 Kotlin 中序列化/反序列化 JSON 数据需要 Jackson 的 Kotlin 模块。 在类路径上找到它时会自动注册。 如果 Jackson 和 Kotlin 存在,但 Jackson Kotlin 模块不存在,则会记录一条警告消息。
如果有人在 start.spring.io 上引导 Kotlin 项目,则默认会提供这些依赖项和插件。 |
11.2. 空安全
Kotlin 的主要功能之一是空安全。
它涉及null
值,而不是将问题推迟到运行时并遇到NullPointerException
.
这有助于消除常见的错误来源,而无需支付包装器的成本,例如Optional
.
Kotlin 还允许使用具有可空值的函数式构造,如本 Kotlin 中空安全综合指南中所述。
尽管 Java 不允许在其类型系统中表达空安全性,但 Spring Framework、Spring Data 和 Reactor 现在通过工具友好的注释提供其 API 的空安全性。 默认情况下,Kotlin 中使用的 Java API 中的类型被识别为放宽空检查的平台类型。Kotlin 对 JSR 305 注解的支持与可空性注解相结合,为 Kotlin 中的相关 Spring API 提供了空安全性。
可以通过添加-Xjsr305
compiler 标志,并带有以下选项:-Xjsr305={strict|warn|ignore}
.
默认行为与-Xjsr305=warn
.
这strict
value 需要在从 Spring API 推断的 Kotlin 类型中考虑 null 安全性,但应该知道 Spring API 可空性声明即使在次要版本之间也可能发生变化,并且将来可能会添加更多检查)。
11.3. Kotlin API
11.3.1. 运行应用程序
Spring Boot 提供了一种惯用的方式来运行应用程序runApplication<MyApplication>(*args)
如以下示例所示:
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
这是SpringApplication.run(MyApplication::class.java, *args)
.
它还允许自定义应用程序,如以下示例所示:
runApplication<MyApplication>(*args) {
setBannerMode(OFF)
}
11.3.2. 扩展
Kotlin 扩展提供了使用附加功能扩展现有类的能力。 Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利。
TestRestTemplate
扩展,类似于 Spring Framework 提供的扩展RestOperations
在 Spring Framework 中,都提供了。
除其他外,扩展可以利用 Kotlin 具体化类型参数。
11.4. 依赖管理
为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。
使用 Maven,可以通过将kotlin.version
提供属性和插件管理kotlin-maven-plugin
.
使用 Gradle,Spring Boot 插件会自动对齐kotlin.version
替换为 Kotlin 插件的版本。
Spring Boot 还通过导入 Kotlin 协程 BOM 来管理协程依赖项的版本。
可以通过设置kotlin-coroutines.version
财产。
org.jetbrains.kotlinx:kotlinx-coroutines-reactor 如果一个引导一个 Kotlin 项目,并且至少有一个对 start.spring.io 的响应式依赖项,则默认情况下会提供依赖项。 |
11.5. @ConfigurationProperties
@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
)
}
11.6. 测试
虽然可以使用 JUnit 4 来测试 Kotlin 代码,但默认情况下提供 JUnit 5,建议使用。
JUnit 5 允许对测试类进行一次实例化,并重用于该类的所有测试。
这使得使用@BeforeAll
和@AfterAll
非静态方法的注释,非常适合 Kotlin。
要模拟 Kotlin 类,建议使用 MockK。
如果您需要MockK
相当于 Mockito 特定的@MockBean
和@SpyBean
附注,您可以使用提供类似的 SpringMockK@MockkBean
和@SpykBean
附注。
11.7. 资源
11.7.2. 示例
-
spring-boot-kotlin-demo:常规 Spring Boot + Spring Data JPA 项目
-
mixit:Spring Boot 2 + WebFlux + 响应式 Spring Data MongoDB
-
spring-kotlin-fullstack:WebFlux Kotlin 全栈示例,前端使用 Kotlin2js 而不是 JavaScript 或 TypeScript
-
spring-petclinic-kotlin:Spring PetClinic 示例应用程序的 Kotlin 版本
-
spring-kotlin-deepdive:从 Boot 1.0 + Java 到 Boot 2.0 + Kotlin 的分步迁移
-
spring-boot-coroutines-demo:协程示例项目
12. 接下来要读什么
如果您想了解有关本节中讨论的任何类的更多信息,请参阅 Spring Boot API 文档,或者您可以直接浏览源代码。 如果您有具体问题,请参阅作方法部分。
如果您对 Spring Boot 的核心功能感到满意,您可以继续阅读有关生产就绪功能的信息。