核心功能

1. SpringApplication 应用程序

该类提供了一种便捷的方法来引导从方法启动的 Spring 应用程序。 在许多情况下,您可以委托给 static 方法,如以下示例所示:SpringApplicationmain()SpringApplication.runspring-doc.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.cn

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

2023-11-23 07:23:23.238  INFO 28579 --- [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 1.8.0_392 on myhost with PID 28579 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-11-23 07:23:23.245  INFO 28579 --- [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2023-11-23 07:23:24.552  INFO 28579 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-11-23 07:23:24.567  INFO 28579 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-23 07:23:24.567  INFO 28579 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.83]
2023-11-23 07:23:24.658  INFO 28579 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-11-23 07:23:24.659  INFO 28579 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1346 ms
2023-11-23 07:23:25.336  INFO 28579 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-11-23 07:23:25.355  INFO 28579 --- [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 2.63 seconds (JVM running for 3.003)

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

要在启动期间添加其他日志记录,可以在 的子类中覆盖。logStartupInfo(boolean)SpringApplication

1.1. 启动失败

如果您的应用程序无法启动,注册后将有机会提供专用错误消息和解决问题的具体操作。 例如,如果您在 port 上启动一个 Web 应用程序,并且该端口已在使用中,您应该会看到类似于以下消息的内容:FailureAnalyzers8080spring-doc.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 属性.org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListenerspring-doc.cn

例如,如果您使用 运行应用程序,则可以按如下方式启用该属性:java -jardebugspring-doc.cn

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

1.2. 延迟初始化

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

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

可以使用 on 或 on 方法以编程方式启用延迟初始化。 或者,可以使用以下示例中所示的属性启用它:lazyInitializationSpringApplicationBuildersetLazyInitializationSpringApplicationspring.main.lazy-initializationspring-doc.cn

性能
spring.main.lazy-initialization=true
Yaml
spring:
  main:
    lazy-initialization: true
如果要对某些 bean 禁用延迟初始化,同时对应用程序的其余部分使用延迟初始化,则可以使用 Comments 将其 lazy 属性显式设置为 false。@Lazy(false)

1.3. 自定义横幅

可以通过将文件添加到 Classpath 或将属性设置为此类文件的位置来更改启动时打印的标题。 如果文件的编码不是 UTF-8,则可以设置 . 除了文本文件之外,还可以将 、 或 image 文件添加到 Classpath 中或设置属性。 图像将转换为 ASCII 艺术表示形式,并打印在任何文本横幅上方。banner.txtspring.banner.locationspring.banner.charsetbanner.gifbanner.jpgbanner.pngspring.banner.image.locationspring-doc.cn

在文件中,您可以使用 中的任何可用键以及以下任何占位符:banner.txtEnvironmentspring-doc.cn

表 1.横幅变量
变量 描述

${application.version}spring-doc.cn

应用程序中声明的应用程序版本号 。 例如,打印为 。MANIFEST.MFImplementation-Version: 1.01.0spring-doc.cn

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

应用程序的版本号,在 中声明并格式化为显示(用括号括起来,前缀为 )。 例如。MANIFEST.MFv(v1.0)spring-doc.cn

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

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

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

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

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

其中 是 ANSI 转义码的名称。 有关详细信息,请参阅 AnsiPropertySourceNAMEspring-doc.cn

${application.title}spring-doc.cn

应用程序中声明的应用程序标题。 例如,打印为 .MANIFEST.MFImplementation-Title: MyAppMyAppspring-doc.cn

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

您还可以使用该属性来确定横幅是否必须打印在 () 上、发送到配置的记录器 () 或根本不生成 ()。spring.main.banner-modeSystem.outconsolelogoffspring-doc.cn

打印的横幅将注册为以下名称下的单例 Bean: 。springBootBannerspring-doc.cn

这些 , 和 属性仅在您使用 Spring Boot Starters或与之一起使用时可用。 如果您运行的是解压缩的 jar 并使用 .application.titleapplication.versionapplication.formatted-versionjava -jarjava -cpjava -cp <classpath> <mainclass>spring-doc.cn

要使用这些属性,请使用 java -jar 将应用程序启动为打包的 jar,或使用 java org.springframework.boot.loader.JarLauncher 将应用程序启动为解压缩的 jar。 这将初始化应用程序。banner 属性。application.spring-doc.cn

1.4. 自定义 SpringApplication

如果默认值不符合您的口味,您可以改为创建本地实例并对其进行自定义。 例如,要关闭横幅,您可以编写:SpringApplicationspring-doc.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)
    }
}
传递给的构造函数参数是 Spring bean 的配置源。 在大多数情况下,这些是对类的引用,但它们也可以是直接引用类。SpringApplication@Configuration@Component

也可以使用文件进行配置。 有关详细信息,请参阅 外部化配置SpringApplicationapplication.propertiesspring-doc.cn

有关配置选项的完整列表,请参阅 SpringApplication Javadocspring-doc.cn

1.5. Fluent Builder API

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

这允许您将多个方法调用和包含以及允许您创建层次结构的方法链接在一起,如以下示例所示:SpringApplicationBuilderparentchildspring-doc.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)
创建层次结构时存在一些限制。 例如,Web 组件必须包含在子上下文中,并且父上下文和子上下文都使用相同的 Web 组件。 有关完整详细信息,请参见 SpringApplicationBuilder JavadocApplicationContextEnvironment

1.6. 应用程序可用性

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

此外,您还可以通过将接口注入到您自己的 bean 中来获取可用性状态。ApplicationAvailabilityspring-doc.cn

1.6.1. 活跃状态

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

通常,“Liveness” 状态不应基于外部检查,例如 Health 检查。 如果是这样,则失败的外部系统(数据库、Web API、外部缓存)将在整个平台上触发大规模重启和级联故障。

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

1.6.2. 就绪状态

应用程序的 “Readiness” 状态表明应用程序是否已准备好处理流量。 失败的 “Readiness” 状态告诉平台它暂时不应将流量路由到应用程序。 这通常发生在启动期间、处理组件时,或者如果应用程序决定太忙而无法进行其他流量,则随时发生。CommandLineRunnerApplicationRunnerspring-doc.cn

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

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

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

应用程序组件可以随时通过注入接口并对其调用方法来检索当前可用性状态。 更常见的是,应用程序需要侦听状态更新或更新应用程序的状态。ApplicationAvailabilityspring-doc.cn

例如,我们可以将应用程序的 “Readiness” 状态导出到一个文件中,以便 Kubernetes “exec Probe” 可以查看此文件:spring-doc.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
                break;
            case REFUSING_TRAFFIC:
                // remove file /tmp/healthy
                break;
        }
    }

}
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.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)之外,a 还会发送一些其他应用程序事件。SpringApplicationspring-doc.cn

某些事件实际上是在创建 之前触发的,因此您无法在这些事件上将侦听器注册为 。 您可以使用 method 或 method 注册它们。ApplicationContext@BeanSpringApplication.addListeners(…​)SpringApplicationBuilder.listeners(…​)spring-doc.cn

如果您希望自动注册这些侦听器,而不管应用程序是如何创建的,您都可以将文件添加到项目中并使用 key 引用您的侦听器,如以下示例所示:META-INF/spring.factoriesorg.springframework.context.ApplicationListenerspring-doc.cn

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

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

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

  2. 当 要在上下文中使用的 已知但在创建上下文之前,将发送 An。ApplicationEnvironmentPreparedEventEnvironmentspring-doc.cn

  3. 当准备好并调用ApplicationContextInitializers时,但在加载任何 bean 定义之前,将发送一个。ApplicationContextInitializedEventApplicationContextspring-doc.cn

  4. 在刷新开始之前,但在加载 Bean 定义之后发送 AN。ApplicationPreparedEventspring-doc.cn

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

  6. An 紧随其后发送 with,以指示应用程序被视为实时应用程序。AvailabilityChangeEventLivenessState.CORRECTspring-doc.cn

  7. 在调用任何应用程序和命令行运行程序后发送 AN。ApplicationReadyEventspring-doc.cn

  8. An 紧随其后发送 with,以指示应用程序已准备好为请求提供服务。AvailabilityChangeEventReadinessState.ACCEPTING_TRAFFICspring-doc.cn

  9. 如果启动时出现异常,则发送 An。ApplicationFailedEventspring-doc.cn

上面的列表仅包含绑定到 . 除此之外,以下事件还会在 之后 和 之前发布 :SpringApplicationEventSpringApplicationApplicationPreparedEventApplicationStartedEventspring-doc.cn

  • A 在 准备就绪后发送。 和 分别是 servlet 和 reactive 变体。WebServerInitializedEventWebServerServletWebServerInitializedEventReactiveWebServerInitializedEventspring-doc.cn

  • 刷新 时发送 A。ContextRefreshedEventApplicationContextspring-doc.cn

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

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

要使侦听器能够区分其上下文的事件和后代上下文的事件,它应请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。 可以通过实现来注入上下文,或者,如果侦听器是 bean,则通过使用 。ApplicationContextAware@Autowiredspring-doc.cn

1.8. Web 环境

A 尝试代表您创建正确的 类型。 用于确定 a 的算法如下:SpringApplicationApplicationContextWebApplicationTypespring-doc.cn

  • 如果存在 Spring MVC,则使用AnnotationConfigServletWebServerApplicationContextspring-doc.cn

  • 如果 Spring MVC 不存在而 Spring WebFlux 存在,则使用AnnotationConfigReactiveWebServerApplicationContextspring-doc.cn

  • 否则,使用AnnotationConfigApplicationContextspring-doc.cn

这意味着,如果你在同一个应用程序中使用 Spring MVC 和 Spring WebFlux 中的新版本,则默认情况下将使用 Spring MVC。 您可以通过调用 .WebClientsetWebApplicationType(WebApplicationType)spring-doc.cn

还可以通过调用 .ApplicationContextsetApplicationContextFactory(…​)spring-doc.cn

在 JUnit 测试中使用时,通常需要调用 Call。setWebApplicationType(WebApplicationType.NONE)SpringApplication

1.9. 访问应用程序参数

如果需要访问传递给 的应用程序参数,则可以注入一个 bean。 该接口提供对原始参数以及 parsed 和 arguments 的访问,如以下示例所示:SpringApplication.run(…​)org.springframework.boot.ApplicationArgumentsApplicationArgumentsString[]optionnon-optionspring-doc.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 还向 Spring 注册 a 。 这还允许您使用 annotation 注入单个应用程序参数。CommandLinePropertySourceEnvironment@Value

1.10. 使用 ApplicationRunner 或 CommandLineRunner

如果你需要在启动后运行一些特定的代码,你可以实现 or 接口。 这两个接口的工作方式相同,并提供单个方法,该方法在完成之前调用。SpringApplicationApplicationRunnerCommandLineRunnerrunSpringApplication.run(…​)spring-doc.cn

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

接口以字符串数组的形式提供对应用程序参数的访问,而 则使用前面讨论的接口。 以下示例显示了 with a 方法:CommandLineRunnerApplicationRunnerApplicationArgumentsCommandLineRunnerrunspring-doc.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...
    }

}

如果定义了多个 或 bean,并且必须按特定 Sequences 调用,则还可以实现接口或使用 Comments。CommandLineRunnerApplicationRunnerorg.springframework.core.Orderedorg.springframework.core.annotation.Orderspring-doc.cn

1.11. 应用程序退出

每个 API 都会向 JVM 注册一个 shutdown 钩子,以确保 在退出时正常关闭。 可以使用所有标准的 Spring 生命周期回调(例如接口或 Comments)。SpringApplicationApplicationContextDisposableBean@PreDestroyspring-doc.cn

此外,如果 bean 希望在调用时返回特定的退出代码,则可以实现该接口。 然后,可以将此退出代码传递给 以将其作为状态代码返回,如以下示例所示:org.springframework.boot.ExitCodeGeneratorSpringApplication.exit()System.exit()spring-doc.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)))
}

此外,该接口可能由异常实现。 当遇到此类异常时, Spring Boot 返回已实现的方法提供的退出代码。ExitCodeGeneratorgetExitCode()spring-doc.cn

如果有多个 ,则使用生成的第一个非零退出代码。 要控制生成器的调用顺序,请另外实现接口或使用 Comments。ExitCodeGeneratororg.springframework.core.Orderedorg.springframework.core.annotation.Orderspring-doc.cn

1.12. 管理员功能

可以通过指定属性为应用程序启用与管理员相关的功能。 这会在 platform 上公开 SpringApplicationAdminMXBean。 您可以使用此功能远程管理 Spring Boot 应用程序。 此功能对于任何服务包装器实现也很有用。spring.application.admin.enabledMBeanServerspring-doc.cn

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

1.13. 应用程序启动跟踪

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

您可以在设置实例时选择实施。 例如,要使用 ,您可以编写:ApplicationStartupSpringApplicationBufferingApplicationStartupspring-doc.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)
    }
}

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

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

Spring Boot 附带了该变体;此实现用于缓冲启动步骤并将其排空到外部 Metrics 系统中。 应用程序可以在任何组件中请求 type 为 的 bean。BufferingApplicationStartupBufferingApplicationStartupspring-doc.cn

Spring Boot 还可以配置为公开一个启动端点,该端点将此信息作为 JSON 文档提供。spring-doc.cn

2. 外部化配置

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

属性值可以通过使用 Comments 直接注入到 bean 中,通过 Spring 的抽象访问,或者通过 .@ValueEnvironment@ConfigurationPropertiesspring-doc.cn

Spring Boot 使用一个非常特殊的 Sequences,旨在允许合理地覆盖值。 后面的属性源可以覆盖前面的属性源中定义的值。 按以下顺序考虑源:PropertySourcespring-doc.cn

  1. 默认属性(通过设置 指定)。SpringApplication.setDefaultPropertiesspring-doc.cn

  2. @PropertySource类上的注释。 请注意,在刷新应用程序上下文之前,此类属性源不会添加到 。 现在配置某些属性(例如,在刷新开始之前读取的 and)为时已晚。@ConfigurationEnvironmentlogging.*spring.main.*spring-doc.cn

  3. 配置数据(例如文件)。application.propertiesspring-doc.cn

  4. A 仅在 中具有属性。RandomValuePropertySourcerandom.*spring-doc.cn

  5. OS 环境变量。spring-doc.cn

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

  7. JNDI 属性来自 .java:comp/envspring-doc.cn

  8. ServletContextinit 参数。spring-doc.cn

  9. ServletConfiginit 参数。spring-doc.cn

  10. 属性(嵌入在环境变量或系统属性中的内联 JSON)。SPRING_APPLICATION_JSONspring-doc.cn

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

  12. properties属性。 可用于 @SpringBootTest 和测试注释,用于测试应用程序的特定切片spring-doc.cn

  13. @DynamicPropertySource Comments。spring-doc.cn

  14. @TestPropertySource 测试中的注释。spring-doc.cn

  15. devtools 处于活动状态时目录中的 devtools 全局设置属性$HOME/.config/spring-bootspring-doc.cn

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

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

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

  3. 打包的 jar(和 YAML 变体)之外的应用程序属性application.propertiesspring-doc.cn

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

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

为了提供具体示例,假设您开发了一个使用属性的 a,如以下示例所示:@Componentnamespring-doc.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 中),您可以有一个文件,该文件为 . 在新环境中运行时,可以在 jar 外部提供一个文件,该文件会覆盖 . 对于一次性测试,您可以使用特定的命令行开关(例如)。application.propertiesnameapplication.propertiesnamejava -jar app.jar --name="Spring"spring-doc.cn

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

2.1. 访问命令行属性

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

如果不希望将命令行属性添加到 中,则可以使用 来禁用它们。EnvironmentSpringApplication.setAddCommandLineProperties(false)spring-doc.cn

2.2. JSON 应用程序属性

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

当您的应用程序启动时,将解析 any 或 属性并将其添加到 .spring.application.jsonSPRING_APPLICATION_JSONEnvironmentspring-doc.cn

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

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

在前面的示例中,您最终会在 Spring .my.name=testEnvironmentspring-doc.cn

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

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

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

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

如果要部署到经典 Application Server,则还可以使用名为 的 JNDI 变量。java:comp/env/spring.application.jsonspring-doc.cn

尽管 JSON 中的值将添加到生成的属性源中,但 会将属性视为缺失值。 这意味着 JSON 无法使用值覆盖低阶属性源中的属性。nullPropertySourcesPropertyResolvernullnull

2.3. 外部应用程序属性

Spring Boot 将在应用程序启动时自动从以下位置查找并加载文件:application.propertiesapplication.yamlspring-doc.cn

  1. 从 classpathspring-doc.cn

    1. 类路径根spring-doc.cn

    2. classpath 包/configspring-doc.cn

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

    1. 当前目录spring-doc.cn

    2. 当前目录中的子目录config/spring-doc.cn

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

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

如果您不喜欢作为配置文件名,则可以通过指定 environment 属性来切换到其他文件名。 例如,要查找 and 文件,您可以按如下方式运行应用程序:applicationspring.config.namemyproject.propertiesmyproject.yamlspring-doc.cn

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

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

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

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

如果包含目录(而不是文件),则它们应以 . 在运行时,它们将附加加载之前生成的名称。 中指定的文件将直接导入。spring.config.location/spring.config.namespring.config.locationspring-doc.cn

目录和文件位置值也都进行了扩展,以检查特定于配置文件的文件。 例如,如果您有 of ,您还会发现加载了相应的文件。spring.config.locationclasspath:myconfig.propertiesclasspath:myconfig-<profile>.properties

在大多数情况下,您添加的每个项目都将引用一个文件或目录。 位置按其定义顺序进行处理,较晚的位置可以覆盖较早的位置的值。spring.config.locationspring-doc.cn

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

使用 配置的位置将替换默认位置。 例如,如果配置了值 ,则考虑的完整位置集为:spring.config.locationspring.config.locationoptional:classpath:/custom-config/,optional:file:./custom-config/spring-doc.cn

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

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

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

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

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

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

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

此搜索顺序允许您在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。 您可以在其中一个默认位置 (或您选择的任何其他 basename ) 中为应用程序提供默认值。 然后,可以在运行时使用位于其中一个自定义位置的不同文件覆盖这些默认值。application.propertiesspring.config.namespring-doc.cn

2.3.1. 可选位置

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

如果要指定位置,但不介意它并不总是存在,则可以使用前缀。 你可以将此前缀与and属性以及spring.config.import声明一起使用。optional:spring.config.locationspring.config.additional-locationspring-doc.cn

例如,值 of 允许您的应用程序启动,即使文件缺失也是如此。spring.config.importoptional:file:./myconfig.propertiesmyconfig.propertiesspring-doc.cn

如果要忽略所有并始终继续启动应用程序,则可以使用该属性。 将值设置为 using 或 with a system/environment variable。ConfigDataLocationNotFoundExceptionsspring.config.on-not-foundignoreSpringApplication.setDefaultProperties(…​)spring-doc.cn

2.3.2. 通配符位置

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

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

默认情况下, Spring Boot 包含在默认搜索位置中。 这意味着将搜索 jar 之外的目录的所有子目录。config/*//configspring-doc.cn

您可以自己将通配符位置与 和 属性一起使用。spring.config.locationspring.config.additional-locationspring-doc.cn

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

2.3.3. 分析特定文件

除了属性文件,Spring Boot 还将尝试使用 naming convention 加载特定于配置文件的文件。 例如,如果您的应用程序激活名为 YAML 文件的配置文件并使用 YAML 文件,则 和 都将被考虑。applicationapplication-{profile}prodapplication.yamlapplication-prod.yamlspring-doc.cn

特定于配置文件的属性从与 standard 相同的位置加载,特定于配置文件的文件始终覆盖非特定文件。 如果指定了多个配置文件,则适用 last-wins 策略。 例如,如果配置文件由属性指定,则 中的值可以被 中的值覆盖。application.propertiesprod,livespring.profiles.activeapplication-prod.propertiesapplication-live.propertiesspring-doc.cn

“最后获胜”策略适用于营业地点组级别。 A of 将具有与 .spring.config.locationclasspath:/cfg/,classpath:/ext/classpath:/cfg/;classpath:/ext/spring-doc.cn

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

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

当我们有 a of 时,我们会先处理所有文件,然后再处理所有文件:spring.config.locationclasspath:/cfg/,classpath:/ext//cfg/extspring-doc.cn

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

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

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

当我们有 (带有分隔符) 时,我们会在同一级别处理 and:classpath:/cfg/;classpath:/ext/;/cfg/extspring-doc.cn

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

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

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

具有一组默认配置文件(默认情况下 ),如果未设置活动配置文件,则使用这些配置文件。 换句话说,如果未明确激活任何用户档案,则考虑 中的属性。Environment[default]application-defaultspring-doc.cn

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

2.3.4. 导入其他数据

应用程序属性可以使用该属性从其他位置导入进一步的配置数据。 导入在被发现时进行处理,并被视为插入到声明导入的导入文档的正下方的附加文档。spring.config.importspring-doc.cn

例如,您的 Classpath 文件中可能包含以下内容:application.propertiesspring-doc.cn

性能
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
Yaml
spring:
  application:
    name: "myapp"
  config:
    import: "optional:file:./dev.properties"

这将触发当前目录中文件的导入(如果存在此类文件)。 导入的值将优先于触发导入的文件。 在上面的示例中,可以重新定义为不同的值。dev.propertiesdev.propertiesdev.propertiesspring.application.namespring-doc.cn

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

性能
spring.config.import=my.properties
my.property=value
Yaml
spring:
  config:
    import: "my.properties"
my:
  property: "value"
性能
my.property=value
spring.config.import=my.properties
Yaml
my:
  property: "value"
spring:
  config:
    import: "my.properties"

在上述两个示例中,文件中的值将优先于触发其导入的文件。my.propertiesspring-doc.cn

可以在单个键下指定多个位置。 位置将按照定义的顺序进行处理,以后的导入优先。spring.config.importspring-doc.cn

在适当时,还会考虑导入特定于 Profile 的变体。 上面的示例将导入 both 以及 any variants。my.propertiesmy-<profile>.properties

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

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

如果要支持自己的位置,请参阅包中的 和 类。ConfigDataLocationResolverConfigDataLoaderorg.springframework.boot.context.configspring-doc.cn

2.3.5. 导入无扩展文件

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

例如,假设您有一个希望作为 yaml 导入的文件。 您可以使用以下方法将其导入:/etc/config/myconfigapplication.propertiesspring-doc.cn

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

2.3.6. 使用配置树

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

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

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

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

  2. 多个文件被写入目录树,文件名成为 'key',内容成为 'value'。spring-doc.cn

对于第一种情况,您可以如上所述直接使用 YAML 或 Properties 文件导入。 对于第二种情况,你需要使用前缀,以便 Spring Boot 知道它需要将所有文件作为属性公开。spring.config.importconfigtree:spring-doc.cn

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

etc/
  config/
    myapp/
      username
      password

文件的内容将是 config 值,而 的内容将是 secret。usernamepasswordspring-doc.cn

要导入这些属性,您可以将以下内容添加到您的 or 文件中:application.propertiesapplication.yamlspring-doc.cn

性能
spring.config.import=optional:configtree:/etc/config/
Yaml
spring:
  config:
    import: "optional:configtree:/etc/config/"

然后,你可以以通常的方式从 中访问或注入 和 属性。myapp.usernamemyapp.passwordEnvironmentspring-doc.cn

配置树下的文件夹和文件的名称构成属性名称。 在上面的示例中,要访问属性 as 和 ,您可以设置为 。usernamepasswordspring.config.importoptional:configtree:/etc/config/myapp
带有点表示法的文件名也会正确映射。 例如,在上面的示例中,名为 in 的文件将导致 .myapp.username/etc/configmyapp.usernameEnvironment
配置树值可以绑定到 string 和 types,具体取决于预期的内容。Stringbyte[]

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

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

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

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

性能
spring.config.import=optional:configtree:/etc/config/*/
Yaml
spring:
  config:
    import: "optional:configtree:/etc/config/*/"

这将添加 、 和 属性。db.usernamedb.passwordmq.usernamemq.passwordspring-doc.cn

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

配置树还可用于 Docker 密钥。 当 Docker Swarm 服务被授予对 secret 的访问权限时,secret 将被挂载到容器中。 例如,如果名为 的密钥挂载在 location ,则可以使用以下内容对 Spring 环境可用:db.password/run/secrets/db.passwordspring-doc.cn

性能
spring.config.import=optional:configtree:/run/secrets/
Yaml
spring:
  config:
    import: "optional:configtree:/run/secrets/"

2.3.7. 属性占位符

和 中的值在使用时通过现有值进行筛选,因此您可以引用以前定义的值 (例如,从系统属性或环境变量)。 标准 property-placeholder 语法可以在值中的任何位置使用。 属性占位符还可以使用 a 将默认值与属性名称分开,例如 。application.propertiesapplication.yamlEnvironment${name}:${name:default}spring-doc.cn

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

性能
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
Yaml
app:
  name: "MyApp"
  description: "${app.name} is a Spring Boot application written by ${username:Unknown}"

假设该属性尚未在其他位置设置,则其值为 .usernameapp.descriptionMyApp is a Spring Boot application written by Unknownspring-doc.cn

您应该始终使用其规范形式(kebab-case 仅使用小写字母)在占位符中引用属性名称。 这将允许 Spring Boot 使用与 relaxed binding 时相同的 logic 。@ConfigurationPropertiesspring-doc.cn

例如,将从文件以及系统环境中选取 和 form。 如果你改用,则不会被考虑。${demo.item-price}demo.item-pricedemo.itemPriceapplication.propertiesDEMO_ITEMPRICE${demo.itemPrice}demo.item-priceDEMO_ITEMPRICEspring-doc.cn

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

2.3.8. 使用多文档文件

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

对于文件,使用标准的 YAML 多文档语法。 三个连续的连字符表示一个文档的结尾和下一个文档的开头。application.yamlspring-doc.cn

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

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

对于文件,使用 special 或 comment 来标记文档拆分:application.properties#---!---spring-doc.cn

spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
属性文件分隔符不得有任何前导空格,并且必须恰好包含三个连字符。 紧接在分隔符之前和之后的行不能是相同的注释前缀。
多文档属性文件通常与激活属性(如 . 有关详细信息,请参阅下一节spring.config.activate.on-profile
无法使用 or 注释加载多文档属性文件。@PropertySource@TestPropertySource

2.3.9. 激活属性

有时,仅在满足某些条件时激活一组给定的属性很有用。 例如,您可能具有仅在特定配置文件处于活动状态时才相关的属性。spring-doc.cn

您可以使用 有条件地激活属性文档。spring.config.activate.*spring-doc.cn

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

表 2.激活属性
财产 注意

on-profilespring-doc.cn

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

on-cloud-platformspring-doc.cn

必须检测到 才能使文档处于活动状态。CloudPlatformspring-doc.cn

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

性能
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
Yaml
myprop:
  "always-set"
---
spring:
  config:
    activate:
      on-cloud-platform: "kubernetes"
      on-profile: "prod | staging"
myotherprop: "sometimes-set"

2.4. 加密属性

Spring Boot 不提供对加密属性值的任何内置支持,但是,它确实提供了修改 Spring 中包含的值所需的钩子点。 该界面允许您在应用程序启动之前进行操作。 有关详细信息,请参阅 howto.htmlEnvironmentEnvironmentPostProcessorEnvironmentspring-doc.cn

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

2.5. 使用 YAML

YAML 是 JSON 的超集,因此是指定分层配置数据的便捷格式。 只要你的 Classpath 上有 SnakeYAML 库,该类就会自动支持 YAML 作为属性的替代。SpringApplicationspring-doc.cn

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

2.5.1. 将 YAML 映射到属性

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

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

为了从 访问这些属性,它们将被拼合,如下所示:Environmentspring-doc.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 列表也需要扁平化。 它们表示为带有取消引用器的属性键。 例如,请考虑以下 YAML:[index]spring-doc.cn

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

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

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

2.5.2. 直接加载 YAML

Spring Framework 提供了两个方便的类,可用于加载 YAML 文档。 加载 YAML 作为 YAML,将 YAML 加载为 .YamlPropertiesFactoryBeanPropertiesYamlMapFactoryBeanMapspring-doc.cn

如果要将 YAML 加载为 Spring ,也可以使用该类。YamlPropertySourceLoaderPropertySourcespring-doc.cn

2.6. 配置随机值

这对于注入随机值(例如,注入 secret 或测试用例)很有用。 它可以生成整数、长整型、uuid 或字符串,如以下示例所示:RandomValuePropertySourcespring-doc.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]}
Yaml
my:
  secret: "${random.value}"
  number: "${random.int}"
  bignumber: "${random.long}"
  uuid: "${random.uuid}"
  number-less-than-ten: "${random.int(10)}"
  number-in-range: "${random.int[1024,65536]}"

语法是其中 s 是任何字符 和 整数。 如果提供,则为最小值和最大值(不包括)。random.int*OPEN value (,max) CLOSEOPEN,CLOSEvalue,maxmaxvaluemaxspring-doc.cn

2.7. 配置系统环境属性

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

例如,如果将前缀设置为 ,则诸如 这样的属性也将像在系统环境中一样进行解析。inputremote.timeoutinput.remote.timeoutspring-doc.cn

2.8. 类型安全的配置属性

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

2.8.1. JavaBean 属性绑定

可以绑定声明标准 JavaBean 属性的 Bean,如下例所示:spring-doc.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.cn

  • my.service.enabled,默认值为falsespring-doc.cn

  • my.service.remote-address,其类型可以从 强制转换。Stringspring-doc.cn

  • my.service.security.username,其中包含一个嵌套的 “security” 对象,其名称由属性名称确定。 特别是,该类型根本没有在那里使用,并且可能是 。SecurityPropertiesspring-doc.cn

  • my.service.security.password.spring-doc.cn

  • my.service.security.roles,其中的集合默认为 .StringUSERspring-doc.cn

映射到 Spring Boot 中可用的类的属性(通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公共 API,但类本身的访问器(getter/setter)不能直接使用。@ConfigurationProperties

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

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

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

  • 如果初始化了嵌套的 POJO 属性(如前面示例中的字段),则不需要 setter。 如果希望 Binders 使用其默认构造函数动态创建实例,则需要 setter。Securityspring-doc.cn

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

最后,仅考虑标准 Java Bean 属性,并且不支持对静态属性进行绑定。spring-doc.cn

2.8.2. 构造函数绑定

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

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

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

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

    // fields...

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

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

    // getters...

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

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

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

    public static class Security {

        // fields...

        private final String username;

        private final String password;

        private final List<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.ConstructorBinding
import org.springframework.boot.context.properties.bind.DefaultValue
import java.net.InetAddress

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

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

}

在此设置中,注释用于指示应使用构造函数绑定。 这意味着 Binder 将期望找到一个构造函数,其中包含您希望绑定的参数。 如果您使用的是 Java 16 或更高版本,则可以将构造函数绑定与记录一起使用。 在这种情况下,除非您的记录具有多个构造函数,否则无需使用 .@ConstructorBinding@ConstructorBindingspring-doc.cn

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

可以使用 constructor 参数指定默认值,或者在使用 Java 16 或更高版本时,使用 record 组件指定默认值。 将应用转换服务以将值强制转换为缺失属性的目标类型。@DefaultValueStringspring-doc.cn

参考前面的示例,如果没有属性绑定到 ,则实例将包含 的值 。 要使其包含非 null 实例,即使没有绑定到任何属性(使用 Kotlin 时,这将需要将 和 参数声明为可为 null,因为它们没有默认值),请使用空注解:SecurityMyPropertiesnullsecuritySecurityusernamepasswordSecurity@DefaultValuespring-doc.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>)

}
要使用构造函数绑定,必须使用 or configuration 属性扫描启用该类。 您不能将构造函数绑定与由常规 Spring 机制创建的 bean(例如 bean、使用方法创建的 bean 或使用 bean 加载的 bean )一起使用@EnableConfigurationProperties@Component@Bean@Import)
如果你的类有多个构造函数,你也可以直接在应该绑定的构造函数上使用。@ConstructorBinding
不建议使用 with,因为它主要用作返回类型。 因此,它不太适合 configuration property injection。 为了与其他类型的属性保持一致,如果声明了一个属性并且它没有值,则将绑定一个空值,而不是空值。java.util.Optional@ConfigurationPropertiesOptionalnullOptional

2.8.3. 启用 @ConfigurationProperties 注解类型

Spring Boot 提供了绑定类型并将其注册为 bean 的基础设施。 您可以逐个类启用配置属性,也可以启用配置属性扫描,其工作方式与组件扫描类似。@ConfigurationPropertiesspring-doc.cn

有时,带有 Comments 的类可能不适合扫描,例如,如果您正在开发自己的自动配置或想要有条件地启用它们。 在这些情况下,请使用注释指定要处理的类型列表。 这可以在任何类上完成,如以下示例所示:@ConfigurationProperties@EnableConfigurationProperties@Configurationspring-doc.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

要使用配置属性扫描,请将注释添加到您的应用程序。 通常,它被添加到带有 Comments 的主应用程序类中,但可以添加到任何类中。 默认情况下,将从声明 Comments 的类的包中进行扫描。 如果要定义要扫描的特定包,可以按以下示例所示执行此操作:@ConfigurationPropertiesScan@SpringBootApplication@Configurationspring-doc.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

当使用配置属性 scanning 或通过 注册 bean 时,该 bean 具有约定的名称: ,其中 是注释中指定的环境键前缀,是 bean 的完全限定名称。 如果 Comments 未提供任何前缀,则仅使用 Bean 的完全限定名称。@ConfigurationProperties@EnableConfigurationProperties<prefix>-<fqn><prefix>@ConfigurationProperties<fqn>spring-doc.cn

假设它在包中,则上面示例的 bean 名称为 。com.example.appSomePropertiessome.properties-com.example.app.SomePropertiesspring-doc.cn

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

2.8.4. 使用 @ConfigurationProperties 注解类型

这种配置样式特别适用于外部 YAML 配置,如以下示例所示:SpringApplicationspring-doc.cn

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

要使用 bean,可以像注入任何其他 bean 一样注入它们,如以下示例所示:@ConfigurationPropertiesspring-doc.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()
        // ...
    }

    // ...

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

2.8.5. 第三方配置

除了用于注释类外,您还可以在公共方法上使用它。 当您希望将属性绑定到不受控制的第三方组件时,这样做可能特别有用。@ConfigurationProperties@Beanspring-doc.cn

要从属性中配置 Bean,请添加到其 Bean 注册中,如以下示例所示:Environment@ConfigurationPropertiesspring-doc.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()

}

使用前缀定义的任何 JavaBean 属性都以类似于前面示例的方式映射到该 Bean 上。anotherAnotherComponentSomePropertiesspring-doc.cn

2.8.6. 松散绑定

Spring Boot 使用一些宽松的规则将属性绑定到 bean,因此属性名称和 bean 属性名称之间不需要完全匹配。 这有用的常见示例包括以短划线分隔的环境属性(例如,binds to )和大写的环境属性(例如,binds to )。Environment@ConfigurationPropertiesEnvironmentcontext-pathcontextPathPORTportspring-doc.cn

例如,请考虑以下类:@ConfigurationPropertiesspring-doc.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.cn

表 3.松绑
财产 注意

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

Kebab 大小写,建议在 和 YAML 文件中使用。.propertiesspring-doc.cn

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

标准 Camel 大小写语法。spring-doc.cn

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

下划线表示法,这是在 和 YAML 文件中使用的替代格式。.propertiesspring-doc.cn

MY_MAINPROJECT_PERSON_FIRSTNAMEspring-doc.cn

大写格式,使用系统环境变量时推荐使用。spring-doc.cn

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

属性文件spring-doc.cn

驼峰式大小写、kebab 大小写或下划线表示法spring-doc.cn

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

YAML 文件spring-doc.cn

驼峰式大小写、kebab 大小写或下划线表示法spring-doc.cn

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

环境变量spring-doc.cn

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

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

系统属性spring-doc.cn

驼峰式大小写、kebab 大小写或下划线表示法spring-doc.cn

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

我们建议尽可能以小写 kebab 格式存储属性,例如 .my.person.first-name=Rod
绑定映射

绑定到属性时,可能需要使用特殊的方括号表示法,以便保留原始值。 如果键未被 括起来,则任何非字母数字字符或已删除的字符。Mapkey[]-.spring-doc.cn

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

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

上述属性将绑定到 with 和 作为映射中的键。 斜杠已被删除,因为它没有被方括号括起来。Map/key1/key2key3key3spring-doc.cn

当绑定到标量值时,其中包含的键不需要用 . 标量值包括枚举和包中除 . Binding to 将保留键中的 the 并返回一个条目为 . 对于任何其他类型,如果包含. 例如,绑定到将返回带有条目的 Map,而将返回带有条目的 Map。.[]java.langObjecta.b=cMap<String, String>.{"a.b"="c"}key.a.b=cMap<String, Object>{"a"={"b"="c"}}[a.b]=c{"a.b"="c"}spring-doc.cn

从环境变量绑定

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

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

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

例如,configuration 属性将是名为 .spring.main.log-startup-infoSPRING_MAIN_LOGSTARTUPINFOspring-doc.cn

绑定到对象列表时,也可以使用环境变量。 要绑定到 ,元素编号应在变量名称中用下划线括起来。Listspring-doc.cn

例如,configuration 属性将使用名为 .my.service[0].otherMY_SERVICE_0_OTHERspring-doc.cn

缓存

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

2.8.7. 合并复杂类型

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

例如,假设对象具有 and 属性,默认情况下为 。 以下示例公开了 :MyPojonamedescriptionnullMyPojoMyPropertiesspring-doc.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.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
Yaml
my:
  list:
  - name: "my name"
    description: "my description"
---
spring:
  config:
    activate:
      on-profile: "dev"
my:
  list:
  - name: "my another name"

如果配置文件未处于活动状态,则包含一个条目,如前所述。 但是,如果启用了配置文件,则 只包含一个条目(名称 和 描述 )。 此配置不会向列表添加第二个实例,也不会合并项目。devMyProperties.listMyPojodevlistmy another namenullMyPojospring-doc.cn

在多个配置文件中指定 a 时,将使用优先级最高的配置文件(并且仅使用该配置文件)。 请考虑以下示例:Listspring-doc.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
Yaml
my:
  list:
  - name: "my name"
    description: "my description"
  - name: "another name"
    description: "another description"
---
spring:
  config:
    activate:
      on-profile: "dev"
my:
  list:
  - name: "my another name"

在前面的示例中,如果配置文件处于活动状态,则包含一个条目(名称为 ,描述为 )。 对于 YAML,逗号分隔列表和 YAML 列表都可用于完全覆盖列表的内容。devMyProperties.listMyPojomy another namenullspring-doc.cn

对于属性,您可以绑定从多个源提取的属性值。 但是,对于多个来源中的同一属性,将使用优先级最高的属性。 以下示例公开了 from :MapMap<String, MyPojo>MyPropertiesspring-doc.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.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
Yaml
my:
  map:
    key1:
      name: "my name 1"
      description: "my description 1"
---
spring:
  config:
    activate:
      on-profile: "dev"
my:
  map:
    key1:
      name: "dev name 1"
    key2:
      name: "dev name 2"
      description: "dev description 2"

如果配置文件未处于活动状态,则包含一个具有键的条目(名称为 ,描述为 )。 但是,如果启用配置文件,则包含两个条目,键(名称为,描述为 )和(名称为 ,描述为 )。devMyProperties.mapkey1my name 1my description 1devmapkey1dev name 1my description 1key2dev name 2dev description 2spring-doc.cn

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

2.8.8. 属性转换

Spring Boot 在绑定到 bean 时尝试将外部应用程序属性强制为正确的类型。 如果需要自定义类型转换,则可以提供 bean(带有名为 )或自定义属性编辑器(通过 bean)或自定义(bean定义注释为 )。@ConfigurationPropertiesConversionServiceconversionServiceCustomEditorConfigurerConverters@ConfigurationPropertiesBindingspring-doc.cn

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

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

请考虑以下示例:spring-doc.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 秒的会话超时,则 和 都是等效的。 可以采用以下任何形式指定 500 毫秒的读取超时:和 。30PT30S30s500PT0.5S500msspring-doc.cn

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

默认单位为毫秒,可以使用上面的示例所示进行覆盖。@DurationUnitspring-doc.cn

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

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

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

@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {

    // fields...

    private final Duration sessionTimeout;

    private final Duration readTimeout;

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

    // getters...

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

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

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

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

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

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

该类型从未实际存储周数,它是一个表示“7 天”的快捷方式。java.time.Period
转换数据大小

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

  • 常规表示形式(除非指定了 a,否则使用 bytes 作为默认单位)long@DataSizeUnitspring-doc.cn

  • 值和单位耦合的可读性更强的格式(表示 10 MB)10MBspring-doc.cn

请考虑以下示例:spring-doc.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 MB,并且等效。 可以将 256 字节的大小阈值指定为 或 。1010MB256256Bspring-doc.cn

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

默认单位是 bytes,可以使用上面的示例所示进行覆盖。@DataSizeUnitspring-doc.cn

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

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

@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {

    // fields...

    private final DataSize bufferSize;

    private final DataSize sizeThreshold;

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

    // getters...

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

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

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

@ConfigurationProperties("my")
@ConstructorBinding
class MyProperties(@param:DataSizeUnit(DataUnit.MEGABYTES) @param:DefaultValue("2MB") val bufferSize: DataSize,
        @param:DefaultValue("512B") val sizeThreshold: DataSize)
如果要升级属性,请确保定义单位(如果不是 bytes,则使用 )。 这样做提供了透明的升级路径,同时支持更丰富的格式。Long@DataSizeUnit

2.8.9. @ConfigurationProperties 验证

Spring Boot 尝试在使用 Spring 的 Comments 对类进行 Comments 时对其进行验证。 您可以直接在 configuration 类上使用 JSR-303 约束 Comments。 为此,请确保 Classpath 上具有兼容的 JSR-303 实现,然后将约束 Comments 添加到字段中,如以下示例所示:@ConfigurationProperties@Validatedjavax.validationspring-doc.cn

Java
import java.net.InetAddress;

import javax.validation.constraints.NotNull;

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

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

    @NotNull
    private InetAddress remoteAddress;

    // getters/setters...

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

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

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

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

    var remoteAddress: @NotNull InetAddress? = null

}
您还可以通过使用 注释创建配置属性的方法来触发验证。@Bean@Validated

要确保始终为嵌套属性触发验证,即使未找到任何属性,也必须为关联的字段添加注释。 以下示例基于前面的示例:@ValidMyPropertiesspring-doc.cn

Java
import java.net.InetAddress;

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

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

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

    @NotNull
    private InetAddress remoteAddress;

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

    // getters/setters...

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

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

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

    public static class Security {

        @NotEmpty
        private String username;

        // getters/setters...

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

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

    }

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

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

    var remoteAddress: @NotNull InetAddress? = null

    @Valid
    val security = Security()

    class Security {

        @NotEmpty
        var username: String? = null

    }

}

您还可以通过创建名为 . 该方法应声明 。 配置属性验证器是在应用程序生命周期的早期创建的,将方法声明为 static 可以创建 bean,而不必实例化该类。 这样做可以避免早期实例化可能导致的任何问题。ValidatorconfigurationPropertiesValidator@Beanstatic@Bean@Configurationspring-doc.cn

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

2.8.10. @ConfigurationProperties 与 @Value

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

特征 @ConfigurationProperties @Value

松散的装订spring-doc.cn

是的spring-doc.cn

受限(请参阅下面的注释)spring-doc.cn

元数据支持spring-doc.cn

是的spring-doc.cn

spring-doc.cn

SpEL评估spring-doc.cn

spring-doc.cn

是的spring-doc.cn

如果您确实要使用 ,我们建议您使用属性名称的规范形式(kebab 大小写仅使用小写字母)来引用属性名称。 这将允许 Spring Boot 使用与 relaxed binding 时相同的 logic 。@Value@ConfigurationPropertiesspring-doc.cn

例如,将从文件以及系统环境中选取 和 form。 如果你改用,则不会被考虑。@Value("${demo.item-price}")demo.item-pricedemo.itemPriceapplication.propertiesDEMO_ITEMPRICE@Value("${demo.itemPrice}")demo.item-priceDEMO_ITEMPRICEspring-doc.cn

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

SpEL在解析 Application 属性文件和填充环境时,不会处理这些文件中的表达式。 但是,可以在 中编写表达式。 如果应用程序属性文件中的属性值是表达式,则在通过 .SpEL@ValueSpEL@Valuespring-doc.cn

3. 配置文件

Spring 配置文件提供了一种分离应用程序配置的各个部分并使其仅在某些环境中可用的方法。 Any ,或者可以标记为 to limit 何时加载,如以下示例所示:@Component@Configuration@ConfigurationProperties@Profilespring-doc.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 {

    // ...

}
如果 bean 是通过而不是自动扫描注册的,则需要在具有 Comments 的类上指定 Comments。 在 被扫描的情况下,可以在类本身上指定。@ConfigurationProperties@EnableConfigurationProperties@Profile@Configuration@EnableConfigurationProperties@ConfigurationProperties@Profile@ConfigurationProperties

您可以使用属性来指定哪些配置文件处于活动状态。 您可以使用本章前面介绍的任何方式指定属性。 例如,您可以将其包含在 中,如以下示例所示:spring.profiles.activeEnvironmentapplication.propertiesspring-doc.cn

性能
spring.profiles.active=dev,hsqldb
Yaml
spring:
  profiles:
    active: "dev,hsqldb"

您还可以使用以下开关在命令行中指定它:。--spring.profiles.active=dev,hsqldbspring-doc.cn

如果没有配置文件处于活动状态,则启用默认配置文件。 默认配置文件的名称是 ,并且可以使用 property 进行调整,如以下示例所示:defaultspring.profiles.defaultEnvironmentspring-doc.cn

性能
spring.profiles.default=none
Yaml
spring:
  profiles:
    default: "none"

spring.profiles.active,并且只能在非配置文件特定文档中使用。 这意味着它们不能包含在特定于配置文件的文件或 由 激活的文档中spring.profiles.defaultspring.config.activate.on-profilespring-doc.cn

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

性能
# this document is valid
spring.profiles.active=prod
#---
# this document is invalid
spring.config.activate.on-profile=prod
spring.profiles.active=metrics
Yaml
# 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.activePropertySourceapplication.propertiesspring-doc.cn

有时,将属性添加到活动配置文件中而不是替换它们非常有用。 该属性可用于在由该属性激活的用户档案之上添加活动用户档案。 入口点还具有用于设置其他配置文件的 Java API。 参见SpringApplication中的方法。spring.profiles.includespring.profiles.activeSpringApplicationsetAdditionalProfiles()spring-doc.cn

例如,当运行具有以下属性的应用程序时,即使它使用 --spring.profiles.active 开关运行时,也会激活通用和本地配置文件:spring-doc.cn

性能
spring.profiles.include[0]=common
spring.profiles.include[1]=local
Yaml
spring:
  profiles:
    include:
      - "common"
      - "local"
与 类似,只能在非配置文件特定文档中使用。 这意味着它不能包含在由 激活的特定于配置文件的文件文档中spring.profiles.activespring.profiles.includespring.config.activate.on-profile

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

3.2. 配置文件组

有时,您在应用程序中定义和使用的配置文件过于精细,使用起来变得很麻烦。 例如,您可能拥有用于独立启用数据库和消息传递功能的 and 配置文件。proddbprodmqspring-doc.cn

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

例如,我们可以创建一个由 our 和 profiles 组成的组。productionproddbprodmqspring-doc.cn

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

现在,我们的应用程序可以开始用于一次性激活 和 用户档案。--spring.profiles.active=productionproductionproddbprodmqspring-doc.cn

3.3. 以编程方式设置 Profile

您可以通过在应用程序运行之前调用 来以编程方式设置活动配置文件。 也可以使用 Spring 的界面激活配置文件。SpringApplication.setAdditionalProfiles(…​)ConfigurableEnvironmentspring-doc.cn

3.4. 特定于 Profile 的配置文件

通过 (或 ) 引用的文件的特定于配置文件的变体被视为文件并加载。 有关详细信息,请参阅“分析特定文件”。application.propertiesapplication.yaml@ConfigurationPropertiesspring-doc.cn

4. 日志记录

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

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

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

4.1. 日志格式

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

2023-11-23 07:23:18.861  INFO 28351 --- [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 1.8.0_392 on myhost with PID 28351 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-11-23 07:23:18.868  INFO 28351 --- [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2023-11-23 07:23:20.194  INFO 28351 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-11-23 07:23:20.209  INFO 28351 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-23 07:23:20.209  INFO 28351 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.83]
2023-11-23 07:23:20.342  INFO 28351 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-11-23 07:23:20.342  INFO 28351 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1404 ms
2023-11-23 07:23:20.946  INFO 28351 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-11-23 07:23:20.962  INFO 28351 --- [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 2.7 seconds (JVM running for 3.157)

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

Logback 没有级别。 它映射到 。FATALERROR

4.2. 控制台输出

默认日志配置在写入消息时将消息回显到控制台。 默认情况下,将记录 -level、-level 和 -level 消息。 您还可以通过使用标志启动应用程序来启用 “debug” 模式。ERRORWARNINFO--debugspring-doc.cn

$ java -jar myapp.jar --debug
您还可以在 .debug=trueapplication.properties

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

或者,您可以通过使用标志启动应用程序(或在您的 . 这样做可以为选定的核心 Logger(嵌入式容器、Hibernate 模式生成和整个 Spring 产品组合)启用跟踪日志记录。--tracetrace=trueapplication.propertiesspring-doc.cn

4.2.1. 颜色编码输出

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

颜色编码是使用转换词配置的。 在最简单的形式中,转换器根据对数级别对输出进行着色,如以下示例所示:%clrspring-doc.cn

%clr(%5p)

下表描述了对数级别到颜色的映射:spring-doc.cn

水平 颜色

FATALspring-doc.cn

spring-doc.cn

ERRORspring-doc.cn

spring-doc.cn

WARNspring-doc.cn

黄色spring-doc.cn

INFOspring-doc.cn

绿spring-doc.cn

DEBUGspring-doc.cn

绿spring-doc.cn

TRACEspring-doc.cn

绿spring-doc.cn

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

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

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

4.3. 文件输出

默认情况下, Spring Boot 仅记录到控制台,不写入日志文件。 如果除了控制台输出之外,还想写入日志文件,则需要设置 or 属性(例如,在您的 中)。logging.file.namelogging.file.pathapplication.propertiesspring-doc.cn

下表显示了如何结合使用这些属性:logging.*spring-doc.cn

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

(无)spring-doc.cn

(无)spring-doc.cn

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

特定文件spring-doc.cn

(无)spring-doc.cn

my.logspring-doc.cn

写入指定的日志文件。 名称可以是确切的位置,也可以是相对于当前目录的 名称。spring-doc.cn

(无)spring-doc.cn

特定目录spring-doc.cn

/var/logspring-doc.cn

写入指定目录。 名称可以是确切的位置,也可以是相对于当前目录的 名称。spring.logspring-doc.cn

日志文件在达到 10 MB 时轮换,与控制台输出一样,默认情况下会记录 -level、-level 和 -level 消息。ERRORWARNINFOspring-doc.cn

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

4.4. 文件旋转

如果您使用的是 Logback,则可以使用 or 文件微调日志轮换设置。 对于所有其他日志记录系统,您需要直接自己配置轮换设置(例如,如果您使用 Log4J2,则可以添加 or 文件)。application.propertiesapplication.yamllog4j2.xmllog4j2-spring.xmlspring-doc.cn

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

名字 描述

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

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

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

是否应在应用程序启动时进行日志存档清理。spring-doc.cn

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

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

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

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

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

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

4.5. 日志级别

所有受支持的日志记录系统都可以在 Spring 中设置 Logger 级别(例如,in ),方法是使用 where 是 TRACE、DEBUG、INFO、WARN、ERROR、FATAL 或 OFF 之一。 可以使用 来配置 Logger。Environmentapplication.propertieslogging.level.<logger-name>=<level>levelrootlogging.level.rootspring-doc.cn

以下示例显示了 中的潜在日志记录设置:application.propertiesspring-doc.cn

性能
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
Yaml
logging:
  level:
    root: "warn"
    org.springframework.web: "debug"
    org.hibernate: "error"

还可以使用环境变量设置日志记录级别。 例如,将设置为 .LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUGorg.springframework.webDEBUGspring-doc.cn

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

4.6. 日志组

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

为了帮助解决这个问题, Spring Boot 允许您在 Spring 中定义 logging groups 。 例如,以下是如何通过将 “tomcat” 组添加到您的 :Environmentapplication.propertiesspring-doc.cn

性能
logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat
Yaml
logging:
  group:
    tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat"

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

性能
logging.level.tomcat=trace
Yaml
logging:
  level:
    tomcat: "trace"

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

名字 Logging

Webspring-doc.cn

org.springframework.core.codec, , , ,org.springframework.httporg.springframework.weborg.springframework.boot.actuate.endpoint.weborg.springframework.boot.web.servlet.ServletContextInitializerBeansspring-doc.cn

SQLspring-doc.cn

org.springframework.jdbc.core, ,org.hibernate.SQLorg.jooq.tools.LoggerListenerspring-doc.cn

4.7. 使用日志关闭钩子

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

性能
logging.register-shutdown-hook=false
Yaml
logging:
  register-shutdown-hook: false

4.8. 自定义日志配置

可以通过在 Classpath 中包含适当的库来激活各种日志记录系统,并且可以通过在 Classpath 的根目录中或以下 Spring 属性指定的位置提供合适的配置文件来进一步自定义:。Environmentlogging.configspring-doc.cn

你可以通过使用system属性来强制 Spring Boot 使用特定的日志记录系统。 该值应为 implementation 的完全限定类名。 您还可以使用值 .org.springframework.boot.logging.LoggingSystemLoggingSystemnonespring-doc.cn

由于日志记录是在创建之前初始化的,因此无法从 Spring 文件中控制日志记录。 更改日志记录系统或完全禁用它的唯一方法是通过 System properties。ApplicationContext@PropertySources@Configuration

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

测井系统 定制

Logback (日志返回)spring-doc.cn

logback-spring.xmllogback-spring.groovylogback.xmllogback.groovyspring-doc.cn

日志 4j2spring-doc.cn

log4j2-spring.xmllog4j2.xmlspring-doc.cn

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

logging.propertiesspring-doc.cn

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

为了帮助进行自定义,将一些其他属性从 Spring 属性传输到 System 属性。 这允许记录系统配置来使用属性。例如,在环境变量中设置或设置为环境变量将导致设置 System 属性。 下表描述了传输的属性:Environmentlogging.file.nameapplication.propertiesLOGGING_FILE_NAMELOG_FILEspring-doc.cn

Spring 环境 系统属性 评论

logging.exception-conversion-wordspring-doc.cn

LOG_EXCEPTION_CONVERSION_WORDspring-doc.cn

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

logging.file.namespring-doc.cn

LOG_FILEspring-doc.cn

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

logging.file.pathspring-doc.cn

LOG_PATHspring-doc.cn

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

logging.pattern.consolespring-doc.cn

CONSOLE_LOG_PATTERNspring-doc.cn

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

logging.pattern.dateformatspring-doc.cn

LOG_DATEFORMAT_PATTERNspring-doc.cn

日志日期格式的 Appender 模式。spring-doc.cn

logging.charset.consolespring-doc.cn

CONSOLE_LOG_CHARSETspring-doc.cn

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

logging.pattern.filespring-doc.cn

FILE_LOG_PATTERNspring-doc.cn

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

logging.charset.filespring-doc.cn

FILE_LOG_CHARSETspring-doc.cn

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

logging.pattern.levelspring-doc.cn

LOG_LEVEL_PATTERNspring-doc.cn

渲染日志级别时使用的格式 (default )。%5pspring-doc.cn

PIDspring-doc.cn

PIDspring-doc.cn

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

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

Spring 环境 系统属性 评论

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

LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERNspring-doc.cn

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

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

LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_STARTspring-doc.cn

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

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

LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZEspring-doc.cn

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

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

LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAPspring-doc.cn

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

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

LOGBACK_ROLLINGPOLICY_MAX_HISTORYspring-doc.cn

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

所有受支持的日志记录系统都可以在解析其配置文件时查阅 System properties。 有关示例,请参阅中的默认配置:spring-boot.jarspring-doc.cn

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

您可以通过仅覆盖(或使用 Logback)将 MDC 和其他临时内容添加到日志行中。 例如,如果使用 ,则默认日志格式包含 “user” 的 MDC 条目(如果存在),如以下示例所示。LOG_LEVEL_PATTERNlogging.pattern.levellogging.pattern.level=user:%X{user} %5pspring-doc.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.xmlspring-doc.cn

由于标准配置文件加载得太早,因此不能在其中使用扩展。 您需要使用或定义属性。logback.xmllogback-spring.xmllogging.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. 特定于 Profile 的配置

该标记允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。 元素中的任何位置都支持 Profile 部分。 使用 属性 指定接受配置的配置文件。 标记可以包含配置文件名称(例如 )或配置文件表达式。 配置文件表达式允许表示更复杂的配置文件逻辑,例如 . 有关更多详细信息,请查看参考指南。 以下清单显示了三个示例配置文件:<springProfile><configuration>name<springProfile>stagingproduction & (eu-central | eu-west)spring-doc.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. 环境属性

该标签允许您公开 Spring 中的属性,以便在 Logback 中使用。 如果要在 Logback 配置中访问文件中的值,则这样做可能很有用。 该标签的工作方式与 Logback 的标准标签类似。 但是,您不是指定直接 ,而是指定属性的 (来自 )。 如果需要将属性存储在范围以外的其他位置,则可以使用该属性。 如果需要回退值(如果未在 中设置属性),则可以使用该属性。 以下示例显示了如何公开要在 Logback 中使用的属性:<springProperty>Environmentapplication.properties<property>valuesourceEnvironmentlocalscopeEnvironmentdefaultValuespring-doc.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>
必须在 kebab 大小写中指定 (例如 )。 但是,可以使用宽松规则将属性添加到 中。sourcemy.property-nameEnvironment

5. 国际化

Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户。 默认情况下, Spring Boot 在 Classpath 的根目录中查找是否存在资源包。messagesspring-doc.cn

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

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

性能
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false
Yaml
spring:
  messages:
    basename: "messages,config.i18n.messages"
    fallback-to-system-locale: false
spring.messages.basename支持逗号分隔的位置列表,可以是 package 限定符或从 Classpath 根解析的资源。

有关更多支持的选项,请参见 MessageSourcePropertiesspring-doc.cn

6. 面向方面的编程

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

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

如果 AspectJ 在 Classpath 上,则 Spring Boot 的自动配置将自动启用 AspectJ 自动代理,因此不是必需的。@EnableAspectJAutoProxyspring-doc.cn

7. JSON

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

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

7.1. Jackson

提供了 Jackson 的自动配置,并且 Jackson 是 的一部分。 当 Jackson 位于 Classpath 上时,将自动配置 bean。 提供了几个配置属性,用于自定义 ObjectMapper 的配置spring-boot-starter-jsonObjectMapperspring-doc.cn

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

如果您使用 Jackson 序列化和反序列化 JSON 数据,则可能需要编写自己的 and 类。 自定义序列化器通常通过模块向 Jackson 注册,但 Spring Boot 提供了一个替代 Comments,可以更轻松地直接注册 Spring Beans。JsonSerializerJsonDeserializer@JsonComponentspring-doc.cn

您可以直接在 或 implementations 上使用注释。 您还可以在包含序列化器/反序列化器作为内部类的类上使用它,如以下示例所示:@JsonComponentJsonSerializerJsonDeserializerKeyDeserializerspring-doc.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
import kotlin.jvm.Throws

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

}

中的所有 bean 都会自动注册到 Jackson。 因为 是元注释的,所以通常的组件扫描规则适用。@JsonComponentApplicationContext@JsonComponent@Componentspring-doc.cn

Spring Boot 还提供了JsonObjectSerializerJsonObjectDeserializer基类,它们在序列化对象时为标准 Jackson 版本提供了有用的替代方案。 有关详细信息,请参阅 Javadoc 中的 JsonObjectSerializerJsonObjectDeserializerspring-doc.cn

上面的示例可以重写为使用 /,如下所示:JsonObjectSerializerJsonObjectDeserializerspring-doc.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
import kotlin.jvm.Throws

@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 支持 mixins,可用于将其他注释混合到目标类上已声明的注释中。 Spring Boot 的 Jackson 自动配置将扫描应用程序的包中带有 Comments 的类,并将其注册到自动配置的 . 注册由 Spring Boot 的 .@JsonMixinObjectMapperJsonMixinModulespring-doc.cn

7.2. Gson

提供了 Gson 的自动配置。 当 Gson 位于 Classpath 上时,将自动配置 bean。 提供了多个配置属性用于自定义配置。 为了获得更多控制,可以使用一个或多个 bean。Gsonspring.gson.*GsonBuilderCustomizerspring-doc.cn

7.3. JSON-B

提供了 JSON-B 的自动配置。 当 JSON-B API 和实现在 Classpath 上时,将自动配置一个 bean。 首选的 JSON-B 实现是 Apache Johnzon,它为其提供了依赖项管理。Jsonbspring-doc.cn

8. 任务执行和调度

在上下文中没有 bean 的情况下, Spring Boot 会自动配置一个合理的默认值,这些默认值可以自动与异步任务执行()和 Spring MVC 异步请求处理相关联。ExecutorThreadPoolTaskExecutor@EnableAsyncspring-doc.cn

如果您在上下文中定义了自定义,则常规任务执行(即)将透明地使用它,但不会配置 Spring MVC 支持,因为它需要一个实现(名为 )。 根据您的目标排列方式,您可以将 your 更改为 a 或同时定义 a 和 an 包装您的自定义 .Executor@EnableAsyncAsyncTaskExecutorapplicationTaskExecutorExecutorThreadPoolTaskExecutorThreadPoolTaskExecutorAsyncConfigurerExecutorspring-doc.cn

自动配置允许您轻松创建实例,这些实例可以重现自动配置默认执行的操作。TaskExecutorBuilderspring-doc.cn

线程池使用 8 个核心线程,这些线程可以根据负载进行扩展和收缩。 可以使用命名空间对这些默认设置进行微调,如以下示例所示:spring.task.executionspring-doc.cn

性能
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s
Yaml
spring:
  task:
    execution:
      pool:
        max-size: 16
        queue-capacity: 100
        keep-alive: "10s"

这会将线程池更改为使用有界队列,以便在队列已满(100 个任务)时,线程池增加到最多 16 个线程。 池的收缩更加激进,因为线程在空闲 10 秒(而不是默认 60 秒)时被回收。spring-doc.cn

如果需要将 A 与计划任务执行相关联(例如使用),也可以自动配置 A。 默认情况下,线程池使用一个线程,并且可以使用命名空间对其设置进行微调,如以下示例所示:ThreadPoolTaskScheduler@EnableSchedulingspring.task.schedulingspring-doc.cn

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

如果需要创建自定义执行程序或调度程序,则 Bean 和 Bean 在上下文中都可用。TaskExecutorBuilderTaskSchedulerBuilderspring-doc.cn

9. 测试

Spring Boot 提供了许多 Utilities 和 Comments,以帮助测试您的应用程序。 测试支持由两个模块提供:包含核心项,并支持测试的自动配置。spring-boot-testspring-boot-test-autoconfigurespring-doc.cn

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

如果您有使用 JUnit 4 的测试,则可以使用 JUnit 5 的 vintage 引擎来运行它们。 要使用 vintage 引擎,请添加对 的依赖项,如以下示例所示:junit-vintage-enginespring-doc.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:hamcrestspring-boot-starter-testspring-doc.cn

9.1. 测试范围依赖项

“Starter” (在 中) 包含以下提供的库:spring-boot-starter-testtestscopespring-doc.cn

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

9.2. 测试 Spring 应用程序

依赖项注入的主要优点之一是它应该使您的代码更易于进行单元测试。 您可以使用 operator 实例化对象,甚至无需涉及 Spring。 您还可以使用 mock 对象而不是真正的依赖项。newspring-doc.cn

通常,您需要超越 unit testing 并开始集成测试(使用 Spring)。 能够在不需要部署应用程序或不需要连接到其他基础设施的情况下执行集成测试非常有用。ApplicationContextspring-doc.cn

Spring Framework 包括一个用于此类集成测试的专用测试模块。 您可以直接向 “Starter” 声明依赖项,也可以使用 “Starter” 以传递方式拉取它。org.springframework:spring-testspring-boot-starter-testspring-doc.cn

如果您以前没有使用过该模块,则应首先阅读 Spring Framework 参考文档的相关部分spring-testspring-doc.cn

9.3. 测试 Spring Boot 应用程序

Spring Boot 应用程序是 Spring ,因此除了通常使用普通 Spring 上下文所做的操作之外,无需执行任何特别操作来测试它。ApplicationContextspring-doc.cn

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

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

如果您使用的是 JUnit 4,请不要忘记也添加到您的测试中,否则注释将被忽略。 如果您使用的是 JUnit 5,则无需添加等效的 as,并且其他注释已经使用它进行了注释。@RunWith(SpringRunner.class)@ExtendWith(SpringExtension.class)@SpringBootTest@…​Test

默认情况下,不会启动服务器。 您可以使用 的 属性 of 进一步优化测试的运行方式:@SpringBootTestwebEnvironment@SpringBootTestspring-doc.cn

  • MOCK(默认) :加载 Web 并提供模拟 Web 环境。 使用此注释时,嵌入式服务器不会启动。 如果 Web 环境在 Classpath 上不可用,则此模式透明地回退到创建常规的 non-web 。 它可以与 @AutoConfigureMockMvc@AutoConfigureWebTestClient 结合使用,以便对 Web 应用程序进行基于模拟的测试。ApplicationContextApplicationContextspring-doc.cn

  • RANDOM_PORT:加载 并提供真实的 Web 环境。 嵌入式服务器启动并侦听随机端口。WebServerApplicationContextspring-doc.cn

  • DEFINED_PORT:加载 并提供真实的 Web 环境。 嵌入式服务器启动并侦听定义的端口(来自 )或默认端口 。WebServerApplicationContextapplication.properties8080spring-doc.cn

  • NONE:使用 但不提供任何 Web 环境(模拟或其他)加载。ApplicationContextSpringApplicationspring-doc.cn

如果您的测试是 ,则默认情况下,它会在每个测试方法结束时回滚事务。 但是,由于将这种安排与 OR 一起使用会隐式提供真实的 servlet 环境,因此 HTTP 客户端和服务器在单独的线程中运行,因此在单独的事务中运行。 在这种情况下,在服务器上启动的任何事务都不会回滚。@TransactionalRANDOM_PORTDEFINED_PORT
@SpringBootTest如果您的应用程序对 Management Server 使用不同的端口,则 with 还将在单独的随机端口上启动 Management Server。webEnvironment = WebEnvironment.RANDOM_PORT

9.3.1. 检测 Web 应用程序类型

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

如果两者都存在,则 Spring MVC 优先。 如果要在此场景中测试反应式 Web 应用程序,则必须设置以下属性:spring.main.web-application-typespring-doc.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,则可能习惯于使用来指定要加载的 Spring。 或者,您可能经常在测试中使用嵌套类。@ContextConfiguration(classes=…​)@Configuration@Configurationspring-doc.cn

在测试 Spring Boot 应用程序时,这通常不是必需的。 Spring Boot 的 Comments 会在您未明确定义主配置时自动搜索主配置。@*Testspring-doc.cn

搜索算法从包含测试的包开始工作,直到找到带有 或 注释的类。 只要你以合理的方式构建你的代码,你的主要配置通常会被找到。@SpringBootApplication@SpringBootConfigurationspring-doc.cn

如果使用 test annotation 来测试应用程序的更具体部分,则应避免在 main 方法的 application 类上添加特定于特定区域的配置设置。spring-doc.cn

的底层组件扫描配置定义了用于确保切片按预期工作的排除过滤器。 如果您在 -annotated 类上使用 explicit 指令,请注意这些过滤器将被禁用。 如果您正在使用切片,则应再次定义它们。@SpringBootApplication@ComponentScan@SpringBootApplicationspring-doc.cn

如果要自定义主配置,可以使用嵌套类。 与嵌套类不同,嵌套类将用于代替应用程序的主配置,而嵌套类则除了应用程序的主配置外,还使用嵌套类。@TestConfiguration@Configuration@TestConfigurationspring-doc.cn

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

9.3.3. 排除测试配置

如果您的应用程序使用组件扫描(例如,如果您使用 或 ),您可能会发现仅为特定测试创建的顶级配置类会意外地随处可见。@SpringBootApplication@ComponentScanspring-doc.cn

正如我们之前看到的,可以用于测试的内部类来自定义主要配置。 也可以在顶级类上使用。这样做表示不应通过扫描来选取该类。 然后,您可以在需要的地方显式导入该类,如以下示例所示:@TestConfiguration@TestConfigurationspring-doc.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() {
        // ...
    }

}
如果直接使用 (即,不是通过 ),则需要向 它注册 。 有关详细信息,请参阅 Javadoc@ComponentScan@SpringBootApplicationTypeExcludeFilter
导入的 an 比 inner-class 更早处理,而 imported 将在通过组件扫描找到任何配置之前处理。 一般来说,这种 Sequences 的差异没有明显的影响,但是如果你依赖于 bean 覆盖,则需要注意这一点。@TestConfiguration@TestConfiguration@TestConfiguration

9.3.4. 使用应用程序参数

如果您的应用程序需要参数,则可以 使用 attribute 注入它们。@SpringBootTestargsspring-doc.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.5. 使用 Mock 环境进行测试

默认情况下,不会启动服务器,而是设置用于测试 Web 端点的模拟环境。@SpringBootTestspring-doc.cn

借助 Spring MVC,我们可以使用 MockMvc 或 查询我们的 Web 端点,如以下示例所示:WebTestClientspring-doc.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 图层,而不开始 完整 ,请考虑改用 @WebMvcTestApplicationContext

使用 Spring WebFlux 端点,你可以使用WebTestClient,如以下示例所示:spring-doc.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.cn

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

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

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

该 annotation 可用于将实际使用的端口注入到测试中。 为方便起见,需要对已启动的服务器进行 REST 调用的测试还可以使用 WebTestClient,该客户端解析到正在运行的服务器的相对链接,并附带用于验证响应的专用 API,如以下示例所示:@LocalServerPort@Autowirespring-doc.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

此设置需要在 classpath 上。 如果你不能或不打算添加 webflux,Spring Boot 还提供了一个工具:spring-webfluxTestRestTemplatespring-doc.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.7. 自定义 WebTestClient

要自定义 Bean,请配置 Bean。 任何此类 bean 都使用用于创建 的 调用。WebTestClientWebTestClientBuilderCustomizerWebTestClient.BuilderWebTestClientspring-doc.cn

9.3.8. 使用 JMX

由于测试上下文框架会缓存上下文,因此默认情况下会禁用 JMX,以防止相同的组件在同一域上注册。 如果此类测试需要访问 ,则还要考虑将其标记为 dirty:MBeanServerspring-doc.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 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
import javax.management.MBeanServer

@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {

    @Test
    fun exampleTest() {
        assertThat(mBeanServer.domains).contains("java.lang")
        // ...
    }

}

9.3.9. 使用 Metrics

无论您的 Classpath 如何,在使用 时,不会自动配置计量注册表(内存中支持的除外)。@SpringBootTestspring-doc.cn

如果需要将指标导出到其他后端作为集成测试的一部分,请使用 .@AutoConfigureMetricsspring-doc.cn

9.3.10. Mocking 和 Spying Bean

运行测试时,有时需要模拟应用程序上下文中的某些组件。 例如,您可能在某些远程服务上有一个在开发过程中不可用的门面。 当您想要模拟在真实环境中可能难以触发的故障时,模拟也很有用。spring-doc.cn

Spring Boot 包含一个注释,可用于为 . 您可以使用注释添加新的 bean 或替换单个现有的 bean 定义。 注解可以直接用于测试类、测试中的字段或类和字段。 当用于字段时,创建的 mock 的实例也会被注入。 Mock bean 在每个测试方法之后都会自动重置。@MockBeanApplicationContext@Configurationspring-doc.cn

如果您的测试使用 Spring Boot 的 test annotations(例如 ),则会自动启用此功能。 要将此功能与不同的 arrangement 一起使用,必须显式添加侦听器,如以下示例所示:@SpringBootTestspring-doc.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 {

    // ...

}

以下示例将现有 bean 替换为 mock 实现:RemoteServicespring-doc.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 的行为。 执行测试时,应用程序上下文刷新已完成,配置模拟行为为时已晚。 在这种情况下,我们建议使用一种方法来创建和配置 mock。@Bean

此外,您还可以使用 Mockito 包装任何现有 bean。 有关完整详细信息,请参阅 Javadoc@SpyBeanspyspring-doc.cn

CGLib 代理(例如为作用域 bean 创建的代理)将代理方法声明为 . 这会阻止 Mockito 正常运行,因为它无法模拟或监视其默认配置中的方法。 如果要模拟或监视这样的 bean,请通过添加到应用程序的测试依赖项来配置 Mockito 以使用其内联模拟生成器。 这允许 Mockito 模拟和监视方法。finalfinalorg.mockito:mockito-inlinefinal
虽然 Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但使用或影响缓存键,这很可能会增加上下文的数量。@MockBean@SpyBean
如果用于使用按名称引用参数的方法监视 bean,则必须使用 . 这可确保在监视 Bean 后,参数名称可用于缓存基础结构。@SpyBean@Cacheable-parameters
当您用于监视由 Spring 代理的 bean 时,在某些情况下可能需要删除 Spring 的代理,例如,当使用 or 设置期望时。 用于执行此操作。@SpyBeangivenwhenAopTestUtils.getTargetObject(yourProxiedSpy)

9.3.11. 自动配置的测试

Spring Boot 的自动配置系统适用于应用程序,但有时对于测试来说可能有点太多了。 通常,仅加载测试应用程序的 “slice” 所需的配置部分会有所帮助。 例如,你可能想要测试 Spring MVC 控制器是否正确映射 URL,并且你不想在这些测试中涉及数据库调用,或者你可能想要测试 JPA 实体,并且你对这些测试运行时的 Web 层不感兴趣。spring-doc.cn

该模块包含许多可用于自动配置此类 “切片” 的 Comments。 它们中的每一个都以类似的方式工作,提供一个用于加载 的注释,以及一个或多个可用于自定义自动配置设置的注释。spring-boot-test-autoconfigure@…​TestApplicationContext@AutoConfigure…​spring-doc.cn

每个 slice 将组件扫描限制为适当的组件,并加载一组非常受限的 auto-configuration classes。 如果需要排除其中一个,大多数 Comments 都会提供一个属性。 或者,您也可以使用 .@…​TestexcludeAutoConfiguration@ImportAutoConfiguration#exclude
不支持在一个测试中使用多个 Comments 来包含多个 “slice”。 如果您需要多个 “切片”,请选择其中一个注释并手动包含其他 “切片” 的注释。@…​Test@…​Test@AutoConfigure…​
也可以将 Comments 与标准注释一起使用。 如果您对 “切片” 应用程序不感兴趣,但想要一些自动配置的测试 bean,则可以使用此组合。@AutoConfigure…​@SpringBootTest

9.3.12. 自动配置的 JSON 测试

要测试对象 JSON 序列化和反序列化是否按预期工作,您可以使用注释。 自动配置可用的受支持 JSON 映射器,该映射器可以是以下库之一:@JsonTest@JsonTestspring-doc.cn

附录中提供了 启用的自动配置列表。@JsonTest

如果需要配置自动配置的元素,则可以使用 annotation.@AutoConfigureJsonTestersspring-doc.cn

Spring Boot 包括基于 AssertJ 的帮助程序,这些帮助程序与 JSONAssert 和 JsonPath 库一起使用,以检查 JSON 是否按预期显示。 、、 、 和 类可以分别用于 Jackson、 Gson、 Jsonb 和 Strings。 测试类上的任何帮助程序字段都可以在使用 . 以下示例显示了 Jackson 的 test 类:JacksonTesterGsonTesterJsonbTesterBasicJsonTester@Autowired@JsonTestspring-doc.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 路径上的数字值上断言,则可能无法使用,具体取决于类型。 相反,您可以使用 AssertJ 来断言该值与给定条件匹配。 例如,以下示例断言实际数字是接近偏移量 的 float 值。isEqualTosatisfies0.150.01spring-doc.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.13. 自动配置的 Spring MVC 测试

要测试 Spring MVC 控制器是否按预期工作,请使用 Comments。 自动配置 Spring MVC 基础结构,并将扫描的 bean 限制为 、 和 。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@WebMvcTest@WebMvcTest@Controller@ControllerAdvice@JsonComponentConverterGenericConverterFilterHandlerInterceptorWebMvcConfigurerWebMvcRegistrationsHandlerMethodArgumentResolver@Component@ConfigurationProperties@WebMvcTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@WebMvcTest
如果需要注册额外的组件,例如 Jackson ,则可以在测试中使用导入其他配置类。Module@Import

通常,仅限于单个控制器,并结合使用,为所需的协作者提供模拟实现。@WebMvcTest@MockBeanspring-doc.cn

@WebMvcTest还自动配置 . Mock MVC 提供了一种强大的方法来快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。MockMvcspring-doc.cn

您还可以在非 - (例如 ) 中自动配置,方法是使用 . 以下示例使用 :MockMvc@WebMvcTest@SpringBootTest@AutoConfigureMockMvcMockMvc
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 过滤器),则可以在 Comments 中使用属性。@AutoConfigureMockMvc

如果使用 HtmlUnit 和 Selenium,则自动配置还会提供 HtmlUnit bean 和/或 Selenium bean。 下面的示例使用 HtmlUnit:WebClientWebDriverspring-doc.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 将 bean 放在一个特殊的“范围”中,以确保驱动程序在每次测试后退出并注入新实例。 如果您不希望此行为,可以添加到您的定义中。WebDriver@Scope("singleton")WebDriver@Bean
Spring Boot 创建的范围将替换任何用户定义的同名范围。 如果您定义自己的范围,您可能会发现它在使用 .webDriverwebDriver@WebMvcTest

如果您在 Classpath 上安装了 Spring Security,则还将扫描 bean。 您可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。 有关如何使用 Spring Security 支持的更多详细信息,请参阅此 howto.html操作方法部分。@WebMvcTestWebSecurityConfigurerMockMvcspring-doc.cn

有时编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试

9.3.14. 自动配置的 Spring WebFlux 测试

要测试 Spring WebFlux 控制器是否按预期工作,你可以使用 Comments。 自动配置 Spring WebFlux 基础结构,并将扫描的 bean 限制为 、 和 。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@WebFluxTest@WebFluxTest@Controller@ControllerAdvice@JsonComponentConverterGenericConverterWebFilterWebFluxConfigurer@Component@ConfigurationProperties@WebFluxTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了 启用的自动配置列表。@WebFluxTest
如果需要注册额外的组件,例如 Jackson ,则可以使用 on your test 导入其他配置类。Module@Import

通常,仅限于单个控制器,并与 annotation 结合使用,为所需的协作者提供 mock 实现。@WebFluxTest@MockBeanspring-doc.cn

@WebFluxTest还自动配置了 WebTestClient,它提供了一种强大的方法来快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。spring-doc.cn

您还可以在非 - (例如 ) 中自动配置,方法是使用 . 以下示例显示了一个同时使用 和 a 的类: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 应用程序支持,因为在模拟的 Web 应用程序中使用目前仅适用于 WebFlux。WebTestClient
@WebFluxTest无法检测通过 Functional Web 框架注册的路由。 要在上下文中测试 bean,请考虑使用 或 using 导入您自己。RouterFunctionRouterFunction@Import@SpringBootTest
@WebFluxTest无法检测注册为 类型 的自定义安全配置 。 要将其包含在测试中,您需要导入使用 或 using 注册 bean 的配置。@BeanSecurityWebFilterChain@Import@SpringBootTest
有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试

9.3.15. 自动配置的 Spring GraphQL 测试

Spring GraphQL 提供了一个专用的测试支持模块;您需要将其添加到您的项目中:spring-doc.cn

Maven 系列
<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。 测试器在测试中被大量使用,因此请务必熟悉它的使用。 有一些变体,Spring Boot 将根据测试类型自动配置它们:GraphQlTesterspring-doc.cn

  • 在服务器端执行测试,没有客户端或传输ExecutionGraphQlServiceTesterspring-doc.cn

  • 使用连接到服务器的客户端执行测试,无论是否有实时服务器HttpGraphQlTesterspring-doc.cn

Spring Boot 可帮助您使用注释测试 Spring GraphQL 控制器。 自动配置 Spring GraphQL 基础设施,无需任何传输或服务器。 这会将扫描的 bean 限制为 、 和 。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@GraphQlTest@GraphQlTest@ControllerRuntimeWiringConfigurerJsonComponentConverterGenericConverterDataFetcherExceptionResolverInstrumentationGraphQlSourceBuilderCustomizer@Component@ConfigurationProperties@GraphQlTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了 启用的自动配置列表。@GraphQlTest
如果需要注册额外的组件,例如 Jackson ,则可以使用 on your test 导入其他配置类。Module@Import

通常,仅限于一组控制器,并与 annotation 结合使用,为所需的协作者提供 mock 实现。@GraphQlTest@MockBeanspring-doc.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测试是完整的集成测试,涉及整个应用程序。 当使用随机或定义的端口时,将配置一个实时服务器并自动提供一个 bean,以便您可以使用它来测试您的服务器。 配置 MOCK 环境后,您还可以通过使用以下 Comments 来请求测试类 Bean :HttpGraphQlTesterHttpGraphQlTester@AutoConfigureHttpGraphQlTesterspring-doc.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.16. 自动配置的 Data Cassandra 测试

可用于测试 Cassandra 应用程序。 默认情况下,它配置一个 ,扫描类,并配置 Spring Data Cassandra 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 Cassandra 与 Spring Boot 结合使用的更多信息,请参阅“data.html”。@DataCassandraTestCassandraTemplate@Table@Component@ConfigurationProperties@DataCassandraTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataCassandraTest

以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:spring-doc.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.17. 自动配置的数据 Couchbase 测试

您可以使用它来测试 Couchbase 应用程序。 默认情况下,它配置或扫描类,并配置 Spring Data Couchbase 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 Couchbase 与 Spring Boot 结合使用的更多信息,请参阅本章前面的“data.html”。@DataCouchbaseTestCouchbaseTemplateReactiveCouchbaseTemplate@Document@Component@ConfigurationProperties@DataCouchbaseTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataCouchbaseTest

以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:spring-doc.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.18. 自动配置的数据 Elasticsearch 测试

您可以使用它来测试 Elasticsearch 应用程序。 默认情况下,它会配置一个 、 扫描类,并配置 Spring Data Elasticsearch 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 Elasticsearch 与 Spring Boot 结合使用的更多信息,请参阅本章前面的“data.html”。@DataElasticsearchTestElasticsearchRestTemplate@Document@Component@ConfigurationProperties@DataElasticsearchTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataElasticsearchTest

以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:spring-doc.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.19. 自动配置的数据 JPA 测试

您可以使用注释来测试 JPA 应用程序。 默认情况下,它会扫描类并配置 Spring Data JPA 存储库。 如果 Classpath 上有嵌入式数据库可用,则它也会配置一个。 默认情况下,通过将属性设置为 SQL 查询来记录 SQL 查询。 可以使用 annotation 的属性禁用此功能。@DataJpaTest@Entityspring.jpa.show-sqltrueshowSql()spring-doc.cn

使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@Component@ConfigurationProperties@DataJpaTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataJpaTest

默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework Reference Documentation 中的相关部分。 如果这不是您想要的,则可以禁用测试或整个类的事务 management,如下所示:spring-doc.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 测试还可以注入 TestEntityManager Bean,它为专为测试设计的标准 JPA 提供了替代方案。EntityManagerspring-doc.cn

TestEntityManager也可以通过添加 . 执行此操作时,请确保您的测试在事务中运行,例如通过添加测试类或方法。@AutoConfigureTestEntityManager@Transactional

如果您需要,也可以使用 A。 以下示例显示了正在使用的注释:JdbcTemplate@DataJpaTestspring-doc.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")
    }

}

内存嵌入式数据库通常适用于测试,因为它们速度很快且不需要任何安装。 但是,如果您希望对实际数据库运行测试,则可以使用 Comments,如以下示例所示:@AutoConfigureTestDatabasespring-doc.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.20. 自动配置的 JDBC 测试

@JdbcTest类似于,但适用于只需要 Spring Data JDBC 而不使用 Spring Data JDBC 的测试。 默认情况下,它会配置一个内存中嵌入式数据库和一个 . 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@DataJpaTestDataSourceJdbcTemplate@Component@ConfigurationProperties@JdbcTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了 启用的自动配置列表。@JdbcTest

默认情况下,JDBC 测试是事务性的,并在每个测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework Reference Documentation 中的相关部分。 如果这不是您想要的,则可以为测试或整个类禁用事务管理,如下所示:spring-doc.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

如果您希望测试针对实际数据库运行,则可以像使用 Comments 一样使用 Comments。 (请参阅“自动配置的数据 JPA 测试”)@AutoConfigureTestDatabase@DataJpaTestspring-doc.cn

9.3.21. 自动配置的数据 JDBC 测试

@DataJdbcTest类似于,但适用于使用 Spring Data JDBC 存储库的测试。 默认情况下,它配置内存中嵌入式数据库、 和 Spring Data JDBC 存储库。 使用 Comments 时,仅扫描 sub-classes,不扫描 regular 和 bean。 可用于包含 bean。@JdbcTestJdbcTemplateAbstractJdbcConfiguration@DataJdbcTest@Component@ConfigurationProperties@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了 启用的自动配置列表。@DataJdbcTest

默认情况下,数据 JDBC 测试是事务性的,并在每个测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework Reference Documentation 中的相关部分。 如果这不是您想要的,则可以禁用测试或整个测试类的事务管理,如 JDBC 示例所示spring-doc.cn

如果您希望测试针对实际数据库运行,则可以像使用 Comments 一样使用 Comments。 (请参阅“自动配置的数据 JPA 测试”)@AutoConfigureTestDatabase@DataJpaTestspring-doc.cn

9.3.22. 自动配置的数据 R2DBC 测试

@DataR2dbcTest类似于,但适用于使用 Spring Data R2DBC 存储库的测试。 默认情况下,它配置内存中的嵌入式数据库、 和 Spring Data R2DBC 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@DataJdbcTestR2dbcEntityTemplate@Component@ConfigurationProperties@DataR2dbcTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了 启用的自动配置列表。@DataR2dbcTest

默认情况下,Data R2DBC 测试不是事务性的。spring-doc.cn

如果您希望测试针对实际数据库运行,则可以像使用 Comments 一样使用 Comments。 (请参阅“自动配置的数据 JPA 测试”)@AutoConfigureTestDatabase@DataJpaTestspring-doc.cn

9.3.23. 自动配置的 jOOQ 测试

您可以以与 jOOQ 相关的测试类似的方式使用。 由于 jOOQ 严重依赖与数据库架构相对应的基于 Java 的架构,因此使用 existing。 如果要将其替换为内存中数据库,可以使用 覆盖这些设置。 (有关在 Spring Boot 中使用 jOOQ 的更多信息,请参阅“data.html”。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@JooqTest@JdbcTestDataSource@AutoConfigureTestDatabase@Component@ConfigurationProperties@JooqTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了 启用的自动配置列表。@JooqTest

@JooqTest配置 . 以下示例显示了正在使用的注释:DSLContext@JooqTestspring-doc.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.cn

9.3.24. 自动配置的数据 MongoDB 测试

可用于测试 MongoDB 应用程序。 默认情况下,它会配置内存中的嵌入式 MongoDB(如果可用),配置,扫描类,并配置 Spring Data MongoDB 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 MongoDB 与 Spring Boot 结合使用的更多信息,请参阅“data.html”。@DataMongoTestMongoTemplate@Document@Component@ConfigurationProperties@DataMongoTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataMongoTest

下面的类显示了正在使用的 Annotation:@DataMongoTestspring-doc.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) {

    // ...

}

内存中嵌入式 MongoDB 通常适用于测试,因为它速度快且不需要任何开发人员安装。 但是,如果您希望针对真实的 MongoDB 服务器运行测试,则应排除嵌入式 MongoDB 自动配置,如以下示例所示:spring-doc.cn

Java
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;

@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class MyDataMongoDbTests {

    // ...

}
Kotlin
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest

@DataMongoTest(excludeAutoConfiguration = [EmbeddedMongoAutoConfiguration::class])
class MyDataMongoDbTests {

    // ...

}

9.3.25. 自动配置的数据 Neo4j 测试

您可以使用它来测试 Neo4j 应用程序。 默认情况下,它会扫描类并配置 Spring Data Neo4j 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 Neo4J 与 Spring Boot 结合使用的更多信息,请参阅“data.html”。@DataNeo4jTest@Node@Component@ConfigurationProperties@DataNeo4jTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataNeo4jTest

以下示例显示了在 Spring Boot 中使用 Neo4J 测试的典型设置:spring-doc.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 Reference Documentation 中的相关部分。 如果这不是您想要的,则可以为测试或整个类禁用事务管理,如下所示:spring-doc.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.26. 自动配置的数据 Redis 测试

您可以使用它来测试 Redis 应用程序。 默认情况下,它会扫描类并配置 Spring Data Redis 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 Redis 与 Spring Boot 结合使用的更多信息,请参阅“data.html”。@DataRedisTest@RedisHash@Component@ConfigurationProperties@DataRedisTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataRedisTest

以下示例显示了正在使用的注释:@DataRedisTestspring-doc.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.27. 自动配置的数据 LDAP 测试

可用于测试 LDAP 应用程序。 默认情况下,它会配置内存中的嵌入式 LDAP(如果可用),配置,扫描类,并配置 Spring Data LDAP 存储库。 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。 (有关将 LDAP 与 Spring Boot 结合使用的更多信息,请参阅“data.html”。@DataLdapTestLdapTemplate@Entry@Component@ConfigurationProperties@DataLdapTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@DataLdapTest

以下示例显示了正在使用的注释:@DataLdapTestspring-doc.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.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.28. 自动配置的 REST 客户端

您可以使用注释来测试 REST 客户端。 默认情况下,它会自动配置 Jackson、GSON 和 Jsonb 支持,配置 ,并添加对 . 使用 Comments 时,不会扫描 Regular 和 bean。 可用于包含 bean。@RestClientTestRestTemplateBuilderMockRestServiceServer@Component@ConfigurationProperties@RestClientTest@EnableConfigurationProperties@ConfigurationPropertiesspring-doc.cn

附录中提供了启用的自动配置设置的列表。@RestClientTest

应使用 的 or 属性指定要测试的特定 bean,如以下示例所示:valuecomponents@RestClientTestspring-doc.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 MyRestClientTests {

    @Autowired
    private RemoteVehicleDetailsService service;

    @Autowired
    private MockRestServiceServer server;

    @Test
    void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
        this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
        String greeting = this.service.callRestService();
        assertThat(greeting).isEqualTo("hello");
    }

}
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 MyRestClientTests(
    @Autowired val service: RemoteVehicleDetailsService,
    @Autowired val server: MockRestServiceServer) {

    @Test
    fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails(): Unit {
        server.expect(MockRestRequestMatchers.requestTo("/greet/details"))
            .andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
        val greeting = service.callRestService()
        assertThat(greeting).isEqualTo("hello")
    }

}

9.3.29. 自动配置的 Spring REST Docs 测试

您可以使用注释在带有 Mock MVC、REST Assure 或 WebTestClient 的测试中使用 Spring REST Docs。 它消除了 Spring REST Docs 中对 JUnit 扩展的需求。@AutoConfigureRestDocsspring-doc.cn

@AutoConfigureRestDocs可用于覆盖默认输出目录(如果您使用的是 Maven 或 Gradle)。 它还可用于配置出现在任何记录的 URI 中的主机、方案和端口。target/generated-snippetsbuild/generated-snippetsspring-doc.cn

使用模拟 MVC 自动配置的 Spring REST 文档测试

@AutoConfigureRestDocs自定义 Bean 以在测试基于 servlet 的 Web 应用程序时使用 Spring REST Docs。 你可以通过使用它来注入它,并在测试中使用它,就像你通常使用 Mock MVC 和 Spring REST Docs 时一样,如以下示例所示:MockMvc@Autowiredspring-doc.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 配置的控制比 的属性提供更多的控制,则可以使用 bean,如以下示例所示:@AutoConfigureRestDocsRestDocsMockMvcConfigurationCustomizerspring-doc.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 对参数化输出目录的支持,你可以创建一个 bean。 使用此结果处理程序的 auto-configuration 调用,从而导致每个调用自动生成默认代码段。 以下示例显示了一个 being defined:RestDocumentationResultHandleralwaysDoMockMvcRestDocumentationResultHandlerspring-doc.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也可以在测试反应式 Web 应用程序时使用。 你可以通过使用 using 来注入它,并在测试中使用它,就像你通常使用 Spring REST Docs 时一样,如以下示例所示:WebTestClient@Autowired@WebFluxTestspring-doc.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 配置的控制比 的属性提供更多的控制,则可以使用 bean,如以下示例所示:@AutoConfigureRestDocsRestDocsWebTestClientConfigurationCustomizerspring-doc.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 对参数化输出目录的支持,你可以使用 a 为每个实体交换结果配置一个使用者。 以下示例显示了这样一个被定义:WebTestClientBuilderCustomizerWebTestClientBuilderCustomizerspring-doc.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使预配置为使用 Spring REST Docs 的 bean 可用于您的测试。 你可以通过使用它来注入它,并在测试中使用它,就像你通常使用 REST Assured 和 Spring REST Docs 时一样,如以下示例所示:RequestSpecification@Autowiredspring-doc.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.restassured3.RestAssuredRestDocumentation.document;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

    @Test
    void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
        given(documentationSpec)
            .filter(document("list-users"))
        .when()
            .port(port)
            .get("/")
        .then().assertThat()
            .statusCode(is(200));
    }

}
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.restassured3.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 配置的控制比 的属性 提供更多的控制,则可以使用 bean,如以下示例所示:@AutoConfigureRestDocsRestDocsRestAssuredConfigurationCustomizerspring-doc.cn

Java
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {

    @Override
    public void customize(RestAssuredRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}
Kotlin
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer
import org.springframework.restdocs.templates.TemplateFormats

@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer {

    override fun customize(configurer: RestAssuredRestDocumentationConfigurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
    }

}

9.3.30. 自动配置的 Spring Web 服务测试

自动配置的 Spring Web 服务客户端测试

可用于测试使用 Spring Web Services 项目调用 Web 服务的应用程序。 默认情况下,它会配置一个模拟 bean 并自动自定义您的 . (有关将 Web 服务与 Spring Boot 结合使用的更多信息,请参阅“io.html”。@WebServiceClientTestWebServiceServerWebServiceTemplateBuilderspring-doc.cn

附录中提供了启用的自动配置设置的列表。@WebServiceClientTest

以下示例显示了正在使用的注释:@WebServiceClientTestspring-doc.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 服务服务器测试

可用于测试使用 Spring Web Services 项目实现 Web 服务的应用程序。 默认情况下,它配置一个可用于调用 Web 服务端点的 Bean。 (有关将 Web 服务与 Spring Boot 结合使用的更多信息,请参阅“io.html”。@WebServiceServerTestMockWebServiceClientspring-doc.cn

附录中提供了启用的自动配置设置的列表。@WebServiceServerTest

以下示例显示了正在使用的注释:@WebServiceServerTestspring-doc.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.31. 其他自动配置和切片

每个切片都提供一个或多个注释,即定义应作为切片的一部分包含的自动配置。 通过创建自定义注释或添加到测试,可以逐个测试添加其他自动配置,如以下示例所示:@AutoConfigure…​@AutoConfigure…​@ImportAutoConfigurationspring-doc.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
确保不要使用常规 Comments 来导入自动配置,因为它们是由 Spring Boot 以特定方式处理的。@Import

或者,可以通过在存储的文件中注册切片注释来为切片注释的任何使用添加其他自动配置,如以下示例所示:META-INF/springspring-doc.cn

META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports
com.example.IntegrationAutoConfiguration

在此示例中,在每个带有 .com.example.IntegrationAutoConfiguration@JdbcTestspring-doc.cn

您可以在此文件中使用 Comments。#
切片或批注可以通过这种方式进行自定义,只要它使用 .@AutoConfigure…​@ImportAutoConfiguration

9.3.32. 用户配置和切片

如果您以合理的方式构建代码,则默认情况下会使用您的类作为测试的配置。@SpringBootApplicationspring-doc.cn

因此,重要的是不要在应用程序的主类中混淆特定于其特定功能区域的配置设置。spring-doc.cn

假设您正在使用 Spring Batch 并且依赖于它的自动配置。 您可以按如下方式定义 your :@SpringBootApplicationspring-doc.cn

Java
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {

    // ...

}
Kotlin
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
@EnableBatchProcessing
class MyApplication {

    // ...

}

因为这个类是测试的源配置,所以任何切片测试实际上都会尝试启动 Spring Batch,这绝对不是你想要做的。 建议的方法是将特定于区域的配置移动到与应用程序位于同一级别的单独类,如以下示例所示:@Configurationspring-doc.cn

Java
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class MyBatchConfiguration {

    // ...

}
Kotlin
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
class MyBatchConfiguration {

    // ...

}
根据应用程序的复杂程度,您可能有一个用于自定义的类,或者每个域区域有一个类。 后一种方法允许您在其中一个测试中启用它(如有必要),并使用 annotation。 有关何时可能需要为 slice 测试启用特定类的更多详细信息,请参阅此操作方法部分@Configuration@Import@Configuration

测试切片从扫描中排除类。 例如,对于 a ,以下配置将不会在测试切片加载的应用程序上下文中包含给定的 bean:@Configuration@WebMvcTestWebMvcConfigurerspring-doc.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 {
            // ...
        }
    }

}

但是,下面的配置将导致测试切片加载自定义。WebMvcConfigurerspring-doc.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 {

    // ...

}

另一个混淆的来源是 Classpath scanning。 假设您以合理的方式构建了代码,但需要扫描其他包。 您的应用程序可能类似于以下代码:spring-doc.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 {

    // ...

}

这样做可以有效地覆盖默认的组件 scan 指令,其副作用是扫描这两个包,而不管你选择了哪个 slice。 例如,a 似乎突然扫描了应用程序的组件和用户配置。 同样,将 custom 指令移动到单独的类是解决此问题的好方法。@DataJpaTestspring-doc.cn

如果这不是您的选项,则可以在测试的层次结构中创建一个 somewhere ,以便改用它。 或者,您可以为测试指定一个源,这将禁用查找默认源的行为。@SpringBootConfiguration

9.3.33. 使用 Spock 测试 Spring Boot 应用程序

Spock 2.x 可用于测试 Spring Boot 应用程序。 为此,请将对 Spock 模块的依赖项添加到应用程序的构建中。 将 Spring 的测试框架集成到 Spock 中。 有关更多详细信息,请参阅 Spock 的 Spring 模块的文档spock-springspock-springspring-doc.cn

9.4. 测试工具

在测试应用程序时通常有用的一些测试实用程序类打包为 的一部分。spring-bootspring-doc.cn

9.4.1. ConfigDataApplicationContextInitializer

ConfigDataApplicationContextInitializer是可以应用于测试以加载 Spring Boot 文件。 当您不需要 提供的完整功能集时,可以使用它,如以下示例所示:ApplicationContextInitializerapplication.properties@SpringBootTestspring-doc.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 {

    // ...

}
单独使用不支持注射。 它唯一的工作是确保将文件加载到 Spring 的 . 要获得支持,您需要额外配置 a 或使用 ,这将为您自动配置 一个。ConfigDataApplicationContextInitializer@Value("${…​}")application.propertiesEnvironment@ValuePropertySourcesPlaceholderConfigurer@SpringBootTest

9.4.2. TestPropertyValues

TestPropertyValues用于快速向 或 添加属性。 您可以使用字符串调用它,如下所示:ConfigurableEnvironmentConfigurableApplicationContextkey=valuespring-doc.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.4.3. 输出捕获

OutputCapture是可用于捕获和输出的 JUnit。 使用 add 和 inject 作为测试类构造函数或测试方法的参数,如下所示:ExtensionSystem.outSystem.err@ExtendWith(OutputCaptureExtension.class)CapturedOutputspring-doc.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.4.4. TestRestTemplate

TestRestTemplate是 Spring 的便捷替代方案,在集成测试中很有用。 你可以获取一个 vanilla 模板或发送 Basic HTTP 身份验证的模板(带有用户名和密码)。 在任一情况下,模板都是容错的。 这意味着它的行为对测试友好,不会在 4xx 和 5xx 错误上引发异常。 相反,可以通过返回的 state code 及其 status code 来检测此类错误。RestTemplateResponseEntityspring-doc.cn

Spring Framework 5.0 提供了一个适用于 WebFlux 集成测试以及 WebFlux 和 MVC 端到端测试的新功能。 它为断言提供了流畅的 API,这与 .WebTestClientTestRestTemplate

建议使用 Apache HTTP 客户端(版本 4.3.2 或更高版本),但并非强制性要求。 如果你在 Classpath 上有它,则通过适当地配置 Client 端来响应。 如果您确实使用 Apache 的 HTTP 客户端,则会启用一些其他测试友好功能:TestRestTemplatespring-doc.cn

  • 不遵循重定向(因此您可以断言响应位置)。spring-doc.cn

  • Cookie 将被忽略(因此模板是无状态的)。spring-doc.cn

TestRestTemplate可以直接在集成测试中实例化,如以下示例所示:spring-doc.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")
    }

}

或者,如果将注释与 或 一起使用,则可以注入完全配置的注释并开始使用它。 如有必要,可以通过 Bean 应用其他定制。 任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如以下示例所示:@SpringBootTestWebEnvironment.RANDOM_PORTWebEnvironment.DEFINED_PORTTestRestTemplateRestTemplateBuilderspring-doc.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. 创建您自己的自动配置

如果您在开发共享库的公司工作,或者如果您从事开源或商业库的工作,则可能需要开发自己的自动配置。 自动配置类可以捆绑在外部 jar 中,并且仍然由 Spring Boot 拾取。spring-doc.cn

Auto-configuration 可以与 “starter” 相关联,该 “starter” 提供 auto-configuration 代码以及您将与之一起使用的典型 libraries。 我们首先介绍构建自己的自动配置所需了解的内容,然后我们继续介绍创建自定义Starters所需的典型步骤spring-doc.cn

10.1. 了解自动配置的 bean

实现自动配置的类用 . 这个 Comments 本身是 元 Comments 的,使自动配置成为标准类。 其他注释用于限制何时应应用自动配置。 通常,自动配置类使用 和 annotations。 这可确保仅在找到相关类且您尚未声明自己的类时应用自动配置。@AutoConfiguration@Configuration@Configuration@Conditional@ConditionalOnClass@ConditionalOnMissingBean@Configurationspring-doc.cn

你可以浏览spring-boot-autoconfigure的源代码来查看 Spring 提供的类(参见META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件)。@AutoConfigurationspring-doc.cn

10.2. 查找 Auto-configuration Candidate

Spring Boot 检查已发布的 jar 中是否存在文件。 该文件应列出您的配置类,每行一个类名,如以下示例所示:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsspring-doc.cn

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以使用该字符向 imports 文件添加注释。#
自动配置只能通过在 imports 文件中命名来加载。 确保它们在特定的包空间中定义,并且它们永远不会成为组件扫描的目标。 此外,自动配置类不应启用组件扫描来查找其他组件。 应该改用特定的 s。@Import

如果您的配置需要按特定顺序应用,则可以在 @AutoConfiguration 注释或专用的 @AutoConfigureBefore 和 @AutoConfigureAfter 注释上使用 、 和 属性。 例如,如果您提供特定于 Web 的配置,则可能需要在 .beforebeforeNameafterafterNameWebMvcAutoConfigurationspring-doc.cn

如果要订购某些不应直接了解彼此的自动配置,也可以使用 。 该 annotation 与常规 annotation 具有相同的语义,但为 auto-configuration 类提供了专用的顺序。@AutoConfigureOrder@Orderspring-doc.cn

与标准类一样,应用自动配置类的顺序仅影响其 bean 的定义顺序。 这些 bean 的后续创建顺序不受影响,并且由每个 bean 的依赖关系和任何关系决定。@Configuration@DependsOnspring-doc.cn

10.3. 条件注解

您几乎总是希望在 auto-configuration 类中包含一个或多个 Comments。 注解是一个常见的示例,用于允许开发人员在对您的默认值不满意时覆盖自动配置。@Conditional@ConditionalOnMissingBeanspring-doc.cn

Spring Boot 包含许多 Comments,您可以通过 Comments 类或单个方法在自己的代码中重用这些 Comments。 这些注释包括:@Conditional@Configuration@Beanspring-doc.cn

10.3.1. 类条件

和 注解允许根据特定类的存在与否来包含类。 由于注释元数据是使用 ASM 解析的,因此您可以使用该属性来引用实际类,即使该类实际上可能并未出现在正在运行的应用程序类路径上。 如果您希望通过使用值来指定类名,也可以使用该属性。@ConditionalOnClass@ConditionalOnMissingClass@ConfigurationvaluenameStringspring-doc.cn

此机制不适用于通常返回类型是条件目标的方法:在方法的条件应用之前,JVM 将加载类和可能处理的方法引用,如果类不存在,则这些引用将失败。@Beanspring-doc.cn

若要处理此方案,可以使用单独的类来隔离条件,如以下示例所示:@Configurationspring-doc.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@ConditionalOnMissingClassname

10.3.2. Bean 条件

和 注解允许根据特定 bean 的存在与否来包含 bean。 您可以使用该属性按类型指定 bean 或按名称指定 bean。 该属性允许您限制搜索 bean 时应考虑的层次结构。@ConditionalOnBean@ConditionalOnMissingBeanvaluenamesearchApplicationContextspring-doc.cn

当放置在方法上时,目标类型默认为方法的返回类型,如以下示例所示:@Beanspring-doc.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()
    }

}

在前面的示例中,如果 .someServiceSomeServiceApplicationContextspring-doc.cn

您需要非常小心 bean 定义的添加顺序,因为这些条件是根据到目前为止已处理的内容进行评估的。 因此,我们建议仅在自动配置类上使用 and 注释(因为可以保证在添加任何用户定义的 bean 定义后加载这些 Comments)。@ConditionalOnBean@ConditionalOnMissingBean
@ConditionalOnBean并且不会阻止创建类。 在类级别使用这些条件与使用 Comments 标记每个包含的方法之间的唯一区别是,如果条件不匹配,前者会阻止将类注册为 Bean。@ConditionalOnMissingBean@Configuration@Bean@Configuration
声明方法时,请在方法的 return 类型中提供尽可能多的类型信息。 例如,如果 Bean 的具体类实现了一个接口,则 Bean 方法的返回类型应该是具体类,而不是接口。 在使用 Bean 条件时,在方法中提供尽可能多的类型信息尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息。@Bean@Bean

10.3.3. 属性条件

该注释允许根据 Spring Environment 属性包含配置。 使用 and 属性指定应检查的属性。 默认情况下,将匹配存在且不等于的任何属性。 您还可以使用 和 属性创建更高级的检查。@ConditionalOnPropertyprefixnamefalsehavingValuematchIfMissingspring-doc.cn

10.3.4. 资源条件

该注释允许仅在存在特定资源时包含配置。 可以使用通常的 Spring 约定指定资源,如以下示例所示:。@ConditionalOnResourcefile:/home/user/test.datspring-doc.cn

10.3.5. Web 应用程序条件

和 annotations 允许根据应用程序是否为 Web 应用程序来包含配置。 基于 servlet 的 Web 应用程序是使用 Spring 、定义范围或具有 . 反应式 Web 应用程序是使用 . 或具有 .@ConditionalOnWebApplication@ConditionalOnNotWebApplicationWebApplicationContextsessionConfigurableWebEnvironmentReactiveWebApplicationContextConfigurableReactiveWebEnvironmentspring-doc.cn

和 注释允许根据应用程序是否是部署到 servlet 容器的传统 WAR 应用程序来包含配置。 对于使用嵌入式 Web 服务器运行的应用程序,此条件将不匹配。@ConditionalOnWarDeployment@ConditionalOnNotWarDeploymentspring-doc.cn

10.3.6. SPEL 表达式条件

该 Comments 允许根据 SPEL 表达式的结果包含配置。@ConditionalOnExpressionspring-doc.cn

在表达式中引用 bean 将导致该 bean 在上下文刷新处理中非常早地初始化。 因此,bean 将不符合后处理条件(例如配置属性绑定),并且其状态可能不完整。

10.4. 测试您的 Auto-configuration

自动配置可能受许多因素影响:用户配置(定义和自定义)、条件评估(存在特定库)等。 具体来说,每个测试都应该创建一个定义明确的 Well Defined,用于表示这些自定义项的组合。 提供了实现此目的的好方法。@BeanEnvironmentApplicationContextApplicationContextRunnerspring-doc.cn

ApplicationContextRunner通常定义为 Test 类的字段,用于收集基本、通用的配置。 以下示例确保始终调用该 API:MyServiceAutoConfigurationspring-doc.cn

Java
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
Kotlin
val contextRunner = ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration::class.java))
如果必须定义多个 auto-configurations,则无需对它们的声明进行排序,因为它们的调用顺序与运行应用程序时的顺序完全相同。

每个测试都可以使用运行程序来表示特定的使用案例。 例如,下面的示例调用用户配置 () 并检查自动配置是否正确退出。 调用提供了可与 一起使用的回调上下文。UserConfigurationrunAssertJspring-doc.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")
    }

}

还可以轻松自定义 ,如以下示例所示:Environmentspring-doc.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")
    }
}

运行程序还可用于显示 . 报表可以打印 或 级别。 以下示例显示如何使用 在 auto-configuration tests 中打印报告。ConditionEvaluationReportINFODEBUGConditionEvaluationReportLoggingListenerspring-doc.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(new ConditionEvaluationReportLoggingListener(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(LogLevel.INFO))
            .run { context: AssertableApplicationContext? -> }
    }

}

10.4.1. 模拟 Web 上下文

如果需要测试仅在 servlet 或反应式 Web 应用程序上下文中运行的自动配置,请分别使用 or。WebApplicationContextRunnerReactiveWebApplicationContextRunnerspring-doc.cn

10.4.2. 覆盖 Classpath

还可以测试当特定类和/或包在运行时不存在时会发生什么。 Spring Boot 附带了一个 Runner 可以轻松使用。 在以下示例中,我们断言如果不存在,则正确禁用自动配置:FilteredClassLoaderMyServicespring-doc.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")
        }
}

10.5. 创建您自己的 Starter

典型的 Spring Boot Starters包含用于自动配置和自定义给定技术基础设施的代码,我们称之为 “acme”。 为了使其易于扩展,可以将专用命名空间中的许多 Configuration Key 公开给环境。 最后,提供了一个 “starter” 依赖项,以帮助用户尽可能轻松地入门。spring-doc.cn

具体来说,自定义Starters可以包含以下内容:spring-doc.cn

  • 包含 “acme” 的自动配置代码的模块。autoconfigurespring-doc.cn

  • 为模块提供依赖项的模块以及 “acme” 和通常有用的任何其他依赖项。 简而言之,添加 starter 应该提供开始使用该库所需的一切。starterautoconfigurespring-doc.cn

这种分为两个模块的做法是完全没有必要的。 如果 “acme” 有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的。 此外,您还可以制作一个 starter 来提供有关这些可选依赖项的意见。 与此同时,其他人可以只依赖该模块,并制作自己的不同意见的Starters。autoconfigurespring-doc.cn

如果自动配置相对简单且没有可选功能,那么在 starter 中合并两个模块绝对是一个选项。spring-doc.cn

10.5.1. 命名

您应该确保为 starter 提供适当的命名空间。 不要以 , 以 开头的模块名称,即使您使用不同的 Maven 也是如此。 将来,我们可能会为您自动配置的事物提供官方支持。spring-bootgroupIdspring-doc.cn

根据经验,您应该在 starter 之后命名组合模块。 例如,假设您正在为 “acme” 创建一个Starters,并将 auto-configure 模块命名为 starter 和 starter 。 如果您只有一个模块将两者组合在一起,请将其命名为 。acme-spring-bootacme-spring-boot-starteracme-spring-boot-starterspring-doc.cn

10.5.2. 配置键

如果您的 starter 提供 Configuration Key,请为它们使用唯一的命名空间。 特别是,不要在 Spring Boot 使用的名称空间中包含您的键(例如、、、等)。 如果你使用相同的命名空间,我们将来可能会以破坏你的模块的方式修改这些命名空间。 根据经验,请在所有键前加上您拥有的 namespace (例如 )。servermanagementspringacmespring-doc.cn

通过为每个属性添加字段 javadoc 来确保记录配置键,如以下示例所示:spring-doc.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))
您应该只使用带有字段 Javadoc 的纯文本,因为它们在添加到 JSON 之前不会进行处理。@ConfigurationProperties

以下是我们在内部遵循的一些规则,以确保描述一致:spring-doc.cn

  • 请勿以 “The” 或 “A” 开头描述。spring-doc.cn

  • 对于类型,请以 “Whether” 或 “Enable” 开始描述。booleanspring-doc.cn

  • 对于基于集合的类型,以 “Comma-separated list” 开始描述spring-doc.cn

  • 如果默认单位与毫秒不同,请使用 instead instead and 并描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。java.time.Durationlongspring-doc.cn

  • 除非必须在运行时确定,否则不要在描述中提供默认值。spring-doc.cn

确保触发元数据生成,以便 IDE 帮助也可用于您的密钥。 您可能需要查看生成的元数据 () 以确保您的密钥已正确记录。 在兼容的 IDE 中使用您自己的 starter 也是验证元数据质量的好主意。META-INF/spring-configuration-metadata.jsonspring-doc.cn

10.5.3. “autoconfigure” 模块

该模块包含开始使用该库所需的一切。 它还可能包含配置键定义(例如 )和可用于进一步自定义组件初始化方式的任何回调接口。autoconfigure@ConfigurationPropertiesspring-doc.cn

您应该将库的依赖项标记为可选,以便可以更轻松地将模块包含在项目中。 如果这样做,则不会提供库,并且默认情况下, Spring Boot 会退缩。autoconfigure

Spring Boot 使用 Comments 处理器在元数据文件 () 中收集自动配置的条件。 如果存在该文件,则使用它来紧急过滤不匹配的自动配置,这将缩短启动时间。META-INF/spring-autoconfigure-metadata.propertiesspring-doc.cn

使用 Maven 构建时,建议在包含 auto-configurations 的模块中添加以下依赖项:spring-doc.cn

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure-processor</artifactId>
    <optional>true</optional>
</dependency>

如果您直接在应用程序中定义了 auto-configurations,请确保配置 以防止目标将依赖项添加到 fat jar 中:spring-boot-maven-pluginrepackagespring-doc.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 时,应在配置中声明依赖项,如以下示例所示:annotationProcessorspring-doc.cn

dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}

10.5.4. 入门模块

Starters真的是一个空罐子。 它的唯一目的是提供使用库所需的依赖项。 您可以将其视为对开始所需内容的固执己见的看法。spring-doc.cn

不要对添加起始项的项目做出假设。 如果要自动配置的库通常需要其他启动程序,请同时提及它们。 如果可选依赖项的数量很高,则可能很难提供一组适当的默认依赖项,因为您应该避免包含对于库的典型使用不必要的依赖项。 换句话说,您不应包含可选依赖项。spring-doc.cn

无论哪种方式,你的 starter 都必须直接或间接引用核心 Spring Boot starter () (如果你的 starter 依赖于另一个 starter,则无需添加它)。 如果项目是仅使用您的自定义Starters创建的,则 Spring Boot 的核心功能将因核心Starters的存在而得到尊重。spring-boot-starter

11. Kotlin 支持

Kotlin 是一种面向 JVM(和其他平台)的静态类型语言,它允许编写简洁优雅的代码,同时提供与用 Java 编写的现有库的互操作性spring-doc.cn

Spring Boot 通过利用其他 Spring 项目(如 Spring Framework、Spring Data 和 Reactor)中的支持来提供 Kotlin 支持。 有关更多信息,请参阅 Spring Framework Kotlin 支持文档spring-doc.cn

开始使用 Spring Boot 和 Kotlin 的最简单方法是遵循此综合教程。 您可以使用 start.spring.io 创建新的 Kotlin 项目。 如果您需要支持,请随时加入 Kotlin Slack 的 #spring 频道,或者在 Stack Overflow 上使用 和 标签提问。springkotlinspring-doc.cn

11.1. 要求

Spring Boot 至少需要 Kotlin 1.3.x,并通过依赖项管理管理合适的 Kotlin 版本。 要使用 Kotlin,并且必须存在于 Classpath 中。 变体 和 也可以使用。org.jetbrains.kotlin:kotlin-stdliborg.jetbrains.kotlin:kotlin-reflectkotlin-stdlibkotlin-stdlib-jdk7kotlin-stdlib-jdk8spring-doc.cn

由于 Kotlin 类默认是 final,因此您可能希望配置 kotlin-spring 插件,以便自动打开 Spring 注释的类,以便可以代理它们。spring-doc.cn

在 Kotlin 中序列化/反序列化 JSON 数据需要 Jackson 的 Kotlin 模块。 在 Classpath 上找到它时,它会自动注册。 如果存在 Jackson 和 Kotlin,但不存在 Jackson Kotlin 模块,则会记录一条警告消息。spring-doc.cn

如果在 start.spring.io 上引导 Kotlin 项目,则默认提供这些依赖项和插件。

11.2. 空安全

Kotlin 的主要功能之一是 null 安全。 它在编译时处理值,而不是将问题推迟到运行时并遇到 . 这有助于消除常见的错误来源,而无需支付像 . Kotlin 还允许使用具有可为 null 值的函数结构,如本 Kotlin 中的 null 安全性综合指南中所述。nullNullPointerExceptionOptionalspring-doc.cn

尽管 Java 不允许在其类型系统中表达 null 安全性,但 Spring Framework、Spring Data 和 Reactor 现在通过工具友好的注释提供其 API 的 null 安全性。 默认情况下,Kotlin 中使用的 Java API 中的类型被识别为放宽了 null 检查的平台类型Kotlin 对 JSR 305 注释的支持与可为 null 性注释相结合,为 Kotlin 中的相关 Spring API 提供了空安全性。spring-doc.cn

可以通过添加编译器标志和以下选项来配置 JSR 305 检查:. 默认行为与 相同。 在从 Spring API 推断的 Kotlin 类型中,需要考虑 null-safety 该值,但应在知道 Spring API 可空性声明甚至在次要版本之间演变,并且将来可能会添加更多检查的情况下使用该值。-Xjsr305-Xjsr305={strict|warn|ignore}-Xjsr305=warnstrictspring-doc.cn

尚不支持泛型类型参数、varargs 和数组元素可为 null 性。 有关最新信息,请参阅 SPR-15942。 另请注意,Spring Boot 自己的 API 尚未注释

11.3. Kotlin API

11.3.1. runApplication

Spring Boot 提供了一种惯用的方式来运行应用程序,如以下示例所示:runApplication<MyApplication>(*args)spring-doc.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.cn

runApplication<MyApplication>(*args) {
    setBannerMode(OFF)
}

11.3.2. 扩展

Kotlin 扩展提供了使用其他功能扩展现有类的功能。 Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利。spring-doc.cn

TestRestTemplate提供了扩展,类似于 Spring Framework 为 Spring Framework 提供的扩展。 此外,这些扩展还可以利用 Kotlin 具体化类型参数。RestOperationsspring-doc.cn

11.4. 依赖项管理

为了避免在 Classpath 上混合不同版本的 Kotlin 依赖项, Spring Boot 导入了 Kotlin BOM。spring-doc.cn

使用 Maven,可以通过设置属性来自定义 Kotlin 版本,并为 . 使用 Gradle 时,Spring Boot 插件会自动与 Kotlin 插件的版本保持一致。kotlin.versionkotlin-maven-pluginkotlin.versionspring-doc.cn

Spring Boot 还通过导入 Kotlin Coroutines BOM 来管理 Coroutines 依赖项的版本。 可以通过设置属性来自定义版本。kotlin-coroutines.versionspring-doc.cn

org.jetbrains.kotlinx:kotlinx-coroutines-reactor如果引导 Kotlin 项目时至少有一个对 start.spring.io 的反应式依赖项,则默认提供依赖项。

11.5. @ConfigurationProperties

@ConfigurationProperties与 @ConstructorBinding 结合使用时支持具有不可变属性的类,如以下示例所示:valspring-doc.cn

@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
        val name: String,
        val description: String,
        val myService: MyService) {

    data class MyService(
            val apiToken: String,
            val uri: URI
    )
}
要使用 annotation 处理器生成您自己的元数据应该为 kapt 配置依赖项。 请注意,由于 kapt 提供的模型的限制,某些功能(例如检测默认值或已弃用的项目)无法正常工作。spring-boot-configuration-processor

11.6. 测试

虽然可以使用 JUnit 4 来测试 Kotlin 代码,但 JUnit 5 是默认提供的,建议使用 JUnit 5。 JUnit 5 允许测试类实例化一次,并重复用于该类的所有测试。 这使得在非静态方法上使用 and 注解成为可能,这非常适合 Kotlin。@BeforeAll@AfterAllspring-doc.cn

要模拟 Kotlin 类,建议使用 MockK。 如果你需要 Mockito 特定的 @MockBean@SpyBean 注释的等效项,你可以使用 SpringMockK,它提供类似的 COMMENTS 。MockK@MockkBean@SpykBeanspring-doc.cn

12. 下一步要读什么

如果您想了解有关本节中讨论的任何类的更多信息,请参阅 Spring Boot API 文档,或者您可以直接浏览源代码。 如果您有具体问题,请参阅操作方法部分。spring-doc.cn

如果您对 Spring Boot 的核心功能感到满意,则可以继续阅读有关生产就绪功能的信息。spring-doc.cn