核心功能

1. 弹簧应用

SpringApplicationclass 提供了一种方便的方法来引导从main()方法。 在许多情况下,您可以委托给静态SpringApplication.run方法,如以下示例所示:spring-doc.cadn.net.cn

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

@SpringBootApplication
public class MyApplication {

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

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication


@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

当应用程序启动时,您应该会看到类似于以下输出的内容:spring-doc.cadn.net.cn

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v3.2.12)

2024-11-21T07:54:31.952Z  INFO 109723 --- [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 17.0.13 with PID 109723 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2024-11-21T07:54:31.973Z  INFO 109723 --- [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2024-11-21T07:54:35.091Z  INFO 109723 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-11-21T07:54:35.118Z  INFO 109723 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-11-21T07:54:35.118Z  INFO 109723 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.33]
2024-11-21T07:54:35.256Z  INFO 109723 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-11-21T07:54:35.260Z  INFO 109723 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3064 ms
2024-11-21T07:54:36.449Z  INFO 109723 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
2024-11-21T07:54:36.470Z  INFO 109723 --- [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 5.778 seconds (process running for 6.606)

默认情况下,INFO将显示日志记录消息,包括一些相关的启动详细信息,例如启动应用程序的用户。如果您需要INFO,您可以设置它,如日志级别中所述。应用程序版本是使用主应用程序类包中的实现版本确定的。可以通过设置spring.main.log-startup-infofalse. 这也将关闭应用程序活动配置文件的日志记录。spring-doc.cadn.net.cn

若要在启动期间添加其他日志记录,可以覆盖logStartupInfo(boolean)SpringApplication.

1.1. 启动失败

如果您的应用程序无法启动,请注册FailureAnalyzers有机会提供专门的错误消息和解决问题的具体作。例如,如果您在端口上启动 Web 应用程序8080并且该端口已在使用中,您应该会看到类似于以下消息的内容:spring-doc.cadn.net.cn

***************************
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属性使DEBUGLoggingorg.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener.spring-doc.cadn.net.cn

例如,如果您使用java -jar,您可以启用debug属性如下:spring-doc.cadn.net.cn

$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug

1.2. 延迟初始化

SpringApplication允许延迟初始化应用程序。启用延迟初始化后,将根据需要创建 Bean,而不是在应用程序启动期间创建 Bean。因此,启用延迟初始化可以减少应用程序启动所需的时间。在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 Bean在收到 HTTP 请求之前不会初始化。spring-doc.cadn.net.cn

延迟初始化的一个缺点是它可能会延迟发现应用程序的问题。如果延迟初始化了配置错误的 Bean,那么在启动期间将不再发生故障,并且只有在初始化 Bean 时问题才会变得明显。还必须注意确保 JVM 有足够的内存来容纳应用程序的所有 Bean,而不仅仅是那些在启动期间初始化的 Bean。由于这些原因,默认情况下不启用延迟初始化,建议在启用延迟初始化之前对 JVM 的堆大小进行微调。spring-doc.cadn.net.cn

可以使用lazyInitialization方法SpringApplicationBuildersetLazyInitialization方法SpringApplication. 或者,可以使用spring.main.lazy-initialization属性,如以下示例所示:spring-doc.cadn.net.cn

性能
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.spring-doc.cadn.net.cn

在您的内部banner.txt文件,您可以使用Environment以及以下任何占位符:spring-doc.cadn.net.cn

表 1.横幅变量
变量 描述

${application.version}spring-doc.cadn.net.cn

应用程序的版本号,如MANIFEST.MF. 例如Implementation-Version: 1.0打印为1.0.spring-doc.cadn.net.cn

${application.formatted-version}spring-doc.cadn.net.cn

应用程序的版本号,如MANIFEST.MF并格式化为显示(用括号括起来,前缀为v). 例如(v1.0).spring-doc.cadn.net.cn

${spring-boot.version}spring-doc.cadn.net.cn

您正在使用的 Spring Boot 版本。 例如3.2.12.spring-doc.cadn.net.cn

${spring-boot.formatted-version}spring-doc.cadn.net.cn

您正在使用的 Spring Boot 版本,格式化为显示(用括号括起来,前缀为v). 例如(v3.2.12).spring-doc.cadn.net.cn

${Ansi.NAME}(或${AnsiColor.NAME},${AnsiBackground.NAME},${AnsiStyle.NAME})spring-doc.cadn.net.cn

哪里NAME是 ANSI 转义码的名称。 看AnsiPropertySource了解详情。spring-doc.cadn.net.cn

${application.title}spring-doc.cadn.net.cn

您的应用程序的标题,如MANIFEST.MF. 例如Implementation-Title: MyApp打印为MyApp.spring-doc.cadn.net.cn

SpringApplication.setBanner(…​)方法,如果要以编程方式生成横幅,则可以使用。 使用org.springframework.boot.Banner接口并实现您自己的printBanner()方法。

您还可以使用spring.main.banner-mode属性来确定是否必须在System.out (console),发送到配置的记录器 (log),或者根本不生产(off).spring-doc.cadn.net.cn

打印的横幅以以下名称注册为单例 bean:springBootBanner.spring-doc.cadn.net.cn

application.title,application.versionapplication.formatted-version属性仅在使用java -jarjava -cp使用 Spring Boot Starters。 如果您运行一个解压的 jar 并以java -cp <classpath> <mainclass>或将应用程序作为本机映像运行。spring-doc.cadn.net.cn

要使用application.属性,使用java -jar或作为未包装的罐子使用java org.springframework.boot.loader.launch.JarLauncher. 这将初始化application.banner 属性,然后再构建类路径并启动您的应用程序。spring-doc.cadn.net.cn

1.4. 自定义 SpringApplication

如果SpringApplication默认值不符合您的喜好,您可以创建一个本地实例并对其进行自定义。 例如,要关闭横幅,您可以编写:spring-doc.cadn.net.cn

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

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

}
Kotlin
import org.springframework.boot.Banner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args) {
        setBannerMode(Banner.Mode.OFF)
    }
}
传递给SpringApplication是 Spring Bean 的配置源。 在大多数情况下,这些都是对@Configuration类,但它们也可以是直接引用@Component类。

也可以配置SpringApplication通过使用application.properties文件。 有关详细信息,请参阅外部化配置spring-doc.cadn.net.cn

有关配置选项的完整列表,请参阅SpringApplicationJavadoc.spring-doc.cadn.net.cn

1.5. Fluent Builder API

如果您需要构建ApplicationContext层次结构(具有父/子关系的多个上下文)或者如果您更喜欢使用“流畅”的构建器 API,则可以使用SpringApplicationBuilder.spring-doc.cadn.net.cn

SpringApplicationBuilder允许您将多个方法调用链接在一起,并包含parentchild允许您创建层次结构的方法,如以下示例所示:spring-doc.cadn.net.cn

Java
new SpringApplicationBuilder().sources(Parent.class)
    .child(Application.class)
    .bannerMode(Banner.Mode.OFF)
    .run(args);
Kotlin
SpringApplicationBuilder()
    .sources(Parent::class.java)
    .child(Application::class.java)
    .bannerMode(Banner.Mode.OFF)
    .run(*args)
创建ApplicationContext等级制度。 例如,Web 组件必须包含在子上下文中,并且相同的Environment用于父上下文和子上下文。 请参阅SpringApplicationBuilderJavadoc了解完整详情。

1.6. 应用程序可用性

部署在平台上时,应用程序可以使用 Kubernetes 探针等基础设施向平台提供有关其可用性的信息。 Spring Boot 包括对常用的“活动”和“就绪”可用性状态的开箱即用支持。 如果您使用的是 Spring Boot 的“执行器”支持,则这些状态将作为运行状况端点组公开。spring-doc.cadn.net.cn

此外,您还可以通过注入ApplicationAvailability接口到你自己的 bean 中。spring-doc.cadn.net.cn

1.6.1. 活体状态

应用程序的“活跃度”状态表明其内部状态是否允许其正常工作,或者如果当前出现故障,则是否允许其自行恢复。损坏的“活跃度”状态意味着应用程序处于无法恢复的状态,基础结构应重新启动应用程序。spring-doc.cadn.net.cn

通常,“活动”状态不应基于外部检查,例如运行状况检查。 如果发生这种情况,出现故障的外部系统(数据库、Web API、外部缓存)将触发大规模重启和跨平台的级联故障。

Spring Boot 应用程序的内部状态主要由 SpringApplicationContext. 如果应用程序上下文已成功启动,Spring Boot 会假定应用程序处于有效状态。 一旦上下文刷新,应用程序就会被视为活动,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件spring-doc.cadn.net.cn

1.6.2. 就绪状态

应用程序的“就绪”状态表明应用程序是否已准备好处理流量。 失败的“就绪”状态告诉平台,它目前不应将流量路由到应用程序。 这通常发生在启动期间,而CommandLineRunnerApplicationRunner正在处理组件,或者如果应用程序认为它太忙而无法进行其他流量,则随时处理组件。spring-doc.cadn.net.cn

调用应用程序和命令行运行器后,应用程序就被视为就绪,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件spring-doc.cadn.net.cn

预期在启动期间运行的任务应由CommandLineRunnerApplicationRunner组件,而不是使用 Spring 组件生命周期回调,例如@PostConstruct.

1.6.3. 管理应用程序可用性状态

应用程序组件可以随时检索当前可用性状态,方法是将ApplicationAvailability接口和调用方法。 更常见的是,应用程序将希望侦听状态更新或更新应用程序的状态。spring-doc.cadn.net.cn

例如,我们可以将应用程序的“就绪”状态导出到一个文件中,以便 Kubernetes“exec Probe”可以查看此文件:spring-doc.cadn.net.cn

Java
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyReadinessStateExporter {

    @EventListener
    public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
        switch (event.getState()) {
            case ACCEPTING_TRAFFIC -> {
                // create file /tmp/healthy
            }
            case REFUSING_TRAFFIC -> {
                // remove file /tmp/healthy
            }
        }
    }

}
Kotlin
import org.springframework.boot.availability.AvailabilityChangeEvent
import org.springframework.boot.availability.ReadinessState
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component

@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 -> {
                // ...
            }
        }
    }

}

当应用程序中断且无法恢复时,我们还可以更新应用程序的状态:spring-doc.cadn.net.cn

Java
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class MyLocalCacheVerifier {

    private final ApplicationEventPublisher eventPublisher;

    public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void checkLocalCache() {
        try {
            // ...
        }
        catch (CacheCompletelyBrokenException ex) {
            AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
        }
    }

}
Kotlin
import org.springframework.boot.availability.AvailabilityChangeEvent
import org.springframework.boot.availability.LivenessState
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component

@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发送一些额外的应用程序事件。spring-doc.cadn.net.cn

有些事件实际上是在ApplicationContext创建,因此您无法将侦听器注册为@Bean. 您可以使用SpringApplication.addListeners(…​)方法或SpringApplicationBuilder.listeners(…​)方法。spring-doc.cadn.net.cn

如果您希望自动注册这些侦听器,无论应用程序的创建方式如何,都可以添加META-INF/spring.factories文件添加到项目中,并使用org.springframework.context.ApplicationListener键,如以下示例所示:spring-doc.cadn.net.cn

org.springframework.context.ApplicationListener=com.example.project.MyListener

应用程序事件在应用程序运行时按以下顺序发送:spring-doc.cadn.net.cn

  1. ApplicationStartingEvent在运行开始时但在任何处理之前发送,侦听器和初始值设定项的注册除外。spring-doc.cadn.net.cn

  2. ApplicationEnvironmentPreparedEventEnvironment在上下文中使用是已知的,但在创建上下文之前。spring-doc.cadn.net.cn

  3. ApplicationContextInitializedEventApplicationContext已准备好,并且已调用 ApplicationContextInitializers,但在加载任何 bean 定义之前。spring-doc.cadn.net.cn

  4. ApplicationPreparedEvent在刷新开始之前但在加载 Bean 定义之后发送。spring-doc.cadn.net.cn

  5. ApplicationStartedEvent在刷新上下文之后,但在调用任何应用程序和命令行运行器之前发送。spring-doc.cadn.net.cn

  6. AvailabilityChangeEvent紧随其后发送LivenessState.CORRECT以指示应用程序被视为活动。spring-doc.cadn.net.cn

  7. ApplicationReadyEvent在调用任何应用程序和命令行运行器后发送。spring-doc.cadn.net.cn

  8. AvailabilityChangeEvent紧随其后发送ReadinessState.ACCEPTING_TRAFFIC以指示应用程序已准备好处理请求。spring-doc.cadn.net.cn

  9. ApplicationFailedEvent如果启动时出现异常,则发送。spring-doc.cadn.net.cn

以上列表仅包括SpringApplicationEvents 绑定到SpringApplication. 除了这些之外,以下事件还发布在之后ApplicationPreparedEvent和之前ApplicationStartedEvent:spring-doc.cadn.net.cn

  • 一个WebServerInitializedEventWebServer准备好了。ServletWebServerInitializedEventReactiveWebServerInitializedEvent分别是 servlet 和 reactive 变体。spring-doc.cadn.net.cn

  • 一个ContextRefreshedEventApplicationContext刷新。spring-doc.cadn.net.cn

您通常不需要使用应用程序事件,但知道它们存在会很方便。 在内部,Spring Boot 使用事件来处理各种任务。
事件侦听器不应运行可能冗长的任务,因为它们默认在同一线程中执行。 请考虑改用应用程序和命令行运行器

应用程序事件是使用 Spring Framework 的事件发布机制发送的。 此机制的一部分可确保在子上下文中发布到侦听器的事件也发布到任何祖先上下文中的侦听器。 因此,如果您的应用程序使用SpringApplication实例,侦听器可能会接收相同类型应用程序事件的多个实例。spring-doc.cadn.net.cn

为了允许您的侦听器区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。 可以通过实现ApplicationContextAware或者,如果侦听器是 bean,则使用@Autowired.spring-doc.cadn.net.cn

1.8. Web 环境

一个SpringApplication尝试创建正确类型的ApplicationContext代表您。 用于确定WebApplicationType如下:spring-doc.cadn.net.cn

这意味着,如果您使用的是 Spring MVC 和新的WebClient来自同一应用程序中的 Spring WebFlux,默认情况下将使用 Spring MVC。 您可以通过调用setWebApplicationType(WebApplicationType).spring-doc.cadn.net.cn

也可以完全控制ApplicationContext调用setApplicationContextFactory(…​).spring-doc.cadn.net.cn

通常希望调用setWebApplicationType(WebApplicationType.NONE)使用时SpringApplication在 JUnit 测试中。

1.9. 访问应用程序参数

如果您需要访问传递给SpringApplication.run(…​),您可以注入一个org.springframework.boot.ApplicationArguments豆。 这ApplicationArguments接口提供对原始String[]参数以及解析的optionnon-option参数,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.util.List;

import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        if (debug) {
            System.out.println(files);
        }
        // if run with "--debug logfile.txt" prints ["logfile.txt"]
    }

}
Kotlin
import org.springframework.boot.ApplicationArguments
import org.springframework.stereotype.Component

@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已启动,您可以实现ApplicationRunnerCommandLineRunner接口。 两个接口的工作方式相同,并提供一个run方法,该方法在SpringApplication.run(…​)完成。spring-doc.cadn.net.cn

此协定非常适合在应用程序启动后但在开始接受流量之前运行的任务。

CommandLineRunner接口以字符串数组的形式提供对应用程序参数的访问,而ApplicationRunner使用ApplicationArguments前面讨论的界面。 以下示例显示了CommandLineRunner使用run方法:spring-doc.cadn.net.cn

Java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        // Do something...
    }

}
Kotlin
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component

@Component
class MyCommandLineRunner : CommandLineRunner {

    override fun run(vararg args: String) {
        // Do something...
    }

}

如果有多个CommandLineRunnerApplicationRunnerbean 定义了必须按特定顺序调用的 bean,您可以额外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。spring-doc.cadn.net.cn

1.11. 应用程序退出

SpringApplication向 JVM 注册一个 shutdown 钩子,以确保ApplicationContext在退出时优雅地关闭。 所有标准的 Spring 生命周期回调(例如DisposableBean接口或@PreDestroy注释)可以使用。spring-doc.cadn.net.cn

此外,bean 可以实现org.springframework.boot.ExitCodeGenerator接口,如果他们希望在以下情况下返回特定的退出代码SpringApplication.exit()被称为。 然后可以将此退出代码传递给System.exit()将其作为状态代码返回,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MyApplication {

    @Bean
    public ExitCodeGenerator exitCodeGenerator() {
        return () -> 42;
    }

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

}
Kotlin
import org.springframework.boot.ExitCodeGenerator
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean

import kotlin.system.exitProcess

@SpringBootApplication
class MyApplication {

    @Bean
    fun exitCodeGenerator() = ExitCodeGenerator { 42 }

}

fun main(args: Array<String>) {
    exitProcess(SpringApplication.exit(
        runApplication<MyApplication>(*args)))
}

此外,ExitCodeGenerator接口可以通过异常实现。 当遇到此类异常时,Spring Boot 返回由实现的getExitCode()方法。spring-doc.cadn.net.cn

如果有多个ExitCodeGenerator,则使用生成的第一个非零退出代码。 要控制生成器的调用顺序,请额外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。spring-doc.cadn.net.cn

1.12. 管理功能

可以通过指定spring.application.admin.enabled财产。 这会公开SpringApplicationAdminMXBean在平台上MBeanServer. 您可以使用此功能远程管理 Spring Boot 应用程序。 此功能对于任何服务包装器实现也很有用。spring-doc.cadn.net.cn

如果您想知道应用程序在哪个 HTTP 端口上运行,请获取键为local.server.port.

1.13. 应用程序启动跟踪

在应用程序启动期间,SpringApplicationApplicationContext执行许多与应用程序生命周期相关的任务, Bean 生命周期甚至处理应用程序事件。 跟ApplicationStartup, Spring 框架允许您使用StartupStep对象. 收集此数据可以用于分析目的,或者只是为了更好地了解应用程序启动过程。spring-doc.cadn.net.cn

您可以选择ApplicationStartup在设置SpringApplication实例。 例如,要使用BufferingApplicationStartup,你可以写:spring-doc.cadn.net.cn

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setApplicationStartup(new BufferingApplicationStartup(2048));
        application.run(args);
    }

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args) {
        applicationStartup = BufferingApplicationStartup(2048)
    }
}

第一个可用的实现FlightRecorderApplicationStartup由 Spring Framework 提供。 它将特定于 Spring 的启动事件添加到 Java Flight Recorder 会话中,用于分析应用程序并将其 Spring 上下文生命周期与 JVM 事件(例如分配、GC、类加载等)相关联。 配置完成后,您可以通过在启用飞行记录器的情况下运行应用程序来记录数据:spring-doc.cadn.net.cn

$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar

Spring Boot 附带了BufferingApplicationStartup变体;此实现旨在缓冲启动步骤并将它们清空到外部指标系统中。 应用程序可以请求类型为BufferingApplicationStartup在任何组件中。spring-doc.cadn.net.cn

Spring Boot 也可以配置为公开startup端点以 JSON 文档的形式提供此信息。spring-doc.cadn.net.cn

1.14. 虚拟线程

如果您在 Java 21 或更高版本上运行,则可以通过设置属性spring.threads.virtual.enabledtrue.spring-doc.cadn.net.cn

在为您的应用程序打开此选项之前,您应该考虑阅读官方的 Java 虚拟线程文档。 在某些情况下,应用程序可能会因为“固定虚拟线程”而遇到较低的吞吐量;本页还介绍了如何使用 JDK Flight Recorder 或jcmdCLI 中。spring-doc.cadn.net.cn

如果启用了虚拟线程,则配置线程池的属性将不再有效。 这是因为虚拟线程是在 JVM 范围的平台线程池上调度的,而不是在专用线程池上调度的。
虚拟线程的一个副作用是它们是守护进程线程。 如果 JVM 的所有线程都是守护进程线程,则 JVM 将退出。 当您依赖@Scheduledbean,以保持应用程序的活动状态。 如果使用虚拟线程,则调度程序线程是虚拟线程,因此是守护进程线程,不会使 JVM 保持活动状态。 这不仅会影响调度,其他技术也会出现这种情况。 为了使 JVM 在所有情况下都保持运行,建议将属性spring.main.keep-alivetrue. 这确保了 JVM 保持活动状态,即使所有线程都是虚拟线程。

2. 外部化配置

Spring Boot 允许您将配置外部化,以便您可以在不同的环境中使用相同的应用程序代码。 您可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。spring-doc.cadn.net.cn

属性值可以通过使用@Value注释,通过 Spring 的Environment抽象,或者通过@ConfigurationProperties.spring-doc.cadn.net.cn

Spring Boot 使用非常特殊的PropertySourceorder 的命令,旨在允许合理地覆盖值。 较高的属性源可以覆盖较早的属性源中定义的值。 按以下顺序考虑来源:spring-doc.cadn.net.cn

  1. 默认属性(通过设置SpringApplication.setDefaultProperties).spring-doc.cadn.net.cn

  2. @PropertySource注释@Configuration类。 请注意,此类属性源不会添加到Environment直到刷新应用程序上下文。 这为时已晚,无法配置某些属性,例如logging.*spring.main.*在刷新开始之前读取。spring-doc.cadn.net.cn

  3. 配置数据(例如application.properties文件)。spring-doc.cadn.net.cn

  4. 一个RandomValuePropertySource仅在random.*.spring-doc.cadn.net.cn

  5. 作系统环境变量。spring-doc.cadn.net.cn

  6. Java 系统属性 (System.getProperties()).spring-doc.cadn.net.cn

  7. JNDI 属性java:comp/env.spring-doc.cadn.net.cn

  8. ServletContextinit 参数。spring-doc.cadn.net.cn

  9. ServletConfiginit 参数。spring-doc.cadn.net.cn

  10. 属性来自SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的内联 JSON)。spring-doc.cadn.net.cn

  11. 命令行参数。spring-doc.cadn.net.cn

  12. properties属性。 适用于@SpringBootTest以及用于测试应用程序特定切片的测试注释spring-doc.cadn.net.cn

  13. @DynamicPropertySource测试中的注释。spring-doc.cadn.net.cn

  14. @TestPropertySource测试上的注释。spring-doc.cadn.net.cn

  15. Devtools 全局设置属性中的$HOME/.config/spring-boot目录。spring-doc.cadn.net.cn

配置文件按以下顺序考虑:spring-doc.cadn.net.cn

  1. 打包在 jar 中的应用程序属性application.properties和 YAML 变体)。spring-doc.cadn.net.cn

  2. 打包在jar中的特定于配置文件的应用程序属性application-{profile}.properties和 YAML 变体)。spring-doc.cadn.net.cn

  3. 打包 jar 外部的应用程序属性application.properties和 YAML 变体)。spring-doc.cadn.net.cn

  4. 打包的 jar 之外的特定于配置文件的应用程序属性application-{profile}.properties和 YAML 变体)。spring-doc.cadn.net.cn

建议对整个应用程序坚持使用一种格式。 如果您有包含两者的配置文件.properties和 YAML 格式在同一位置,.properties优先。
如果使用环境变量而不是系统属性,则大多数作系统不允许使用以句点分隔的键名,但可以改用下划线(例如,SPRING_CONFIG_NAME而不是spring.config.name). 有关详细信息,请参阅从环境变量绑定。
如果您的应用程序在 Servlet 容器或应用程序服务器中运行,那么 JNDI 属性(在java:comp/env) 或 servlet 上下文初始化参数可以代替环境变量或系统属性,或者与环境变量或系统属性一起使用。

为了提供一个具体的例子,假设您开发了一个@Component使用name属性,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component

@Component
class MyBean {

    @Value("\${name}")
    private val name: String? = null

    // ...

}

在您的应用程序类路径上(例如,在 jar 中),您可以有一个application.properties文件,该文件为name. 在新环境中运行时,application.properties可以在 jar 外部提供文件,以覆盖name. 对于一次性测试,您可以使用特定的命令行开关(例如java -jar app.jar --name="Spring").spring-doc.cadn.net.cn

envconfigprops终结点可用于确定属性具有特定值的原因。 可以使用这两个终结点来诊断意外的属性值。 有关详细信息,请参阅“生产就绪功能”部分。

2.1. 访问命令行属性

默认情况下,SpringApplication转换任何命令行选项参数(即以 开头的参数,例如----server.port=9000) 设置为property并将它们添加到 SpringEnvironment. 如前所述,命令行属性始终优先于基于文件的属性源。spring-doc.cadn.net.cn

如果您不希望将命令行属性添加到Environment,您可以使用以下命令禁用它们SpringApplication.setAddCommandLineProperties(false).spring-doc.cadn.net.cn

2.2. JSON应用程序属性

环境变量和系统属性通常具有限制,这意味着某些属性名称无法使用。 为了帮助解决这个问题,Spring Boot 允许您将属性块编码为单个 JSON 结构。spring-doc.cadn.net.cn

当您的应用程序启动时,任何spring.application.jsonSPRING_APPLICATION_JSON属性将被解析并添加到Environment.spring-doc.cadn.net.cn

例如,SPRING_APPLICATION_JSON属性可以在 UN*X shell 的命令行中作为环境变量提供:spring-doc.cadn.net.cn

$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar

在前面的示例中,您最终会遇到my.name=test在SpringEnvironment.spring-doc.cadn.net.cn

也可以将相同的 JSON 作为系统属性提供:spring-doc.cadn.net.cn

$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar

或者,您可以使用命令行参数提供 JSON:spring-doc.cadn.net.cn

$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'

如果您要部署到经典的 Application Server,您还可以使用名为java:comp/env/spring.application.json.spring-doc.cadn.net.cn

虽然nullJSON 中的值将添加到生成的属性源中,PropertySourcesPropertyResolver对待null属性作为缺失值。 这意味着 JSON 不能使用null价值。

2.3. 外部应用程序属性

Spring Boot 将自动查找并加载application.propertiesapplication.yaml应用程序启动时从以下位置发送的文件:spring-doc.cadn.net.cn

  1. 从类路径spring-doc.cadn.net.cn

    1. 类路径根spring-doc.cadn.net.cn

    2. 类路径/configspring-doc.cadn.net.cn

  2. 从当前目录spring-doc.cadn.net.cn

    1. 当前目录spring-doc.cadn.net.cn

    2. config/子目录spring-doc.cadn.net.cn

    3. 的直接子目录config/子目录spring-doc.cadn.net.cn

该列表按优先级排序(较低项的值覆盖较早的项)。 加载文件中的文档将添加为PropertySources到SpringEnvironment.spring-doc.cadn.net.cn

如果你不喜欢application作为配置文件名,您可以通过指定spring.config.nameenvironment 属性。 例如,要查找myproject.propertiesmyproject.yaml文件,您可以按如下方式运行您的应用程序:spring-doc.cadn.net.cn

$ java -jar myproject.jar --spring.config.name=myproject

您还可以使用spring.config.locationenvironment 属性。 此属性接受一个或多个要检查的位置的逗号分隔列表。spring-doc.cadn.net.cn

以下示例演示如何指定两个不同的文件:spring-doc.cadn.net.cn

$ java -jar myproject.jar --spring.config.location=\
    optional:classpath:/default.properties,\
    optional:classpath:/override.properties
使用前缀optional:如果位置是可选的,并且您不介意它们是否存在。
spring.config.name,spring.config.locationspring.config.additional-location很早就用于确定必须加载哪些文件。 它们必须定义为环境属性(通常是作系统环境变量、系统属性或命令行参数)。

如果spring.config.location包含目录(与文件相反),它们应该以 结尾。 在运行时,它们将附加从/spring.config.name在加载之前。 中指定的文件spring.config.location直接导入。spring-doc.cadn.net.cn

目录和文件位置值也会展开,以检查特定于配置文件的文件。 例如,如果您有spring.config.locationclasspath:myconfig.properties,您还会发现合适的classpath:myconfig-<profile>.properties文件已加载。

在大多数情况下,每个spring.config.location您添加的项将引用单个文件或目录。 位置按定义的顺序进行处理,后面的位置可以覆盖前面位置的值。spring-doc.cadn.net.cn

如果您有复杂的位置设置,并且使用特定于配置文件的配置文件,则可能需要提供进一步的提示,以便 Spring Boot 知道应该如何对它们进行分组。 位置组是在同一级别考虑的所有位置的集合。 例如,您可能希望对所有类路径位置进行分组,然后对所有外部位置进行分组。 位置组中的项目应用 分隔。 有关更多详细信息,请参阅“配置文件特定文件”部分中的示例。;spring-doc.cadn.net.cn

使用spring.config.location替换默认位置。 例如,如果spring.config.location配置为optional:classpath:/custom-config/,optional:file:./custom-config/,考虑的完整位置集为:spring-doc.cadn.net.cn

  1. optional:classpath:custom-config/spring-doc.cadn.net.cn

  2. optional:file:./custom-config/spring-doc.cadn.net.cn

如果您更喜欢添加其他位置而不是替换它们,您可以使用spring.config.additional-location. 从其他位置加载的属性可以覆盖默认位置中的属性。 例如,如果spring.config.additional-location配置为optional:classpath:/custom-config/,optional:file:./custom-config/,考虑的完整位置集为:spring-doc.cadn.net.cn

  1. optional:classpath:/;optional:classpath:/config/spring-doc.cadn.net.cn

  2. optional:file:./;optional:file:./config/;optional:file:./config/*/spring-doc.cadn.net.cn

  3. optional:classpath:custom-config/spring-doc.cadn.net.cn

  4. optional:file:./custom-config/spring-doc.cadn.net.cn

通过此搜索排序,您可以在一个配置文件中指定默认值,然后有选择地覆盖另一个配置文件中的这些值。 您可以在application.properties(或您选择的任何其他基本名称spring.config.name) 在默认位置之一。 然后,可以在运行时使用位于其中一个自定义位置的不同文件覆盖这些默认值。spring-doc.cadn.net.cn

2.3.1. 可选位置

默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出一个ConfigDataLocationNotFoundException并且您的应用程序将不会启动。spring-doc.cadn.net.cn

如果您想指定一个位置,但您不介意它是否总是存在,您可以使用optional:前缀。 您可以将此前缀与spring.config.locationspring.config.additional-location属性,以及spring.config.import声明。spring-doc.cadn.net.cn

例如,一个spring.config.importoptional:file:./myconfig.properties允许您的应用程序启动,即使myconfig.properties文件丢失。spring-doc.cadn.net.cn

如果您想忽略所有ConfigDataLocationNotFoundExceptions并始终继续启动您的应用程序,您可以使用spring.config.on-not-found财产。 将值设置为ignoreSpringApplication.setDefaultProperties(…​)或使用系统/环境变量。spring-doc.cadn.net.cn

2.3.2. 通配符位置

如果配置文件位置包含最后一个路径段的字符,则该位置被视为通配符位置。 加载配置时,通配符会展开,以便也会检查直接子目录。 当配置属性有多个来源时,通配符位置在 Kubernetes 等环境中特别有用。*spring-doc.cadn.net.cn

例如,如果你有一些 Redis 配置和一些 MySQL 配置,你可能希望将这两部分配置分开,同时要求这两部分都存在于application.properties文件。 这可能会导致两个单独的application.properties挂载在不同位置的文件,例如/config/redis/application.properties/config/mysql/application.properties. 在这种情况下,通配符位置为config/*/,将导致两个文件都被处理。spring-doc.cadn.net.cn

默认情况下,Spring Boot 包括config/*/在默认搜索位置中。 这意味着/config将搜索 jar 外部的目录。spring-doc.cadn.net.cn

您可以自己使用通配符位置,并使用spring.config.locationspring.config.additional-location性能。spring-doc.cadn.net.cn

通配符位置必须仅包含一个并以 结尾的搜索位置,即目录或**/*/<filename>用于文件搜索位置。 带有通配符的位置根据文件名的绝对路径按字母顺序排序。
通配符位置仅适用于外部目录。 不能在classpath:位置。

2.3.3. 配置文件特定文件

以及application属性文件,Spring Boot 还将尝试使用命名约定加载特定于配置文件的文件application-{profile}. 例如,如果您的应用程序激活名为prod并使用 YAML 文件,则同时使用application.yamlapplication-prod.yaml将被考虑。spring-doc.cadn.net.cn

特定于配置文件的属性从与标准相同的位置加载application.properties,特定于配置文件的文件始终覆盖非特定文件。 如果指定了多个配置文件,则应用后赢策略。 例如,如果配置文件prod,livespring.profiles.active属性,值application-prod.properties可以被那些application-live.properties.spring-doc.cadn.net.cn

后赢策略适用于位置组级别。 一个spring.config.locationclasspath:/cfg/,classpath:/ext/将不会具有与classpath:/cfg/;classpath:/ext/.spring-doc.cadn.net.cn

例如,继续我们的prod,live上面的示例,我们可能有以下文件:spring-doc.cadn.net.cn

/cfg
  application-live.properties
/ext
  application-live.properties
  application-prod.properties

当我们有一个spring.config.locationclasspath:/cfg/,classpath:/ext/我们处理所有/cfg文件在所有文件之前/ext文件:spring-doc.cadn.net.cn

  1. /cfg/application-live.propertiesspring-doc.cadn.net.cn

  2. /ext/application-prod.propertiesspring-doc.cadn.net.cn

  3. /ext/application-live.propertiesspring-doc.cadn.net.cn

当我们有classpath:/cfg/;classpath:/ext/相反(使用分隔符)我们处理;/cfg/ext在同一级别:spring-doc.cadn.net.cn

  1. /ext/application-prod.propertiesspring-doc.cadn.net.cn

  2. /cfg/application-live.propertiesspring-doc.cadn.net.cn

  3. /ext/application-live.propertiesspring-doc.cadn.net.cn

Environment具有一组默认配置文件(默认情况下,[default]),如果未设置活动配置文件,则使用该配置文件。 换句话说,如果没有显式激活配置文件,则属性application-default被考虑。spring-doc.cadn.net.cn

属性文件仅加载一次。 如果您已经直接导入了配置文件特定的属性文件,则不会再次导入该文件。

2.3.4. 导入附加数据

应用程序属性可以使用spring.config.import财产。 导入在发现时进行处理,并被视为插入紧邻声明导入的文档下方的附加文档。spring-doc.cadn.net.cn

例如,您的类路径中可能有以下内容application.properties文件:spring-doc.cadn.net.cn

性能
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到不同的值。spring-doc.cadn.net.cn

无论声明多少次,导入都只会导入一次。 在 properties/yaml 文件中的单个文档中定义导入的顺序无关紧要。 例如,下面的两个示例产生相同的结果:spring-doc.cadn.net.cn

性能
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-doc.cadn.net.cn

可以在单个spring.config.import钥匙。 位置将按照定义的顺序进行处理,以后导入优先。spring-doc.cadn.net.cn

在适当的情况下,还会考虑导入特定于配置文件的变体。 上面的示例将导入my.properties以及任何my-<profile>.properties变种。

Spring Boot 包括可插拔的 API,允许支持各种不同的位置地址。 默认情况下,您可以导入 Java 属性、YAML 和“配置树”。spring-doc.cadn.net.cn

第三方 jar 可以提供对其他技术的支持(不需要文件是本地的)。 例如,您可以想象配置数据来自外部存储,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。spring-doc.cadn.net.cn

如果您想支持自己的位置,请参阅ConfigDataLocationResolverConfigDataLoaderorg.springframework.boot.context.config包。spring-doc.cadn.net.cn

2.3.5. 导入无扩展名文件

某些云平台无法向卷挂载文件添加文件扩展名。 要导入这些无扩展名文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。 您可以通过将扩展提示放在方括号中来做到这一点。spring-doc.cadn.net.cn

例如,假设您有一个/etc/config/myconfig您希望作为 yaml 导入的文件。 您可以从application.properties使用以下内容:spring-doc.cadn.net.cn

性能
spring.config.import=file:/etc/config/myconfig[.yaml]
亚姆尔
spring:
  config:
    import: "file:/etc/config/myconfig[.yaml]"

2.3.6. 使用配置树

在云平台(如 Kubernetes)上运行应用程序时,通常需要读取平台提供的配置值。 出于此类目的使用环境变量并不少见,但这可能有缺点,特别是如果该值应该保密。spring-doc.cadn.net.cn

作为环境变量的替代方法,许多云平台现在允许您将配置映射到已挂载的数据卷中。 例如,Kubernetes 可以同时卷挂载ConfigMapsSecrets.spring-doc.cadn.net.cn

可以使用两种常见的卷挂载模式:spring-doc.cadn.net.cn

  1. 单个文件包含一组完整的属性(通常写为 YAML)。spring-doc.cadn.net.cn

  2. 多个文件被写入目录树,文件名成为“键”,内容成为“值”。spring-doc.cadn.net.cn

对于第一种情况,您可以使用spring.config.import上所述。 对于第二种情况,您需要使用configtree:前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。spring-doc.cadn.net.cn

例如,假设 Kubernetes 已挂载以下卷:spring-doc.cadn.net.cn

etc/
  config/
    myapp/
      username
      password

的内容usernamefile 将是一个配置值,而password将是一个秘密。spring-doc.cadn.net.cn

要导入这些属性,您可以将以下内容添加到application.propertiesapplication.yaml文件:spring-doc.cadn.net.cn

性能
spring.config.import=optional:configtree:/etc/config/
亚姆尔
spring:
  config:
    import: "optional:configtree:/etc/config/"

然后,您可以访问或注入myapp.usernamemyapp.password属性Environment以通常的方式。spring-doc.cadn.net.cn

配置树下的文件夹和文件的名称构成属性名称。 在上面的示例中,要访问属性usernamepassword,您可以设置spring.config.importoptional:configtree:/etc/config/myapp.
带有点表示法的文件名也会正确映射。 例如,在上面的示例中,名为myapp.username/etc/config将导致myapp.username属性中的Environment.
配置树值可以绑定到两个字符串Stringbyte[]类型取决于预期的内容。

如果要从同一父文件夹导入多个配置树,则可以使用通配符快捷方式。 任何configtree:location 结尾为 将所有直接子项导入为配置树。 与非通配符导入一样,每个配置树下的文件夹和文件的名称构成属性名称。/*/spring-doc.cadn.net.cn

例如,给定以下卷:spring-doc.cadn.net.cn

etc/
  config/
    dbconfig/
      db/
        username
        password
    mqconfig/
      mq/
        username
        password

您可以使用configtree:/etc/config/*/作为导入位置:spring-doc.cadn.net.cn

性能
spring.config.import=optional:configtree:/etc/config/*/
亚姆尔
spring:
  config:
    import: "optional:configtree:/etc/config/*/"

这将添加db.username,db.password,mq.usernamemq.password性能。spring-doc.cadn.net.cn

使用通配符加载的目录按字母顺序排序。 如果您需要不同的订单,则应将每个位置列为单独的导入

配置树也可用于 Docker 密钥。 当 Docker swarm 服务被授予对机密的访问权限时,该机密将装载到容器中。 例如,如果名为db.password安装在位置/run/secrets/,您可以制作db.password使用以下命令提供给 Spring 环境:spring-doc.cadn.net.cn

性能
spring.config.import=optional:configtree:/run/secrets/
亚姆尔
spring:
  config:
    import: "optional:configtree:/run/secrets/"

2.3.7. 属性占位符

中的值application.propertiesapplication.yaml通过现有的Environment使用它们时,您可以返回以前定义的值(例如,从系统属性或环境变量)。 标准${name}属性占位符语法可以在值内的任何位置使用。 属性占位符还可以使用:将默认值与属性名称分开,例如${name:default}.spring-doc.cadn.net.cn

以下示例显示了带默认值和不带默认值的占位符的使用:spring-doc.cadn.net.cn

性能
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-doc.cadn.net.cn

您应该始终使用占位符中的规范形式(仅使用小写字母的烤肉串大小写)来引用占位符中的属性名称。 这将允许 Spring Boot 使用与松绑定时相同的逻辑 @ConfigurationProperties.spring-doc.cadn.net.cn

例如${demo.item-price}会捡起demo.item-pricedemo.itemPrice表格来自application.properties文件,以及DEMO_ITEMPRICE来自系统环境。 如果您使用${demo.itemPrice}相反demo.item-priceDEMO_ITEMPRICE不会被考虑。spring-doc.cadn.net.cn

您还可以使用此技术创建现有 Spring Boot 属性的“短”变体。 有关详细信息,请参阅howto.html作方法。

2.3.8. 使用多文档文件

Spring Boot允许您将单个物理文件拆分为多个逻辑文档,每个逻辑文档都是独立添加的。文档从上到下按顺序处理。以后的文档可以覆盖在早期文档中定义的属性。spring-doc.cadn.net.cn

application.yaml文件,则使用标准的 YAML 多文档语法。三个连续的连字符表示一个文档的结束,以及下一个文档的开始。spring-doc.cadn.net.cn

例如,以下文件有两个逻辑文档:spring-doc.cadn.net.cn

spring:
  application:
    name: "MyApp"
---
spring:
  application:
    name: "MyCloudApp"
  config:
    activate:
      on-cloud-platform: "kubernetes"

application.properties提交特殊或#---!---comment 用于标记文档拆分:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

您可以使用spring.config.activate.*.spring-doc.cadn.net.cn

以下激活属性可用:spring-doc.cadn.net.cn

表 2.激活属性
属性 注意

on-profilespring-doc.cadn.net.cn

必须匹配才能使文档处于活动状态的配置文件表达式。spring-doc.cadn.net.cn

on-cloud-platformspring-doc.cadn.net.cn

CloudPlatform必须检测到该文档才能处于活动状态。spring-doc.cadn.net.cn

例如,以下内容指定第二个文档仅在 Kubernetes 上运行时处于活动状态,并且仅当“prod”或“staging”配置文件处于活动状态时才处于活动状态:spring-doc.cadn.net.cn

性能
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.htmlspring-doc.cadn.net.cn

如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目支持在 HashiCorp Vault 中存储外部化配置。spring-doc.cadn.net.cn

2.5. 使用 YAML

YAML 是 JSON 的超集,因此是指定分层配置数据的便捷格式。 这SpringApplication每当你的类路径上有 SnakeYAML 库时,class 就会自动支持 YAML 作为属性的替代方案。spring-doc.cadn.net.cn

如果您使用“Starters”,则 SnakeYAML 由spring-boot-starter.

2.5.1. 将 YAML 映射到属性

YAML 文档需要从其分层格式转换为可与 Spring 一起使用的平面结构Environment. 例如,考虑以下 YAML 文档:spring-doc.cadn.net.cn

environments:
  dev:
    url: "https://dev.example.com"
    name: "Developer Setup"
  prod:
    url: "https://another.example.com"
    name: "My Cool App"

为了从Environment,它们将被扁平化如下:spring-doc.cadn.net.cn

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:spring-doc.cadn.net.cn

my:
 servers:
 - "dev.example.com"
 - "another.example.com"

前面的示例将转换为以下属性:spring-doc.cadn.net.cn

my.servers[0]=dev.example.com
my.servers[1]=another.example.com
使用[index]表示法可以绑定到 JavaListSet使用 Spring Boot 的Binder类。 有关更多详细信息,请参阅下面的“类型安全配置属性”部分。
无法使用@PropertySource@TestPropertySource附注。 因此,如果您需要以这种方式加载值,则需要使用属性文件。

2.5.2. 直接加载 YAML

Spring Framework 提供了两个方便的类,可用于加载 YAML 文档。 这YamlPropertiesFactoryBean将 YAML 加载为PropertiesYamlMapFactoryBean将 YAML 加载为Map.spring-doc.cadn.net.cn

您还可以使用YamlPropertySourceLoader类,如果要将 YAML 加载为 SpringPropertySource.spring-doc.cadn.net.cn

2.6. 配置随机值

RandomValuePropertySource对于注入随机值(例如,注入机密或测试用例)很有用。 它可以生成整数、long、uuid 或字符串,如以下示例所示:spring-doc.cadn.net.cn

性能
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是最大值(不包括)。spring-doc.cadn.net.cn

2.7. 配置系统环境属性

Spring Boot 支持为环境属性设置前缀。 如果系统环境由具有不同配置要求的多个 Spring Boot 应用程序共享,这将很有用。 系统环境属性的前缀可以直接在SpringApplication.spring-doc.cadn.net.cn

例如,如果将前缀设置为input,属性,例如remote.timeout也将解析为input.remote.timeout在系统环境中。spring-doc.cadn.net.cn

2.8. 类型安全配置属性

使用@Value("${property}")注入配置属性的注释有时可能很麻烦,尤其是在您使用多个属性或数据本质上是分层的时。 Spring Boot 提供了一种使用属性的替代方法,允许强类型 Bean 管理和验证应用程序的配置。spring-doc.cadn.net.cn

2.8.1. JavaBean 属性绑定

可以绑定声明标准 JavaBean 属性的 bean,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my.service")
public class MyProperties {

    private boolean enabled;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    // getters / setters...

    public boolean isEnabled() {
        return this.enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        private String username;

        private String password;

        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

        // getters / setters...

        public String getUsername() {
            return this.username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return this.password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public List<String> getRoles() {
            return this.roles;
        }

        public void setRoles(List<String> roles) {
            this.roles = roles;
        }

    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import java.net.InetAddress

@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 定义了以下属性:spring-doc.cadn.net.cn

映射到@ConfigurationPropertiesSpring Boot 中可用的类是公共 API,但类本身的访问器(getter/setter)并不意味着直接使用。

这种排列依赖于默认的空构造函数,并且 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。 在以下情况下,可以省略 setter:spring-doc.cadn.net.cn

  • 映射,只要它们被初始化,就需要一个 getter,但不一定是 setter,因为它们可以被 binder 改变。spring-doc.cadn.net.cn

  • 可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)来访问集合和数组。 在后一种情况下,二传器是强制性的。 我们建议始终为此类类型添加 setter。 如果初始化集合,请确保它不是不可变的(如前面的示例所示)。spring-doc.cadn.net.cn

  • 如果嵌套的 POJO 属性被初始化(如Security字段),不需要 setter。 如果希望 Binder 使用其默认构造函数动态创建实例,则需要一个 setter。spring-doc.cadn.net.cn

有些人使用 Project Lombok 自动添加 getter 和 setter。 确保 Lombok 不会为此类类型生成任何特定的构造函数,因为它会被容器自动用于实例化对象。spring-doc.cadn.net.cn

最后,仅考虑标准的 Java Bean 属性,不支持绑定静态属性。spring-doc.cadn.net.cn

2.8.2. 构造函数绑定

上一节中的示例可以以不可变的方式重写,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;

@ConfigurationProperties("my.service")
public class MyProperties {

    // fields...

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

    public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }

    // getters...

    public boolean isEnabled() {
        return this.enabled;
    }

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        // fields...

        private final String username;

        private final String password;

        private final List<String> roles;

        public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }

        // getters...

        public String getUsername() {
            return this.username;
        }

        public String getPassword() {
            return this.password;
        }

        public List<String> getRoles() {
            return this.roles;
        }

    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import java.net.InetAddress

@ConfigurationProperties("my.service")
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
        val security: Security) {

    class Security(val username: String, val password: String,
            @param:DefaultValue("USER") val roles: List<String>)

}

在此设置中,单个参数化构造函数的存在意味着应使用构造函数绑定。 这意味着绑定器将找到一个构造函数,其中包含您希望绑定的参数。 如果您的类有多个构造函数,则@ConstructorBinding注释可用于指定要用于构造函数绑定的构造函数。 要选择退出具有单个参数化构造函数的类的构造函数绑定,必须使用@Autowired或制作private. 构造函数绑定可以与记录一起使用。 除非您的记录有多个构造函数,否则无需使用@ConstructorBinding.spring-doc.cadn.net.cn

构造函数绑定类的嵌套成员(例如Security在上面的示例中)也将通过其构造函数进行绑定。spring-doc.cadn.net.cn

可以使用以下命令指定默认值@DefaultValue在构造函数参数和记录组件上。 转换服务将应用于强制注释的Stringvalue 设置为缺失属性的目标类型。spring-doc.cadn.net.cn

参考前面的例子,如果没有属性绑定到SecurityMyProperties实例将包含一个nullsecurity. 要使其包含Security即使没有属性绑定到它(使用 Kotlin 时,这也需要usernamepassword参数Security声明为可为 null,因为它们没有默认值),请使用空的@DefaultValue注解:spring-doc.cadn.net.cn

Java
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
    this.enabled = enabled;
    this.remoteAddress = remoteAddress;
    this.security = security;
}
Kotlin
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(例如@Componentbeans,使用@Bean方法或使用@Import)
要使用构造函数绑定,必须使用-parameters. 如果您使用 Spring Boot 的 Gradle 插件或使用 Maven 和spring-boot-starter-parent.
使用java.util.Optional@ConfigurationProperties不推荐,因为它主要用作返回类型。 因此,它不太适合配置属性注入。 为了与其他类型的属性保持一致,如果您确实声明了Optional属性,它没有价值,null而不是空的Optional将被绑定。
在属性名称中使用保留关键字,例如my.service.import,使用@Name构造函数参数上的注释。

2.8.3. 启用@ConfigurationProperties注解的类型

Spring Boot 提供了绑定的基础设施@ConfigurationProperties类型并将它们注册为 bean。 您可以逐类启用配置属性,也可以启用与组件扫描类似的配置属性扫描。spring-doc.cadn.net.cn

有时,用@ConfigurationProperties可能不适合扫描,例如,如果您正在开发自己的自动配置或想要有条件地启用它们。在这些情况下,请使用@EnableConfigurationProperties注解。 这可以在任何@Configuration类,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {

}
Kotlin
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration
Java
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("some.properties")
public class SomeProperties {

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("some.properties")
class SomeProperties

要使用配置属性扫描,请将@ConfigurationPropertiesScan注解到您的应用程序。通常,它被添加到标注为主应用程序类中@SpringBootApplication但它可以添加到任何@Configuration类。 默认情况下,扫描将从声明注释的类的包中进行。如果要定义要扫描的特定包,可以如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan

@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication

@ConfigurationPropertiesbean 使用配置属性扫描或通过@EnableConfigurationProperties,bean 有一个常规名称:<prefix>-<fqn>哪里<prefix>@ConfigurationPropertiesannotation 和<fqn>是 bean 的完全限定名称。 如果注释未提供任何前缀,则仅使用 Bean 的完全限定名称。spring-doc.cadn.net.cn

假设它在com.example.apppackage,则SomeProperties上面的例子是some.properties-com.example.app.SomeProperties.spring-doc.cadn.net.cn

我们建议@ConfigurationProperties只处理环境,特别是不从上下文中注入其他 bean。 对于极端情况,可以使用setter注入或任何*Aware框架提供的接口(例如EnvironmentAware如果您需要访问Environment). 如果您仍然想使用构造函数注入其他 bean,则必须使用@Component并使用基于 JavaBean 的属性绑定。spring-doc.cadn.net.cn

2.8.4. 使用@ConfigurationProperties注释类型

这种配置风格特别适用于SpringApplication外部 YAML 配置,如以下示例所示:spring-doc.cadn.net.cn

my:
  service:
    remote-address: 192.168.1.1
    security:
      username: "admin"
      roles:
      - "USER"
      - "ADMIN"

@ConfigurationPropertiesbean,您可以像任何其他 bean 一样注入它们,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.stereotype.Service;

@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();
        // ...
    }

    // ...

}
Kotlin
import org.springframework.stereotype.Service

@Service
class MyService(val properties: MyProperties) {

    fun openConnection() {
        val server = Server(properties.remoteAddress)
        server.start()
        // ...
    }

    // ...

}
@ConfigurationProperties还允许您生成元数据文件,IDE 可以使用这些文件为您自己的键提供自动完成功能。 有关详细信息,请参阅附录

2.8.5. 第三方配置

以及使用@ConfigurationProperties要注释类,您也可以在公共上使用它@Bean方法。 当您想要将属性绑定到您无法控制的第三方组件时,这样做可能特别有用。spring-doc.cadn.net.cn

要从Environment属性, 添加@ConfigurationProperties到其 bean 注册,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "another")
    public AnotherComponent anotherComponent() {
        return new AnotherComponent();
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class ThirdPartyConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "another")
    fun anotherComponent(): AnotherComponent = AnotherComponent()

}

使用another前缀映射到该AnotherComponentbean 的方式与前面类似SomeProperties例。spring-doc.cadn.net.cn

2.8.6. 松绑

Spring Boot 使用一些宽松的绑定规则Environment属性设置为@ConfigurationPropertiesbean,因此不需要Environment属性名称和 bean 属性名称。这有用的常见示例包括破折号分隔的环境属性(例如,context-path绑定到contextPath)和大写的环境属性(例如PORT绑定到port).spring-doc.cadn.net.cn

例如,请考虑以下内容@ConfigurationProperties类:spring-doc.cadn.net.cn

Java
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = "my.main-project.person")
class MyPersonProperties {

    var firstName: String? = null

}

使用上述代码,可以使用以下属性名称:spring-doc.cadn.net.cn

表 3.松绑
属性 注意

my.main-project.person.first-namespring-doc.cadn.net.cn

烤肉盒,推荐用于.properties和 YAML 文件。spring-doc.cadn.net.cn

my.main-project.person.firstNamespring-doc.cadn.net.cn

标准驼峰命名法语法。spring-doc.cadn.net.cn

my.main-project.person.first_namespring-doc.cadn.net.cn

下划线表示法,这是用于.properties和 YAML 文件。spring-doc.cadn.net.cn

MY_MAINPROJECT_PERSON_FIRSTNAMEspring-doc.cadn.net.cn

大写格式,使用系统环境变量时建议使用。spring-doc.cadn.net.cn

prefix注释的值必须采用 kebab 大小写(小写并以 分隔,例如-my.main-project.person).
表 4.每个属性源的放宽绑定规则
属性来源 简单 列表

属性文件spring-doc.cadn.net.cn

驼峰式大小写、烤肉串大小写或下划线表示法spring-doc.cadn.net.cn

使用或逗号分隔值的标准列表语法[ ]spring-doc.cadn.net.cn

YAML 文件spring-doc.cadn.net.cn

驼峰式大小写、烤肉串大小写或下划线表示法spring-doc.cadn.net.cn

标准 YAML 列表语法或逗号分隔值spring-doc.cadn.net.cn

环境变量spring-doc.cadn.net.cn

大写格式,下划线作为分隔符(请参阅从环境变量绑定)。spring-doc.cadn.net.cn

用下划线括起来的数值(请参阅从环境变量绑定)spring-doc.cadn.net.cn

系统属性spring-doc.cadn.net.cn

驼峰式大小写、烤肉串大小写或下划线表示法spring-doc.cadn.net.cn

使用或逗号分隔值的标准列表语法[ ]spring-doc.cadn.net.cn

我们建议,如果可能,将属性存储为小写烤肉串格式,例如my.person.first-name=Rod.
绑定贴图

绑定到Map属性,您可能需要使用特殊的括号表示法,以便原始的key价值被保留。 如果键不被 、任何非字母数字字符包围,或[]-.被删除。spring-doc.cadn.net.cn

例如,考虑将以下属性绑定到Map<String,String>:spring-doc.cadn.net.cn

性能
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3
亚姆尔
my:
  map:
    "[/key1]": "value1"
    "[/key2]": "value2"
    "/key3": "value3"
对于 YAML 文件,需要用引号括起来才能正确解析键。

上述属性将绑定到Map/key1,/key2key3作为地图中的按键。 斜杠已从key3因为它没有被方括号包围。spring-doc.cadn.net.cn

绑定到标量值时,键.在它们中不需要被 包围。 标量值包括枚举和[]java.lang包,除了Object. 捆绑a.b=cMap<String, String>将保留.并返回带有条目的 Map{"a.b"="c"}. 对于任何其他类型,如果您key包含一个.. 例如,绑定a.b=cMap<String, Object>将返回一个带有条目的地图{"a"={"b"="c"}}[a.b]=c将返回一个带有条目的地图{"a.b"="c"}.spring-doc.cadn.net.cn

从环境变量绑定

大多数作系统对可用于环境变量的名称施加了严格的规则。 例如,Linux shell 变量只能包含字母 (azAZ)、数字 (09) 或下划线字符 ()。 按照惯例,Unix shell 变量的名称也将采用大写。_spring-doc.cadn.net.cn

Spring Boot 的宽松绑定规则尽可能地设计为与这些命名限制兼容。spring-doc.cadn.net.cn

要将规范表单中的属性名称转换为环境变量名称,您可以遵循以下规则:spring-doc.cadn.net.cn

例如,配置属性spring.main.log-startup-info将是一个名为SPRING_MAIN_LOGSTARTUPINFO.spring-doc.cadn.net.cn

绑定到对象列表时也可以使用环境变量。 要绑定到List,则变量名称中的元素编号应用下划线括起来。 例如,配置属性my.service[0].other将使用名为MY_SERVICE_0_OTHER.spring-doc.cadn.net.cn

对环境变量绑定的支持应用于systemEnvironment属性源,以及名称以-systemEnvironment.spring-doc.cadn.net.cn

从环境变量绑定映射

当 Spring Boot 将环境变量绑定到属性类时,它会在绑定之前将环境变量名称小写。 大多数时候,这个细节并不重要,除非绑定到Map性能。spring-doc.cadn.net.cn

中的键Map始终为小写,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "my.props")
public class MyMapsProperties {

    private final Map<String, String> values = new HashMap<>();

    public Map<String, String> getValues() {
        return this.values;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = "my.props")
class MyMapsProperties {

    val values: Map<String, String> = HashMap()

}

设置MY_PROPS_VALUES_KEY=valuevalues Map包含一个{"key"="value"}进入。spring-doc.cadn.net.cn

只有环境变量名称是小写的,而不是值。 设置MY_PROPS_VALUES_KEY=VALUEvalues Map包含一个{"key"="VALUE"}进入。spring-doc.cadn.net.cn

缓存

宽松绑定使用缓存来提高性能。默认情况下,此缓存仅应用于不可变属性源。 若要自定义此行为,例如,若要为可变属性源启用缓存,请使用ConfigurationPropertyCaching.spring-doc.cadn.net.cn

2.8.7. 合并复杂类型

当列表配置在多个位置时,覆盖的工作原理是替换整个列表。spring-doc.cadn.net.cn

例如,假设MyPojo对象替换为namedescription属性null默认情况下。 以下示例公开了MyPojo对象来自MyProperties:spring-doc.cadn.net.cn

Java
import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my")
public class MyProperties {

    private final List<MyPojo> list = new ArrayList<>();

    public List<MyPojo> getList() {
        return this.list;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("my")
class MyProperties {

    val list: List<MyPojo> = ArrayList()

}

请考虑以下配置:spring-doc.cadn.net.cn

性能
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实例添加到列表中,并且它不会合并项目。spring-doc.cadn.net.cn

List在多个配置文件中指定,则使用优先级最高的配置文件(并且仅使用该配置文件)。请考虑以下示例:spring-doc.cadn.net.cn

性能
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 列表都可用于完全覆盖列表的内容。spring-doc.cadn.net.cn

Map属性,您可以与从多个源提取的属性值绑定。 但是,对于多个源中的同一属性,将使用优先级最高的属性。 以下示例公开了Map<String, MyPojo>MyProperties:spring-doc.cadn.net.cn

Java
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my")
public class MyProperties {

    private final Map<String, MyPojo> map = new LinkedHashMap<>();

    public Map<String, MyPojo> getMap() {
        return this.map;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("my")
class MyProperties {

    val map: Map<String, MyPojo> = LinkedHashMap()

}

请考虑以下配置:spring-doc.cadn.net.cn

性能
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).spring-doc.cadn.net.cn

上述合并规则适用于所有属性源中的属性,而不仅仅是文件。

2.8.8. 属性转换

Spring Boot 在绑定到@ConfigurationProperties豆。 如果您需要自定义类型转换,您可以提供ConversionServicebean(一个名为conversionService)或自定义属性编辑器(通过CustomEditorConfigurerbean)或自定义Converters(bean 定义注释为@ConfigurationPropertiesBinding).spring-doc.cadn.net.cn

由于此 bean 是在应用程序生命周期的早期请求的,因此请确保限制您的ConversionService正在使用。 通常,所需的任何依赖项在创建时可能不会完全初始化。 您可能想要重命名您的自定义ConversionService如果配置密钥强制不需要它,并且仅依赖于符合@ConfigurationPropertiesBinding.
转换持续时间

Spring Boot 专门支持表达持续时间。如果您公开java.time.Duration属性,则应用程序属性中的以下格式可用:spring-doc.cadn.net.cn

请考虑以下示例:spring-doc.cadn.net.cn

Java
import java.time.Duration;
import java.time.temporal.ChronoUnit;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;

@ConfigurationProperties("my")
public class MyProperties {

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);

    private Duration readTimeout = Duration.ofMillis(1000);

    // getters / setters...

    public Duration getSessionTimeout() {
        return this.sessionTimeout;
    }

    public void setSessionTimeout(Duration sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public Duration getReadTimeout() {
        return this.readTimeout;
    }

    public void setReadTimeout(Duration readTimeout) {
        this.readTimeout = readTimeout;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.convert.DurationUnit
import java.time.Duration
import java.time.temporal.ChronoUnit

@ConfigurationProperties("my")
class MyProperties {

    @DurationUnit(ChronoUnit.SECONDS)
    var sessionTimeout = Duration.ofSeconds(30)

    var readTimeout = Duration.ofMillis(1000)

}

要将会话超时指定为 30 秒,30,PT30S30s都是等价的。 500毫秒的读取超时可以用以下任何一种形式指定:500,PT0.5S500ms.spring-doc.cadn.net.cn

您还可以使用任何受支持的单元。 这些都是:spring-doc.cadn.net.cn

默认单位为毫秒,可以使用@DurationUnit如上面的示例所示。spring-doc.cadn.net.cn

如果更喜欢使用构造函数绑定,则可以公开相同的属性,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.time.Duration;
import java.time.temporal.ChronoUnit;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;

@ConfigurationProperties("my")
public class MyProperties {

    // fields...

    private final Duration sessionTimeout;

    private final Duration readTimeout;

    public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
            @DefaultValue("1000ms") Duration readTimeout) {
        this.sessionTimeout = sessionTimeout;
        this.readTimeout = readTimeout;
    }

    // getters...

    public Duration getSessionTimeout() {
        return this.sessionTimeout;
    }

    public Duration getReadTimeout() {
        return this.readTimeout;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.boot.convert.DurationUnit
import java.time.Duration
import java.time.temporal.ChronoUnit

@ConfigurationProperties("my")
class MyProperties(@param:DurationUnit(ChronoUnit.SECONDS) @param:DefaultValue("30s") val sessionTimeout: Duration,
        @param:DefaultValue("1000ms") val readTimeout: Duration)
如果您要升级Long属性,请确保定义单位(使用@DurationUnit),如果不是毫秒。 这样做可以提供透明的升级路径,同时支持更丰富的格式。
转换期间

除了持续时间之外,Spring Boot 还可以使用java.time.Period类型。 应用程序属性中可以使用以下格式:spring-doc.cadn.net.cn

简单格式支持以下单位:spring-doc.cadn.net.cn

java.time.Periodtype 实际上从未存储周数,它是一个快捷方式,意思是“7 天”。
转换数据大小

Spring Framework 有一个DataSize以字节为单位表示大小的值类型。 如果将DataSize属性,则应用程序属性中的以下格式可用:spring-doc.cadn.net.cn

请考虑以下示例:spring-doc.cadn.net.cn

Java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

@ConfigurationProperties("my")
public class MyProperties {

    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize bufferSize = DataSize.ofMegabytes(2);

    private DataSize sizeThreshold = DataSize.ofBytes(512);

    // getters/setters...

    public DataSize getBufferSize() {
        return this.bufferSize;
    }

    public void setBufferSize(DataSize bufferSize) {
        this.bufferSize = bufferSize;
    }

    public DataSize getSizeThreshold() {
        return this.sizeThreshold;
    }

    public void setSizeThreshold(DataSize sizeThreshold) {
        this.sizeThreshold = sizeThreshold;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.convert.DataSizeUnit
import org.springframework.util.unit.DataSize
import org.springframework.util.unit.DataUnit

@ConfigurationProperties("my")
class MyProperties {

    @DataSizeUnit(DataUnit.MEGABYTES)
    var bufferSize = DataSize.ofMegabytes(2)

    var sizeThreshold = DataSize.ofBytes(512)

}

要指定 10 兆字节的缓冲区大小,1010MB是等价的。 256 字节的大小阈值可以指定为256256B.spring-doc.cadn.net.cn

您还可以使用任何受支持的单元。 这些都是:spring-doc.cadn.net.cn

默认单位是字节,可以使用@DataSizeUnit如上面的示例所示。spring-doc.cadn.net.cn

如果更喜欢使用构造函数绑定,则可以公开相同的属性,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

@ConfigurationProperties("my")
public class MyProperties {

    // fields...

    private final DataSize bufferSize;

    private final DataSize sizeThreshold;

    public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
            @DefaultValue("512B") DataSize sizeThreshold) {
        this.bufferSize = bufferSize;
        this.sizeThreshold = sizeThreshold;
    }

    // getters...

    public DataSize getBufferSize() {
        return this.bufferSize;
    }

    public DataSize getSizeThreshold() {
        return this.sizeThreshold;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.boot.convert.DataSizeUnit
import org.springframework.util.unit.DataSize
import org.springframework.util.unit.DataUnit

@ConfigurationProperties("my")
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-303jakarta.validation约束注释。 为此,请确保您的类路径上有一个合规的 JSR-303 实现,然后向字段添加约束注释,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.net.InetAddress;

import jakarta.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    // getters/setters...

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

}
Kotlin
import jakarta.validation.constraints.NotNull
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.validation.annotation.Validated
import java.net.InetAddress

@ConfigurationProperties("my.service")
@Validated
class MyProperties {

    var remoteAddress: @NotNull InetAddress? = null

}
您还可以通过注释@Bean使用@Validated.

为确保始终为嵌套属性触发验证,即使未找到属性,也必须使用@Valid. 以下示例基于前面的内容构建MyProperties例:spring-doc.cadn.net.cn

Java
import java.net.InetAddress;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    @Valid
    private final Security security = new Security();

    // getters/setters...

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        @NotEmpty
        private String username;

        // getters/setters...

        public String getUsername() {
            return this.username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

    }

}
Kotlin
import jakarta.validation.Valid
import jakarta.validation.constraints.NotEmpty
import jakarta.validation.constraints.NotNull
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.validation.annotation.Validated
import java.net.InetAddress

@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-doc.cadn.net.cn

spring-boot-actuator模块包含一个端点,该端点将所有@ConfigurationProperties豆。 将 Web 浏览器指向/actuator/configprops或使用等效的 JMX 端点。 有关详细信息,请参阅“生产就绪功能”部分。

2.8.10. @ConfigurationProperties与@Value

@Value注释是核心容器功能,它不提供与类型安全配置属性相同的功能。 下表汇总了@ConfigurationProperties@Value:spring-doc.cadn.net.cn

特征 @ConfigurationProperties @Value

宽松绑定spring-doc.cadn.net.cn

是的spring-doc.cadn.net.cn

有限(见下面的注释)spring-doc.cadn.net.cn

元数据支持spring-doc.cadn.net.cn

是的spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

SpEL评估spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

是的spring-doc.cadn.net.cn

如果您确实想使用@Value,我们建议您使用规范形式(仅使用小写字母的烤肉串大小写)来引用属性名称。 这将允许 Spring Boot 使用与松绑定时相同的逻辑 @ConfigurationProperties.spring-doc.cadn.net.cn

例如@Value("${demo.item-price}")会捡起demo.item-pricedemo.itemPrice表格来自application.properties文件,以及DEMO_ITEMPRICE来自系统环境。 如果您使用@Value("${demo.itemPrice}")相反demo.item-priceDEMO_ITEMPRICE不会被考虑。spring-doc.cadn.net.cn

如果您为自己的组件定义了一组配置键,我们建议您将它们分组到一个 POJO 中,并用@ConfigurationProperties. 这样做将为您提供结构化的、类型安全的对象,您可以将其注入到您自己的 bean 中。spring-doc.cadn.net.cn

SpEL在分析这些文件并填充环境时,不会处理应用程序属性文件中的表达式。 但是,可以编写一个SpEL表达式@Value. 如果应用程序属性文件中的属性值是SpELexpression,则通过@Value.spring-doc.cadn.net.cn

3. 个人资料

Spring Profiles 提供了一种分离应用程序配置部分并使其仅在某些环境中可用的方法。 任何@Component,@Configuration@ConfigurationProperties可以标记为@Profile以限制加载时间,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {

    // ...

}
Kotlin
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

@Configuration(proxyBeanMethods = false)
@Profile("production")
class ProductionConfiguration {

    // ...

}
如果@ConfigurationPropertiesbeans 通过@EnableConfigurationProperties而不是自动扫描,@Profile注释需要在@Configuration具有@EnableConfigurationProperties注解。 在以下情况下@ConfigurationProperties被扫描,@Profile可以在@ConfigurationProperties类本身。

您可以使用spring.profiles.active Environment属性来指定哪些配置文件处于活动状态。 您可以通过本章前面所述的任何方式指定属性。 例如,您可以将其包含在application.properties,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.profiles.active=dev,hsqldb
亚姆尔
spring:
  profiles:
    active: "dev,hsqldb"

您也可以使用以下开关在命令行上指定它:--spring.profiles.active=dev,hsqldb.spring-doc.cadn.net.cn

如果没有处于活动状态的配置文件,则启用默认配置文件。 默认配置文件的名称为default并且可以使用spring.profiles.default Environment属性,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.profiles.default=none
亚姆尔
spring:
  profiles:
    default: "none"

spring.profiles.activespring.profiles.default只能用于非特定于配置文件的文档。 这意味着它们不能包含在配置文件特定文件spring.config.activate.on-profile.spring-doc.cadn.net.cn

例如,第二个文档配置无效:spring-doc.cadn.net.cn

性能
# 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-doc.cadn.net.cn

有时,将属性添加到活动配置文件而不是替换它们会很有用。 这spring.profiles.include属性可用于在激活的配置文件之上添加活动配置文件spring.profiles.active财产。 这SpringApplication入口点还有一个用于设置其他配置文件的 Java API。请参阅setAdditionalProfiles()方法spring-doc.cadn.net.cn

例如,当运行具有以下属性的应用程序时,即使它使用--spring.profiles.active开关:spring-doc.cadn.net.cn

性能
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.

如果给定的配置文件处于活动状态,则下一节中将介绍的配置文件组也可用于添加活动配置文件。spring-doc.cadn.net.cn

3.2. 配置文件组

有时,您在应用程序中定义和使用的配置文件过于细粒度,使用起来很麻烦。 例如,您可能有proddbprodmq用于独立启用数据库和消息传递功能的配置文件。spring-doc.cadn.net.cn

为了帮助解决这个问题,Spring Boot 允许您定义配置文件组。 配置文件组允许您为相关的配置文件组定义逻辑名称。spring-doc.cadn.net.cn

例如,我们可以创建一个production由我们的proddbprodmq配置 文件。spring-doc.cadn.net.cn

性能
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
亚姆尔
spring:
  profiles:
    group:
      production:
      - "proddb"
      - "prodmq"

我们的应用程序现在可以使用--spring.profiles.active=production激活production,proddbprodmq一次点击配置文件。spring-doc.cadn.net.cn

spring.profiles.activespring.profiles.include,spring.profiles.group只能用于非特定于配置文件的文档。 这意味着它不能包含在配置文件特定文件spring.config.activate.on-profile.

3.3. 以编程方式设置配置文件

您可以通过调用SpringApplication.setAdditionalProfiles(…​)在应用程序运行之前。 也可以使用 Spring 的ConfigurableEnvironment接口。spring-doc.cadn.net.cn

3.4. 特定于配置文件的配置文件

两者的配置文件特定变体application.properties(或application.yaml)和通过@ConfigurationProperties被视为文件并加载。 有关详细信息,请参阅“配置文件特定文件”。spring-doc.cadn.net.cn

4. 日志记录

Spring Boot 将 Commons Logging 用于所有内部日志记录,但使底层日志实现保持打开状态。 为 Java Util LoggingLog4j2Logback 提供了默认配置。 在每种情况下,记录器都预先配置为使用控制台输出,也提供可选的文件输出。spring-doc.cadn.net.cn

默认情况下,如果您使用“Starters”,则使用 Logback 进行日志记录。还包括适当的 Logback 路由,以确保使用 Java Util Logging、Commons Logging、Log4J 或 SLF4J 的依赖库都能正常工作。spring-doc.cadn.net.cn

有很多可用于 Java 的日志记录框架。如果上面的列表看起来令人困惑,请不要担心。通常,您不需要更改日志记录依赖项,并且 Spring Boot 默认值工作正常。
将应用程序部署到 Servlet 容器或应用程序服务器时,使用 Java Util Logging API 执行的日志记录不会路由到应用程序的日志中。这可以防止容器或已部署到容器的其他应用程序执行的日志记录出现在应用程序的日志中。

4.1. 日志格式

Spring Boot 的默认日志输出类似于以下示例:spring-doc.cadn.net.cn

2024-11-21T07:54:17.816Z  INFO 109369 --- [myapp] [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 17.0.13 with PID 109369 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2024-11-21T07:54:17.836Z  INFO 109369 --- [myapp] [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2024-11-21T07:54:22.108Z  INFO 109369 --- [myapp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-11-21T07:54:22.155Z  INFO 109369 --- [myapp] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-11-21T07:54:22.155Z  INFO 109369 --- [myapp] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.33]
2024-11-21T07:54:22.402Z  INFO 109369 --- [myapp] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-11-21T07:54:22.424Z  INFO 109369 --- [myapp] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4342 ms
2024-11-21T07:54:24.813Z  INFO 109369 --- [myapp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
2024-11-21T07:54:24.865Z  INFO 109369 --- [myapp] [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 8.97 seconds (process running for 10.255)

输出以下项目:spring-doc.cadn.net.cn

Logback 没有FATAL水平。 它映射到ERROR.
如果你有一个spring.application.name属性,但不想记录它,您可以设置logging.include-application-namefalse.

4.2. 控制台输出

默认日志配置在写入消息时将消息回显到控制台。 默认情况下,ERROR-水平WARN-level 和INFO-level 消息。 您还可以通过使用--debug旗。spring-doc.cadn.net.cn

$ java -jar myapp.jar --debug
您还可以指定debug=true在你的application.properties.

启用调试模式后,将配置一系列核心记录器(嵌入式容器、Hibernate 和 Spring Boot)以输出更多信息。 启用调试模式不会将应用程序配置为记录所有消息DEBUG水平。spring-doc.cadn.net.cn

或者,您可以通过使用--trace标志(或trace=true在你的application.properties). 这样做可以为选择的核心记录器(嵌入式容器,Hibernate模式生成和整个Spring产品组合)启用跟踪日志记录。spring-doc.cadn.net.cn

4.2.1. 颜色编码输出

如果您的终端支持 ANSI,则使用颜色输出来帮助提高可读性。 您可以设置spring.output.ansi.enabled设置为支持的以覆盖自动检测。spring-doc.cadn.net.cn

颜色编码是通过使用%clr转换词。 在最简单的形式中,转换器根据对数级别为输出着色,如以下示例所示:spring-doc.cadn.net.cn

%clr(%5p)

下表描述了日志级别到颜色的映射:spring-doc.cadn.net.cn

水平 颜色

FATALspring-doc.cadn.net.cn

spring-doc.cadn.net.cn

ERRORspring-doc.cadn.net.cn

spring-doc.cadn.net.cn

WARNspring-doc.cadn.net.cn

黄色spring-doc.cadn.net.cn

INFOspring-doc.cadn.net.cn

绿spring-doc.cadn.net.cn

DEBUGspring-doc.cadn.net.cn

绿spring-doc.cadn.net.cn

TRACEspring-doc.cadn.net.cn

绿spring-doc.cadn.net.cn

或者,您可以通过将其作为转换选项提供来指定应使用的颜色或样式。 例如,要使文本变黄,请使用以下设置:spring-doc.cadn.net.cn

%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){yellow}

支持以下颜色和样式:spring-doc.cadn.net.cn

4.3. 文件输出

默认情况下,Spring Boot 仅记录到控制台,不写入日志文件。 如果除了控制台输出之外还想写日志文件,则需要设置一个logging.file.namelogging.file.path属性(例如,在application.properties). 如果同时设置了这两个属性,logging.file.path被忽略,只有logging.file.name被使用。spring-doc.cadn.net.cn

下表显示了如何logging.*属性可以一起使用:spring-doc.cadn.net.cn

表 5.日志记录属性
logging.file.name logging.file.path 描述

(无)spring-doc.cadn.net.cn

(无)spring-doc.cadn.net.cn

仅控制台日志记录。spring-doc.cadn.net.cn

特定文件(例如my.log)spring-doc.cadn.net.cn

(无)spring-doc.cadn.net.cn

写入logging.file.name. 位置可以是绝对位置,也可以是相对于当前目录的位置。spring-doc.cadn.net.cn

(无)spring-doc.cadn.net.cn

特定目录(例如/var/log)spring-doc.cadn.net.cn

spring.loglogging.file.path. 该目录可以是绝对目录,也可以是相对于当前目录的目录。spring-doc.cadn.net.cn

特定文件spring-doc.cadn.net.cn

特定目录spring-doc.cadn.net.cn

写入logging.file.name并忽略logging.file.path. 位置可以是绝对位置,也可以是相对于当前目录的位置。spring-doc.cadn.net.cn

日志文件在达到 10 MB 时轮换,与控制台输出一样,ERROR-水平WARN-level 和INFO默认情况下,记录 -level 消息。spring-doc.cadn.net.cn

日志记录属性独立于实际日志记录基础结构。 因此,特定的配置键(例如logback.configurationFile对于 Logback),不是由 spring Boot 管理。

4.4. 文件轮换

如果您使用的是 Logback,则可以使用application.propertiesapplication.yaml文件。 对于所有其他日志记录系统,您需要直接自己配置轮换设置(例如,如果您使用 Log4j2,则可以添加一个log4j2.xmllog4j2-spring.xml文件)。spring-doc.cadn.net.cn

支持以下轮换策略属性:spring-doc.cadn.net.cn

名称 描述

logging.logback.rollingpolicy.file-name-patternspring-doc.cadn.net.cn

用于创建日志存档的文件名模式。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.clean-history-on-startspring-doc.cadn.net.cn

如果应用程序启动时应进行日志存档清理。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.max-file-sizespring-doc.cadn.net.cn

存档前日志文件的最大大小。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.total-size-capspring-doc.cadn.net.cn

日志存档在被删除之前可以占用的最大大小。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.max-historyspring-doc.cadn.net.cn

要保留的最大存档日志文件数(默认为 7)。spring-doc.cadn.net.cn

4.5. 日志级别

所有受支持的日志记录系统都可以在 Spring 中设置记录器级别Environment(例如,在application.properties) 使用logging.level.<logger-name>=<level>哪里level是 TRACE、DEBUG、INFO、WARN、ERROR、FATAL 或 OFF 之一。 这root记录器可以通过使用logging.level.root.spring-doc.cadn.net.cn

以下示例显示了application.properties:spring-doc.cadn.net.cn

性能
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.webDEBUG.spring-doc.cadn.net.cn

上述方法仅适用于包级日志记录。 由于宽松绑定总是将环境变量转换为小写,因此无法以这种方式为单个类配置日志记录。 如果需要为类配置日志记录,可以使用SPRING_APPLICATION_JSON变量。

4.6. 日志组

能够将相关的记录器组合在一起以便可以同时配置它们通常很有用。 例如,您可能通常会更改所有与 Tomcat 相关的记录器的日志记录级别,但您无法轻松记住顶级包。spring-doc.cadn.net.cn

为了帮助解决这个问题,Spring Boot 允许您在 Spring 中定义日志记录组Environment. 例如,下面是如何通过将“tomcat”组添加到application.properties:spring-doc.cadn.net.cn

性能
logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat
亚姆尔
logging:
  group:
    tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat"

定义后,您可以使用一行更改组中所有记录器的级别:spring-doc.cadn.net.cn

性能
logging.level.tomcat=trace
亚姆尔
logging:
  level:
    tomcat: "trace"

Spring Boot 包括以下预定义的日志记录组,这些日志记录组可以开箱即用:spring-doc.cadn.net.cn

名称 Logging

Webspring-doc.cadn.net.cn

org.springframework.core.codec,org.springframework.http,org.springframework.web,org.springframework.boot.actuate.endpoint.web,org.springframework.boot.web.servlet.ServletContextInitializerBeansspring-doc.cadn.net.cn

SQL的spring-doc.cadn.net.cn

org.springframework.jdbc.core,org.hibernate.SQL,org.jooq.tools.LoggerListenerspring-doc.cadn.net.cn

4.7. 使用日志关闭钩子

为了在应用程序终止时释放日志资源,提供了一个关闭钩子,该钩子将在 JVM 退出时触发日志系统清理。 除非您的应用程序部署为 war 文件,否则此关闭挂钩会自动注册。 如果您的应用程序具有复杂的上下文层次结构,则关闭挂钩可能无法满足您的需求。 如果没有,请禁用关闭挂钩并调查底层日志记录系统直接提供的选项。 例如,Logback 提供了上下文选择器,允许在自己的上下文中创建每个 Logger。 您可以使用logging.register-shutdown-hook属性来禁用关闭钩子。 将其设置为false将禁用注册。 您可以在application.propertiesapplication.yaml文件:spring-doc.cadn.net.cn

性能
logging.register-shutdown-hook=false
亚姆尔
logging:
  register-shutdown-hook: false

4.8. 自定义日志配置

可以通过在类路径上包含适当的库来激活各种日志记录系统,并且可以通过在类路径的根目录或下一个 Spring 指定的位置提供合适的配置文件来进一步自定义Environment财产:logging.config.spring-doc.cadn.net.cn

您可以使用org.springframework.boot.logging.LoggingSystem系统属性。 该值应是LoggingSystem实现。 您还可以使用none.spring-doc.cadn.net.cn

由于日志记录是在ApplicationContext创建时,无法控制日志记录@PropertySources在Spring@Configuration文件。 更改日志记录系统或完全禁用它的唯一方法是通过系统属性。

根据日志记录系统,将加载以下文件:spring-doc.cadn.net.cn

测井系统 定制

返回spring-doc.cadn.net.cn

logback-spring.xml,logback-spring.groovy,logback.xmllogback.groovyspring-doc.cadn.net.cn

日志4j2spring-doc.cadn.net.cn

log4j2-spring.xmllog4j2.xmlspring-doc.cadn.net.cn

JDK(Java Util 日志记录)spring-doc.cadn.net.cn

logging.propertiesspring-doc.cadn.net.cn

如果可能,我们建议您使用-spring变体(例如,logback-spring.xml而不是logback.xml). 如果使用标准配置位置,Spring 无法完全控制日志初始化。
Java Util Logging 存在已知的类加载问题,这些问题会导致从“可执行 jar”运行时出现问题。 如果可能的话,我们建议您在从“可执行 jar”运行时避免使用它。

为了帮助自定义,一些其他属性是从 Spring 转移的Environment到系统属性。 这允许通过日志记录系统配置来使用属性。例如,将logging.file.nameapplication.propertiesLOGGING_FILE_NAME作为环境变量将导致LOG_FILE系统属性。 下表介绍了转移的属性:spring-doc.cadn.net.cn

弹簧环境 系统属性 评论

logging.exception-conversion-wordspring-doc.cadn.net.cn

LOG_EXCEPTION_CONVERSION_WORDspring-doc.cadn.net.cn

记录异常时使用的转换字。spring-doc.cadn.net.cn

logging.file.namespring-doc.cadn.net.cn

LOG_FILEspring-doc.cadn.net.cn

如果已定义,则在默认日志配置中使用。spring-doc.cadn.net.cn

logging.file.pathspring-doc.cadn.net.cn

LOG_PATHspring-doc.cadn.net.cn

如果已定义,则在默认日志配置中使用。spring-doc.cadn.net.cn

logging.pattern.consolespring-doc.cadn.net.cn

CONSOLE_LOG_PATTERNspring-doc.cadn.net.cn

要在控制台 (stdout) 上使用的日志模式。spring-doc.cadn.net.cn

logging.pattern.dateformatspring-doc.cadn.net.cn

LOG_DATEFORMAT_PATTERNspring-doc.cadn.net.cn

日志日期格式的附加器模式。spring-doc.cadn.net.cn

logging.charset.consolespring-doc.cadn.net.cn

CONSOLE_LOG_CHARSETspring-doc.cadn.net.cn

用于控制台日志记录的字符集。spring-doc.cadn.net.cn

logging.threshold.consolespring-doc.cadn.net.cn

CONSOLE_LOG_THRESHOLDspring-doc.cadn.net.cn

用于控制台日志记录的日志级别阈值。spring-doc.cadn.net.cn

logging.pattern.filespring-doc.cadn.net.cn

FILE_LOG_PATTERNspring-doc.cadn.net.cn

要在文件中使用的日志模式(如果LOG_FILE已启用)。spring-doc.cadn.net.cn

logging.charset.filespring-doc.cadn.net.cn

FILE_LOG_CHARSETspring-doc.cadn.net.cn

用于文件日志记录的字符集(如果LOG_FILE已启用)。spring-doc.cadn.net.cn

logging.threshold.filespring-doc.cadn.net.cn

FILE_LOG_THRESHOLDspring-doc.cadn.net.cn

用于文件日志记录的日志级别阈值。spring-doc.cadn.net.cn

logging.pattern.levelspring-doc.cadn.net.cn

LOG_LEVEL_PATTERNspring-doc.cadn.net.cn

渲染日志级别时要使用的格式(默认%5p).spring-doc.cadn.net.cn

PIDspring-doc.cadn.net.cn

PIDspring-doc.cadn.net.cn

当前进程 ID(如果可能,则发现,并且尚未定义为 OS 环境变量)。spring-doc.cadn.net.cn

如果您使用 Logback,则还会传输以下属性:spring-doc.cadn.net.cn

弹簧环境 系统属性 评论

logging.logback.rollingpolicy.file-name-patternspring-doc.cadn.net.cn

LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERNspring-doc.cadn.net.cn

滚动日志文件名的模式(默认${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz).spring-doc.cadn.net.cn

logging.logback.rollingpolicy.clean-history-on-startspring-doc.cadn.net.cn

LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_STARTspring-doc.cadn.net.cn

是否在启动时清理存档日志文件。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.max-file-sizespring-doc.cadn.net.cn

LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZEspring-doc.cadn.net.cn

最大日志文件大小。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.total-size-capspring-doc.cadn.net.cn

LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAPspring-doc.cadn.net.cn

要保留的日志备份的总大小。spring-doc.cadn.net.cn

logging.logback.rollingpolicy.max-historyspring-doc.cadn.net.cn

LOGBACK_ROLLINGPOLICY_MAX_HISTORYspring-doc.cadn.net.cn

要保留的最大存档日志文件数。spring-doc.cadn.net.cn

所有受支持的日志记录系统都可以在解析其配置文件时查阅系统属性。 请参阅中的默认配置spring-boot.jar例如:spring-doc.cadn.net.cn

如果要在日志记录属性中使用占位符,则应使用 Spring Boot 的语法,而不是底层框架的语法。 值得注意的是,如果您使用 Logback,您应该使用:作为属性名称与其默认值之间的分隔符,而不是使用:-.spring-doc.cadn.net.cn

您可以通过仅覆盖LOG_LEVEL_PATTERN(或logging.pattern.level与 Logback)。 例如,如果使用logging.pattern.level=user:%X{user} %5p,则缺省日志格式包含“user”的 MDC 条目(如果存在),如以下示例所示。spring-doc.cadn.net.cn

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配置文件。spring-doc.cadn.net.cn

因为标准logback.xml配置文件加载过早,您无法在其中使用扩展。 您需要使用logback-spring.xml或定义logging.config财产。
这些扩展不能与 Logback 的配置扫描一起使用。 如果尝试这样做,对配置文件进行更改会导致类似于记录以下内容之一的错误:
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]

4.9.1. 特定于配置文件的配置

<springProfile>标签允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。 配置文件部分在<configuration>元素。 使用name属性来指定哪个配置文件接受配置。 这<springProfile>标记可以包含配置文件名称(例如staging) 或配置文件表达式。 配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west). 查看 Spring Framework 参考指南了解更多详细信息。 以下列表显示了三个示例配置文件:spring-doc.cadn.net.cn

<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 中使用:spring-doc.cadn.net.cn

<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通过使用宽松的规则。

4.10. Log4j2 扩展

Spring Boot 包括许多对 Log4j2 的扩展,可以帮助进行高级配置。 您可以在任何log4j2-spring.xml配置文件。spring-doc.cadn.net.cn

因为标准log4j2.xml配置文件加载过早,您无法在其中使用扩展。 您需要使用log4j2-spring.xml或定义logging.config财产。
这些扩展取代了 Log4J 提供的 Spring Boot 支持。 您应该确保不包含org.apache.logging.log4j:log4j-spring-boot模块。

4.10.1. 特定于配置文件的配置

<SpringProfile>标签允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。 配置文件部分在<Configuration>元素。 使用name属性来指定哪个配置文件接受配置。 这<SpringProfile>标记可以包含配置文件名称(例如staging) 或配置文件表达式。 配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west). 查看 Spring Framework 参考指南了解更多详细信息。 以下列表显示了三个示例配置文件:spring-doc.cadn.net.cn

<SpringProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
</SpringProfile>

<SpringProfile name="dev | staging">
    <!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</SpringProfile>

<SpringProfile name="!production">
    <!-- configuration to be enabled when the "production" profile is not active -->
</SpringProfile>

4.10.2. 环境属性查找

如果您想引用 Spring 中的属性Environment在您的 Log4j2 配置中,您可以使用spring:前缀查找。 如果您想从application.properties文件。spring-doc.cadn.net.cn

以下示例演示如何设置名为applicationName上面写着spring.application.name从Spring开始Environment:spring-doc.cadn.net.cn

<Properties>
    <Property name="applicationName">${spring:spring.application.name}</Property>
</Properties>
查找键应在烤肉串大小写中指定(例如my.property-name).

4.10.3. Log4j2 系统属性

Log4j2 支持许多可用于配置各种项目的系统属性。 例如,log4j2.skipJansisystem 属性可用于配置ConsoleAppender将尝试在 Windows 上使用 Jansi 输出流。spring-doc.cadn.net.cn

Log4j2初始化后加载的所有系统属性都可以从SpringEnvironment. 例如,您可以添加log4j2.skipJansi=false给你的application.properties文件以拥有ConsoleAppender在 Windows 上使用 Jansi。spring-doc.cadn.net.cn

SpringEnvironment仅当系统属性和作系统环境变量不包含正在加载的值时才考虑。
在早期 Log4j2 初始化期间加载的系统属性不能引用 SpringEnvironment. 例如,在 Spring 环境可用之前,使用了 Log4j2 用于允许选择默认 Log4j2 实现的属性。

5. 国际化

Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户。 默认情况下,Spring Boot 会查找messages资源包。spring-doc.cadn.net.cn

当已配置资源包的默认属性文件可用 (messages.properties默认情况下)。 如果资源包仅包含特定于语言的属性文件,则需要添加默认值。 如果未找到与任何已配置的基本名称匹配的属性文件,则不会有自动配置MessageSource.

资源包的基本名称以及其他几个属性可以使用spring.messages命名空间,如以下示例所示:spring-doc.cadn.net.cn

性能
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以获取更多支持的选项。spring-doc.cadn.net.cn

6. 面向方面编程

Spring Boot 为面向方面编程 (AOP) 提供自动配置。 您可以在 Spring Framework 参考文档中了解有关 Spring 的 AOP 的更多信息。spring-doc.cadn.net.cn

默认情况下,Spring Boot 的自动配置将 Spring AOP 配置为使用 CGLib 代理。 要改用 JDK 代理,请将spring.aop.proxy-target-classfalse.spring-doc.cadn.net.cn

如果 AspectJ 在类路径上,Spring Boot 的自动配置将自动启用 AspectJ 自动代理,以便@EnableAspectJAutoProxy不是必需的。spring-doc.cadn.net.cn

7. JSON

Spring Boot 提供与三个 JSON 映射库的集成:spring-doc.cadn.net.cn

Jackson 是首选和默认库。spring-doc.cadn.net.cn

7.1. Jackson

提供了 Jackson 的自动配置,Jackson 是spring-boot-starter-json. 当Jackson在班级路径上时,ObjectMapperbean 是自动配置的。 提供了多个配置属性自定义ObjectMapper.spring-doc.cadn.net.cn

7.1.1. 自定义序列化器和反序列化器

如果您使用 Jackson 序列化和反序列化 JSON 数据,您可能需要编写自己的 JSON 数据JsonSerializerJsonDeserializer类。 自定义序列化器通常通过模块向 Jackson 注册,但 Spring Boot 提供了另一种选择@JsonComponent注释,使直接注册 Spring Bean 变得更加容易。spring-doc.cadn.net.cn

您可以使用@JsonComponent注释直接JsonSerializer,JsonDeserializerKeyDeserializer实现。 您还可以在包含序列化程序/反序列化程序作为内部类的类上使用它,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import org.springframework.boot.jackson.JsonComponent;

@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);
        }

    }

}
Kotlin
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import org.springframework.boot.jackson.JsonComponent
import java.io.IOException

@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)
        }
    }

}

@JsonComponentbean 中的ApplicationContext自动在Jackson注册。 因为@JsonComponent元注释为@Component,则适用通常的组件扫描规则。spring-doc.cadn.net.cn

Spring Boot 还提供了JsonObjectSerializerJsonObjectDeserializer基类,在序列化对象时为标准 Jackson 版本提供有用的替代方法。 看JsonObjectSerializerJsonObjectDeserializer在 Javadoc 中了解详细信息。spring-doc.cadn.net.cn

上面的示例可以重写为JsonObjectSerializer/JsonObjectDeserializer如下:spring-doc.cadn.net.cn

Java
import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;

import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.jackson.JsonObjectDeserializer;
import org.springframework.boot.jackson.JsonObjectSerializer;

@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);
        }

    }

}
Kotlin
`object`

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.ObjectCodec
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.SerializerProvider
import org.springframework.boot.jackson.JsonComponent
import org.springframework.boot.jackson.JsonObjectDeserializer
import org.springframework.boot.jackson.JsonObjectSerializer
import java.io.IOException

@JsonComponent
class MyJsonComponent {

    class Serializer : JsonObjectSerializer<MyObject>() {
        @Throws(IOException::class)
        override fun serializeObject(value: MyObject, jgen: JsonGenerator, provider: SerializerProvider) {
            jgen.writeStringField("name", value.name)
            jgen.writeNumberField("age", value.age)
        }
    }

    class Deserializer : JsonObjectDeserializer<MyObject>() {
        @Throws(IOException::class)
        override fun deserializeObject(jsonParser: JsonParser, context: DeserializationContext,
                codec: ObjectCodec, tree: JsonNode): MyObject {
            val name = nullSafeValue(tree["name"], String::class.java)
            val age = nullSafeValue(tree["age"], Int::class.java)
            return MyObject(name, age)
        }
    }

}

7.1.2. 混合

Jackson 支持混合,可用于将其他注释混合到目标类上已声明的注释中。 Spring Boot 的 Jackson 自动配置将扫描应用程序的包以查找带有@JsonMixin并将它们注册到自动配置的ObjectMapper. 注册由 Spring Boot 的JsonMixinModule.spring-doc.cadn.net.cn

7.2. Gson

提供 Gson 的自动配置。 当 Gson 在类路径上时Gsonbean 是自动配置的。 几个spring.gson.*提供了用于自定义配置的配置属性。 要获得更多控制权,请GsonBuilderCustomizer可以使用豆子。spring-doc.cadn.net.cn

7.3. JSON-B

提供了 JSON-B 的自动配置。当 JSON-B API 和实现位于类路径上时,一个Jsonbbean 将被自动配置。 首选的 JSON-B 实现是 Eclipse Yasson,它提供了依赖管理。spring-doc.cadn.net.cn

8. 任务执行和调度

在没有Executorbean 时,Spring Boot 会自动配置AsyncTaskExecutor. 启用虚拟线程时(使用 Java 21+ 和spring.threads.virtual.enabled设置为true) 这将是一个SimpleAsyncTaskExecutor使用虚拟线程。 否则,它将是一个ThreadPoolTaskExecutor具有合理的默认值。 无论哪种情况,自动配置的执行器都将自动用于:spring-doc.cadn.net.cn

如果您定义了自定义Executor在上下文中,常规任务执行(即@EnableAsync)和 Spring for GraphQL 将使用它。 但是,Spring MVC 和 Spring WebFlux 支持仅在它是AsyncTaskExecutor实现(名为applicationTaskExecutor). 根据您的目标安排,您可以更改您的Executor变成一个AsyncTaskExecutor或同时定义AsyncTaskExecutorAsyncConfigurer包装您的自定义Executor.spring-doc.cadn.net.cn

自动配置的ThreadPoolTaskExecutorBuilder允许您轻松创建实例,以重现自动配置默认执行的作。spring-doc.cadn.net.cn

ThreadPoolTaskExecutor是自动配置的,线程池使用8个核心线程,可以根据负载增长和缩小。 这些默认设置可以使用spring.task.execution命名空间,如以下示例所示:spring-doc.cadn.net.cn

性能
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 秒)时被回收。spring-doc.cadn.net.cn

如果调度程序需要与计划任务执行相关联,也可以自动配置调度程序(使用@EnableScheduling例如)。spring-doc.cadn.net.cn

如果启用了虚拟线程(使用 Java 21+ 和spring.threads.virtual.enabled设置为true) 这将是一个SimpleAsyncTaskScheduler使用虚拟线程。 这SimpleAsyncTaskScheduler将忽略任何与池相关的属性。spring-doc.cadn.net.cn

如果未启用虚拟线程,它将是一个ThreadPoolTaskScheduler具有合理的默认值。 这ThreadPoolTaskScheduler默认使用一个线程,可以使用spring.task.scheduling命名空间,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2
亚姆尔
spring:
  task:
    scheduling:
      thread-name-prefix: "scheduling-"
      pool:
        size: 2

一个ThreadPoolTaskExecutorBuilderbean,一个SimpleAsyncTaskExecutorBuilderbean,一个ThreadPoolTaskSchedulerBuilderbean 和SimpleAsyncTaskSchedulerBuilder如果需要创建自定义执行程序或调度程序,则在上下文中可用。 这SimpleAsyncTaskExecutorBuilderSimpleAsyncTaskSchedulerBuilder如果启用了 Bean(使用 Java 21+ 和spring.threads.virtual.enabled设置为true).spring-doc.cadn.net.cn

9. 测试

Spring Boot 提供了许多实用程序和注释,可以在测试应用程序时提供帮助。 测试支持由两个模块提供:spring-boot-test包含核心项目,并且spring-boot-test-autoconfigure支持测试的自动配置。spring-doc.cadn.net.cn

大多数开发人员使用spring-boot-starter-test“Starter”,它导入了 Spring Boot 测试模块以及 JUnit Jupiter、AssertJ、Hamcrest 和许多其他有用的库。spring-doc.cadn.net.cn

如果您有使用 JUnit 4 的测试,则可以使用 JUnit 5 的老式引擎来运行它们。 要使用复古引擎,请添加依赖项junit-vintage-engine,如以下示例所示:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

hamcrest-core被排除在外,有利于org.hamcrest:hamcrest这是spring-boot-starter-test.spring-doc.cadn.net.cn

9.1. 测试范围依赖关系

spring-boot-starter-test“Starter”(在test scope) 包含以下提供的库:spring-doc.cadn.net.cn

我们通常发现这些常用库在编写测试时很有用。 如果这些库不符合您的需求,您可以添加自己的其他测试依赖项。spring-doc.cadn.net.cn

9.2. 测试 Spring 应用程序

依赖注入的主要优点之一是它应该使您的代码更容易进行单元测试。 您可以使用new运算符,甚至不涉及 Spring。 您还可以使用模拟对象而不是实际依赖项。spring-doc.cadn.net.cn

通常,您需要超越单元测试并开始集成测试(使用 SpringApplicationContext). 无需部署应用程序或需要连接到其他基础结构即可执行集成测试非常有用。spring-doc.cadn.net.cn

Spring Framework 包括一个用于此类集成测试的专用测试模块。 您可以直接向org.springframework:spring-test或使用spring-boot-starter-test“Starter”以传递方式将其拉入。spring-doc.cadn.net.cn

如果您尚未使用spring-test模块之前,您应该首先阅读 Spring Framework 参考文档的相关部分spring-doc.cadn.net.cn

9.3. 测试 Spring Boot 应用程序

Spring Boot 应用程序是 SpringApplicationContext,因此除了您通常使用普通 Spring 上下文所做的作之外,无需做任何非常特别的事情来测试它。spring-doc.cadn.net.cn

默认情况下,Spring Boot 的外部属性、日志记录和其他功能才会安装在上下文中,前提是使用SpringApplication创建它。

Spring Boot 提供了一个@SpringBootTest注释,可用作标准的替代方案spring-test @ContextConfiguration当您需要 Spring Boot 功能时,注释。 注释的工作原理是创建ApplicationContext通过SpringApplication. 除了@SpringBootTest还提供了许多其他注释,用于测试应用程序的更具体切片spring-doc.cadn.net.cn

如果您使用的是 JUnit 4,请不要忘记同时添加@RunWith(SpringRunner.class)否则将忽略注释。 如果您使用的是 JUnit 5,则无需添加等效的@ExtendWith(SpringExtension.class)@SpringBootTest和另一个@…​Test注释已经用它进行了注释。

默认情况下,@SpringBootTest不会启动服务器。 您可以使用webEnvironment属性@SpringBootTest要进一步优化测试的运行方式,请执行以下作:spring-doc.cadn.net.cn

  • MOCK(默认) :加载网页ApplicationContext并提供模拟 Web 环境。 使用此注释时,不会启动嵌入式服务器。 如果您的类路径上没有 Web 环境,则此模式会透明地回退到创建常规的非 WebApplicationContext. 它可以与@AutoConfigureMockMvc@AutoConfigureWebTestClient用于对 Web 应用程序进行基于模拟的测试。spring-doc.cadn.net.cn

  • RANDOM_PORT:加载一个WebServerApplicationContext并提供真实的 Web 环境。 嵌入式服务器在随机端口上启动并侦听。spring-doc.cadn.net.cn

  • DEFINED_PORT:加载一个WebServerApplicationContext并提供真实的 Web 环境。 嵌入式服务器启动并在定义的端口上监听(从application.properties) 或默认端口8080.spring-doc.cadn.net.cn

  • NONE:加载ApplicationContext通过使用SpringApplication但不提供任何 Web 环境(模拟或其他)。spring-doc.cadn.net.cn

如果您的测试是@Transactional,默认情况下,它会在每个测试方法的末尾回滚事务。 但是,由于将这种安排与RANDOM_PORTDEFINED_PORT隐式地提供了一个真正的 servlet 环境,HTTP 客户端和服务器在单独的线程中运行,因此在单独的事务中运行。 在这种情况下,在服务器上启动的任何事务都不会回滚。
@SpringBootTestwebEnvironment = WebEnvironment.RANDOM_PORT如果应用程序为管理服务器使用不同的端口,则还将在单独的随机端口上启动管理服务器。

9.3.1. 检测 Web 应用程序类型

如果 Spring MVC 可用,则配置基于 MVC 的常规应用程序上下文。 如果您只有 Spring WebFlux,我们将检测到它并配置基于 WebFlux 的应用程序上下文。spring-doc.cadn.net.cn

如果两者都存在,则 Spring MVC 优先。 如果要在此方案中测试响应式 Web 应用程序,则必须将spring.main.web-application-type财产:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {

    // ...

}
Kotlin
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {

    // ...

}

9.3.2. 检测测试配置

如果您熟悉 Spring Test Framework,您可能习惯于@ContextConfiguration(classes=…​)以指定哪个弹簧@Configuration加载。 或者,您可能经常使用 nested@Configuration类。spring-doc.cadn.net.cn

在测试 Spring Boot 应用程序时,通常不需要这样做。 Spring Boot 的@*Test注释会在未显式定义主配置时自动搜索主配置。spring-doc.cadn.net.cn

搜索算法从包含测试的包开始工作,直到找到一个用@SpringBootApplication@SpringBootConfiguration. 只要您以合理的方式构建代码,通常会找到您的主要配置。spring-doc.cadn.net.cn

如果使用测试注释来测试应用程序的更具体切片,则应避免添加特定于主方法的应用程序类上特定区域的配置设置。spring-doc.cadn.net.cn

的底层组件扫描配置@SpringBootApplication定义用于确保切片按预期工作的排除过滤器。 如果您使用显式@ComponentScan指令@SpringBootApplication-annotated 类,请注意这些过滤器将被禁用。 如果使用切片,则应重新定义它们。spring-doc.cadn.net.cn

如果要自定义主配置,可以使用嵌套的@TestConfiguration类。 与嵌套的@Configuration类,它将用于代替应用程序的主要配置,一个嵌套的@TestConfigurationclass 是除应用程序的主要配置之外使用的。spring-doc.cadn.net.cn

Spring 的测试框架在测试之间缓存应用程序上下文。 因此,只要您的测试共享相同的配置(无论它是如何发现的),加载上下文的潜在耗时过程只发生一次。

9.3.3. 使用测试配置主方法

通常,由@SpringBootTest将是你的主要@SpringBootApplication. 在大多数结构良好的应用程序中,此配置类还将包括main用于启动应用程序的方法。spring-doc.cadn.net.cn

例如,以下是典型 Spring Boot 应用程序的非常常见的代码模式:spring-doc.cadn.net.cn

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

@SpringBootApplication
public class MyApplication {

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

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.docs.using.structuringyourcode.locatingthemainclass.MyApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

在上面的示例中,main方法除了委托给SpringApplication.run. 但是,有可能有一个更复杂的main在调用之前应用自定义的方法SpringApplication.run.spring-doc.cadn.net.cn

例如,这是一个更改横幅模式并设置其他配置文件的应用程序:spring-doc.cadn.net.cn

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

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.setAdditionalProfiles("myprofile");
        application.run(args);
    }

}
Kotlin
import org.springframework.boot.Banner
import org.springframework.boot.runApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args) {
        setBannerMode(Banner.Mode.OFF)
        setAdditionalProfiles("myprofile")
    }
}

由于自定义项中的main方法会影响结果ApplicationContext,您可能还想使用main创建ApplicationContext用于您的测试。默认情况下,@SpringBootTest不会打电话给你的main方法,而是直接使用类本身来创建ApplicationContextspring-doc.cadn.net.cn

如果要更改此行为,可以更改useMainMethod属性@SpringBootTestUseMainMethod.ALWAYSUseMainMethod.WHEN_AVAILABLE. 当设置为ALWAYS,如果没有,则测试将失败main方法。当设置为WHEN_AVAILABLEmain如果可用,则使用方法,否则将使用标准加载机制。spring-doc.cadn.net.cn

例如,以下测试将调用main方法MyApplication为了创建ApplicationContext. 如果 main 方法设置了额外的配置文件,那么当ApplicationContext开始。spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod;

@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {

    @Test
    void exampleTest() {
        // ...
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod

@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {

    @Test
    fun exampleTest() {
        // ...
    }

}

9.3.4. 排除测试配置

如果您的应用程序使用组件扫描(例如,如果您使用@SpringBootApplication@ComponentScan),您可能会发现仅为特定测试创建的顶级配置类在任何地方都被意外地选中。spring-doc.cadn.net.cn

正如我们之前所看到的,@TestConfiguration可用于测试的内部类,以自定义主配置。@TestConfiguration也可以在顶级类上使用。这样做表示不应通过扫描来选取该类。 然后,您可以在需要的地方显式导入类,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {

    @Test
    void exampleTest() {
        // ...
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import

@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {

    @Test
    fun exampleTest() {
        // ...
    }

}
如果您直接使用@ComponentScan(也就是说,不是通过@SpringBootApplication) 您需要注册TypeExcludeFilter有了它。 有关详细信息,请参阅 Javadoc
一个导入的@TestConfiguration比内部类更早处理@TestConfiguration和导入的@TestConfiguration将在通过组件扫描找到任何配置之前进行处理。 一般来说,这种排序差异没有明显的影响,但如果您依赖 bean 覆盖,则需要注意这一点。

9.3.5. 使用应用程序参数

如果您的应用程序需要参数,您可以 有@SpringBootTest使用args属性。spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {

    @Test
    void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest

@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.6. 使用模拟环境进行测试

默认情况下,@SpringBootTest不启动服务器,而是设置一个模拟环境来测试 Web 端点。spring-doc.cadn.net.cn

使用 Spring MVC,我们可以使用MockMvcWebTestClient,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

    @Test
    void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
    }

    // If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
    @Test
    void testWithWebTestClient(@Autowired WebTestClient webClient) {
        webClient
                .get().uri("/")
                .exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("Hello World");
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@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如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;

@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody

@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-doc.cadn.net.cn

例如,Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。 这意味着,虽然您可以按预期测试 MVC 层引发和处理异常,但无法直接测试是否呈现了特定的自定义错误页。 如果需要测试这些较低级别的问题,可以启动完全运行的服务器,如下一节所述。spring-doc.cadn.net.cn

9.3.7. 使用正在运行的服务器进行测试

如果您需要启动一个完全运行的服务器,我们建议您使用随机端口。 如果您使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT),则每次运行测试时都会随机选择可用端口。spring-doc.cadn.net.cn

@LocalServerPort注释可用于将实际使用的端口注入到测试中。 为方便起见,需要对已启动服务器进行 REST 调用的测试还可以自动连接WebTestClient,它解析到正在运行的服务器的相对链接,并附带一个用于验证响应的专用 API,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody

@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设备:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.client.TestRestTemplate

@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.8. 自定义 WebTestClient

要自定义WebTestClientbean,配置一个WebTestClientBuilderCustomizer豆。 任何此类 bean 都使用WebTestClient.Builder用于创建WebTestClient.spring-doc.cadn.net.cn

9.3.9. 使用 JMX

由于测试上下文框架缓存上下文,因此默认情况下禁用 JMX 以防止相同的组件在同一域上注册。 如果此类测试需要访问MBeanServer,也可以考虑将其标记为脏:spring-doc.cadn.net.cn

Java
import javax.management.MBeanServer;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {

    @Autowired
    private MBeanServer mBeanServer;

    @Test
    void exampleTest() {
        assertThat(this.mBeanServer.getDomains()).contains("java.lang");
        // ...
    }

}
Kotlin
import javax.management.MBeanServer

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext

@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. 使用观察

如果使用@AutoConfigureObservability,它会自动配置一个ObservationRegistry.spring-doc.cadn.net.cn

9.3.11. 使用指标

无论您的类路径如何,在使用@SpringBootTest.spring-doc.cadn.net.cn

如果您需要在集成测试中将指标导出到其他后端,请使用@AutoConfigureObservability.spring-doc.cadn.net.cn

如果使用@AutoConfigureObservability,它会自动配置内存中的MeterRegistry. 不支持在切片测试中导出数据,并支持@AutoConfigureObservability注解。spring-doc.cadn.net.cn

9.3.12. 使用跟踪

无论您的类路径如何,在使用@SpringBootTest.spring-doc.cadn.net.cn

如果您需要这些组件作为集成测试的一部分,请使用@AutoConfigureObservability.spring-doc.cadn.net.cn

如果您创建了自己的报告组件(例如,自定义SpanExporterSpanHandler)并且您不希望它们在测试中处于活动状态,则可以使用@ConditionalOnEnabledTracing注释来禁用它们。spring-doc.cadn.net.cn

如果使用@AutoConfigureObservability,它会自动配置无作Tracer. 不支持在切片测试中导出数据,并支持@AutoConfigureObservability注解。spring-doc.cadn.net.cn

9.3.13. 模拟和间谍 Beans

运行测试时,有时需要模拟应用程序上下文中的某些组件。 例如,您可能在某些远程服务上有一个在开发过程中不可用的外观。 当您想要模拟在真实环境中可能难以触发的故障时,模拟也很有用。spring-doc.cadn.net.cn

Spring Boot 包括一个@MockBean注释,可用于为 bean 中的 bean 定义 Mockito 模拟ApplicationContext. 您可以使用注释添加新 Bean 或替换单个现有 Bean 定义。 注释可以直接用于测试类、测试中的字段或@Configuration类和字段。 在字段上使用时,还会注入创建的模拟的实例。 模拟 bean 在每个测试方法后自动重置。spring-doc.cadn.net.cn

如果你的测试使用 Spring Boot 的测试注解之一(例如@SpringBootTest),则此功能将自动启用。 要将此功能与不同的排列方式一起使用,必须显式添加侦听器,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;

@ContextConfiguration(classes = MyConfig.class)
@TestExecutionListeners({ MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class })
class MyTests {

    // ...

}
Kotlin
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.TestExecutionListeners

@ContextConfiguration(classes = [MyConfig::class])
@TestExecutionListeners(
    MockitoTestExecutionListener::class,
    ResetMocksTestExecutionListener::class
)
class MyTests {

    // ...

}

以下示例将现有的RemoteServicebean 与模拟实现:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

@SpringBootTest
class MyTests {

    @Autowired
    private Reverser reverser;

    @MockBean
    private RemoteService remoteService;

    @Test
    void exampleTest() {
        given(this.remoteService.getValue()).willReturn("spring");
        String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
        assertThat(reverse).isEqualTo("gnirps");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean

@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. 有关完整详细信息,请参阅 Javadocspring-doc.cadn.net.cn

虽然 Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但使用@MockBean@SpyBean影响缓存键,这很可能会增加上下文的数量。
如果您正在使用@SpyBean@Cacheable按名称引用参数的方法,则应用程序必须使用-parameters. 这确保了一旦 bean 被监视,参数名称可供缓存基础设施使用。
当您使用@SpyBean要监视由 Spring 代理的 bean,在某些情况下,您可能需要删除 Spring 的代理,例如在使用givenwhen. 用AopTestUtils.getTargetObject(yourProxiedSpy)这样做。

9.3.14. 自动配置的测试

Spring Boot 的自动配置系统适用于应用程序,但有时对于测试来说可能有点太多了。 仅加载测试应用程序的“切片”所需的配置部分通常会有所帮助。 例如,您可能想要测试 Spring MVC 控制器是否正确映射 URL,并且您不想在这些测试中涉及数据库调用,或者您可能想要测试 JPA 实体,并且在这些测试运行时您对 Web 层不感兴趣。spring-doc.cadn.net.cn

spring-boot-test-autoconfigure模块包含许多注释,可用于自动配置此类“切片”。 它们中的每一个都以类似的方式工作,提供一个@…​Test加载ApplicationContext以及一个或多个@AutoConfigure…​可用于自定义自动配置设置的注释。spring-doc.cadn.net.cn

每个切片将组件扫描限制为适当的组件,并加载一组非常有限的自动配置类。 如果您需要排除其中之一,大多数@…​Test注释提供excludeAutoConfiguration属性。 或者,您可以使用@ImportAutoConfiguration#exclude.
使用多个@…​Test不支持在一个测试中使用注释。 如果您需要多个“切片”,请选择其中一个@…​Test注释,并包含@AutoConfigure…​手工注释其他“切片”。
也可以使用@AutoConfigure…​标注与标准@SpringBootTest注解。 如果您对“切片”应用程序不感兴趣,但想要一些自动配置的测试 Bean,则可以使用此组合。

9.3.15. 自动配置的 JSON 测试

要测试对象 JSON 序列化和反序列化是否按预期工作,您可以使用@JsonTest注解。@JsonTest自动配置可用的受支持的 JSON 映射器,它可以是以下库之一:spring-doc.cadn.net.cn

启用的自动配置列表@JsonTest可以在附录中找到

如果您需要配置自动配置的元素,可以使用@AutoConfigureJsonTesters注解。spring-doc.cadn.net.cn

Spring Boot 包括基于 AssertJ 的帮助程序,可与 JSONAssert 和 JsonPath 库配合使用,以检查 JSON 是否按预期显示。 这JacksonTester,GsonTester,JsonbTesterBasicJsonTester类可以分别用于 Jackson、Gson、Jsonb 和 Strings。 测试类上的任何帮助程序字段都可以是@Autowired使用时@JsonTest. 以下示例显示了 Jackson 的测试类:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;

import static org.assertj.core.api.Assertions.assertThat;

@JsonTest
class MyJsonTests {

    @Autowired
    private JacksonTester<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");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.json.JsonTest
import org.springframework.boot.test.json.JacksonTester

@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.150.01.spring-doc.cadn.net.cn

Java
@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)));
}
Kotlin
@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.16. 自动配置的 Spring MVC 测试

要测试 Spring MVC 控制器是否按预期工作,请使用@WebMvcTest注解。@WebMvcTest自动配置 Spring MVC 基础设施并将扫描的 bean 限制为@Controller,@ControllerAdvice,@JsonComponent,Converter,GenericConverter,Filter,HandlerInterceptor,WebMvcConfigurer,WebMvcRegistrationsHandlerMethodArgumentResolver. 定期@Component@ConfigurationProperties@WebMvcTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置设置列表@WebMvcTest可以在附录中找到
如果您需要注册额外的组件,例如 JacksonModule,您可以使用以下命令导入其他配置类@Import在你的测试中。

经常@WebMvcTest仅限于单个控制器,并与@MockBean为所需的协作者提供模拟实现。spring-doc.cadn.net.cn

@WebMvcTest还自动配置MockMvc. Mock MVC 提供了一种快速测试 MVC 控制器的强大方法,而无需启动完整的 HTTP 服务器。spring-doc.cadn.net.cn

您还可以自动配置MockMvc在非@WebMvcTest(例如@SpringBootTest),通过用@AutoConfigureMockMvc. 以下示例使用MockMvc:
Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(UserVehicleController.class)
class MyControllerTests {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot"))
            .willReturn(new VehicleDetails("Honda", "Civic"));
        this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andExpect(content().string("Honda Civic"));
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@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,则自动配置还会提供一个 HtmlUnitWebClientbean 和/或 SeleniumWebDriver豆。 以下示例使用 HtmlUnit:spring-doc.cadn.net.cn

Java
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {

    @Autowired
    private WebClient webClient;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
        HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
        assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
    }

}
Kotlin
import com.gargoylesoftware.htmlunit.WebClient
import com.gargoylesoftware.htmlunit.html.HtmlPage
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean

@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 将WebDriverbean 在一个特殊的“作用域”中,以确保驱动程序在每次测试后退出并注入一个新实例。 如果您不希望此行为,可以将@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)给你的WebDriver @Bean定义。
webDriverSpring Boot 创建的范围将替换任何用户定义的同名范围。 如果您定义自己的webDriverscope 你可能会发现它在使用@WebMvcTest.

如果类路径上有 Spring Security,@WebMvcTest也会扫描WebSecurityConfigurer豆。 您可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。 有关如何使用 Spring Security 的MockMvc可以在此howto.html作方法部分找到支持。spring-doc.cadn.net.cn

有时编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试

9.3.17. 自动配置的 Spring WebFlux 测试

要测试 Spring WebFlux 控制器是否按预期工作,您可以使用@WebFluxTest注解。@WebFluxTest自动配置 Spring WebFlux 基础设施并将扫描的 bean 限制为@Controller,@ControllerAdvice,@JsonComponent,Converter,GenericConverter,WebFilterWebFluxConfigurer. 定期@Component@ConfigurationProperties@WebFluxTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置列表@WebFluxTest可以在附录中找到
如果您需要注册额外的组件,例如 JacksonModule,您可以使用以下命令导入其他配置类@Import在你的测试中。

经常@WebFluxTest仅限于单个控制器,并与@MockBean注释,为所需的协作者提供模拟实现。spring-doc.cadn.net.cn

@WebFluxTest还自动配置WebTestClient,它提供了一种快速测试 WebFlux 控制器的强大方法,而无需启动完整的 HTTP 服务器。spring-doc.cadn.net.cn

您还可以自动配置WebTestClient在非@WebFluxTest(例如@SpringBootTest),通过用@AutoConfigureWebTestClient. 以下示例显示了同时使用@WebFluxTestWebTestClient:
Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.mockito.BDDMockito.given;

@WebFluxTest(UserVehicleController.class)
class MyControllerTests {

    @Autowired
    private WebTestClient webClient;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() {
        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");
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody

@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 框架注册的路由。 用于测试RouterFunctionbean 中,请考虑导入RouterFunction使用@Import或通过使用@SpringBootTest.
@WebFluxTest无法检测注册为@Bean类型SecurityWebFilterChain. 要将其包含在测试中,您需要使用@Import或通过使用@SpringBootTest.
有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试

9.3.18. 自动配置的 Spring GraphQL 测试

Spring GraphQL 提供了一个专用的测试支持模块;您需要将其添加到您的项目中:spring-doc.cadn.net.cn

专家
<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>
Gradle
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 将根据测试类型自动配置它们:spring-doc.cadn.net.cn

  • ExecutionGraphQlServiceTester在服务器端执行测试,无需客户端或传输spring-doc.cadn.net.cn

  • HttpGraphQlTester使用连接到服务器的客户端执行测试,无论是否使用实时服务器spring-doc.cadn.net.cn

Spring Boot 可帮助您使用@GraphQlTest注解。@GraphQlTest自动配置 Spring GraphQL 基础设施,不涉及任何传输或服务器。 这会将扫描的 bean 限制为@Controller,RuntimeWiringConfigurer,JsonComponent,Converter,GenericConverter,DataFetcherExceptionResolver,InstrumentationGraphQlSourceBuilderCustomizer. 定期@Component@ConfigurationProperties@GraphQlTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置列表@GraphQlTest可以在附录中找到

经常@GraphQlTest仅限于一组控制器,并与@MockBean注释,为所需的协作者提供模拟实现。spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.GraphQlTester;

@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!");
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest
import org.springframework.graphql.test.tester.GraphQlTester

@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测试是完全集成测试,涉及整个应用程序。 使用随机或定义的端口时,将配置实时服务器,并配置HttpGraphQlTesterBean 是自动贡献的,因此您可以使用它来测试您的服务器。 配置 MOCK 环境后,您还可以请求HttpGraphQlTesterbean 通过使用@AutoConfigureHttpGraphQlTester:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.test.tester.HttpGraphQlTester;

@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!");
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.graphql.test.tester.HttpGraphQlTester
import org.springframework.http.HttpHeaders
import org.springframework.test.web.reactive.server.WebTestClient

@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.19. 自动配置的数据 Cassandra 测试

您可以使用@DataCassandraTest以测试 Cassandra 应用程序。 默认情况下,它配置了一个CassandraTemplate,扫描@Table类,并配置 Spring Data Cassandra 存储库。 定期@Component@ConfigurationProperties@DataCassandraTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关将 Cassandra 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataCassandraTest可以在附录中找到

以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;

@DataCassandraTest
class MyDataCassandraTests {

    @Autowired
    private SomeRepository repository;

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest

@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)

9.3.20. 自动配置的 Data Couchbase 测试

您可以使用@DataCouchbaseTest以测试 Couchbase 应用程序。 默认情况下,它配置了一个CouchbaseTemplateReactiveCouchbaseTemplate,扫描@Document类,并配置 Spring Data Couchbase 存储库。 定期@Component@ConfigurationProperties@DataCouchbaseTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关将 Couchbase 与 Spring Boot 一起使用的更多信息,请参阅本章前面的“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataCouchbaseTest可以在附录中找到

以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;

@DataCouchbaseTest
class MyDataCouchbaseTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest

@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {

    // ...

}

9.3.21. 自动配置的数据 Elasticsearch 测试

您可以使用@DataElasticsearchTest以测试 Elasticsearch 应用程序。 默认情况下,它配置一个ElasticsearchRestTemplate,扫描@Document类,并配置 Spring Data Elasticsearch 存储库。 定期@Component@ConfigurationProperties@DataElasticsearchTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关将 Elasticsearch 与 Spring Boot 一起使用的更多信息,请参阅本章前面的“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataElasticsearchTest可以在附录中找到

以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;

@DataElasticsearchTest
class MyDataElasticsearchTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest

@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {

    // ...

}

9.3.22. 自动配置的数据 JPA 测试

您可以使用@DataJpaTest注释来测试 JPA 应用程序。 默认情况下,它会扫描@Entity类并配置 Spring Data JPA 存储库。 如果类路径上有一个嵌入式数据库可用,它也会配置一个。 默认情况下,通过将spring.jpa.show-sql属性设置为true. 可以使用showSql属性。spring-doc.cadn.net.cn

定期@Component@ConfigurationProperties@DataJpaTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataJpaTest可以在附录中找到

默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。 如果这不是您想要的,您可以禁用测试或整个类的事务管理,如下所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {

    // ...

}
Kotlin
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {

    // ...

}

数据 JPA 测试还可以注入一个TestEntityManagerbean,它提供了标准 JPA 的替代方案EntityManager这是专门为测试而设计的。spring-doc.cadn.net.cn

TestEntityManager也可以通过添加@AutoConfigureTestEntityManager. 这样做时,请确保您的测试在事务中运行,例如通过添加@Transactional在测试类或方法上。

一个JdbcTemplate如果您需要,也可以使用。 以下示例显示了@DataJpaTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
class MyRepositoryTests {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository repository;

    @Test
    void testExample() {
        this.entityManager.persist(new User("sboot", "1234"));
        User user = this.repository.findByUsername("sboot");
        assertThat(user.getUsername()).isEqualTo("sboot");
        assertThat(user.getEmployeeNumber()).isEqualTo("1234");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager

@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注释,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {

    // ...

}
Kotlin
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MyRepositoryTests {

    // ...

}

9.3.23. 自动配置的 JDBC 测试

@JdbcTest类似于@DataJpaTest但适用于只需要DataSource并且不要使用 Spring Data JDBC。 默认情况下,它配置内存中嵌入式数据库和JdbcTemplate. 定期@Component@ConfigurationProperties@JdbcTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置列表@JdbcTest可以在附录中找到

默认情况下,JDBC 测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。 如果这不是您想要的,您可以禁用测试或整个类的事务管理,如下所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {

}
Kotlin
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests

如果您希望测试针对真实数据库运行,则可以使用@AutoConfigureTestDatabase注释的方式与@DataJpaTest. (请参阅“自动配置的数据 JPA 测试”。spring-doc.cadn.net.cn

9.3.24. 自动配置的数据 JDBC 测试

@DataJdbcTest类似于@JdbcTest但适用于使用 Spring Data JDBC 存储库的测试。 默认情况下,它配置一个内存中嵌入式数据库,一个JdbcTemplate和 Spring Data JDBC 存储库。 只AbstractJdbcConfiguration@DataJdbcTest使用注释,常规@Component@ConfigurationProperties不扫描豆子。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置列表@DataJdbcTest可以在附录中找到

默认情况下,数据 JDBC 测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。 如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如 JDBC 示例所示spring-doc.cadn.net.cn

如果您希望测试针对真实数据库运行,则可以使用@AutoConfigureTestDatabase注释的方式与@DataJpaTest. (请参阅“自动配置的数据 JPA 测试”。spring-doc.cadn.net.cn

9.3.25. 自动配置的数据 R2DBC 测试

@DataR2dbcTest类似于@DataJdbcTest但适用于使用 Spring Data R2DBC 存储库的测试。 默认情况下,它配置一个内存中嵌入式数据库,一个R2dbcEntityTemplate和 Spring Data R2DBC 存储库。 定期@Component@ConfigurationProperties@DataR2dbcTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置列表@DataR2dbcTest可以在附录中找到

默认情况下,数据 R2DBC 测试不是事务性的。spring-doc.cadn.net.cn

如果您希望测试针对真实数据库运行,则可以使用@AutoConfigureTestDatabase注释的方式与@DataJpaTest. (请参阅“自动配置的数据 JPA 测试”。spring-doc.cadn.net.cn

9.3.26. 自动配置的 jOOQ 测试

您可以使用@JooqTest以类似于@JdbcTest但用于与 jOOQ 相关的测试。 由于 jOOQ 严重依赖与数据库模式相对应的基于 Java 的模式,因此现有的DataSource被使用。 如果要用内存数据库替换它,可以使用@AutoConfigureTestDatabase以覆盖这些设置。 (有关将 jOOQ 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。 定期@Component@ConfigurationProperties@JooqTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置列表@JooqTest可以在附录中找到

@JooqTest配置一个DSLContext. 以下示例显示了@JooqTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.jooq.DSLContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;

@JooqTest
class MyJooqTests {

    @Autowired
    private DSLContext dslContext;

    // ...

}
Kotlin
import org.jooq.DSLContext
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jooq.JooqTest

@JooqTest
class MyJooqTests(@Autowired val dslContext: DSLContext) {

    // ...

}

JOOQ 测试是事务性的,默认情况下在每个测试结束时回滚。 如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如 JDBC 示例所示spring-doc.cadn.net.cn

9.3.27. 自动配置的数据MongoDB测试

您可以使用@DataMongoTest测试 MongoDB 应用程序。 默认情况下,它配置了一个MongoTemplate,扫描@Document类,并配置 Spring Data MongoDB 存储库。 定期@Component@ConfigurationProperties@DataMongoTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关将MongoDB与Spring Boot一起使用的更多信息,请参阅“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataMongoTest可以在附录中找到

以下类显示了@DataMongoTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;

@DataMongoTest
class MyDataMongoDbTests {

    @Autowired
    private MongoTemplate mongoTemplate;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest
import org.springframework.data.mongodb.core.MongoTemplate

@DataMongoTest
class MyDataMongoDbTests(@Autowired val mongoTemplate: MongoTemplate) {

    // ...

}

9.3.28. 自动配置数据 Neo4j 测试

您可以使用@DataNeo4jTest测试 Neo4j 应用程序。 默认情况下,它会扫描@Node类,并配置 Spring Data Neo4j 存储库。 定期@Component@ConfigurationProperties@DataNeo4jTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关将 Neo4J 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataNeo4jTest可以在附录中找到

以下示例显示了在 Spring Boot 中使用 Neo4J 测试的典型设置:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;

@DataNeo4jTest
class MyDataNeo4jTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest

@DataNeo4jTest
class MyDataNeo4jTests(@Autowired val repository: SomeRepository) {

    // ...

}

默认情况下,Data Neo4j 测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。 如果这不是您想要的,您可以禁用测试或整个类的事务管理,如下所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {

}
Kotlin
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests
响应式访问不支持事务测试。 如果使用此样式,则必须配置@DataNeo4jTest如上所述的测试。

9.3.29. 自动配置的数据 Redis 测试

您可以使用@DataRedisTest测试 Redis 应用程序。 默认情况下,它会扫描@RedisHash类并配置 Spring Data Redis 存储库。 定期@Component@ConfigurationProperties@DataRedisTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关将 Redis 与 Spring Boot 一起使用的更多信息,请参阅“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataRedisTest可以在附录中找到

以下示例显示了@DataRedisTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;

@DataRedisTest
class MyDataRedisTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest

@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {

    // ...

}

9.3.30. 自动配置的数据 LDAP 测试

您可以使用@DataLdapTest以测试 LDAP 应用程序。 默认情况下,它配置内存中嵌入式 LDAP(如果可用),配置LdapTemplate,扫描@Entry类,并配置 Spring Data LDAP 存储库。 定期@Component@ConfigurationProperties@DataLdapTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。 (有关在 Spring Boot 中使用 LDAP 的更多信息,请参阅“data.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@DataLdapTest可以在附录中找到

以下示例显示了@DataLdapTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;

@DataLdapTest
class MyDataLdapTests {

    @Autowired
    private LdapTemplate ldapTemplate;

    // ...

}
Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest
import org.springframework.ldap.core.LdapTemplate

@DataLdapTest
class MyDataLdapTests(@Autowired val ldapTemplate: LdapTemplate) {

    // ...

}

内存嵌入式 LDAP 通常适用于测试,因为它速度快且不需要任何开发人员安装。 但是,如果您更喜欢针对实际的 LDAP 服务器运行测试,则应排除嵌入式 LDAP 自动配置,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;

@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {

    // ...

}
Kotlin
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest

@DataLdapTest(excludeAutoConfiguration = [EmbeddedLdapAutoConfiguration::class])
class MyDataLdapTests {

    // ...

}

9.3.31. 自动配置的 REST 客户端

您可以使用@RestClientTest注释来测试 REST 客户端。 默认情况下,它会自动配置 Jackson、GSON 和 Jsonb 支持,配置一个RestTemplateBuilderRestClient.Builder,并添加了对MockRestServiceServer. 定期@Component@ConfigurationProperties@RestClientTest注释。@EnableConfigurationProperties可用于包含@ConfigurationProperties豆。spring-doc.cadn.net.cn

启用的自动配置设置列表@RestClientTest可以在附录中找到

要测试的特定 bean 应使用valuecomponents属性@RestClientTest.spring-doc.cadn.net.cn

使用RestTemplateBuilder在被测豆中和RestTemplateBuilder.rootUri(String rootUri)在构建RestTemplate,则应从MockRestServiceServer期望值,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;

@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestTemplateServiceTests {

    @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");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest
import org.springframework.http.MediaType
import org.springframework.test.web.client.MockRestServiceServer
import org.springframework.test.web.client.match.MockRestRequestMatchers
import org.springframework.test.web.client.response.MockRestResponseCreators

@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestTemplateServiceTests(
    @Autowired val service: RemoteVehicleDetailsService,
    @Autowired val server: MockRestServiceServer) {

    @Test
    fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
        server.expect(MockRestRequestMatchers.requestTo("/greet/details"))
            .andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
        val greeting = service.callRestService()
        assertThat(greeting).isEqualTo("hello")
    }

}

使用RestClient.Builder在被测的 bean 中,或者当使用RestTemplateBuilder无需调用rootUri(String rootURI),则必须在MockRestServiceServer期望值,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;

@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientServiceTests {

    @Autowired
    private RemoteVehicleDetailsService service;

    @Autowired
    private MockRestServiceServer server;

    @Test
    void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
        this.server.expect(requestTo("https://example.com/greet/details"))
            .andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
        String greeting = this.service.callRestService();
        assertThat(greeting).isEqualTo("hello");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest
import org.springframework.http.MediaType
import org.springframework.test.web.client.MockRestServiceServer
import org.springframework.test.web.client.match.MockRestRequestMatchers
import org.springframework.test.web.client.response.MockRestResponseCreators

@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestClientServiceTests(
    @Autowired val service: RemoteVehicleDetailsService,
    @Autowired val server: MockRestServiceServer) {

    @Test
    fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
        server.expect(MockRestRequestMatchers.requestTo("https://example.com/greet/details"))
            .andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
        val greeting = service.callRestService()
        assertThat(greeting).isEqualTo("hello")
    }

}

9.3.32. 自动配置的 Spring REST Docs 测试

您可以使用@AutoConfigureRestDocs注释以在测试中使用 Mock MVC、REST Assured 或 WebTestClient 的 Spring REST Docs。 它消除了对 Spring REST Docs 中 JUnit 扩展的需求。spring-doc.cadn.net.cn

@AutoConfigureRestDocs可用于覆盖默认输出目录 (target/generated-snippets如果您使用的是 Maven 或build/generated-snippets如果您使用的是 Gradle)。 它还可用于配置任何记录的 URI 中显示的主机、方案和端口。spring-doc.cadn.net.cn

使用模拟 MVC 自动配置的 Spring REST Docs 测试

@AutoConfigureRestDocs自定义MockMvcbean 在测试基于 servlet 的 Web 应用程序时使用 Spring REST Docs。 您可以使用以下命令来注入它@Autowired并在测试中使用它,就像在使用 Mock MVC 和 Spring REST Docs 时一样,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

    @Autowired
    private MockMvc mvc;

    @Test
    void listUsers() throws Exception {
        this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andDo(document("list-users"));
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@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,您可以使用RestDocsMockMvcConfigurationCustomizerbean,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {

    @Override
    public void customize(MockMvcRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}
Kotlin
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer
import org.springframework.restdocs.templates.TemplateFormats

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsMockMvcConfigurationCustomizer {

    override fun customize(configurer: MockMvcRestDocumentationConfigurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
    }

}

如果您想利用 Spring REST Docs 对参数化输出目录的支持,您可以创建一个RestDocumentationResultHandler豆。 自动配置调用alwaysDo使用此结果处理程序,从而导致每个MockMvc调用自动生成默认代码片段。 以下示例显示了RestDocumentationResultHandler被定义:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;

@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {

    @Bean
    public RestDocumentationResultHandler restDocumentation() {
        return MockMvcRestDocumentation.document("{method-name}");
    }

}
Kotlin
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler

@TestConfiguration(proxyBeanMethods = false)
class MyResultHandlerConfiguration {

    @Bean
    fun restDocumentation(): RestDocumentationResultHandler {
        return MockMvcRestDocumentation.document("{method-name}")
    }

}
使用 WebTestClient 自动配置 Spring REST Docs 测试

@AutoConfigureRestDocs也可以与WebTestClient在测试响应式 Web 应用程序时。 您可以使用以下命令来注入它@Autowired并在测试中使用它,就像您通常使用@WebFluxTest和 Spring REST Docs,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;

@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void listUsers() {
        this.webTestClient
            .get().uri("/")
        .exchange()
        .expectStatus()
            .isOk()
        .expectBody()
            .consumeWith(document("list-users"));
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient

@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,您可以使用RestDocsWebTestClientConfigurationCustomizerbean,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {

    @Override
    public void customize(WebTestClientRestDocumentationConfigurer configurer) {
        configurer.snippets().withEncoding("UTF-8");
    }

}
Kotlin
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsWebTestClientConfigurationCustomizer {

    override fun customize(configurer: WebTestClientRestDocumentationConfigurer) {
        configurer.snippets().withEncoding("UTF-8")
    }

}

如果您想利用 Spring REST Docs 对参数化输出目录的支持,您可以使用WebTestClientBuilderCustomizer为每个实体交换结果配置使用者。 以下示例显示了这样的WebTestClientBuilderCustomizer被定义:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer;
import org.springframework.context.annotation.Bean;

import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;

@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {

    @Bean
    public WebTestClientBuilderCustomizer restDocumentation() {
        return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
    }

}
Kotlin
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient

@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使RequestSpecificationbean,预配置为使用 Spring REST Docs,可用于您的测试。 您可以使用以下命令来注入它@Autowired并在测试中使用它,就像使用 REST Assured 和 Spring REST Docs 时一样,如以下示例所示:spring-doc.cadn.net.cn

Java
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

    @Test
    void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
        given(documentationSpec)
            .filter(document("list-users"))
        .when()
            .port(port)
            .get("/")
        .then().assertThat()
            .statusCode(is(200));
    }

}
Kotlin
import io.restassured.RestAssured
import io.restassured.specification.RequestSpecification
import org.hamcrest.Matchers
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.restdocs.restassured.RestAssuredRestDocumentation

@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,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {

    @Override
    public void customize(RestAssuredRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}
Kotlin
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer
import org.springframework.restdocs.templates.TemplateFormats

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer {

    override fun customize(configurer: RestAssuredRestDocumentationConfigurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
    }

}

9.3.33. 自动配置的 Spring Web 服务测试

自动配置的 Spring Web Services 客户端测试

您可以使用@WebServiceClientTest测试使用 Spring Web Services 项目调用 Web 服务的应用程序。 默认情况下,它配置一个模拟WebServiceServerbean 并自动自定义您的WebServiceTemplateBuilder. (有关将 Web 服务与 Spring Boot 一起使用的更多信息,请参阅“io.html”。spring-doc.cadn.net.cn

启用的自动配置设置列表@WebServiceClientTest可以在附录中找到

以下示例显示了@WebServiceClientTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.xml.transform.StringSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;

@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {

    @Autowired
    private MockWebServiceServer server;

    @Autowired
    private SomeWebService someWebService;

    @Test
    void mockServerCall() {
        this.server
            .expect(payload(new StringSource("<request/>")))
            .andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
        assertThat(this.someWebService.test())
            .extracting(Response::getStatus)
            .isEqualTo(200);
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest
import org.springframework.ws.test.client.MockWebServiceServer
import org.springframework.ws.test.client.RequestMatchers
import org.springframework.ws.test.client.ResponseCreators
import org.springframework.xml.transform.StringSource

@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”。spring-doc.cadn.net.cn

启用的自动配置设置列表@WebServiceServerTest可以在附录中找到

以下示例显示了@WebServiceServerTest使用中的注释:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest;
import org.springframework.ws.test.server.MockWebServiceClient;
import org.springframework.ws.test.server.RequestCreators;
import org.springframework.ws.test.server.ResponseMatchers;
import org.springframework.xml.transform.StringSource;

@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {

    @Autowired
    private MockWebServiceClient client;

    @Test
    void mockServerCall() {
        this.client
            .sendRequest(RequestCreators.withPayload(new StringSource("<ExampleRequest/>")))
            .andExpect(ResponseMatchers.payload(new StringSource("<ExampleResponse>42</ExampleResponse>")));
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest
import org.springframework.ws.test.server.MockWebServiceClient
import org.springframework.ws.test.server.RequestCreators
import org.springframework.ws.test.server.ResponseMatchers
import org.springframework.xml.transform.StringSource

@WebServiceServerTest(ExampleEndpoint::class)
class MyWebServiceServerTests(@Autowired val client: MockWebServiceClient) {

    @Test
    fun mockServerCall() {
        client
            .sendRequest(RequestCreators.withPayload(StringSource("<ExampleRequest/>")))
            .andExpect(ResponseMatchers.payload(StringSource("<ExampleResponse>42</ExampleResponse>")))
    }

}

9.3.34. 其他自动配置和切片

每个切片提供一个或多个@AutoConfigure…​注释,即定义应作为切片的一部分包含的自动配置。 可以通过创建自定义@AutoConfigure…​注释或通过添加@ImportAutoConfiguration到测试,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {

}
Kotlin
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration::class)
class MyJdbcTests
确保不要使用常规的@Import注解来导入自动配置,因为它们由 Spring Boot 以特定方式处理。

或者,可以通过在存储在META-INF/spring如以下示例所示:spring-doc.cadn.net.cn

META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports
com.example.IntegrationAutoConfiguration

在此示例中,com.example.IntegrationAutoConfiguration在每个标注为@JdbcTest.spring-doc.cadn.net.cn

您可以在此文件中使用注释。#
切片或@AutoConfigure…​注释可以以这种方式自定义,只要它使用@ImportAutoConfiguration.

9.3.35. 用户配置和切片

如果您以合理的方式构建代码,则@SpringBootApplicationclass 默认用作测试的配置。spring-doc.cadn.net.cn

然后,重要的是不要在应用程序的主类中乱扔特定于其特定功能区域的配置设置。spring-doc.cadn.net.cn

假设您使用的是 Spring Data MongoDB,您依赖于它的自动配置,并且您已经启用了审计。 您可以定义您的@SpringBootApplication如下:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {

    // ...

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.data.mongodb.config.EnableMongoAuditing

@SpringBootApplication
@EnableMongoAuditing
class MyApplication {

    // ...

}

因为这个类是测试的源配置,所以任何切片测试实际上都会尝试启用 Mongo 审计,这绝对不是你想做的。 建议的方法是将特定于区域的配置移动到单独的@Configuration类,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {

    // ...

}
Kotlin
import org.springframework.context.annotation.Configuration
import org.springframework.data.mongodb.config.EnableMongoAuditing

@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
class MyMongoConfiguration {

    // ...

}
根据应用程序的复杂性,您可能有一个@Configurationclass 或每个域区域一个类。 后一种方法允许您在其中一个测试中启用它,如有必要,使用@Import注解。 请参阅此作方法部分,了解有关何时可能想要启用特定@Configuration用于切片测试的类。

测试切片排除@Configuration类。 例如,对于@WebMvcTest,则以下配置将不包括给定的WebMvcConfigurerbean 在测试切片加载的应用程序上下文中:spring-doc.cadn.net.cn

Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {

    @Bean
    public WebMvcConfigurer testConfigurer() {
        return new WebMvcConfigurer() {
            // ...
        };
    }

}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration(proxyBeanMethods = false)
class MyWebConfiguration {

    @Bean
    fun testConfigurer(): WebMvcConfigurer {
        return object : WebMvcConfigurer {
            // ...
        }
    }

}

但是,以下配置将导致自定义WebMvcConfigurer由测试切片加载。spring-doc.cadn.net.cn

Java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    // ...

}
Kotlin
import org.springframework.stereotype.Component
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Component
class MyWebMvcConfigurer : WebMvcConfigurer {

    // ...

}

另一个混淆的来源是类路径扫描。 假设在以合理的方式构建代码时,需要扫描一个额外的包。 应用程序可能类似于以下代码:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

    // ...

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.ComponentScan

@SpringBootApplication
@ComponentScan("com.example.app", "com.example.another")
class MyApplication {

    // ...

}

这样做会有效地覆盖默认组件扫描指令,无论您选择的切片如何,都会产生扫描这两个包的副作用。 例如,一个@DataJpaTest似乎突然扫描了应用程序的组件和用户配置。 同样,将自定义指令移动到单独的类是解决此问题的好方法。spring-doc.cadn.net.cn

如果这不适合您,您可以创建一个@SpringBootConfiguration在测试层次结构中的某个位置,以便使用它。 或者,您可以为测试指定源,这会禁用查找默认源的行为。

9.3.36. 使用 Spock 测试 Spring Boot 应用程序

Spock 2.2 或更高版本可用于测试 Spring Boot 应用程序。 为此,请添加对-groovy-4.0斯波克的版本spock-spring模块添加到应用程序的构建中。spock-spring将 Spring 的测试框架集成到 Spock 中。 有关更多详细信息,请参阅 Spock 的 Spring 模块的文档spring-doc.cadn.net.cn

9.4. 测试容器

Testcontainers 库提供了一种管理在 Docker 容器内运行的服务的方法。 它与 JUnit 集成,允许您编写一个测试类,该类可以在任何测试运行之前启动容器。 Testcontainers 对于编写与真正的后端服务(如 MySQL、MongoDB、Cassandra 等)通信的集成测试特别有用。spring-doc.cadn.net.cn

Test容器可以在 Spring Boot 测试中使用,如下所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

    @Container
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

    @Test
    void myTest() {
        // ...
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

    @Test
    fun myTest() {
        // ...
    }

    companion object {

        @Container
        val neo4j = Neo4jContainer("neo4j:5")

    }

}

这将在运行任何测试之前启动一个运行 Neo4j 的 docker 容器(如果 Docker 在本地运行)。 在大多数情况下,需要将应用程序配置为连接到容器中运行的服务。spring-doc.cadn.net.cn

9.4.1. 服务连接

服务连接是与任何远程服务的连接。 Spring Boot 的自动配置可以使用服务连接的详细信息,并使用它们来建立与远程服务的连接。 执行此作时,连接详细信息优先于任何与连接相关的配置属性。spring-doc.cadn.net.cn

使用 Testcontainers 时,可以通过注释测试类中的容器字段来自动为容器中运行的服务创建连接详细信息。spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

    @Container
    @ServiceConnection
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

    @Test
    void myTest() {
        // ...
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

    @Test
    fun myTest() {
        // ...
    }

    companion object {

        @Container
        @ServiceConnection
        val neo4j = Neo4jContainer("neo4j:5")

    }

}

由于@ServiceConnection,上述配置允许应用程序中的 Neo4j 相关 bean 与在 Testcontainers 管理的 Docker 容器内运行的 Neo4j 进行通信。 这是通过自动定义Neo4jConnectionDetailsbean,然后由 Neo4j 自动配置使用,覆盖任何与连接相关的配置属性。spring-doc.cadn.net.cn

您需要添加spring-boot-testcontainers模块作为测试依赖项,以便将服务连接与 Testcontainers 一起使用。

服务连接注释由ContainerConnectionDetailsFactory注册的类spring.factories. 一个ContainerConnectionDetailsFactory可以创建一个ConnectionDetailsbean 基于特定的Container子类或 Docker 镜像名称。spring-doc.cadn.net.cn

以下服务连接工厂在spring-boot-testcontainers罐:spring-doc.cadn.net.cn

连接详细信息 匹配时间

ActiveMQConnectionDetailsspring-doc.cadn.net.cn

名为“symptoma/activemq”的容器spring-doc.cadn.net.cn

CassandraConnectionDetailsspring-doc.cadn.net.cn

类型CassandraContainerspring-doc.cadn.net.cn

CouchbaseConnectionDetailsspring-doc.cadn.net.cn

类型CouchbaseContainerspring-doc.cadn.net.cn

ElasticsearchConnectionDetailsspring-doc.cadn.net.cn

类型ElasticsearchContainerspring-doc.cadn.net.cn

FlywayConnectionDetailsspring-doc.cadn.net.cn

类型JdbcDatabaseContainerspring-doc.cadn.net.cn

JdbcConnectionDetailsspring-doc.cadn.net.cn

类型JdbcDatabaseContainerspring-doc.cadn.net.cn

KafkaConnectionDetailsspring-doc.cadn.net.cn

类型org.testcontainers.containers.KafkaContainerRedpandaContainerspring-doc.cadn.net.cn

LiquibaseConnectionDetailsspring-doc.cadn.net.cn

类型JdbcDatabaseContainerspring-doc.cadn.net.cn

MongoConnectionDetailsspring-doc.cadn.net.cn

类型MongoDBContainerspring-doc.cadn.net.cn

Neo4jConnectionDetailsspring-doc.cadn.net.cn

类型Neo4jContainerspring-doc.cadn.net.cn

OtlpMetricsConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”的容器spring-doc.cadn.net.cn

OtlpTracingConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”的容器spring-doc.cadn.net.cn

PulsarConnectionDetailsspring-doc.cadn.net.cn

类型PulsarContainerspring-doc.cadn.net.cn

R2dbcConnectionDetailsspring-doc.cadn.net.cn

类型MariaDBContainer,MSSQLServerContainer,MySQLContainer,OracleContainerPostgreSQLContainerspring-doc.cadn.net.cn

RabbitConnectionDetailsspring-doc.cadn.net.cn

类型RabbitMQContainerspring-doc.cadn.net.cn

RedisConnectionDetailsspring-doc.cadn.net.cn

名为“redis”的容器spring-doc.cadn.net.cn

ZipkinConnectionDetailsspring-doc.cadn.net.cn

名为“openzipkin/zipkin”的容器spring-doc.cadn.net.cn

默认情况下,将为给定的 Bean 创建所有适用的连接详细信息 BeanContainer. 例如,一个PostgreSQLContainer将创建两者JdbcConnectionDetailsR2dbcConnectionDetails.spring-doc.cadn.net.cn

如果只想创建适用类型的子集,可以使用type属性@ServiceConnection.spring-doc.cadn.net.cn

默认情况下Container.getDockerImageName().getRepository()用于获取用于查找连接详细信息的名称。 Docker 映像名称的存储库部分忽略任何注册表和版本。 只要 Spring Boot 能够获取Container,当使用static字段,如上例所示。spring-doc.cadn.net.cn

如果您使用@Bean方法,Spring Boot 不会调用 bean 方法来获取 Docker 镜像名称,因为这会导致急切初始化问题。 相反,bean 方法的返回类型用于找出应该使用哪个连接详细信息。 只要您使用类型化容器,这就有效,例如Neo4jContainerRabbitMQContainer. 如果您正在使用GenericContainer,例如使用 Redis,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.testcontainers.containers.GenericContainer;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;

@TestConfiguration(proxyBeanMethods = false)
public class MyRedisConfiguration {

    @Bean
    @ServiceConnection(name = "redis")
    public GenericContainer<?> redisContainer() {
        return new GenericContainer<>("redis:7");
    }

}
Kotlin
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.containers.GenericContainer

@TestConfiguration(proxyBeanMethods = false)
class MyRedisConfiguration {

    @Bean
    @ServiceConnection(name = "redis")
    fun redisContainer(): GenericContainer<*> {
        return GenericContainer("redis:7")
    }

}

Spring Boot 无法从GenericContainer使用哪个容器镜像,因此name属性从@ServiceConnection必须用于提供该提示。spring-doc.cadn.net.cn

您还可以使用name属性@ServiceConnection以覆盖将使用的连接详细信息,例如在使用自定义图像时。 如果您使用的是 Docker 镜像registry.mycompany.com/mirror/myredis,你会使用@ServiceConnection(name="redis")确保RedisConnectionDetails被创建。spring-doc.cadn.net.cn

9.4.2. 动态属性

服务连接的一个稍微冗长但更灵活的替代方案是@DynamicPropertySource. 静态@DynamicPropertySource方法允许向 Spring 环境添加动态属性值。spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

    @Container
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

    @Test
    void myTest() {
        // ...
    }

    @DynamicPropertySource
    static void neo4jProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

    @Test
    fun myTest() {
        // ...
    }

    companion object {

        @Container
        val neo4j = Neo4jContainer("neo4j:5")

        @DynamicPropertySource
        fun neo4jProperties(registry: DynamicPropertyRegistry) {
            registry.add("spring.neo4j.uri") { neo4j.boltUrl }
        }

    }

}

上述配置允许应用程序中与 Neo4j 相关的 Bean 与在 Testcontainers 管理的 Docker 容器内运行的 Neo4j 进行通信。spring-doc.cadn.net.cn

9.5. 测试实用程序

一些在测试应用程序时通常有用的测试实用程序类被打包为spring-boot.spring-doc.cadn.net.cn

9.5.1. ConfigDataApplicationContext初始化器

ConfigDataApplicationContextInitializer是一个ApplicationContextInitializer您可以将其应用于测试以加载 Spring Bootapplication.properties文件。 当不需要@SpringBootTest,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;

@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {

    // ...

}
Kotlin
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer
import org.springframework.test.context.ContextConfiguration

@ContextConfiguration(classes = [Config::class], initializers = [ConfigDataApplicationContextInitializer::class])
class MyConfigFileTests {

    // ...

}
ConfigDataApplicationContextInitializer单独不提供支持@Value("${…​}")注射。 它唯一的工作是确保application.properties文件被加载到 Spring 的Environment. 为@Value支持,您需要额外配置一个PropertySourcesPlaceholderConfigurer或使用@SpringBootTest,它会自动为您配置一个。

9.5.2. TestProperty值

TestPropertyValues允许您快速将属性添加到ConfigurableEnvironmentConfigurableApplicationContext. 你可以用key=value字符串,如下所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment;

import static org.assertj.core.api.Assertions.assertThat;

class MyEnvironmentTests {

    @Test
    void testPropertySources() {
        MockEnvironment environment = new MockEnvironment();
        TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
        assertThat(environment.getProperty("name")).isEqualTo("Boot");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.test.util.TestPropertyValues
import org.springframework.mock.env.MockEnvironment

class MyEnvironmentTests {

    @Test
    fun testPropertySources() {
        val environment = MockEnvironment()
        TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment)
        assertThat(environment.getProperty("name")).isEqualTo("Boot")
    }

}

9.5.3. 输出捕获

OutputCapture是 JUnitExtension可用于捕获System.outSystem.err输出。 要使用它,请添加@ExtendWith(OutputCaptureExtension.class)并注入CapturedOutput作为测试类构造函数或测试方法的参数,如下所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {

    @Test
    void testName(CapturedOutput output) {
        System.out.println("Hello World!");
        assertThat(output).contains("World");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.system.CapturedOutput
import org.springframework.boot.test.system.OutputCaptureExtension

@ExtendWith(OutputCaptureExtension::class)
class MyOutputCaptureTests {

    @Test
    fun testName(output: CapturedOutput?) {
        println("Hello World!")
        assertThat(output).contains("World")
    }

}

9.5.4. TestRest模板

TestRestTemplate是 Spring 的RestTemplate这在集成测试中很有用。 您可以获得一个普通模板或发送基本 HTTP 身份验证(使用用户名和密码)的模板。 无论哪种情况,模板都是容错的。 这意味着它以测试友好的方式运行,不会在 4xx 和 5xx 错误上抛出异常。 相反,可以通过返回的ResponseEntity及其状态代码。spring-doc.cadn.net.cn

Spring Framework 5.0 提供了一个新的WebTestClient适用于 WebFlux 集成测试以及 WebFlux 和 MVC 端到端测试。 它为断言提供了一个流畅的 API,这与TestRestTemplate.

建议(但不是强制性)使用 Apache HTTP 客户端(版本 5.1 或更高版本)。 如果你的类路径上有它,那么TestRestTemplate通过适当配置客户端来响应。 如果您确实使用 Apache 的 HTTP 客户端,则会启用一些额外的测试友好功能:spring-doc.cadn.net.cn

TestRestTemplate可以直接在集成测试中实例化,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.assertThat;

class MyTests {

    private 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");
    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.test.web.client.TestRestTemplate

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_PORTWebEnvironment.DEFINED_PORT,您可以注入完全配置的TestRestTemplate并开始使用它。 如有必要,可以通过RestTemplateBuilder豆。 任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.time.Duration;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {

    @Autowired
    private TestRestTemplate template;

    @Test
    void testRequest() {
        HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
        assertThat(headers.getLocation()).hasHost("other.example.com");
    }

    @TestConfiguration(proxyBeanMethods = false)
    static class RestTemplateBuilderConfiguration {

        @Bean
        RestTemplateBuilder restTemplateBuilder() {
            return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
                .setReadTimeout(Duration.ofSeconds(1));
        }

    }

}
Kotlin
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import java.time.Duration

@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. Docker Compose 支持

Docker Compose 是一种流行的技术,可用于为您的应用程序所需的服务定义和管理多个容器。 一个compose.yml文件通常在定义和配置服务容器的应用程序旁边创建。spring-doc.cadn.net.cn

Docker Compose 的典型工作流程是运行docker compose up,使用它连接到已启动的服务来处理您的应用程序,然后运行docker compose down当你完成时。spring-doc.cadn.net.cn

spring-boot-docker-compose模块可以包含在项目中,以支持使用 Docker Compose 处理容器。 将模块依赖项添加到您的构建中,如以下 Maven 和 Gradle 列表所示:spring-doc.cadn.net.cn

专家
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-docker-compose</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
Gradle
dependencies {
    developmentOnly("org.springframework.boot:spring-boot-docker-compose")
}

当此模块作为依赖项包含在内时,Spring Boot 将执行以下作:spring-doc.cadn.net.cn

如果 Docker Compose 服务在启动应用程序时已经在运行,则 Spring Boot 将仅为每个受支持的容器创建服务连接 bean。 它不会调用docker compose up,它不会调用docker compose stop当应用程序关闭时。spring-doc.cadn.net.cn

默认情况下,重新打包的存档不包含 Spring Boot 的 Docker Compose。 如果要使用此支持,则需要包含它。 使用 Maven 插件时,将excludeDockerCompose属性设置为false. 使用 Gradle 插件时,配置任务的类路径以包含developmentOnly配置.

10.1. 前提条件

您需要拥有dockerdocker compose(或docker-compose) CLI 应用程序。 支持的最低 Docker Compose 版本为 2.2.0。spring-doc.cadn.net.cn

10.2. 服务连接

服务连接是与任何远程服务的连接。 Spring Boot 的自动配置可以使用服务连接的详细信息,并使用它们来建立与远程服务的连接。 执行此作时,连接详细信息优先于任何与连接相关的配置属性。spring-doc.cadn.net.cn

使用 Spring Boot 的 Docker Compose 支持时,服务连接将建立到容器映射的端口。spring-doc.cadn.net.cn

Docker compose 通常以这样一种方式使用,即容器内的端口映射到计算机上的临时端口。 例如,Postgres 服务器可以使用端口 5432 在容器内运行,但在本地映射到完全不同的端口。 服务连接将始终发现并使用本地映射的端口。

使用容器的映像名称建立服务连接。 当前支持以下服务连接:spring-doc.cadn.net.cn

连接详细信息 匹配时间

ActiveMQConnectionDetailsspring-doc.cadn.net.cn

名为“symptoma/activemq”的容器spring-doc.cadn.net.cn

CassandraConnectionDetailsspring-doc.cadn.net.cn

名为“cassandra”的容器spring-doc.cadn.net.cn

ElasticsearchConnectionDetailsspring-doc.cadn.net.cn

名为“elasticsearch”的容器spring-doc.cadn.net.cn

JdbcConnectionDetailsspring-doc.cadn.net.cn

名为“gvenzl/oracle-free”、“gvenzl/oracle-xe”、“mariadb”、“mssql/server”、“mysql”或“postgres”的容器spring-doc.cadn.net.cn

MongoConnectionDetailsspring-doc.cadn.net.cn

名为“mongo”的容器spring-doc.cadn.net.cn

Neo4jConnectionDetailsspring-doc.cadn.net.cn

名为“neo4j”的容器spring-doc.cadn.net.cn

OtlpMetricsConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”的容器spring-doc.cadn.net.cn

OtlpTracingConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”的容器spring-doc.cadn.net.cn

PulsarConnectionDetailsspring-doc.cadn.net.cn

名为“apachepulsar/pulsar”的容器spring-doc.cadn.net.cn

R2dbcConnectionDetailsspring-doc.cadn.net.cn

名为“gvenzl/oracle-free”、“gvenzl/oracle-xe”、“mariadb”、“mssql/server”、“mysql”或“postgres”的容器spring-doc.cadn.net.cn

RabbitConnectionDetailsspring-doc.cadn.net.cn

名为“rabbitmq”的容器spring-doc.cadn.net.cn

RedisConnectionDetailsspring-doc.cadn.net.cn

名为“redis”的容器spring-doc.cadn.net.cn

ZipkinConnectionDetailsspring-doc.cadn.net.cn

名为“openzipkin/zipkin”的容器。spring-doc.cadn.net.cn

10.3. 自定义图像

有时,可能需要使用自己的映像版本来提供服务。您可以使用任何自定义映像,只要其行为方式与标准映像相同即可。具体而言,标准映像支持的任何环境变量也必须在自定义映像中使用。spring-doc.cadn.net.cn

如果您的图片使用不同的名称,您可以在compose.yml文件,以便 Spring Boot 可以提供服务连接。使用名为org.springframework.boot.service-connection以提供服务名称。spring-doc.cadn.net.cn

services:
  redis:
    image: 'mycompany/mycustomredis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.service-connection: redis

10.4. 跳过特定容器

如果您在compose.yml您不想连接到应用程序,您可以使用标签来忽略它。任何标记为org.springframework.boot.ignore将被 Spring Boot 忽略。spring-doc.cadn.net.cn

services:
  redis:
    image: 'redis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.ignore: true

10.5. 使用特定的 Compose 文件

如果 compose 文件与应用程序不在同一目录中,或者名称不同,则可以使用spring.docker.compose.file在你的application.propertiesapplication.yaml指向其他文件。 属性可以定义为精确路径或相对于应用程序的路径。spring-doc.cadn.net.cn

性能
spring.docker.compose.file=../my-compose.yml
亚姆尔
spring:
  docker:
    compose:
      file: "../my-compose.yml"

10.6. 等待容器就绪

由 Docker Compose 启动的容器可能需要一些时间才能完全准备就绪。 检查就绪情况的推荐方法是添加healthcheck部分compose.yml文件。spring-doc.cadn.net.cn

由于这种情况并不少见healthcheck要省略的配置compose.yml文件,Spring Boot 还会直接检查服务就绪情况。 缺省情况下,当可以建立与其映射端口的 TCP/IP 连接时,容器被视为就绪。spring-doc.cadn.net.cn

您可以通过添加org.springframework.boot.readiness-check.tcp.disable标签compose.yml文件。spring-doc.cadn.net.cn

services:
  redis:
    image: 'redis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.readiness-check.tcp.disable: true

您还可以更改application.propertiesapplication.yaml文件:spring-doc.cadn.net.cn

性能
spring.docker.compose.readiness.tcp.connect-timeout=10s
spring.docker.compose.readiness.tcp.read-timeout=5s
亚姆尔
spring:
  docker:
    compose:
      readiness:
        tcp:
          connect-timeout: 10s
          read-timeout: 5s

可以使用以下命令配置总体超时spring.docker.compose.readiness.timeout.spring-doc.cadn.net.cn

10.7. 控制 Docker Compose 生命周期

默认情况下,Spring Boot 调用docker compose up当您的应用程序启动时和docker compose stop当它关闭时。 如果您希望拥有不同的生命周期管理,可以使用spring.docker.compose.lifecycle-management财产。spring-doc.cadn.net.cn

支持以下值:spring-doc.cadn.net.cn

此外,您还可以使用spring.docker.compose.start.command属性更改是否docker compose updocker compose start被使用。 这spring.docker.compose.stop.command允许您配置 ifdocker compose downdocker compose stop被使用。spring-doc.cadn.net.cn

以下示例演示如何配置生命周期管理:spring-doc.cadn.net.cn

性能
spring.docker.compose.lifecycle-management=start-and-stop
spring.docker.compose.start.command=start
spring.docker.compose.stop.command=down
spring.docker.compose.stop.timeout=1m
亚姆尔
spring:
  docker:
    compose:
      lifecycle-management: start-and-stop
      start:
        command: start
      stop:
        command: down
        timeout: 1m

10.8. 激活 Docker Compose 配置文件

Docker Compose 配置文件与 Spring 配置文件类似,因为它们允许您针对特定环境调整 Docker Compose 配置。如果要激活特定的 Docker Compose 配置文件,可以使用spring.docker.compose.profiles.active您的财产application.propertiesapplication.yaml文件:spring-doc.cadn.net.cn

性能
spring.docker.compose.profiles.active=myprofile
亚姆尔
spring:
  docker:
    compose:
      profiles:
        active: "myprofile"

10.9. 在测试中使用Docker Compose

默认情况下,运行测试时禁用 Spring Boot 的 Docker Compose 支持。spring-doc.cadn.net.cn

要在测试中启用 Docker Compose 支持,请将spring.docker.compose.skip.in-testsfalse.spring-doc.cadn.net.cn

使用 Gradle 时,您还需要更改spring-boot-docker-compose依赖项来自developmentOnlytestAndDevelopmentOnly:spring-doc.cadn.net.cn

Gradle
dependencies {
    testAndDevelopmentOnly("org.springframework.boot:spring-boot-docker-compose")
}

11. 测试容器支持

除了使用 Testcontainers 进行集成测试外,还可以在开发时使用它们。 接下来的部分将提供有关这方面的更多详细信息。spring-doc.cadn.net.cn

11.1. 在开发时使用 Testcontainers

这种方法允许开发人员为应用程序所依赖的服务快速启动容器,无需手动配置数据库服务器等内容。 以这种方式使用 Testcontainers 提供了类似于 Docker Compose 的功能,不同之处在于您的容器配置是 Java 而不是 YAML 。spring-doc.cadn.net.cn

要在开发时使用 Testcontainers,您需要使用“test”类路径而不是“main”启动应用程序。 这将允许您访问所有声明的测试依赖项,并为您提供一个自然的位置来编写测试配置。spring-doc.cadn.net.cn

要创建应用程序的测试可启动版本,您应该在src/test目录。 例如,如果您的主应用程序位于src/main/java/com/example/MyApplication.java,您应该创建src/test/java/com/example/TestMyApplication.javaspring-doc.cadn.net.cn

TestMyApplication类可以使用SpringApplication.from(…​)启动真实应用程序的方法:spring-doc.cadn.net.cn

Java
import org.springframework.boot.SpringApplication;

public class TestMyApplication {

    public static void main(String[] args) {
        SpringApplication.from(MyApplication::main).run(args);
    }

}
Kotlin
import org.springframework.boot.fromApplication

fun main(args: Array<String>) {
    fromApplication<MyApplication>().run(*args)
}

您还需要定义Container要与应用程序一起启动的实例。 为此,您需要确保spring-boot-testcontainers模块已添加为testDependency。 完成后,您可以创建一个@TestConfiguration声明@Bean方法。spring-doc.cadn.net.cn

您还可以注释您的@Bean方法@ServiceConnection为了创建ConnectionDetails豆。 有关支持技术的详细信息,请参阅服务连接部分spring-doc.cadn.net.cn

典型的 Testcontainers 配置如下所示:spring-doc.cadn.net.cn

Java
import org.testcontainers.containers.Neo4jContainer;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

    @Bean
    @ServiceConnection
    public Neo4jContainer<?> neo4jContainer() {
        return new Neo4jContainer<>("neo4j:5");
    }

}
Kotlin
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.containers.Neo4jContainer

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

    @Bean
    @ServiceConnection
    fun neo4jContainer(): Neo4jContainer<*> {
        return Neo4jContainer("neo4j:5")
    }

}
的生命周期Containerbean 由 Spring Boot 自动管理。 容器将自动启动和停止。
您可以使用spring.testcontainers.beans.startup属性来更改容器的启动方式。 默认情况下sequential使用启动,但您也可以选择parallel如果您希望并行启动多个容器。

定义测试配置后,您可以使用with(…​)将其附加到测试Starters的方法:spring-doc.cadn.net.cn

Java
import org.springframework.boot.SpringApplication;

public class TestMyApplication {

    public static void main(String[] args) {
        SpringApplication.from(MyApplication::main).with(MyContainersConfiguration.class).run(args);
    }

}
Kotlin
import org.springframework.boot.fromApplication
import org.springframework.boot.with

fun main(args: Array<String>) {
    fromApplication<MyApplication>().with(MyContainersConfiguration::class).run(*args)
}

您现在可以启动TestMyApplication就像你对待任何普通 Java 一样main方法应用程序来启动应用程序及其需要运行的容器。spring-doc.cadn.net.cn

您可以使用 Maven 目标spring-boot:test-run或 Gradle 任务bootTestRun从命令行执行此作。

11.1.1. 在开发时贡献动态属性

如果要在开发时从Container @Bean方法,你可以通过注入一个DynamicPropertyRegistry. 其工作方式与@DynamicPropertySource注解您可以在测试中使用。 它允许您添加容器启动后将可用的属性。spring-doc.cadn.net.cn

典型的配置如下所示:spring-doc.cadn.net.cn

Java
import org.testcontainers.containers.MongoDBContainer;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.DynamicPropertyRegistry;

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

    @Bean
    public MongoDBContainer mongoDbContainer(DynamicPropertyRegistry properties) {
        MongoDBContainer container = new MongoDBContainer("mongo:5.0");
        properties.add("spring.data.mongodb.host", container::getHost);
        properties.add("spring.data.mongodb.port", container::getFirstMappedPort);
        return container;
    }

}
Kotlin
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.test.context.DynamicPropertyRegistry
import org.testcontainers.containers.MongoDBContainer

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

    @Bean
    fun monogDbContainer(properties: DynamicPropertyRegistry): MongoDBContainer {
        var container = MongoDBContainer("mongo:5.0")
        properties.add("spring.data.mongodb.host", container::getHost)
        properties.add("spring.data.mongodb.port", container::getFirstMappedPort)
        return container
    }

}
使用@ServiceConnection建议尽可能使用,但是,对于尚未具有动态属性的技术来说,动态属性可能是一个有用的回退@ServiceConnection支持。

11.1.2. 导入 Testcontainer 声明类

使用 Testcontainers 时的常见模式是声明Container实例作为静态字段。 通常,这些字段直接在测试类上定义。 它们也可以在父类或测试实现的接口上声明。spring-doc.cadn.net.cn

例如,以下内容MyContainers接口声明mongoneo4j器皿:spring-doc.cadn.net.cn

import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;

import org.springframework.boot.testcontainers.service.connection.ServiceConnection;

public interface MyContainers {

    @Container
    @ServiceConnection
    MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");

    @Container
    @ServiceConnection
    Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:5");

}

如果您已经以这种方式定义了容器,或者您只是更喜欢这种样式,则可以导入这些声明类,而不是将容器定义为@Bean方法。 为此,请添加@ImportTestcontainersComments 添加到您的测试配置类中:spring-doc.cadn.net.cn

Java
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.context.ImportTestcontainers;

@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers.class)
public class MyContainersConfiguration {

}
Kotlin
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.context.ImportTestcontainers

@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers::class)
class MyContainersConfiguration
如果您不打算使用服务连接功能,但想使用@DynamicPropertySource相反,请删除@ServiceConnection注释Container领域。 您还可以添加@DynamicPropertySource带注释的方法添加到您的声明类中。

11.1.3. 在开发时将 DevTools 与 Testcontainers 一起使用

使用 devtools 时,您可以使用@RestartScope. 当 devtools 重新启动应用程序时,不会重新创建此类 Bean。 这对于 Testcontainer 特别有用Containerbean,因为尽管应用程序重新启动,它们仍保持其状态。spring-doc.cadn.net.cn

Java
import org.testcontainers.containers.MongoDBContainer;

import org.springframework.boot.devtools.restart.RestartScope;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

    @Bean
    @RestartScope
    @ServiceConnection
    public MongoDBContainer mongoDbContainer() {
        return new MongoDBContainer("mongo:5.0");
    }

}
Kotlin
import org.springframework.boot.devtools.restart.RestartScope
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.containers.MongoDBContainer

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

    @Bean
    @RestartScope
    @ServiceConnection
    fun monogDbContainer(): MongoDBContainer {
        return MongoDBContainer("mongo:5.0")
    }

}
如果您使用的是 Gradle 并想使用此功能,则需要更改spring-boot-devtools依赖项来自developmentOnlytestAndDevelopmentOnly. 默认范围为developmentOnlybootTestRun任务不会获取代码中的更改,因为 devtools 未处于活动状态。

12. 创建您自己的自动配置

如果您在开发共享库的公司工作,或者如果您从事开源或商业库的工作,则可能需要开发自己的自动配置。 自动配置类可以捆绑在外部 jar 中,并且仍然被 Spring Boot 获取。spring-doc.cadn.net.cn

自动配置可以与提供自动配置代码以及您将与之一起使用的典型库的“Starters”相关联。 我们首先介绍构建自己的自动配置所需了解的内容,然后继续执行创建自定义Starters所需的典型步骤spring-doc.cadn.net.cn

12.1. 了解自动配置的 Bean

实现自动配置的类用@AutoConfiguration. 此注释本身是用@Configuration,使自动配置成为标准@Configuration类。 附加@Conditional注释用于约束何时应用自动配置。 通常,自动配置类使用@ConditionalOnClass@ConditionalOnMissingBean附注。 这确保了自动配置仅在找到相关类并且您尚未声明自己的类时才适用@Configuration.spring-doc.cadn.net.cn

12.2. 查找自动配置候选者

Spring Boot 检查是否存在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。 该文件应列出您的配置类,每行一个类名,如以下示例所示:spring-doc.cadn.net.cn

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以使用该字符向导入文件添加注释。#
自动配置只能通过在导入文件中命名来加载。 确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。 此外,自动配置类不应启用组件扫描以查找其他组件。 特定@Import应该使用注释。

如果您的配置需要按特定顺序应用,您可以使用before,beforeName,afterafterName属性@AutoConfiguration注释或专用@AutoConfigureBefore@AutoConfigureAfter附注。 例如,如果您提供特定于 Web 的配置,则可能需要在WebMvcAutoConfiguration.spring-doc.cadn.net.cn

如果您想订购某些不应相互直接了解的自动配置,您还可以使用@AutoConfigureOrder. 该注解与常规@Order注释,但为自动配置类提供专用顺序。spring-doc.cadn.net.cn

与标准一样@Configurationclasses,自动配置类的应用顺序仅影响其 bean 的定义顺序。 随后创建这些 Bean 的顺序不受影响,由每个 Bean 的依赖关系和任何@DependsOn关系。spring-doc.cadn.net.cn

12.3. 条件注释

您几乎总是希望包含一个或多个@Conditional自动配置类上的注释。 这@ConditionalOnMissingBean注释是一个常见的示例,用于允许开发人员在对默认值不满意时覆盖自动配置。spring-doc.cadn.net.cn

Spring Boot 包括许多@Conditional可以通过注释在自己的代码中重用的注释@Configuration班级或个人@Bean方法。 这些注释包括:spring-doc.cadn.net.cn

12.3.1. 类条件

@ConditionalOnClass@ConditionalOnMissingClass注释让@Configuration根据特定类的存在与否来包含类。 由于注释元数据是使用 ASM 解析的,因此您可以使用value属性来引用真实的类,即使该类实际上可能不会出现在正在运行的应用程序类路径上。 您还可以使用name属性,如果您更喜欢使用String价值。spring-doc.cadn.net.cn

此机制不以同样的方式应用于@Bean通常返回类型是条件目标的方法:在方法上的条件应用之前,JVM 将加载类并可能处理方法引用,如果类不存在,这些引用将失败。spring-doc.cadn.net.cn

为了处理这种情况,请单独的@Configurationclass 可用于隔离条件,如以下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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();
        }

    }

}
Kotlin
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
// Some conditions ...
class MyAutoConfiguration {

    // Auto-configured beans ...
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(SomeService::class)
    class SomeServiceConfiguration {

        @Bean
        @ConditionalOnMissingBean
        fun someService(): SomeService {
            return SomeService()
        }

    }

}
如果您使用@ConditionalOnClass@ConditionalOnMissingClass作为元注释的一部分,要编写您自己的组合注释,您必须使用name因为在这种情况下引用类不会被处理。

12.3.2. Bean 条件

@ConditionalOnBean@ConditionalOnMissingBean注释允许根据特定 Bean 的存在与否包含 Bean。 您可以使用value属性按类型指定 bean 或name按名称指定 bean。 这search属性允许您限制ApplicationContext搜索 bean 时应考虑的层次结构。spring-doc.cadn.net.cn

当放置在@Bean方法,目标类型默认为方法的返回类型,如下例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
        return new SomeService();
    }

}
Kotlin
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    fun someService(): SomeService {
        return SomeService()
    }

}

在前面的示例中,someService如果没有类型的 bean,则将创建 beanSomeService已包含在ApplicationContext.spring-doc.cadn.net.cn

您需要非常小心添加 bean 定义的顺序,因为这些条件是根据到目前为止处理的内容进行评估的。 因此,我们建议仅使用@ConditionalOnBean@ConditionalOnMissingBean自动配置类上的注释(因为保证在添加任何用户定义的 Bean 定义后加载这些注释)。
@ConditionalOnBean@ConditionalOnMissingBean不阻止@Configuration类的创建。 在类级别使用这些条件与标记每个包含@Bean方法,前者阻止注册@Configurationclass 作为 bean 的 be。
当声明@Bean方法,在方法的返回类型中提供尽可能多的类型信息。 例如,如果 bean 的具体类实现了一个接口,则 bean 方法的返回类型应该是具体类而不是接口。 在@Bean在使用 bean 条件时,方法尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息。

12.3.3. 财产条件

@ConditionalOnProperty注释允许根据 Spring Environment 属性包含配置。 使用prefixname属性来指定应检查的属性。 默认情况下,存在且不等于false匹配。 您还可以使用havingValuematchIfMissing属性。spring-doc.cadn.net.cn

如果在name属性,则所有属性都必须通过测试才能匹配条件。spring-doc.cadn.net.cn

12.3.4. 资源条件

@ConditionalOnResource注释允许仅在存在特定资源时包含配置。 可以使用通常的 Spring 约定来指定资源,如以下示例所示:file:/home/user/test.dat.spring-doc.cadn.net.cn

12.3.5. Web 应用程序条件

@ConditionalOnWebApplication@ConditionalOnNotWebApplication注释允许根据应用程序是否是 Web 应用程序包含配置。 基于 servlet 的 Web 应用程序是使用 Spring 的任何应用程序WebApplicationContext,定义一个sessionscope,或者具有ConfigurableWebEnvironment. 响应式 Web 应用程序是使用ReactiveWebApplicationContext,或者具有ConfigurableReactiveWebEnvironment.spring-doc.cadn.net.cn

@ConditionalOnWarDeployment@ConditionalOnNotWarDeployment注释允许根据应用程序是否是部署到 Servlet 容器的传统 WAR 应用程序来包含配置。 此条件与使用嵌入式 Web 服务器运行的应用程序不匹配。spring-doc.cadn.net.cn

12.3.6. SpEL表达条件

@ConditionalOnExpression注释允许根据 SpEL 表达式的结果包含配置。spring-doc.cadn.net.cn

在表达式中引用 Bean 将导致该 Bean 在上下文刷新处理中非常早地初始化。 因此,Bean 将不符合后处理条件(例如配置属性绑定),并且其状态可能不完整。

12.4. 测试您的自动配置

自动配置可能受到许多因素的影响:用户配置 (@Beandefinition 和Environment定制)、条件评估(是否存在特定库)等。 具体来说,每个测试都应创建一个定义明确的ApplicationContext这代表了这些自定义的组合。ApplicationContextRunner提供了实现这一目标的好方法。spring-doc.cadn.net.cn

ApplicationContextRunner在本机映像中运行测试时不起作用。

ApplicationContextRunner通常定义为测试类的一个字段,用于收集基本的通用配置。以下示例确保MyServiceAutoConfiguration总是被调用:spring-doc.cadn.net.cn

Java
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
Kotlin
val contextRunner = ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration::class.java))
如果必须定义多个自动配置,则无需对其声明进行排序,因为它们的调用顺序与运行应用程序时完全相同。

每个测试都可以使用运行器来表示特定的用例。 例如,下面的示例调用用户配置 (UserConfiguration) 并检查自动配置是否正确回退。 调用run提供了一个回调上下文,可以与AssertJ.spring-doc.cadn.net.cn

Java
@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");
    }

}
Kotlin
@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,如以下示例所示:spring-doc.cadn.net.cn

Java
@Test
void serviceNameCanBeConfigured() {
    this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
        assertThat(context).hasSingleBean(MyService.class);
        assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
    });
}
Kotlin
@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. 报告可打印至INFODEBUG水平。 以下示例演示如何使用ConditionEvaluationReportLoggingListener在自动配置测试中打印报告。spring-doc.cadn.net.cn

Java
import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

class MyConditionEvaluationReportingTests {

    @Test
    void autoConfigTest() {
        new ApplicationContextRunner()
            .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
            .run((context) -> {
                // Test something...
            });
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
import org.springframework.boot.logging.LogLevel
import org.springframework.boot.test.context.assertj.AssertableApplicationContext
import org.springframework.boot.test.context.runner.ApplicationContextRunner

class MyConditionEvaluationReportingTests {

    @Test
    fun autoConfigTest() {
        ApplicationContextRunner()
            .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
            .run { context: AssertableApplicationContext? -> }
    }

}

12.4.1. 模拟 Web 上下文

如果您需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请使用WebApplicationContextRunnerReactiveWebApplicationContextRunner分别。spring-doc.cadn.net.cn

12.4.2. 覆盖类路径

还可以测试当特定类和/或包在运行时不存在时会发生什么。 Spring Boot 附带了一个FilteredClassLoader跑步者可以轻松使用。 在下面的示例中,我们断言如果MyService不存在,则自动配置已正确禁用:spring-doc.cadn.net.cn

Java
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
    this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
        .run((context) -> assertThat(context).doesNotHaveBean("myService"));
}
Kotlin
@Test
fun serviceIsIgnoredIfLibraryIsNotPresent() {
    contextRunner.withClassLoader(FilteredClassLoader(MyService::class.java))
        .run { context: AssertableApplicationContext? ->
            assertThat(context).doesNotHaveBean("myService")
        }
}

12.5. 创建自己的Starters

一个典型的 Spring Boot Starters包含用于自动配置和自定义给定技术的基础设施的代码,我们称之为“acme”。 为了使其易于扩展,可以向环境公开专用命名空间中的许多配置键。 最后,提供了一个“入门”依赖项,以帮助用户尽可能轻松地入门。spring-doc.cadn.net.cn

具体来说,自定义Starters可以包含以下内容:spring-doc.cadn.net.cn

  • autoconfigure包含“acme”的自动配置代码的模块。spring-doc.cadn.net.cn

  • starter模块,该模块为autoconfigure模块以及“acme”和任何其他通常有用的依赖项。 简而言之,添加Starters应该提供开始使用该库所需的一切。spring-doc.cadn.net.cn

这种分为两个模块的分离绝没有必要。 如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的事实。 此外,您还可以制作一个Starters来提供有关这些可选依赖项的意见。 同时,其他人只能依靠autoconfigure模块并制作自己的入门,并提出不同的意见。spring-doc.cadn.net.cn

如果自动配置相对简单且没有可选功能,那么合并Starters中的两个模块绝对是一种选择。spring-doc.cadn.net.cn

12.5.1. 命名

您应该确保为您的Starters提供适当的命名空间。 不要以spring-boot,即使您使用不同的 MavengroupId. 我们可能会在未来为您自动配置的内容提供官方支持。spring-doc.cadn.net.cn

根据经验,您应该以Starters命名组合模块。 例如,假设您正在为“acme”创建一个Starters,并将自动配置模块命名为acme-spring-boot和Startersacme-spring-boot-starter. 如果只有一个模块将两者组合在一起,请将其命名为acme-spring-boot-starter.spring-doc.cadn.net.cn

12.5.2. 配置键

如果您的Starters提供配置键,请为它们使用唯一的命名空间。 特别是,不要在 Spring Boot 使用的命名空间中包含您的密钥(例如server,management,spring,依此类推)。 如果您使用相同的命名空间,我们将来可能会以破坏您的模块的方式修改这些命名空间。 根据经验,在所有键前面加上您拥有的命名空间(例如acme).spring-doc.cadn.net.cn

确保通过为每个属性添加字段 javadoc 来记录配置键,如以下示例所示:spring-doc.cadn.net.cn

Java
import java.time.Duration;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme")
public class AcmeProperties {

    /**
     * Whether to check the location of acme resources.
     */
    private boolean checkLocation = true;

    /**
     * Timeout for establishing a connection to the acme server.
     */
    private Duration loginTimeout = Duration.ofSeconds(3);

    // getters/setters ...

    public boolean isCheckLocation() {
        return this.checkLocation;
    }

    public void setCheckLocation(boolean checkLocation) {
        this.checkLocation = checkLocation;
    }

    public Duration getLoginTimeout() {
        return this.loginTimeout;
    }

    public void setLoginTimeout(Duration loginTimeout) {
        this.loginTimeout = loginTimeout;
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import java.time.Duration

@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 之前不会被处理。

以下是我们在内部遵循的一些规则,以确保描述一致:spring-doc.cadn.net.cn

确保触发元数据生成,以便 IDE 帮助也可用于您的密钥。 您可能需要查看生成的元数据 (META-INF/spring-configuration-metadata.json) 以确保您的密钥已正确记录。 在兼容的 IDE 中使用自己的Starters也是验证元数据质量的好主意。spring-doc.cadn.net.cn

12.5.3. “autoconfigure”模块

autoconfigure模块包含开始使用库所需的一切。 它还可能包含配置键定义(例如@ConfigurationProperties)和任何可用于进一步自定义组件初始化方式的回调接口。spring-doc.cadn.net.cn

您应该将库的依赖项标记为可选,以便可以包含autoconfigure模块。 如果以这种方式执行此作,则不会提供库,并且默认情况下,Spring Boot 会退后。

Spring Boot 使用注释处理器在元数据文件 (META-INF/spring-autoconfigure-metadata.properties). 如果存在该文件,则用于急切过滤不匹配的自动配置,这将缩短启动时间。spring-doc.cadn.net.cn

使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖项:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure-processor</artifactId>
    <optional>true</optional>
</dependency>

如果您已直接在应用程序中定义了自动配置,请确保将spring-boot-maven-plugin以防止repackage将依赖项添加到 Uber jar 中的目标:spring-doc.cadn.net.cn

<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配置,如以下示例所示:spring-doc.cadn.net.cn

dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}

12.5.4. 入门模块

发酵剂真的是一个空罐子。 它的唯一目的是提供使用库所需的依赖项。 您可以将其视为对开始所需内容的固执己见。spring-doc.cadn.net.cn

不要对添加Starters的项目做出假设。 如果您自动配置的库通常需要其他Starters,请同时提及它们。 如果可选依赖项的数量较多,则提供一组适当的默认依赖项可能会很困难,因为您应该避免包含对库的典型使用不需要的依赖项。 换句话说,您不应包含可选的依赖项。spring-doc.cadn.net.cn

无论哪种方式,您的Starters都必须引用核心 Spring Boot Starters(spring-boot-starter)直接或间接(如果您的Starters依赖于另一个Starters,则无需添加它)。 如果仅使用自定义Starters创建项目,则 Spring Boot 的核心功能将因核心Starters的存在而得到尊重。

13. Kotlin 支持

Kotlin 是一种针对 JVM(和其他平台)的静态类型语言,它允许编写简洁而优雅的代码,同时提供与用 Java 编写的现有库的互作性spring-doc.cadn.net.cn

Spring Boot 通过利用其他 Spring 项目(例如 Spring Framework、Spring Data 和 Reactor)中的支持来提供 Kotlin 支持。 有关更多信息,请参阅 Spring Framework Kotlin 支持文档spring-doc.cadn.net.cn

开始使用 Spring Boot 和 Kotlin 的最简单方法是遵循这个综合教程。 您可以使用 start.spring.io 创建新的 Kotlin 项目。 请随时加入 Kotlin Slack 的 #spring 频道或使用springkotlin标签,如果您需要支持。spring-doc.cadn.net.cn

13.1. 要求

Spring Boot 至少需要 Kotlin 1.7.x,并通过依赖管理管理合适的 Kotlin 版本。 要使用 Kotlin,org.jetbrains.kotlin:kotlin-stdliborg.jetbrains.kotlin:kotlin-reflect必须存在于类路径上。 这kotlin-stdlib变种kotlin-stdlib-jdk7kotlin-stdlib-jdk8也可以使用。spring-doc.cadn.net.cn

由于 Kotlin 类默认是最终的,因此您可能希望配置 kotlin-spring 插件,以便自动打开带 Spring 注释的类,以便可以代理它们。spring-doc.cadn.net.cn

在 Kotlin 中序列化/反序列化 JSON 数据需要 Jackson 的 Kotlin 模块。 在类路径上找到它时会自动注册。 如果 Jackson 和 Kotlin 存在,但 Jackson Kotlin 模块不存在,则会记录一条警告消息。spring-doc.cadn.net.cn

如果有人在 start.spring.io 上引导 Kotlin 项目,则默认会提供这些依赖项和插件。

13.2. 空安全

Kotlin 的主要功能之一是空安全。 它涉及null值,而不是将问题推迟到运行时并遇到NullPointerException. 这有助于消除常见的错误来源,而无需支付包装器的成本,例如Optional. Kotlin 还允许使用具有可空值的函数式构造,如本 Kotlin 中空安全综合指南中所述。spring-doc.cadn.net.cn

尽管 Java 不允许在其类型系统中表达空安全性,但 Spring Framework、Spring Data 和 Reactor 现在通过工具友好的注释提供其 API 的空安全性。 默认情况下,Kotlin 中使用的 Java API 中的类型被识别为放宽空检查的平台类型Kotlin 对 JSR 305 注解的支持与可空性注解相结合,为 Kotlin 中的相关 Spring API 提供了空安全性。spring-doc.cadn.net.cn

可以通过添加-Xjsr305compiler 标志,并带有以下选项:-Xjsr305={strict|warn|ignore}. 默认行为与-Xjsr305=warn. 这strictvalue 需要在从 Spring API 推断的 Kotlin 类型中考虑 null 安全性,但应该知道 Spring API 可空性声明即使在次要版本之间也可能发生变化,并且将来可能会添加更多检查)。spring-doc.cadn.net.cn

尚不支持泛型类型参数、varargs 和数组元素可空性。 有关最新信息,请参阅 SPR-15942。 另请注意,Spring Boot 自己的 API 尚未进行注释

13.3. Kotlin API

13.3.1. 运行应用程序

Spring Boot 提供了一种惯用的方式来运行应用程序runApplication<MyApplication>(*args)如以下示例所示:spring-doc.cadn.net.cn

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

这是SpringApplication.run(MyApplication::class.java, *args). 它还允许自定义应用程序,如以下示例所示:spring-doc.cadn.net.cn

runApplication<MyApplication>(*args) {
    setBannerMode(OFF)
}

13.3.2. 扩展

Kotlin 扩展提供了通过附加功能扩展现有类的能力。Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利。spring-doc.cadn.net.cn

TestRestTemplate扩展,类似于 Spring Framework 提供的扩展RestOperations在 Spring Framework 中,都提供了。 除其他外,扩展可以利用 Kotlin 具体化类型参数。spring-doc.cadn.net.cn

13.4. 依赖管理

为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。spring-doc.cadn.net.cn

使用 Maven,可以通过将kotlin.version提供属性和插件管理kotlin-maven-plugin. 使用 Gradle,Spring Boot 插件会自动对齐kotlin.version替换为 Kotlin 插件的版本。spring-doc.cadn.net.cn

Spring Boot 还通过导入 Kotlin 协程 BOM 来管理协程依赖项的版本。 可以通过设置kotlin-coroutines.version财产。spring-doc.cadn.net.cn

org.jetbrains.kotlinx:kotlinx-coroutines-reactor如果一个引导一个 Kotlin 项目,并且至少有一个对 start.spring.io 的响应式依赖项,则默认情况下会提供依赖项。

13.5. @ConfigurationProperties

@ConfigurationProperties当与构造函数结合使用时,绑定支持具有不可变的数据类val属性,如以下示例所示:spring-doc.cadn.net.cn

@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
        val name: String,
        val description: String,
        val myService: MyService) {

    data class MyService(
            val apiToken: String,
            val uri: URI
    )
}

由于它们与 Java 互作性的限制,对值类的支持是有限的。 特别是,依赖值类的默认值将不适用于配置属性绑定。 在这种情况下,应改用数据类。spring-doc.cadn.net.cn

要使用注释处理器生成您自己的元数据kapt应配置使用spring-boot-configuration-processorDependency。 请注意,由于 kapt 提供的模型中的限制,某些功能(例如检测默认值或已弃用的项)无法正常工作。

13.6. 测试

虽然可以使用 JUnit 4 来测试 Kotlin 代码,但默认情况下提供 JUnit 5,建议使用。 JUnit 5 允许对测试类进行一次实例化,并重用于该类的所有测试。 这使得使用@BeforeAll@AfterAll非静态方法的注释,非常适合 Kotlin。spring-doc.cadn.net.cn

要模拟 Kotlin 类,建议使用 MockK。 如果您需要MockK相当于 Mockito 特定的@MockBean@SpyBean附注,您可以使用提供类似的 SpringMockK@MockkBean@SpykBean附注。spring-doc.cadn.net.cn

14. SSL的

Spring Boot 提供了配置 SSL 信任材料的能力,这些材料可以应用于多种类型的连接,以支持安全通信。 配置属性,前缀spring.ssl.bundle可用于指定信任材料和关联信息的命名集。spring-doc.cadn.net.cn

14.1. 使用 Java KeyStore 文件配置 SSL

配置属性,前缀spring.ssl.bundle.jks可用于配置使用 Java 创建的信任材料包keytool实用程序并以 JKS 或 PKCS12 格式存储在 Java KeyStore 文件中。 每个捆绑包都有一个用户提供的名称,可用于引用捆绑包。spring-doc.cadn.net.cn

当用于保护嵌入式 Web 服务器时,keystore通常配置一个包含证书和私钥的 Java 密钥库,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.ssl.bundle.jks.mybundle.key.alias=application
spring.ssl.bundle.jks.mybundle.keystore.location=classpath:application.p12
spring.ssl.bundle.jks.mybundle.keystore.password=secret
spring.ssl.bundle.jks.mybundle.keystore.type=PKCS12
亚姆尔
spring:
  ssl:
    bundle:
      jks:
        mybundle:
          key:
            alias: "application"
          keystore:
            location: "classpath:application.p12"
            password: "secret"
            type: "PKCS12"

当用于保护客户端连接时,truststore通常配置了包含服务器证书的 Java 密钥库,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.ssl.bundle.jks.mybundle.truststore.location=classpath:server.p12
spring.ssl.bundle.jks.mybundle.truststore.password=secret
亚姆尔
spring:
  ssl:
    bundle:
      jks:
        mybundle:
          truststore:
            location: "classpath:server.p12"
            password: "secret"

有关支持的属性的完整集,请参阅 JksSslBundlePropertiesspring-doc.cadn.net.cn

如果使用环境变量来配置捆绑包,则捆绑包的名称始终转换为小写

14.2. 使用 PEM 编码证书配置 SSL

配置属性,前缀spring.ssl.bundle.pem可用于以 PEM 编码文本的形式配置信任材料包。 每个捆绑包都有一个用户提供的名称,可用于引用捆绑包。spring-doc.cadn.net.cn

当用于保护嵌入式 Web 服务器时,keystore通常配置证书和私钥,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.ssl.bundle.pem.mybundle.keystore.certificate=classpath:application.crt
spring.ssl.bundle.pem.mybundle.keystore.private-key=classpath:application.key
亚姆尔
spring:
  ssl:
    bundle:
      pem:
        mybundle:
          keystore:
            certificate: "classpath:application.crt"
            private-key: "classpath:application.key"

当用于保护客户端连接时,truststore通常配置服务器证书,如下例所示:spring-doc.cadn.net.cn

性能
spring.ssl.bundle.pem.mybundle.truststore.certificate=classpath:server.crt
亚姆尔
spring:
  ssl:
    bundle:
      pem:
        mybundle:
          truststore:
            certificate: "classpath:server.crt"

PEM 内容可以直接用于certificateprivate-key性能。 如果属性值包含BEGINEND标记,则它们将被视为 PEM 内容而不是资源位置。spring-doc.cadn.net.cn

以下示例显示了如何定义信任库证书:spring-doc.cadn.net.cn

性能
spring.ssl.bundle.pem.mybundle.truststore.certificate=\
-----BEGIN CERTIFICATE-----\n\
MIID1zCCAr+gAwIBAgIUNM5QQv8IzVQsgSmmdPQNaqyzWs4wDQYJKoZIhvcNAQEL\n\
BQAwezELMAkGA1UEBhMCWFgxEjAQBgNVBAgMCVN0YXRlTmFtZTERMA8GA1UEBwwI\n\
...\n\
V0IJjcmYjEZbTvpjFKznvaFiOUv+8L7jHQ1/Yf+9c3C8gSjdUfv88m17pqYXd+Ds\n\
HEmfmNNjht130UyjNCITmLVXyy5p35vWmdf95U3uEbJSnNVtXH8qRmN9oK9mUpDb\n\
ngX6JBJI7fw7tXoqWSLHNiBODM88fUlQSho8\n\
-----END CERTIFICATE-----\n
亚姆尔
spring:
  ssl:
    bundle:
      pem:
        mybundle:
          truststore:
            certificate: |
              -----BEGIN CERTIFICATE-----
              MIID1zCCAr+gAwIBAgIUNM5QQv8IzVQsgSmmdPQNaqyzWs4wDQYJKoZIhvcNAQEL
              BQAwezELMAkGA1UEBhMCWFgxEjAQBgNVBAgMCVN0YXRlTmFtZTERMA8GA1UEBwwI
              ...
              V0IJjcmYjEZbTvpjFKznvaFiOUv+8L7jHQ1/Yf+9c3C8gSjdUfv88m17pqYXd+Ds
              HEmfmNNjht130UyjNCITmLVXyy5p35vWmdf95U3uEbJSnNVtXH8qRmN9oK9mUpDb
              ngX6JBJI7fw7tXoqWSLHNiBODM88fUlQSho8
              -----END CERTIFICATE-----

有关受支持属性的完整集,请参阅 PemSslBundlePropertiesspring-doc.cadn.net.cn

如果使用环境变量来配置捆绑包,则捆绑包的名称始终转换为小写

14.3. 应用 SSL 捆绑包

使用属性配置后,可以在 Spring Boot 自动配置的各种类型连接的配置属性中按名称引用 SSL 捆绑包。 有关详细信息,请参阅有关嵌入式 Web 服务器数据技术REST 客户端的部分。spring-doc.cadn.net.cn

14.4. 使用 SSL 捆绑包

Spring Boot 自动配置类型为SslBundles提供对使用spring.ssl.bundle性能。spring-doc.cadn.net.cn

SslBundle可以从自动配置的SslBundlesbean 并用于创建用于在客户机库中配置 SSL 连接的对象。 这SslBundle提供了一种获取这些 SSL 对象的分层方法:spring-doc.cadn.net.cn

  • getStores()提供对密钥存储和信任存储的访问java.security.KeyStore实例以及任何所需的密钥存储密码。spring-doc.cadn.net.cn

  • getManagers()提供对java.net.ssl.KeyManagerFactoryjava.net.ssl.TrustManagerFactory实例以及java.net.ssl.KeyManagerjava.net.ssl.TrustManager他们创建的数组。spring-doc.cadn.net.cn

  • createSslContext()提供了一种便捷的方式来获取新的java.net.ssl.SSLContext实例。spring-doc.cadn.net.cn

此外,SslBundle提供有关正在使用的密钥、要使用的协议以及应应用于 SSL 引擎的任何选项的详细信息。spring-doc.cadn.net.cn

以下示例显示了检索SslBundle并使用它来创建一个SSLContext:spring-doc.cadn.net.cn

Java
import javax.net.ssl.SSLContext;

import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.stereotype.Component;

@Component
public class MyComponent {

    public MyComponent(SslBundles sslBundles) {
        SslBundle sslBundle = sslBundles.getBundle("mybundle");
        SSLContext sslContext = sslBundle.createSslContext();
        // do something with the created sslContext
    }

}
Kotlin
import org.springframework.boot.ssl.SslBundles
import org.springframework.stereotype.Component

@Component
class MyComponent(sslBundles: SslBundles) {

    init {
        val sslBundle = sslBundles.getBundle("mybundle")
        val sslContext = sslBundle.createSslContext()
        // do something with the created sslContext
    }

}

14.5. 重新加载 SSL 捆绑包

当密钥材料发生变化时,可以重新加载 SSL 捆绑包。 使用捆绑包的组件必须与可重新加载的 SSL 捆绑包兼容。 目前,以下组件是兼容的:spring-doc.cadn.net.cn

要启用重新加载,您需要通过配置属性选择加入,如以下示例所示:spring-doc.cadn.net.cn

性能
spring.ssl.bundle.pem.mybundle.reload-on-update=true
spring.ssl.bundle.pem.mybundle.keystore.certificate=file:/some/directory/application.crt
spring.ssl.bundle.pem.mybundle.keystore.private-key=file:/some/directory/application.key
亚姆尔
spring:
  ssl:
    bundle:
      pem:
        mybundle:
          reload-on-update: true
          keystore:
            certificate: "file:/some/directory/application.crt"
            private-key: "file:/some/directory/application.key"

然后,文件观察者正在监视文件,如果它们发生变化,则将重新加载 SSL 捆绑包。 这反过来又会触发使用组件中的重新加载,例如 Tomcat 轮换启用了 SSL 的连接器中的证书。spring-doc.cadn.net.cn

您可以使用spring.ssl.bundle.watch.file.quiet-period财产。spring-doc.cadn.net.cn

15. 接下来要读什么

如果您想了解有关本节中讨论的任何类的更多信息,请参阅 Spring Boot API 文档,或者您可以直接浏览源代码。 如果您有具体问题,请参阅作方法部分。spring-doc.cadn.net.cn

如果您对 Spring Boot 的核心功能感到满意,您可以继续阅读有关生产就绪功能的信息。spring-doc.cadn.net.cn