本节深入探讨 Spring Boot 的细节。在这里,您可以了解您可能想要使用和自定义的主要功能。如果您还没有这样做,您可能需要阅读“ getting-started.html ”和“ using.html ”部分,以便您有良好的基础知识。

1. SpringApplication

该类SpringApplication提供了一种方便的方法来引导从main()方法启动的 Spring 应用程序。在许多情况下,您可以委托给静态SpringApplication.run方法,如下例所示:

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

}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication


@SpringBootApplication
class MyApplication

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

当您的应用程序启动时,您应该会看到类似于以下输出的内容:

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

2021-02-03 10:33:25.224 INFO 17321 --- [main] osbdssSpringApplicationExample:在 PID 17321 的 mycomputer 上使用 Java 1.8.0_232 启动 SpringApplicationExample(/apps/myjar.jar 由 pwebb 启动)
2021-02-03 10:33:25.226 INFO 17900 --- [main] osbdssSpringApplicationExample:没有活动配置文件集,回退到默认配置文件:默认
2021-02-03 10:33:26.046 INFO 17321 --- [main] osbwembedded.tomcat.TomcatWebServer:Tomcat 使用端口初始化:8080 (http)
2021-02-03 10:33:26.054 INFO 17900 --- [main] o.apache.catalina.core.StandardService:启动服务 [Tomcat]
2021-02-03 10:33:26.055 INFO 17900 --- [main] org.apache.catalina.core.StandardEngine:启动 Servlet 引擎:[Apache Tomcat/9.0.41]
2021-02-03 10:33:26.097 INFO 17900 --- [main] oaccC[Tomcat].[localhost].[/]:初始化 Spring 嵌入式 WebApplicationContext
2021-02-03 10:33:26.097 INFO 17900 --- [main] wscServletWebServerApplicationContext:Root WebApplicationContext:初始化在 821 毫秒内完成
2021-02-03 10:33:26.144 INFO 17900 --- [main] s.tomcat.SampleTomcatApplication:ServletContext 已初始化
2021-02-03 10:33:26.376 INFO 17900 --- [main] osbwembedded.tomcat.TomcatWebServer:Tomcat 在端口上启动:8080 (http),上下文路径为“”
2021-02-03 10:33:26.384 INFO 17900 --- [main] osbdssSpringApplicationExample:在 1.514 秒内启动 SampleTomcatApplication(JVM 运行 1.823)

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

要在启动期间添加额外的日志记录,您可以logStartupInfo(boolean)SpringApplication.

1.1。启动失败

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

******************************
应用程序无法启动
******************************

描述:

嵌入式 servlet 容器无法启动。端口 8080 已被使用。

行动:

识别并停止正在侦听端口 8080 的进程或将此应用程序配置为侦听另一个端口。
Spring Boot 提供了许多FailureAnalyzer实现,您可以添加自己的.

如果没有故障分析器能够处理异常,您仍然可以显示完整的条件报告以更好地了解问题所在。为此,您需要启用该debug属性启用DEBUG日志记录org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

例如,如果您正在使用 运行应用程序,则可以按如下java -jar方式启用该属性:debug

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

1.2. 延迟初始化

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

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

lazyInitialization可以使用方法 onSpringApplicationBuildersetLazyInitialization方法 on以编程方式启用延迟初始化SpringApplication。或者,可以使用spring.main.lazy-initialization以下示例中所示的属性启用它:

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

1.3. 自定义横幅

banner.txt可以通过将文件添加到类路径或将spring.banner.location属性设置为此类文件的位置来更改启动时打印的横幅。如果文件的编码不是 UTF-8,您可以设置spring.banner.charset. 除了文本文件之外,您还可以将banner.gifbanner.jpgbanner.png图像文件添加到您的类路径或设置spring.banner.image.location属性。图像被转换为​​ ASCII 艺术表示并打印在任何文本横幅上方。

在您的banner.txt文件中,您可以使用任何可用的键Environment以及以下任何占位符:

表 1. 横幅变量
多变的 描述

${application.version}

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

${application.formatted-version}

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

${spring-boot.version}

您正在使用的 Spring Boot 版本。例如2.7.1.

${spring-boot.formatted-version}

您正在使用的 Spring Boot 版本,已格式化以供显示(用括号括起来并以 为前缀v)。例如(v2.7.1).

${Ansi.NAME}(或${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME}

NAMEANSI 转义码的名称在哪里。详情请参阅AnsiPropertySource

${application.title}

应用程序的标题,如MANIFEST.MF. 例如Implementation-Title: MyApp打印为MyApp.

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

您还可以使用该spring.main.banner-mode属性来确定是否必须在System.out( console) 上打印横幅、将其发送到配置的记录器 ( log) 或根本不生成 ( off)。

打印的横幅以以下名称注册为单例 bean springBootBanner:.

${application.version}${application.formatted-version}属性仅在您使用 Spring Boot 启动器时可用。如果您正在运行解压缩的 jar 并以java -cp <classpath> <mainclass>.

这就是为什么我们建议您始终使用java org.springframework.boot.loader.JarLauncher. application.*这将在构建类路径和启动应用程序之前初始化横幅变量。

1.4. 自定义 SpringApplication

如果SpringApplication默认值不符合您的口味,您可以改为创建本地实例并对其进行自定义。例如,要关闭横幅,您可以编写:

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

}
科特林
import org.springframework.boot.Banner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

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

也可以SpringApplication使用application.properties文件进行配置。有关详细信息,请参阅外部化配置

有关配置选项的完整列表,请参阅SpringApplicationJavadoc

1.5。流畅的构建器 API

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

允许您将SpringApplicationBuilder多个方法调用和包含parent以及child允许您创建层次结构的方法链接在一起,如以下示例所示:

java
new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);
科特林
SpringApplicationBuilder()
    .sources(Parent::class.java)
    .child(Application::class.java)
    .bannerMode(Banner.Mode.OFF)
    .run(*args)
ApplicationContext创建层次结构 时有一些限制。例如,Web 组件必须包含在子上下文中,并且同样Environment用于父上下文和子上下文。有关完整的详细信息,请参阅SpringApplicationBuilderJavadoc

1.6. 应用程序可用性

当部署在平台上时,应用程序可以使用Kubernetes Probes等基础设施向平台提供有关其可用性的信息。Spring Boot 包括对常用“liveness”和“readiness”可用性状态的开箱即用支持。如果您使用 Spring Boot 的“执行器”支持,那么这些状态将作为健康端点组公开。

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

1.6.1. 活跃状态

应用程序的“活跃度”状态表明其内部状态是否允许其正常工作,或者如果当前失败则自行恢复。损坏的“活动”状态意味着应用程序处于无法恢复的状态,基础设施应重新启动应用程序。

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

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

1.6.2. 准备状态

应用程序的“就绪”状态表明应用程序是否已准备好处理流量。失败的“就绪”状态告诉平台它现在不应该将流量路由到应用程序。这通常发生在启动期间、CommandLineRunner处理ApplicationRunner组件时,或者在应用程序决定它太忙而无法获得额外流量的任何时候。

一旦调用了应用程序和命令行运行程序,就认为应用程序已准备就绪,请参阅Spring Boot 应用程序生命周期和相关的应用程序事件

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

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

应用程序组件可以随时检索当前的可用性状态,方法是注入ApplicationAvailability接口并在其上调用方法。更多时候,应用程序会想要监听状态更新或更新应用程序的状态。

例如,我们可以将应用程序的“Readiness”状态导出到一个文件中,以便 Kubernetes “exec Probe”可以查看这个文件:

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

}
科特林
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 -> {
                // ...
            }
        }
    }

}

我们还可以更新应用程序的状态,当应用程序中断并且无法恢复时:

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

}
科特林
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, aSpringApplication还发送一些额外的应用程序事件。

某些事件实际上ApplicationContext是在创建之前触发的,因此您不能在这些事件上将侦听器注册为@Bean. SpringApplication.addListeners(…​)您可以使用方法或方法注册它们SpringApplicationBuilder.listeners(…​)

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

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

当您的应用程序运行时,应用程序事件按以下顺序发送:

  1. AnApplicationStartingEvent在运行开始但在任何处理之前发送,除了侦听器和初始化程序的注册。

  2. ApplicationEnvironmentPreparedEventEnvironment在上下文中使用的 已知但在创建上下文之前发送一个。

  3. ApplicationContextInitializedEventApplicationContext准备好并调用 ApplicationContextInitializers 但在加载任何 bean 定义之前发送一个。

  4. AnApplicationPreparedEvent在刷新开始之前但在加载 bean 定义之后发送。

  5. ApplicationStartedEvent在刷新上下文之后但在调用任何应用程序和命令行运行程序之前发送一个。

  6. AvailabilityChangeEvent在 with 之后立即发送一个LivenessState.CORRECT,表明应用程序被认为是活动的。

  7. ApplicationReadyEvent在调用任何应用程序和命令行运行程序后发送一个。

  8. AvailabilityChangeEvent在 with 之后立即发送一个,ReadinessState.ACCEPTING_TRAFFIC以指示应用程序已准备好为请求提供服务。

  9. 如果ApplicationFailedEvent启动时出现异常,则发送一个。

上面的列表仅包括SpringApplicationEvent与 a 绑定的 s SpringApplication。除此之外,还发布了以下事件 afterApplicationPreparedEvent和 before ApplicationStartedEvent

  • A在准备好 WebServerInitializedEvent后发送。和分别是 servlet 和响应式变体。WebServerServletWebServerInitializedEventReactiveWebServerInitializedEvent

  • A在刷新ContextRefreshedEvent时发送。ApplicationContext

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

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

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

1.8. 网络环境

ASpringApplication尝试ApplicationContext代表您创建正确的类型。用于确定 a 的算法WebApplicationType如下:

  • 如果存在 Spring MVC,AnnotationConfigServletWebServerApplicationContext则使用an

  • 如果 Spring MVC 不存在而 Spring WebFlux 存在,AnnotationConfigReactiveWebServerApplicationContext则使用an

  • 否则,AnnotationConfigApplicationContext使用

这意味着如果您WebClient在同一个应用程序中使用 Spring MVC 和来自 Spring WebFlux 的 new,则默认情况下将使用 Spring MVC。您可以通过调用轻松覆盖它setWebApplicationType(WebApplicationType)

也可以ApplicationContext通过调用来完全控制使用的类型setApplicationContextClass(…​)

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

1.9。访问应用程序参数

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

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"]
    }

}
科特林
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"]
    }

}
CommandLinePropertySourceSpring Boot 还向 Spring 注册了 a Environment。这使您还可以使用@Value注释注入单个应用程序参数。

1.10。使用 ApplicationRunner 或 CommandLineRunner

如果您需要在启动后运行某些特定代码SpringApplication,您可以实现ApplicationRunnerCommandLineRunner接口。两个接口以相同的方式工作,并提供一个run方法,该方法在完成之前调用SpringApplication.run(…​)

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

接口以字符串数组的CommandLineRunner形式提供对应用程序参数的访问,而ApplicationRunner使用ApplicationArguments前面讨论的接口。以下示例显示了一个CommandLineRunner带有方法的run方法:

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...
    }

}
科特林
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component

@Component
class MyCommandLineRunner : CommandLineRunner {

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

}

如果定义了必须按特定顺序调用的多个CommandLineRunneror bean,您可以另外实现接口或使用注解。ApplicationRunnerorg.springframework.core.Orderedorg.springframework.core.annotation.Order

1.11。应用程序退出

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

此外,如果 bean希望在被调用org.springframework.boot.ExitCodeGenerator时返回特定的退出代码,它们可以实现该接口。SpringApplication.exit()然后可以将此退出代码传递给以将System.exit()其作为状态代码返回,如以下示例所示:

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

}
科特林
import org.springframework.boot.ExitCodeGenerator
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean

import kotlin.system.exitProcess

@SpringBootApplication
class MyApplication {

    @Bean
    fun exitCodeGenerator() = ExitCodeGenerator { 42 }

}

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

此外,ExitCodeGenerator接口可能由异常实现。当遇到这样的异常时,Spring Boot 返回实现的getExitCode()方法提供的退出代码。

如果超过ExitCodeGenerator,则使用生成的第一个非零退出代码。要控制调用生成器的顺序,请另外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注释。

1.12。管理员功能

可以通过指定spring.application.admin.enabled属性为应用程序启用与管理相关的功能。这暴露了SpringApplicationAdminMXBean平台上的MBeanServer. 您可以使用此功能远程管理您的 Spring Boot 应用程序。此功能也可用于任何服务包装器实现。

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

1.13。应用程序启动跟踪

在应用程序启动期间,SpringApplication执行ApplicationContext许多与应用程序生命周期、bean 生命周期甚至处理应用程序事件相关的任务。借助ApplicationStartupSpring Framework ,您可以使用StartupStep对象跟踪应用程序的启动顺序。可以出于分析目的收集这些数据,或者只是为了更好地了解应用程序启动过程。

您可以ApplicationStartup在设置SpringApplication实例时选择实现。例如,要使用BufferingApplicationStartup,您可以编写:

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

}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup
import org.springframework.boot.runApplication

@SpringBootApplication
class MyApplication

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

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

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

Spring Boot 附带该BufferingApplicationStartup变体;此实现旨在缓冲启动步骤并将它们排入外部度量系统。BufferingApplicationStartup应用程序可以在任何组件中请求类型的 bean 。

Spring Boot 也可以配置为公开一个以 JSON 文档形式提供此信息的startup端点。

2. 外化配置

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

属性值可以通过使用注解直接注入到你的 bean 中@Value,通过 Spring 的抽象Environment访问,或者通过.@ConfigurationProperties

Spring Boot 使用一种非常特殊的PropertySource顺序,旨在允许合理地覆盖值。属性按以下顺序考虑(较低项目的值覆盖较早的项目):

  1. 默认属性(由设置指定SpringApplication.setDefaultProperties)。

  2. @PropertySource@Configuration您的课程上的注释。请注意,Environment在刷新应用程序上下文之前,不会将此类属性源添加到 中。配置某些属性(例如在刷新开始之前读取的logging.*和)为时已晚。spring.main.*

  3. 配置数据(例如application.properties文件)。

  4. 仅在中RandomValuePropertySource具有属性的 A。random.*

  5. 操作系统环境变量。

  6. Java 系统属性 ( System.getProperties())。

  7. JNDI 属性来自java:comp/env.

  8. ServletContext初始化参数。

  9. ServletConfig初始化参数。

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

  11. 命令行参数。

  12. properties属性在你的测试。可用于测试应用程序的特定部分@SpringBootTest的测试注释。

  13. @TestPropertySource测试上的注释。

  14. $HOME/.config/spring-boot当 devtools 处于活动状态时,目录中的Devtools 全局设置属性。

配置数据文件按以下顺序考虑:

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

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

  3. 打包 jar(application.properties和 YAML 变体)之外的应用程序属性。

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

建议在整个应用程序中坚持使用一种格式。如果您在同一位置有同时具有.properties和格式的配置文件,则优先。 .yml.properties

举一个具体的例子,假设你开发了一个@Component使用name属性的,如下例所示:

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

@Component
public class MyBean {

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

    // ...

}
科特林
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component

@Component
class MyBean {

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

    // ...

}

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

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

2.1。访问命令行属性

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

如果您不想将命令行属性添加到 中Environment,可以使用 禁用它们SpringApplication.setAddCommandLineProperties(false)

2.2. JSON 应用程序属性

环境变量和系统属性通常有限制,这意味着某些属性名称不能使用。为了解决这个问题,Spring Boot 允许您将属性块编码为单个 JSON 结构。

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

例如,SPRING_APPLICATION_JSON可以在 UN*X shell 的命令行中将属性作为环境变量提供:

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

在前面的示例中,您最终my.name=test在 Spring 中Environment

同样的 JSON 也可以作为系统属性提供:

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

或者您可以使用命令行参数提供 JSON:

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

如果要部署到经典应用程序服务器,还可以使用名为java:comp/env/spring.application.json.

尽管nullJSON 中的值将添加到生成的属性源中,但PropertySourcesPropertyResolvernull属性视为缺失值。这意味着 JSON 不能用值覆盖来自低阶属性源的属性null

2.3. 外部应用程序属性

当您的应用程序启动时,Spring Boot 将自动从以下位置查找并加载文件application.propertiesapplication.yaml

  1. 从类路径

    1. 类路径根

    2. 类路径/config

  2. 从当前目录

    1. 当前目录

    2. 当前目录中的/config子目录

    3. 子目录的/config直接子目录

该列表按优先级排序(较低项目的值覆盖较早的项目)。加载文件中的文档将添加PropertySources到 SpringEnvironment中。

如果您不喜欢application作为配置文件名,您可以通过指定spring.config.name环境属性切换到另一个文件名。例如,要查找myproject.propertiesmyproject.yaml文件,您可以按如下方式运行应用程序:

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

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

以下示例显示如何指定两个不同的文件:

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

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

目录和文件位置值也被扩展以检查特定于配置文件的文件。例如,如果您有一个spring.config.locationof classpath:myconfig.properties,您还会发现classpath:myconfig-<profile>.properties加载了适当的文件。

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

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

使用spring.config.location替换默认位置配置的位置。例如,如果spring.config.location配置了 value optional:classpath:/custom-config/,optional:file:./custom-config/,则考虑的完整位置集是:

  1. optional:classpath:custom-config/

  2. optional:file:./custom-config/

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

  1. optional:classpath:/;optional:classpath:/config/

  2. optional:file:./;optional:file:./config/;optional:file:./config/*/

  3. optional:classpath:custom-config/

  4. optional:file:./custom-config/

这种搜索顺序允许您在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。您可以在默认位置之一application.properties(或您选择的任何其他基本名称)中为您的应用程序提供默认值。spring.config.name然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。

如果您使用环境变量而不是系统属性,大多数操作系统不允许使用句点分隔的键名,但您可以使用下划线代替(例如,SPRING_CONFIG_NAME代替spring.config.name)。有关详细信息,请参阅从环境变量绑定
如果您的应用程序在 servlet 容器或应用程序服务器中运行,则可以使用 JNDI 属性(在 中java:comp/env)或 servlet 上下文初始化参数来代替环境变量或系统属性,或者也可以使用环境变量或系统属性。

2.3.1。可选位置

默认情况下,当指定的配置数据位置不存在时,Spring Boot 会抛出一个ConfigDataLocationNotFoundException并且你的应用程序不会启动。

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

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

如果您想忽略所有ConfigDataLocationNotFoundExceptions并始终继续启动您的应用程序,您可以使用该spring.config.on-not-found属性。将值设置为ignore使用SpringApplication.setDefaultProperties(…​)或与系统/环境变量一起使用。

2.3.2. 通配符位置

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

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

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

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

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

2.3.3。配置文件特定文件

除了application属性文件,Spring Boot 还将尝试使用命名约定加载特定于配置文件的文件application-{profile}。例如,如果您的应用程序激活了一个名为prod并使用 YAML 文件的配置文件,那么两者application.yml都会application-prod.yml被考虑。

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

最后获胜的策略适用于营业地点组级别。A spring.config.locationofclasspath:/cfg/,classpath:/ext/不会具有与 相同的覆盖规则classpath:/cfg/;classpath:/ext/

例如,继续prod,live上面的示例,我们可能有以下文件:

/cfg
  应用程序-live.properties
/分机
  应用程序-live.properties
  应用程序-prod.properties

当我们有一个我们在所有文件之前处理spring.config.location所有文件时:classpath:/cfg/,classpath:/ext//cfg/ext

  1. /cfg/application-live.properties

  2. /ext/application-prod.properties

  3. /ext/application-live.properties

当我们classpath:/cfg/;classpath:/ext/改为(使用;分隔符)时,我们/cfg/ext在同一级别处理:

  1. /ext/application-prod.properties

  2. /cfg/application-live.properties

  3. /ext/application-live.properties

Environment一组默认配置文件(默认情况下,[default]),如果没有设置活动配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,则application-default考虑来自的属性。

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

2.3.4。导入附加数据

应用程序属性可以使用该属性从其他位置导入更多配置数据spring.config.import。进口在发现时就被处理,并被视为紧接在声明进口的文件下方插入的附加文件。

例如,您的类路径application.properties文件中可能包含以下内容:

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

这将触发dev.properties当前目录中文件的导入(如果存在这样的文件)。导入的值dev.properties将优先于触发导入的文件。在上面的示例中,dev.properties可以重新定义spring.application.name为不同的值。

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

特性
spring.config.import=my.properties
my.property=value
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.properties文件中的值将优先于触发其导入的文件。

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

适当时,还会考虑导入特定于配置文件的变体。上面的示例将同时导入my.properties任何my-<profile>.properties变体。

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

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

如果您想支持自己的位置,请参阅包中的ConfigDataLocationResolverConfigDataLoaderorg.springframework.boot.context.config

2.3.5。导入无扩展名文件

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

例如,假设您有一个/etc/config/myconfig文件要导入为 yaml。您可以application.properties使用以下命令从您的导入它:

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

2.3.6。使用配置树

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

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

可以使用两种常见的卷挂载模式:

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

  2. 多个文件被写入目录树,文件名成为“键”,内容成为“值”。

对于第一种情况,您可以使用上述spring.config.import方法直接导入 YAML 或属性文件。对于第二种情况,您需要使用前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。configtree:

例如,假设 Kubernetes 挂载了以下卷:

ETC/
  配置/
    我的应用程序/
      用户名
      密码

username文件的内容将是一个配置值,其内容password将是一个秘密。

要导入这些属性,您可以将以下内容添加到您的application.propertiesorapplication.yaml文件中:

特性
spring.config.import=optional:configtree:/etc/config/
yaml
spring:
  config:
    import: "optional:configtree:/etc/config/"

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

配置树下的文件夹构成属性名称。在上面的示例中,要访问 和 的属性usernamepassword您可以设置spring.config.importoptional:configtree:/etc/config/myapp
带有点符号的文件名也被正确映射。例如,在上面的示例中,名为myapp.usernamein的文件/etc/config将导致. myapp.usernameEnvironment
根据预期的内容 , 配置树值可以绑定到字符串String和类型。byte[]

如果您要从同一个父文件夹导入多个配置树,则可以使用通配符快捷方式。任何configtree:以结尾的位置/*/都会将所有直接子项导入为配置树。

例如,给定以下卷:

ETC/
  配置/
    数据库配置/
      D b/
        用户名
        密码
    MQ配置/
      MQ/
        用户名
        密码

您可以configtree:/etc/config/*/用作导入位置:

特性
spring.config.import=optional:configtree:/etc/config/*/
yaml
spring:
  config:
    import: "optional:configtree:/etc/config/*/"

这将添加db.usernamedb.passwordmq.username属性mq.password

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

配置树也可用于 Docker 机密。当 Docker swarm 服务被授予对密钥的访问权限时,该密钥将被挂载到容器中。例如,如果一个名为的 secretdb.password挂载在 location /run/secrets/,您可以db.password使用以下命令将其提供给 Spring 环境:

特性
spring.config.import=optional:configtree:/run/secrets/
yaml
spring:
  config:
    import: "optional:configtree:/run/secrets/"

2.3.7。属性占位符

application.properties和中的值在使用时会application.yml通过现有值进行过滤Environment,因此您可以参考以前定义的值(例如,来自系统属性或环境变量)。标准的${name}属性占位符语法可以在值内的任何地方使用。属性占位符还可以使用 a:来指定默认值,以将默认值与属性名称分开,例如${name:default}

以下示例显示了使用带和不带默认值的占位符:

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

假设该username属性尚未在其他地方设置,app.description则值为MyApp is a Spring Boot application written by Unknown

您应该始终使用其规范形式(kebab-case 仅使用小写字母)引用占位符中的属性名称。这将允许 Spring Boot 使用与放松绑定 时相同的逻辑@ConfigurationProperties

例如,将从文件中以及从系统环境中${demo.item-price}提取demo.item-pricedemo.itemPrice形成表格。如果你改用了,就不会考虑了。application.propertiesDEMO_ITEMPRICE${demo.itemPrice}demo.item-priceDEMO_ITEMPRICE

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

2.3.8。使用多文档文件

Spring Boot 允许您将单个物理文件拆分为多个独立添加的逻辑文档。文档从上到下按顺序处理。后面的文档可以覆盖早期文档中定义的属性。

对于application.yml文件,使用标准的 YAML 多文档语法。三个连续的连字符代表一个文档的结束,以及下一个文档的开始。

例如,以下文件有两个逻辑文档:

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

对于application.properties文件,特殊#---注释用于标记文档拆分:

spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
属性文件分隔符不能有任何前导空格,并且必须正好有三个连字符。分隔符前后的行不能是注释。
多文档属性文件通常与激活属性结合使用,例如spring.config.activate.on-profile. 有关详细信息,请参阅下一节
不能使用@PropertySourceor@TestPropertySource注释加载多文档属性文件。

2.3.9。激活属性

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

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

可以使用以下激活属性:

表 2. 激活属性
财产 笔记

on-profile

要使文档处于活动状态,必须匹配的配置文件表达式。

on-cloud-platform

CloudPlatform必须检测到文档才能处于活动状态。

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

特性
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 中包含的值所需的挂钩点Environment。该EnvironmentPostProcessor界面允许您Environment在应用程序启动之前进行操作。有关详细信息,请参阅howto.html

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

2.5. 使用 YAML

YAML是 JSON 的超集,因此是一种用于指定分层配置数据的便捷格式。只要您的类路径中有SnakeYAML库,SpringApplication该类就会自动支持 YAML 作为属性的替代方案。

如果你使用“Starters”,SnakeYAML 会自动由spring-boot-starter.

2.5.1。将 YAML 映射到属性

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

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

为了从 中访问这些属性Environment,它们将被展平如下:

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

同样,YAML 列表也需要展平。它们表示为带有[index]解引用器的属性键。例如,考虑以下 YAML:

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

前面的示例将转换为以下属性:

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

2.5.2. 直接加载 YAML

Spring Framework 提供了两个方便的类,可用于加载 YAML 文档。将YamlPropertiesFactoryBeanYAML 加载为Properties并将YamlMapFactoryBeanYAML 加载为Map.

YamlPropertySourceLoader如果您想将 YAML 作为 Spring 加载,也可以使用该类PropertySource

2.6. 配置随机值

RandomValuePropertySource对于注入随机值很有用(例如,注入秘密或测试用例)。它可以生成整数、长整数、uuid 或字符串,如下例所示:

特性
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}
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]}"

random.int*语法是任何字符并且是整数的OPEN value (,max) CLOSE地方。如果提供,则为最小值并且为最大值(不包括)。OPEN,CLOSEvalue,maxmaxvaluemax

2.7. 配置系统环境属性

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

例如,如果您将前缀设置为,则诸如在系统环境中input的属性remote.timeout也将被解析。input.remote.timeout

2.8. 类型安全的配置属性

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

2.8.1. JavaBean 属性绑定

可以绑定声明标准 JavaBean 属性的 bean,如下例所示:

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

    }

}
科特林
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 定义了以下属性:

  • my.service.enabled,默认值为false

  • my.service.remote-address, 具有可以从 强制转换的类型String

  • my.service.security.username,具有嵌套的“安全”对象,其名称由属性的名称确定。特别是,那里根本没有使用该类型,并且可能已经使用了SecurityProperties.

  • my.service.security.password.

  • my.service.security.roles, 集合String默认为USER.

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

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

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

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

  • 如果嵌套 POJO 属性已初始化(如Security前面示例中的字段),则不需要 setter。如果您希望活页夹使用其默认构造函数动态创建实例,则需要一个 setter。

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

最后,只考虑标准 Java Bean 属性,不支持对静态属性的绑定。

2.8.2. 构造函数绑定

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

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

    }

}
科特林
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>)

}

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

类的嵌套成员@ConstructorBindingSecurity例如上面的示例)也将通过其构造函数进行绑定。

可以使用@DefaultValue构造函数参数指定默认值,或者在使用 Java 16 或更高版本时,使用记录组件指定默认值。将应用转换服务将String值强制为缺失属性的目标类型。

参考前面的示例,如果没有属性绑定到Security,则MyProperties实例将包含 的nullsecuritySecurity如果您希望即使没有绑定任何属性也返回一个非空实例,您可以使用空@DefaultValue注释来执行此操作:

java
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
    this.enabled = enabled;
    this.remoteAddress = remoteAddress;
    this.security = security;
}
科特林

要使用构造函数绑定,必须使用@EnableConfigurationProperties或配置属性扫描启用类。您不能对由常规 Spring 机制创建的 bean 使用构造函数绑定(例如@Component,bean、使用@Bean方法创建的 bean 或使用加载的 bean @Import
如果你的类有多个构造函数,你也可以@ConstructorBinding直接在应该绑定的构造函数上使用。
不推荐使用java.util.Optionalwith ,@ConfigurationProperties因为它主要用作返回类型。因此,它不太适合配置属性注入。为了与其他类型的属性保持一致,如果您确实声明了一个Optional属性并且它没有值,null而不是一个空的Optional将被绑定。

2.8.3. 启用 @ConfigurationProperties 注释类型

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

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

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

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

}
科特林
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration

要使用配置属性扫描,请将@ConfigurationPropertiesScan注释添加到您的应用程序。通常,它被添加到带有注释的主应用程序类中,@SpringBootApplication但它可以添加到任何@Configuration类中。默认情况下,扫描将从声明注释的类的包中进行。如果要定义要扫描的特定包,可以按照以下示例进行操作:

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 {

}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan

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

@ConfigurationProperties使用配置属性扫描或通过 注册 bean时@EnableConfigurationProperties,bean 有一个约定名称:<prefix>-<fqn>,其中<prefix>@ConfigurationProperties注解中指定的环境键前缀,<fqn>是 bean 的完全限定名称。如果注解不提供任何前缀,则仅使用 bean 的完全限定名称。

上例中的 bean 名称是com.example.app-com.example.app.SomeProperties.

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

2.8.4。使用 @ConfigurationProperties 注释的类型

这种配置风格特别适用于SpringApplication外部 YAML 配置,如以下示例所示:

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

要使用@ConfigurationPropertiesbean,您可以像注入任何其他 bean 一样注入它们,如以下示例所示:

java
import org.springframework.stereotype.Service;

@Service
public class MyService {

    private final SomeProperties properties;

    public MyService(SomeProperties properties) {
        this.properties = properties;
    }

    public void openConnection() {
        Server server = new Server(this.properties.getRemoteAddress());
        server.start();
        // ...
    }

    // ...

}
科特林
import org.springframework.stereotype.Service

@Service
class MyService(val properties: SomeProperties) {

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

    // ...

}
Using@ConfigurationProperties还允许您生成元数据文件,IDE 可以使用这些文件为您自己的密钥提供自动完成功能。详情见附录

2.8.5。第三方配置

除了@ConfigurationProperties用于注释类之外,您还可以在公共@Bean方法上使用它。当您想要将属性绑定到您无法控制的第三方组件时,这样做会特别有用。

要从Environment属性配置 bean,请添加@ConfigurationProperties到其 bean 注册,如以下示例所示:

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

}
科特林
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 属性都以类似于前面示例的方式another映射到该bean。AnotherComponentSomeProperties

2.8.6。轻松绑定

Spring Boot 使用一些宽松的规则将Environment属性绑定到bean,因此属性名称和 bean 属性名称@ConfigurationProperties之间不需要完全匹配。Environment这很有用的常见示例包括破折号分隔的环境属性(例如,context-path绑定到contextPath)和大写环境属性(例如,PORT绑定到port)。

例如,考虑以下@ConfigurationProperties类:

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

}
科特林
import org.springframework.boot.context.properties.ConfigurationProperties

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

    var firstName: String? = null

}

使用上述代码,可以使用以下属性名称:

表 3. 宽松的绑定
财产 笔记

my.main-project.person.first-name

烤肉盒,推荐用于.properties.yml文件。

my.main-project.person.firstName

标准驼峰式语法。

my.main-project.person.first_name

下划线表示法,这是在文件中使用的另一种.properties格式.yml

MY_MAINPROJECT_PERSON_FIRSTNAME

大写格式,在使用系统环境变量时推荐使用。

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

属性文件

Camel case、kebab case 或下划线表示法

[ ]使用逗号分隔值的标准列表语法

YAML 文件

Camel case、kebab case 或下划线表示法

标准 YAML 列表语法或逗号分隔值

环境变量

下划线作为分隔符的大写格式(请参阅从环境变量绑定)。

下划线包围的数值(请参阅从环境变量绑定

系统属性

Camel case、kebab case 或下划线表示法

[ ]使用逗号分隔值的标准列表语法

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

绑定到Map属性时,您可能需要使用特殊的括号表示法,以便key保留原始值。如果键没有被 包围[],任何不是字母数字的字符,-或者.被删除。

例如,考虑将以下属性绑定到 a Map<String,String>

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

上面的属性将绑定到一个Mapwith /key1/key2key3作为映射中的键。斜线已被删除,key3因为它没有被方括号包围。

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

从环境变量绑定

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

Spring Boot 宽松的绑定规则尽可能地与这些命名限制兼容。

要将规范形式的属性名称转换为环境变量名称,您可以遵循以下规则:

  • 将点 ( .) 替换为下划线 ( _)。

  • 删除所有破折号 ( -)。

  • 转换为大写。

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

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

例如,配置属性my.service[0].other将使用名为MY_SERVICE_0_OTHER.

2.8.7。合并复杂类型

当列表配置在多个位置时,覆盖通过替换整个列表来工作。

例如,假设一个MyPojo对象具有默认的namedescription属性。null以下示例公开了MyPojo来自的对象列表MyProperties

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

}
科特林
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("my")
class MyProperties {

    val list: List<MyPojo> = ArrayList()

}

考虑以下配置:

特性
my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
yaml
my:
  list:
  - name: "my name"
    description: "my description"
---
spring:
  config:
    activate:
      on-profile: "dev"
my:
  list:
  - name: "my another name"

如果dev配置文件未激活,则MyProperties.list包含一个MyPojo条目,如先前定义的那样。但是,如果dev启用了配置文件,则list 仍然只包含一个条目(名称为my another name,描述为null)。此配置不会将第二个MyPojo实例添加到列表中,并且不会合并项目。

List在多个配置文件中指定 a 时,将使用具有最高优先级的一个(并且仅使用那个)。考虑以下示例:

特性
my.list[0].name=my name
my.list[0].description=my description
my.list[1].name=another name
my.list[1].description=another description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
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"

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

对于Map属性,您可以绑定从多个来源提取的属性值。但是,对于多个源中的相同属性,将使用具有最高优先级的属性。以下示例公开了Map<String, MyPojo>from MyProperties

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

}
科特林
import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("my")
class MyProperties {

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

}

考虑以下配置:

特性
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2
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"

如果dev配置文件未激活,则MyProperties.map包含一个带有键的条目key1(名称为my name 1,描述为my description 1)。但是,如果dev启用了配置文件,则map包含两个带有键的条目key1(名称为dev name 1,描述为my description 1)和key2(名称为dev name 2,描述为dev description 2)。

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

2.8.8。属性转换

@ConfigurationPropertiesSpring Boot 在绑定到bean时尝试将外部应用程序属性强制转换为正确的类型。如果您需要自定义类型转换,您可以提供一个ConversionServicebean(带有一个名为 的 bean conversionService)或自定义属性编辑器(通过一个CustomEditorConfigurerbean)或自定义Converters(带有注释为 的 bean 定义@ConfigurationPropertiesBinding)。

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

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

  • 常规long表示(使用毫秒作为默认单位,除非@DurationUnit已指定 a)

  • 使用的标准 ISO-8601 格式java.time.Duration

  • 一种更易读的格式,其中值和单位耦合(10s意味着 10 秒)

考虑以下示例:

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

}
科特林
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 秒的会话超时30PT30S30s都是等效的。500ms 的读取超时可以用以下任何一种形式指定500PT0.5S500ms

您还可以使用任何受支持的单位。这些是:

  • ns纳秒

  • us微秒

  • ms毫秒

  • s几秒钟

  • m几分钟

  • h用了几个小时

  • d好几天

默认单位是毫秒,可以使用@DurationUnit上面示例中的说明进行覆盖。

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

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

}
科特林
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.Period类型。以下格式可用于应用程序属性:

  • 常规int表示(使用天作为默认单位,除非@PeriodUnit已指定 a)

  • 使用的标准 ISO-8601 格式java.time.Period

  • 一种更简单的格式,其中值和单位对耦合(1y3d表示 1 年和 3 天)

简单格式支持以下单位:

  • y多年

  • m几个月

  • w数周

  • d好几天

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

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

  • 常规long表示(使用字节作为默认单位,除非@DataSizeUnit已指定 a)

  • 一种更易读的格式,其中值和单位是耦合的(10MB意味着 10 兆字节)

考虑以下示例:

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

}
科特林
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 兆字节,10并且10MB是等价的。可以将 256 字节的大小阈值指定为256256B

您还可以使用任何受支持的单位。这些是:

  • B对于字节

  • KB千字节

  • MB兆字节

  • GB千兆字节

  • TB太字节

默认单位是字节,可以使用@DataSizeUnit上面示例中的说明进行覆盖。

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

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

}
科特林
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)
如果要升级Long属性,请确保定义单位(使用@DataSizeUnit)如果它不是字节。这样做提供了一个透明的升级路径,同时支持更丰富的格式。

2.8.9。@ConfigurationProperties 验证

@ConfigurationProperties每当使用 Spring 的注解进行注解时,Spring Boot 都会尝试验证类@Validatedjavax.validation您可以直接在配置类上使用 JSR-303约束注释。为此,请确保您的类路径中存在兼容的 JSR-303 实现,然后将约束注释添加到您的字段,如以下示例所示:

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

}
科特林
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

为确保始终为嵌套属性触发验证,即使未找到任何属性,关联字段也必须使用 注释@Valid。以下示例基于前面的示例构建MyProperties

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

    }

}
科特林
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

    }

}

您还可以Validator通过创建一个名为configurationPropertiesValidator. @Bean应该声明该方法static。配置属性验证器是在应用程序生命周期的早期创建的,将@Bean方法声明为静态允许创建 bean 而无需实例化@Configuration类。这样做可以避免早期实例化可能导致的任何问题。

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

2.8.10。@ConfigurationProperties 与 @Value

@Value注解是一个核心容器特性,它不提供与类型安全配置属性相同的特性。下表总结了@ConfigurationProperties和支持的功能@Value

特征 @ConfigurationProperties @Value

轻松绑定

是的

有限(见下文注释

元数据支持

是的

SpEL评估

是的

如果您确实想使用@Value,我们建议您使用其规范形式(kebab-case 仅使用小写字母)引用属性名称。这将允许 Spring Boot 使用与放松绑定 时相同的逻辑@ConfigurationProperties

例如,将从文件中以及从系统环境中@Value("{demo.item-price}")提取demo.item-pricedemo.itemPrice形成表格。如果你改用了,就不会考虑了。application.propertiesDEMO_ITEMPRICE@Value("{demo.itemPrice}")demo.item-priceDEMO_ITEMPRICE

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

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

3. 简介

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

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

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

    // ...

}
科特林
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

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

    // ...

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

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

特性
spring.profiles.active=dev,hsqldb
yaml
spring:
  profiles:
    active: "dev,hsqldb"

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

如果没有配置文件处于活动状态,则启用默认配置文件。默认配置文件的名称是default,并且可以使用该spring.profiles.default Environment属性进行调整,如以下示例所示:

特性
spring.profiles.default=none
yaml
spring:
  profiles:
    default: "none"

spring.profiles.active并且spring.profiles.default只能在非配置文件特定文档中使用。这意味着它们不能包含在配置文件特定文件或由.spring.config.activate.on-profile

例如第二个文档配置无效:

特性
# this document is valid
spring.profiles.active=prod
#---
# this document is invalid
spring.config.activate.on-profile=prod
spring.profiles.active=metrics
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.active属性遵循与其他属性相同的排序规则:最高者PropertySource获胜。这意味着您可以在其中指定活动配置文件,application.properties然后使用命令行开关替换它们。

有时,将属性添加到活动配置文件而不是替换它们很有用。该属性可用于在由该属性spring.profiles.include激活的配置文件之上添加活动配置文件。入口点还有一个用于设置附加配置文件的 Java API spring.profiles.activeSpringApplication请参阅SpringApplicationsetAdditionalProfiles()中的方法。

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

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

如果给定的配置文件处于活动状态,则在下一节中描述的配置文件组也可用于添加活动配置文件。

3.2. 配置文件组

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

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

例如,我们可以创建一个production由我们的proddbprodmq配置文件组成的组。

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

我们的应用程序现在可以启动,一--spring.profiles.active=production键激活production,proddbprodmq配置文件。

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

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

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

application.properties(或application.yml)和通过引用的文件的特定配置文件变体@ConfigurationProperties被视为文件并加载。有关详细信息,请参阅“配置文件特定文件”。

4. 记录

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

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

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

4.1。日志格式

Spring Boot 的默认日志输出类似于以下示例:

2019-03-05 10:57:51.112 INFO 45469 --- [main] org.apache.catalina.core.StandardEngine:启动 Servlet 引擎:Apache Tomcat/7.0.52
2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] oaccC[Tomcat].[localhost].[/]:初始化 Spring 嵌入式 WebApplicationContext
2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] osweb.context.ContextLoader:根 WebApplicationContext:初始化在 1358 毫秒内完成
2019-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] osbceServletRegistrationBean:映射 servlet:'dispatcherServlet' 到 [/]
2019-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] osbcembedded.FilterRegistrationBean:映射过滤器:'hiddenHttpMethodFilter' 到:[/*]

输出以下项目:

  • 日期和时间:毫秒精度,易于排序。

  • 日志级别:ERRORWARNINFODEBUGTRACE

  • 进程标识。

  • ---用于区分实际日志消息开始的分隔符。

  • 线程名称:括在方括号中(可能会被截断以用于控制台输出)。

  • 记录器名称:这通常是源类名称(通常缩写)。

  • 日志消息。

Logback 没有FATAL级别。它映射到ERROR.

4.2. 控制台输出

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

$ java -jar myapp.jar --debug
你也可以debug=true在你的application.properties.

启用调试模式后,会配置一系列核心记录器(嵌入式容器、Hibernate 和 Spring Boot)以输出更多信息。启用调试模式不会您的应用程序配置为记录所有DEBUG级别的消息。

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

4.2.1。颜色编码输出

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

颜色编码是通过使用%clr转换字来配置的。在最简单的形式中,转换器根据日志级别为输出着色,如下例所示:

%clr(%5p)

下表描述了日志级别到颜色的映射:

等级 颜色

FATAL

红色的

ERROR

红色的

WARN

黄色

INFO

绿色的

DEBUG

绿色的

TRACE

绿色的

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

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

支持以下颜色和样式:

  • blue

  • cyan

  • faint

  • green

  • magenta

  • red

  • yellow

4.3. 文件输出

默认情况下,Spring Boot 只记录到控制台,不写入日志文件。如果你想在控制台输出之外写日志文件,你需要设置一个logging.file.nameorlogging.file.path属性(例如,在你的application.properties.

下表显示了如何logging.*一起使用这些属性:

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

(没有任何)

(没有任何)

仅控制台日志记录。

具体文件

(没有任何)

my.log

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

(没有任何)

具体目录

/var/log

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

日志文件在达到 10 MB 时会旋转,并且与控制台输出一样,默认情况下会记录ERROR-level、WARN-level 和INFO-level 消息。

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

4.4. 文件轮换

application.properties如果您使用的是 Logback,则可以使用您的orapplication.yaml文件微调日志轮换设置。对于所有其他日志记录系统,您将需要自己直接配置轮换设置(例如,如果您使用 Log4J2,那么您可以添加一个log4j2.xmllog4j2-spring.xml文件)。

支持以下轮换策略属性:

姓名 描述

logging.logback.rollingpolicy.file-name-pattern

用于创建日志存档的文件名模式。

logging.logback.rollingpolicy.clean-history-on-start

如果应在应用程序启动时进行日志归档清理。

logging.logback.rollingpolicy.max-file-size

归档前日志文件的最大大小。

logging.logback.rollingpolicy.total-size-cap

在被删除之前可以占用的最大大小的日志档案。

logging.logback.rollingpolicy.max-history

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

4.5. 日志级别

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

以下示例显示了潜在的日志记录设置application.properties

特性
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
yaml
logging:
  level:
    root: "warn"
    org.springframework.web: "debug"
    org.hibernate: "error"

也可以使用环境变量设置日志记录级别。例如,LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG将设置org.springframework.webDEBUG

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

4.6. 日志组

能够将相关的记录器组合在一起通常很有用,以便可以同时配置它们。例如,您可能通常会更改所有与 Tomcat 相关的记录器的日志记录级别,但您不容易记住顶级包。

为了解决这个问题,Spring Boot 允许您在 Spring 中定义日志记录组Environment。例如,您可以通过以下方式定义“tomcat”组,方法是将其添加到您的application.properties:

特性
logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat
yaml
logging:
  group:
    tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat"

定义后,您可以使用一行更改组中所有记录器的级别:

特性
logging.level.tomcat=trace
yaml
logging:
  level:
    tomcat: "trace"

Spring Boot 包括以下可开箱即用的预定义日志记录组:

姓名 记录器

网络

org.springframework.core.codec, org.springframework.http, org.springframework.web, org.springframework.boot.actuate.endpoint.web,org.springframework.boot.web.servlet.ServletContextInitializerBeans

sql

org.springframework.jdbc.core, org.hibernate.SQL,org.jooq.tools.LoggerListener

4.7. 使用日志关闭挂钩

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

特性
logging.register-shutdown-hook=false
yaml
logging:
  register-shutdown-hook: false

4.8. 自定义日志配置

各种日志系统可以通过在类路径中包含适当的库来激活,并且可以通过在类路径的根目录或由以下 SpringEnvironment属性指定的位置提供合适的配置文件来进一步定制logging.config

org.springframework.boot.logging.LoggingSystem您可以使用system 属性强制 Spring Boot 使用特定的日志记录系统。该值应该是实现的完全限定类名LoggingSystem。您还可以使用none.

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

根据您的日志记录系统,将加载以下文件:

测井系统 定制

回溯

logback-spring.xml, logback-spring.groovy, logback.xml, 或logback.groovy

日志4j2

log4j2-spring.xml或者log4j2.xml

JDK(Java 实用程序日志记录)

logging.properties

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

为了帮助进行自定义,一些其他属性从 Spring 转移Environment到 System 属性,如下表所述:

Spring环境 系统属性 注释

logging.exception-conversion-word

LOG_EXCEPTION_CONVERSION_WORD

记录异常时使用的转换字。

logging.file.name

LOG_FILE

如果已定义,它将在默认日志配置中使用。

logging.file.path

LOG_PATH

如果已定义,它将在默认日志配置中使用。

logging.pattern.console

CONSOLE_LOG_PATTERN

在控制台上使用的日志模式 (stdout)。

logging.pattern.dateformat

LOG_DATEFORMAT_PATTERN

日志日期格式的附加模式。

logging.charset.console

CONSOLE_LOG_CHARSET

用于控制台日志记录的字符集。

logging.pattern.file

FILE_LOG_PATTERN

在文件中使用的日志模式(如果LOG_FILE启用)。

logging.charset.file

FILE_LOG_CHARSET

用于文件日志记录的字符集(如果LOG_FILE启用)。

logging.pattern.level

LOG_LEVEL_PATTERN

呈现日志级别时使用的格式(默认%5p)。

PID

PID

当前进程 ID(如果可能且尚未定义为 OS 环境变量时发现)。

如果使用 Logback,还会传输以下属性:

Spring环境 系统属性 注释

logging.logback.rollingpolicy.file-name-pattern

LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN

翻转日志文件名的模式(默认${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz)。

logging.logback.rollingpolicy.clean-history-on-start

LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START

是否在启动时清理归档日志文件。

logging.logback.rollingpolicy.max-file-size

LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE

最大日志文件大小。

logging.logback.rollingpolicy.total-size-cap

LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP

要保留的日志备份的总大小。

logging.logback.rollingpolicy.max-history

LOGBACK_ROLLINGPOLICY_MAX_HISTORY

要保留的存档日志文件的最大数量。

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

如果你想在日志属性中使用占位符,你应该使用Spring Boot 的语法,而不是底层框架的语法。值得注意的是,如果你使用 Logback,你应该使用:它作为属性名称和它的默认值之间的分隔符,而不是使用:-.

LOG_LEVEL_PATTERN您可以通过仅覆盖(或logging.pattern.level使用 Logback)将 MDC 和其他临时内容添加到日志行。例如,如果您使用logging.pattern.level=user:%X{user} %5p,则默认日志格式包含“用户”的 MDC 条目(如果存在),如以下示例所示。

2019-08-30 12:30:04.031 用户:某人 INFO 22174 --- [nio-8080-exec-0] demo.Controller
处理经过身份验证的请求

4.9. Logback 扩展

Spring Boot 包含许多对 Logback 的扩展,可以帮助进行高级配置。您可以在logback-spring.xml配置文件中使用这些扩展。

因为标准logback.xml配置文件加载得太早,所以不能在里面使用扩展。您需要使用logback-spring.xml或定义logging.config属性。
扩展不能与 Logback 的配置扫描一起使用。如果您尝试这样做,则对配置文件进行更改会导致记录类似于以下内容之一的错误:
[电子邮件保护]中的错误:71 - [springProperty] 没有适用的操作,当前 ElementPath 是 [[configuration][springProperty]][email protected]
中的错误:71 - [springProfile] 没有适用的操作,当前 ElementPath 是 [[configuration][springProfile]]

4.9.1。配置文件特定的配置

<springProfile>标签允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。元素内的任何位置都支持配置文件部分<configuration>。使用该name属性来指定哪个配置文件接受配置。标签可以包含配置<springProfile>文件名称(例如staging)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west). 查看参考指南以获取更多详细信息。以下清单显示了三个示例配置文件:

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

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

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

4.9.2. 环境属性

<springProperty>标签允许您公开 Spring 中的属性Environment以在 Logback 中使用。application.properties如果您想在 Logback 配置中访问文件中的值,这样做会很有用。该标签的工作方式与 Logback 的标准<property>标签类似。value但是,您可以指定source属性的 (来自) ,而不是直接指定Environment。如果您需要将属性存储在范围以外的某个位置local,则可以使用该scope属性。如果您需要一个备用值(如果该属性未在 中设置Environment),您可以使用该defaultValue属性。以下示例显示了如何公开属性以在 Logback 中使用:

<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
        defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
    <remoteHost>${fluentHost}</remoteHost>
    ...
</appender>
必须在 kebab case中source指定(例如my.property-name)。Environment但是,可以使用宽松的规则 将属性添加到 中。

5.国际化

Spring Boot 支持本地化消息,以便您的应用程序可以满足不同语言偏好的用户。默认情况下,Spring Bootmessages在类路径的根目录中查找资源包的存在。

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

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

特性
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false
yaml
spring:
  messages:
    basename: "messages,config.i18n.messages"
    fallback-to-system-locale: false
spring.messages.basename支持以逗号分隔的位置列表,可以是包限定符,也可以是从类路径根解析的资源。

有关MessageSourceProperties更多支持的选项,请参阅。

6.JSON

Spring Boot 提供与三个 JSON 映射库的集成:

  • 格森

  • 杰克逊

  • JSON-B

Jackson 是首选的默认库。

6.1。杰克逊

提供了 Jackson 的自动配置,并且 Jackson 是spring-boot-starter-json. 当 Jackson 在类路径上时,ObjectMapper会自动配置一个 bean。提供了几个配置属性用于自定义ObjectMapper.

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

如果您使用 Jackson 来序列化和反序列化 JSON 数据,您可能需要编写自己的类JsonSerializerJsonDeserializer类。自定义序列化程序通常通过一个模块向 Jackson 注册,但 Spring Boot 提供了一个替代@JsonComponent注解,可以更轻松地直接注册 Spring Bean。

您可以直接在或实现上使用@JsonComponent注释。您还可以在包含序列化器/反序列化器作为内部类的类上使用它,如以下示例所示:JsonSerializerJsonDeserializerKeyDeserializer

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

    }

}
科特林
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)
        }
    }

}

中的所有@JsonComponentbean 都会ApplicationContext自动向 Jackson 注册。因为@JsonComponent是用 元注释的@Component,所以通常的组件扫描规则适用。

Spring Boot 还提供了JsonObjectSerializerJsonObjectDeserializer类,它们在序列化对象时提供了标准 Jackson 版本的有用替代方案。JsonObjectSerializer有关详细信息,请参阅JsonObjectDeserializerJavadoc。

上面的例子可以重写为使用JsonObjectSerializer/JsonObjectDeserializer如下:

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

    }

}
科特林
`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)
        }
    }

}

6.1.2. 混合

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

6.2. 格森

提供了 Gson 的自动配置。当 Gson 在类路径上时,Gson会自动配置一个 bean。提供了几个spring.gson.*配置属性用于自定义配置。要进行更多控制,GsonBuilderCustomizer可以使用一个或多个 bean。

6.3. JSON-B

提供了 JSON-B 的自动配置。当 JSON-B API 和实现在类路径上时,Jsonb将自动配置一个 bean。首选的 JSON-B 实现是为其提供依赖管理的 Apache Johnzon。

7. 任务执行与调度

在上下文中没有Executorbean 的情况下,Spring Boot 会自动配置一个ThreadPoolTaskExecutor合理的默认值,这些默认值可以自动关联到异步任务执行 ( @EnableAsync) 和 Spring MVC 异步请求处理。

如果您在上下文中定义了自定义Executor,则常规任务执行(即@EnableAsync)将透明地使用它,但不会配置 Spring MVC 支持,因为它需要AsyncTaskExecutor实现(名为applicationTaskExecutor)。根据您的目标安排,您可以将您的更改Executor为 aThreadPoolTaskExecutor或定义 aThreadPoolTaskExecutorAsyncConfigurer包装您的 custom Executor

自动配置TaskExecutorBuilder允许您轻松创建实例来重现自动配置默认执行的操作。

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

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

这会将线程池更改为使用有界队列,以便当队列已满(100 个任务)时,线程池增加到最多 16 个线程。由于线程在空闲 10 秒(而不是默认情况下的 60 秒)时被回收,因此池的收缩更具侵略性。

ThreadPoolTaskScheduler如果需要与计划的任务执行相关联(@EnableScheduling例如使用),也可以自动配置A。线程池默认使用一个线程,可以使用命名空间微调其设置spring.task.scheduling,如下例所示:

特性
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2
yaml
spring:
  task:
    scheduling:
      thread-name-prefix: "scheduling-"
      pool:
        size: 2

如果需要创建自定义执行程序或调度程序,则TaskExecutorBuilderbean 和bean 在上下文中都可用。TaskSchedulerBuilder

8. 测试

Spring Boot 提供了许多实用程序和注释来帮助测试您的应用程序。测试支持由两个模块提供:spring-boot-test包含核心项目,并spring-boot-test-autoconfigure支持测试的自动配置。

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

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

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

hamcrest-core被排除在外,赞成org.hamcrest:hamcrest那是 的一部分spring-boot-starter-test

8.1。测试范围依赖

spring-boot-starter-testStarter”(在 中test scope)包含以下提供的库:

  • JUnit 5:单元测试 Java 应用程序的事实标准。

  • Spring Test & Spring Boot Test:Spring Boot 应用程序的实用程序和集成测试支持。

  • AssertJ:一个流畅的断言库。

  • Hamcrest:匹配器对象库(也称为约束或谓词)。

  • Mockito:Java 模拟框架。

  • JSONassert : JSON 的断言库。

  • JsonPath:JSON 的 XPath。

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

8.2. 测试 Spring 应用程序

依赖注入的主要优点之一是它应该使您的代码更容易进行单元测试。您可以使用new运算符来实例化对象,甚至无需涉及 Spring。您还可以使用模拟对象而不是真正的依赖项。

通常,您需要超越单元测试并开始集成测试(使用 Spring ApplicationContext)。能够执行集成测试而不需要部署您的应用程序或需要连接到其他基础设施是很有用的。

Spring 框架包括一个用于此类集成测试的专用测试模块。你可以直接声明一个依赖org.springframework:spring-test或者使用spring-boot-starter-test“Starter”来传递它。

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

8.3. 测试 Spring Boot 应用程序

Spring Boot 应用程序是一个 Spring ApplicationContext,因此除了您通常使用普通 Spring 上下文所做的之外,无需做任何特别的事情来测试它。

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

Spring Boot 提供了注解,当您需要 Spring Boot 特性时@SpringBootTest,可以将其用作标准注解的替代方案。spring-test @ContextConfiguration注释通过在您的测试中创建ApplicationContextSpringApplication使用的. 除了@SpringBootTest许多其他注释之外,还提供了用于测试应用程序更具体的切片

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

默认情况下,@SpringBootTest不会启动服务器。您可以使用 的webEnvironment属性@SpringBootTest来进一步优化测试的运行方式:

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

  • RANDOM_PORT: 加载WebServerApplicationContext并提供一个真实的网络环境。嵌入式服务器启动并侦听随机端口。

  • DEFINED_PORT: 加载WebServerApplicationContext并提供一个真实的网络环境。嵌入式服务器启动并侦听定义的端口(来自您的application.properties)或8080.

  • NONEApplicationContext:通过使用加载一个SpringApplication但不提供任何Web 环境(模拟或其他)。

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

8.3.1. 检测 Web 应用程序类型

如果 Spring MVC 可用,则配置常规的基于 MVC 的应用程序上下文。如果您只有 Spring WebFlux,我们将检测并配置一个基于 WebFlux 的应用程序上下文。

如果两者都存在,则 Spring MVC 优先。如果要在这种情况下测试响应式 Web 应用程序,则必须设置spring.main.web-application-type属性:

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

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

    // ...

}
科特林
import org.springframework.boot.test.context.SpringBootTest

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

    // ...

}

8.3.2. 检测测试配置

如果您熟悉 Spring Test Framework,您可能习惯于使用@ContextConfiguration(classes=…​)以指定@Configuration要加载的 Spring。或者,您可能经常@Configuration在测试中使用嵌套类。

在测试 Spring Boot 应用程序时,通常不需要这样做。@*Test只要您没有明确定义,Spring Boot 的注释就会自动搜索您的主要配置。

搜索算法从包含测试的包开始,直到找到一个用@SpringBootApplicationor注释的类@SpringBootConfiguration。只要您以合理的方式构建代码,通常就会找到您的主要配置。

如果您使用测试注释来测试应用程序的更具体部分,则应避免在主方法的应用程序类上添加特定于特定区域的配置设置。

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

如果要自定义主要配置,可以使用嵌套@TestConfiguration类。与将@Configuration使用嵌套类代替应用程序的主要配置不同,嵌套@TestConfiguration类是在应用程序的主要配置之外使用的。

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

8.3.3. 排除测试配置

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

正如我们之前看到的@TestConfiguration可以在测试的内部类上使用来自定义主要配置。当放置在顶级类上时,@TestConfiguration表示src/test/java不应通过扫描获取其中的类。然后,您可以在需要的地方显式导入该类,如以下示例所示:

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

}
科特林
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import

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

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

}
如果您直接使用@ComponentScan(即不通过@SpringBootApplication),则需要向其注册TypeExcludeFilter。有关详细信息,请参阅Javadoc

8.3.4. 使用应用程序参数

如果您的应用程序需要arguments,您可以使用属性@SpringBootTest注入它们。args

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

}
科特林
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")
    }

}

8.3.5。使用模拟环境进行测试

默认情况下,@SpringBootTest不会启动服务器,而是设置一个模拟环境来测试 Web 端点。

使用 Spring MVC,我们可以使用MockMvcor查询我们的 Web 端点WebTestClient,如下例所示:

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

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

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

    @Test
    fun testWithMockMvc(@Autowired mvc: MockMvc) {
        mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
            .andExpect(MockMvcResultMatchers.content().string("Hello World"))
    }

    // If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient

    @Test
    fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk
            .expectBody<String>().isEqualTo("Hello World")
    }

}
如果您只想专注于 web 层而不是开始一个完整的ApplicationContext请考虑改用@WebMvcTest.

使用 Spring WebFlux 端点,您可以使用WebTestClient如下示例所示:

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

}
科特林
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 容器运行更快。但是,由于 mocking 发生在 Spring MVC 层,依赖于较低级别 servlet 容器行为的代码不能直接使用 MockMvc 进行测试。

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

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

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

@LocalServerPort注释可用于将实际使用的端口注入到您的测试中。为方便起见,需要对启动的服务器进行 REST 调用的测试可以额外添加@Autowire一个WebTestClient,它解析到正在运行的服务器的相关链接,并带有用于验证响应的专用 API,如下例所示:

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

}
科特林
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可用于实时服务器和模拟环境

此设置需要spring-webflux在类路径上。如果你不能或不会添加 webflux,Spring Boot 还提供了一个TestRestTemplate工具:

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

}
科特林
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")
    }

}

8.3.7. 自定义 WebTestClient

要自定义WebTestClientbean,请配置一个WebTestClientBuilderCustomizerbean。任何此类 bean 都WebTestClient.Builder使用用于创建WebTestClient.

8.3.8. 使用 JMX

由于测试上下文框架会缓存上下文,因此默认情况下禁用 JMX,以防止相同的组件在同一域上注册。如果此类测试需要访问MBeanServer,请考虑将其标记为脏:

java
import javax.management.MBeanServer;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;

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

@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {

    @Autowired
    private MBeanServer mBeanServer;

    @Test
    void exampleTest() {
        assertThat(this.mBeanServer.getDomains()).contains("java.lang");
        // ...
    }

}
科特林
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.junit.jupiter.SpringExtension
import javax.management.MBeanServer

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

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

}

8.3.9。使用指标

无论您的类路径如何,使用@SpringBootTest.

如果您需要在集成测试中将指标导出到不同的后端,请使用@AutoConfigureMetrics.

8.3.10。模拟和窥探 Bean

运行测试时,有时需要在应用程序上下文中模拟某些组件。例如,您可能有一些在开发期间不可用的远程服务的外观。当您想要模拟在真实环境中可能难以触发的故障时,模拟也很有用。

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

如果您的测试使用 Spring Boot 的测试注解之一(例如@SpringBootTest),则会自动启用此功能。要以不同的排列方式使用此功能,必须显式添加侦听器,如以下示例所示:

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 {

    // ...

}
科特林
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.TestExecutionListeners

@ContextConfiguration(classes = [MyConfig::class])
@TestExecutionListeners(
    MockitoTestExecutionListener::class,
    ResetMocksTestExecutionListener::class
)
class MyTests {

    // ...

}

以下示例将现有RemoteServicebean 替换为模拟实现:

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

}
科特林
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean

@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {

    @Test
    fun exampleTest() {
        given(remoteService.value).willReturn("spring")
        val reverse = reverser.reverseValue // Calls injected RemoteService
        assertThat(reverse).isEqualTo("gnirps")
    }

}
@MockBean不能用于模拟在应用程序上下文刷新期间执行的 bean 的行为。到执行测试时,应用程序上下文刷新已完成,配置模拟行为为时已晚。我们建议@Bean在这种情况下使用一种方法来创建和配置模拟。

此外,您可以使用@SpyBeanMockito 包装任何现有的 bean spy。有关完整的详细信息,请参阅Javadoc

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

8.3.11。自动配置的测试

Spring Boot 的自动配置系统适用于应用程序,但有时对于测试来说有点太多了。它通常有助于仅加载测试应用程序“切片”所需的配置部分。例如,您可能想要测试 Spring MVC 控制器是否正确映射 URL,并且您不想在这些测试中涉及数据库调用,或者您可能想要测试 JPA 实体,而当这些测试运行。

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

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

8.3.12。自动配置的 JSON 测试

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

  • 杰克逊ObjectMapper,任何@JsonComponent豆子和任何杰克逊Module

  • Gson

  • Jsonb

@JsonTest可以在附录中找到 启用的自动配置列表。

如果需要配置自动配置的元素,可以使用@AutoConfigureJsonTesters注解。

Spring Boot 包括基于 AssertJ 的帮助程序,它们与 JSONAssert 和 JsonPath 库一起检查 JSON 是否按预期显示。、JacksonTesterGsonTester和类可分别用于 Jackson、Gson、Jsonb 和 Strings JsonbTesterBasicJsonTester测试类上的任何辅助字段都可以@Autowired在使用@JsonTest. 以下示例显示了 Jackson 的测试类:

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

}
科特林
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.json.JsonTest
import org.springframework.boot.test.json.JacksonTester

@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {

    @Test
    fun serialize() {
        val details = VehicleDetails("Honda", "Civic")
        // Assert against a `.json` file in the same package as the test
        assertThat(json.write(details)).isEqualToJson("expected.json")
        // Or use JSON path based assertions
        assertThat(json.write(details)).hasJsonPathStringValue("@.make")
        assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
    }

    @Test
    fun deserialize() {
        val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
        assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
        assertThat(json.parseObject(content).make).isEqualTo("Ford")
    }

}
JSON 辅助类也可以直接用于标准单元测试。为此,initFields如果@Before您不使用@JsonTest.

如果您使用 Spring Boot 的基于 AssertJ 的帮助器在给定 JSON 路径上对数字值进行断言,则可能无法使用,isEqualTo具体取决于类型。相反,您可以使用 AssertJsatisfies断言该值与给定条件匹配。例如,以下示例断言实际数字是接近0.15偏移量的浮点值0.01

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)));
}
科特林
@Test
fun someTest() {
    val value = SomeObject(0.152f)
    assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
        .satisfies(ThrowingConsumer { number ->
            assertThat(number.toFloat()).isCloseTo(0.15f, within(0.01f))
        })
}

8.3.13。自动配置的 Spring MVC 测试

要测试 Spring MVC 控制器是否按预期工作,请使用@WebMvcTest注解。 @WebMvcTest自动配置 Spring MVC 基础结构并将扫描的 bean 限制为@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer,WebMvcRegistrationsHandlerMethodArgumentResolver. 使用注解 时不会扫描常规@Component和bean。可用于包含bean。@ConfigurationProperties@WebMvcTest@EnableConfigurationProperties@ConfigurationProperties

@WebMvcTest在附录中找到 启用的自动配置设置列表。
如果您需要注册额外的组件,例如 Jackson Module,您可以通过@Import在测试中使用来导入额外的配置类。

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

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

您还可以通过使用注释来自动配置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"));
    }

}
科特林
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvc) {

    @MockBean
    lateinit var userVehicleService: UserVehicleService

    @Test
    fun testExample() {
        given(userVehicleService.getVehicleDetails("sboot"))
            .willReturn(VehicleDetails("Honda", "Civic"))
        mvc.perform(MockMvcRequestBuilders.get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
            .andExpect(MockMvcResultMatchers.status().isOk)
            .andExpect(MockMvcResultMatchers.content().string("Honda Civic"))
    }

}
如果您需要配置自动配置的元素(例如,何时应应用 servlet 过滤器),您可以在@AutoConfigureMockMvc注解中使用属性。

如果您使用 HtmlUnit 和 Selenium,自动配置还提供 HtmlUnit WebClientbean 和/或 Selenium WebDriverbean。以下示例使用 HtmlUnit:

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

}
科特林
import com.gargoylesoftware.htmlunit.WebClient
import com.gargoylesoftware.htmlunit.html.HtmlPage
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean

@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {

    @MockBean
    lateinit var userVehicleService: UserVehicleService

    @Test
    fun testExample() {
        given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
        val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
        assertThat(page.body.textContent).isEqualTo("Honda Civic")
    }

}
默认情况下,Spring Boot 将WebDriverbean 放在一个特殊的“范围”中,以确保驱动程序在每次测试后退出并注入新实例。如果您不想要这种行为,您可以添加@Scope("singleton")到您的WebDriver @Bean定义中。
Spring Boot 创建的webDriver范围将替换任何用户定义的同名范围。如果您定义自己的webDriver范围,您可能会发现在使用@WebMvcTest.

如果类路径上有 Spring Security,@WebMvcTest也会扫描WebSecurityConfigurerbean。您可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。有关如何使用 Spring SecurityMockMvc支持的更多详细信息,请参见howto.html how-to 部分。

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

8.3.14。自动配置的 Spring WebFlux 测试

要测试Spring WebFlux控制器是否按预期工作,您可以使用@WebFluxTest注释。 @WebFluxTest自动配置 Spring WebFlux 基础结构并将扫描的 bean 限制为@Controller@ControllerAdvice@JsonComponentConverterGenericConverterWebFilterWebFluxConfigurer. 使用注解 时不会扫描常规@Component和bean。可用于包含bean。@ConfigurationProperties@WebFluxTest@EnableConfigurationProperties@ConfigurationProperties

@WebFluxTest可以在附录中找到 启用的自动配置列表。
如果您需要注册额外的组件,例如 Jackson ,您可以在您的测试中 Module使用导入额外的配置类。@Import

通常,@WebFluxTest仅限于单个控制器并与@MockBean注释结合使用,为所需的协作者提供模拟实现。

@WebFluxTest还有 auto-configures WebTestClient,它提供了一种强大的方法来快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。

您还可以通过使用注释来自动配置WebTestClient@WebFluxTest(例如) 。以下示例显示了一个同时使用和 a的类: @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");
    }

}
科特林
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 应用程序支持,因为在模拟WebTestClientWeb 应用程序中使用目前仅适用于 WebFlux。
@WebFluxTest无法检测通过功能 Web 框架注册的路由。要在上下文中测试RouterFunctionbean,请考虑通过 using或 using导入您RouterFunction自己。 @Import@SpringBootTest
@WebFluxTest无法检测注册为@Bean类型的自定义安全配置SecurityWebFilterChain。要将其包含在您的测试中,您需要通过 using@Import或 using导入注册 bean 的配置@SpringBootTest
有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际的服务器运行完整的端到端测试

8.3.15。自动配置的 Spring GraphQL 测试

Spring GraphQL 提供了专门的测试支持模块;您需要将其添加到您的项目中:

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。测试仪在测试中大量使用,所以一定要熟悉使用它。有GraphQlTester变体,Spring Boot 将根据测试类型自动配置它们:

  • ExecutionGraphQlServiceTester服务器端执行测试,没有客户端也没有传输

  • 使用HttpGraphQlTester连接到服务器的客户端执行测试,有或没有实时服务器

Spring Boot 可帮助您使用注释测试Spring GraphQL 控制器@GraphQlTest@GraphQlTest自动配置 Spring GraphQL 基础设施,不涉及任何传输或服务器。这会将扫描的bean 限制为@ControllerRuntimeWiringConfigurerJsonComponentConverterGenericConverterDataFetcherExceptionResolver和。使用注解 时不会扫描常规和bean。可用于包含bean。InstrumentationGraphQlSourceBuilderCustomizer@Component@ConfigurationProperties@GraphQlTest@EnableConfigurationProperties@ConfigurationProperties

@GraphQlTest可以在附录中找到 启用的自动配置列表。
如果您需要注册额外的组件,例如 Jackson ,您可以在您的测试中 Module使用导入额外的配置类。@Import

通常,@GraphQlTest仅限于一组控制器并与@MockBean注释结合使用,为所需的协作者提供模拟实现。

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!");
    }

}
科特林
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测试是完整的集成测试,涉及整个应用程序。当使用随机或定义的端口时,会配置一个实时服务器并HttpGraphQlTester自动提供一个 bean,以便您可以使用它来测试您的服务器。配置 MOCK 环境后,您还可以HttpGraphQlTester通过使用以下命令注释测试类来请求 bean @AutoConfigureHttpGraphQlTester

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!");
    }

}
科特林
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!")
    }
}

8.3.16。自动配置的数据 Cassandra 测试

您可以使用它@DataCassandraTest来测试 Cassandra 应用程序。默认情况下,它配置一个CassandraTemplate,扫描@Table类,并配置 Spring Data Cassandra 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 Cassandra 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties@DataCassandraTest@EnableConfigurationProperties@ConfigurationProperties

@DataCassandraTest在附录中找到 启用的自动配置设置列表。

以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;

@DataCassandraTest
class MyDataCassandraTests {

    @Autowired
    private SomeRepository repository;

}
科特林
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest

@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)

8.3.17。自动配置的 Data Couchbase 测试

您可以@DataCouchbaseTest用来测试 Couchbase 应用程序。默认情况下,它配置一个CouchbaseTemplateor ReactiveCouchbaseTemplate,扫描@Document类,并配置 Spring Data Couchbase 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 Couchbase 的更多信息,请参阅本章前面的“ data.html ”。)@ConfigurationProperties@DataCouchbaseTest@EnableConfigurationProperties@ConfigurationProperties

@DataCouchbaseTest在附录中找到 启用的自动配置设置列表。

以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;

@DataCouchbaseTest
class MyDataCouchbaseTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
科特林
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest

@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {

    // ...

}

8.3.18。自动配置的数据 Elasticsearch 测试

您可以使用它@DataElasticsearchTest来测试 Elasticsearch 应用程序。默认情况下,它配置一个ElasticsearchRestTemplate,扫描@Document类,并配置 Spring Data Elasticsearch 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 Elasticsearch 的更多信息,请参阅本章前面的“ data.html ”。)@ConfigurationProperties@DataElasticsearchTest@EnableConfigurationProperties@ConfigurationProperties

@DataElasticsearchTest在附录中找到 启用的自动配置设置列表。

以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;

@DataElasticsearchTest
class MyDataElasticsearchTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
科特林
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest

@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {

    // ...

}

8.3.19。自动配置的数据 JPA 测试

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

使用注解 时不会扫描常规@Component和bean。可用于包含bean。@ConfigurationProperties@DataJpaTest@EnableConfigurationProperties@ConfigurationProperties

@DataJpaTest在附录中找到 启用的自动配置设置列表。

默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:

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 {

    // ...

}
科特林
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 测试也可以注入一个bean,它提供了一个专门为测试设计TestEntityManager的标准 JPA 的替代方案。EntityManager

TestEntityManager也可以通过添加自动配置到任何基于 Spring 的测试类@AutoConfigureTestEntityManager。这样做时,请确保您的测试在事务中运行,例如通过添加@Transactional您的测试类或方法。

JdbcTemplate如果您需要,也可以使用A。以下示例显示了@DataJpaTest正在使用的注释:

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

}
科特林
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager

@DataJpaTest
class MyRepositoryTests(@Autowired val entityManager: TestEntityManager, @Autowired val repository: UserRepository) {

    @Test
    fun testExample() {
        entityManager.persist(User("sboot", "1234"))
        val user = repository.findByUsername("sboot")
        assertThat(user?.username).isEqualTo("sboot")
        assertThat(user?.employeeNumber).isEqualTo("1234")
    }

}

内存嵌入式数据库通常适用于测试,因为它们速度快且不需要任何安装。但是,如果您更喜欢针对真实数据库运行测试,则可以使用@AutoConfigureTestDatabase注解,如以下示例所示:

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 {

    // ...

}
科特林
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 {

    // ...

}

8.3.20。自动配置的 JDBC 测试

@JdbcTest类似于@DataJpaTest但适用于只需要 aDataSource且不使用 Spring Data JDBC 的测试。默认情况下,它配置一个内存嵌入式数据库和一个JdbcTemplate. 使用注解 时不会扫描常规@Component和bean。可用于包含bean。@ConfigurationProperties@JdbcTest@EnableConfigurationProperties@ConfigurationProperties

@JdbcTest可以在附录中找到 启用的自动配置列表。

默认情况下,JDBC 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:

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 {

}
科特林
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

如果您希望您的测试针对真实数据库运行,您可以使用与@AutoConfigureTestDatabasefor 相同的方式使用注解DataJpaTest。(请参阅“自动配置的数据 JPA 测试”。)

8.3.21。自动配置的数据 JDBC 测试

@DataJdbcTest类似于@JdbcTest但适用于使用 Spring Data JDBC 存储库的测试。默认情况下,它配置了内存中的嵌入式数据库、aJdbcTemplate和 Spring Data JDBC 存储库。使用注解时只AbstractJdbcConfiguration扫描子类@DataJdbcTest,不扫描常规@Component@ConfigurationPropertiesbean。 @EnableConfigurationProperties可用于包含@ConfigurationPropertiesbean。

@DataJdbcTest可以在附录中找到 启用的自动配置列表。

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

如果您希望您的测试针对真实数据库运行,您可以使用与@AutoConfigureTestDatabasefor 相同的方式使用注解DataJpaTest。(请参阅“自动配置的数据 JPA 测试”。)

8.3.22。自动配置的 jOOQ 测试

您可以以与jOOQ 相关测试@JooqTest类似的方式使用。@JdbcTest由于 jOOQ 严重依赖与数据库模式相对应的基于 Java 的模式,因此DataSource使用现有模式。如果您想用内存数据库替换它,您可以使用@AutoConfigureTestDatabase覆盖这些设置。(有关在 Spring Boot 中使用 jOOQ 的更多信息,请参阅“ data.html ”。)使用注解 时不会扫描常规@Component和bean。可用于包含bean。@ConfigurationProperties@JooqTest@EnableConfigurationProperties@ConfigurationProperties

@JooqTest可以在附录中找到 启用的自动配置列表。

@JooqTest配置一个DSLContext. 以下示例显示了@JooqTest正在使用的注释:

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;

    // ...

}
科特林
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 示例中所示

8.3.23。自动配置数据 MongoDB 测试

您可以使用它@DataMongoTest来测试 MongoDB 应用程序。默认情况下,它配置一个内存中的嵌入式 MongoDB(如果可用),配置一个MongoTemplate,扫描@Document类,并配置 Spring Data MongoDB 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 MongoDB 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties@DataMongoTest@EnableConfigurationProperties@ConfigurationProperties

@DataMongoTest在附录中找到 启用的自动配置设置列表。

下面的类显示了@DataMongoTest正在使用的注解:

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;

    // ...

}
科特林
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 自动配置,如以下示例所示:

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

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

    // ...

}
科特林
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest

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

    // ...

}

8.3.24。自动配置的数据 Neo4j 测试

您可以@DataNeo4jTest用来测试 Neo4j 应用程序。默认情况下,它会扫描@Node类并配置 Spring Data Neo4j 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 Neo4J 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties@DataNeo4jTest@EnableConfigurationProperties@ConfigurationProperties

@DataNeo4jTest在附录中找到 启用的自动配置设置列表。

以下示例显示了在 Spring Boot 中使用 Neo4J 测试的典型设置:

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;

@DataNeo4jTest
class MyDataNeo4jTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
科特林
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 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:

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 {

}
科特林
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按上述方式配置测试。

8.3.25。自动配置的数据 Redis 测试

您可以@DataRedisTest用来测试 Redis 应用程序。默认情况下,它会扫描@RedisHash类并配置 Spring Data Redis 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 Redis 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties@DataRedisTest@EnableConfigurationProperties@ConfigurationProperties

@DataRedisTest在附录中找到 启用的自动配置设置列表。

以下示例显示了@DataRedisTest正在使用的注释:

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;

@DataRedisTest
class MyDataRedisTests {

    @Autowired
    private SomeRepository repository;

    // ...

}
科特林
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest

@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {

    // ...

}

8.3.26。自动配置的数据 LDAP 测试

您可以使用它@DataLdapTest来测试 LDAP 应用程序。默认情况下,它配置内存中的嵌入式 LDAP(如果可用)、配置LdapTemplate、扫描@Entry类并配置 Spring Data LDAP 存储库。使用注解 时不会扫描常规@Component和bean。可用于包含bean。(有关在 Spring Boot 中使用 LDAP 的更多信息,请参阅“ data.html ”。)@ConfigurationProperties@DataLdapTest@EnableConfigurationProperties@ConfigurationProperties

@DataLdapTest在附录中找到 启用的自动配置设置列表。

以下示例显示了@DataLdapTest正在使用的注释:

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;

    // ...

}
科特林
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 自动配置,如以下示例所示:

java
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;

@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {

    // ...

}
科特林
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest

@DataLdapTest(excludeAutoConfiguration = [EmbeddedLdapAutoConfiguration::class])
class MyDataLdapTests {

    // ...

}

8.3.27。自动配置的 REST 客户端

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

@RestClientTest在附录中找到 启用的自动配置设置列表。

您要测试的特定 bean 应使用 的valueorcomponents属性指定@RestClientTest,如以下示例所示:

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

}
科特林
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")
    }

}

8.3.28。自动配置的 Spring REST 文档测试

您可以使用@AutoConfigureRestDocs注解在您的测试中使用Spring REST Docs与 Mock MVC、REST Assured 或 WebTestClient。它消除了 Spring REST Docs 中对 JUnit 扩展的需求。

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

使用 Mock MVC 自动配置 Spring REST 文档测试

@AutoConfigureRestDocs在测试基于 servlet 的 Web 应用程序时自定义MockMvcbean 以使用 Spring REST Docs。您可以@Autowired像使用 Mock MVC 和 Spring REST Docs 时一样在测试中使用和使用它,如以下示例所示:

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

}
科特林
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@WebMvcTest(UserController::class)
@AutoConfigureRestDocs
class MyUserDocumentationTests(@Autowired val mvc: MockMvc) {

    @Test
    fun listUsers() {
        mvc.perform(MockMvcRequestBuilders.get("/users").accept(MediaType.TEXT_PLAIN))
            .andExpect(MockMvcResultMatchers.status().isOk)
            .andDo(MockMvcRestDocumentation.document("list-users"))
    }

}

如果您需要对 Spring REST Docs 配置的控制比 的属性提供的更多@AutoConfigureRestDocs,则可以使用RestDocsMockMvcConfigurationCustomizerbean,如以下示例所示:

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

}
科特林
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 对参数化输出目录的支持,你可以创建一个RestDocumentationResultHandlerbean。alwaysDo使用此结果处理程序进行自动配置调用,从而导致每个MockMvc调用自动生成默认片段。下面的例子显示了一个RestDocumentationResultHandler被定义的:

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

}
科特林
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 文档测试

@AutoConfigureRestDocs也可以WebTestClient在测试响应式 Web 应用程序时使用。您可以@Autowired像使用 Spring REST Docs 时一样在测试中使用和使用它@WebFluxTest,如以下示例所示:

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

}
科特林
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient

@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests(@Autowired val webTestClient: WebTestClient) {

    @Test
    fun listUsers() {
        webTestClient
            .get().uri("/")
            .exchange()
            .expectStatus()
            .isOk
            .expectBody()
            .consumeWith(WebTestClientRestDocumentation.document("list-users"))
    }

}

如果您需要对 Spring REST Docs 配置的控制比 的属性提供的更多@AutoConfigureRestDocs,则可以使用RestDocsWebTestClientConfigurationCustomizerbean,如以下示例所示:

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

}
科特林
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 对参数化输出目录的支持,您可以使用 aWebTestClientBuilderCustomizer为每个实体交换结果配置消费者。以下示例显示了这样的WebTestClientBuilderCustomizer定义:

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

}
科特林
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 文档测试

@AutoConfigureRestDocs使RequestSpecification预配置为使用 Spring REST Docs 的 bean 可用于您的测试。您可以@Autowired像使用 REST Assured 和 Spring REST Docs 时一样在测试中使用和使用它,如以下示例所示:

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

}
科特林
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 配置的控制比 的属性提供的更多@AutoConfigureRestDocsRestDocsRestAssuredConfigurationCustomizer则可以使用 bean,如以下示例所示:

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

}
科特林
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())
    }

}

8.3.29。自动配置的 Spring Web 服务测试

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

您可以使用@WebServiceClientTestSpring Web Services 项目来测试调用 Web 服务的应用程序。默认情况下,它配置一个模拟WebServiceServerbean 并自动自定义您的WebServiceTemplateBuilder. (有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅“ io.html ”。)

@WebServiceClientTest在附录中找到 启用的自动配置设置列表。

以下示例显示了@WebServiceClientTest正在使用的注释:

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

}
科特林
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 服务服务器测试

您可以使用@WebServiceServerTestSpring Web Services 项目来测试实现 Web 服务的应用程序。默认情况下,它配置一个MockWebServiceClient可用于调用 Web 服务端点的 bean。(有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅“ io.html ”。)

@WebServiceServerTest在附录中找到 启用的自动配置设置列表。

以下示例显示了@WebServiceServerTest正在使用的注释:

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

}
科特林
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>")))
    }

}

8.3.30。额外的自动配置和切片

每个切片提供一个或多个@AutoConfigure…​注释,即定义应作为切片一部分包含的自动配置。@AutoConfigure…​通过创建自定义注释或添加到测试中,可以逐个测试添加其他自动配置@ImportAutoConfiguration,如以下示例所示:

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 {

}
科特林
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration::class)
class MyJdbcTests
确保不要使用常规@Import注解来导入自动配置,因为它们是由 Spring Boot 以特定方式处理的。

或者,可以为切片注释的任何使用添加额外的自动配置,方法是将它们注册到存储的文件中,META-INF/spring如以下示例所示:

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

在此示例中,在com.example.IntegrationAutoConfiguration使用 注释的每个测试上都启用了@JdbcTest

您可以通过#在此文件中使用注释。
只要@AutoConfigure…​使用@ImportAutoConfiguration.

8.3.31。用户配置和切片

如果您以合理的方式构建代码,则默认情况下@SpringBootApplication您的类将用作测试的配置。

因此,重要的是不要在应用程序的主类中乱扔特定于其功能特定区域的配置设置。

假设您使用的是 Spring Batch,并且您依赖于它的自动配置。您可以定义@SpringBootApplication如下:

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

@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {

    // ...

}
科特林
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
@EnableBatchProcessing
class MyApplication {

    // ...

}

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

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

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

    // ...

}
科特林
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.context.annotation.Configuration

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

    // ...

}
根据您的应用程序的复杂性,您可能有一个@Configuration用于自定义的类或每个域区域一个类。后一种方法允许您在一个测试中启用它,如有必要,使用@Import注释。有关何时可能希望为切片测试启用特定类的 更多详细信息,请参阅此操作指南部分。@Configuration

测试切片将@Configuration类别排除在扫描之外。例如,对于 a @WebMvcTest,以下配置不会WebMvcConfigurer在测试切片加载的应用程序上下文中包含给定的 bean:

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

}
科特林
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration(proxyBeanMethods = false)
class MyWebConfiguration {

    @Bean
    fun testConfigurer(): WebMvcConfigurer {
        return object : WebMvcConfigurer {
            // ...
        }
    }

}

但是,下面的配置将导致自定义WebMvcConfigurer被测试切片加载。

java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    // ...

}
科特林
import org.springframework.stereotype.Component
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Component
class MyWebMvcConfigurer : WebMvcConfigurer {

    // ...

}

另一个混淆来源是类路径扫描。假设,当您以合理的方式构建代码时,您需要扫描一个额外的包。您的应用程序可能类似于以下代码:

java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

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

    // ...

}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.ComponentScan

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

    // ...

}

这样做有效地覆盖了默认的组件扫描指令,其副作用是扫描这两个包,而不管您选择的切片是什么。例如,a@DataJpaTest似乎突然扫描应用程序的组件和用户配置。同样,将自定义指令移动到单独的类是解决此问题的好方法。

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

8.3.32。使用 Spock 测试 Spring Boot 应用程序

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

8.4. 测试工具

测试应用程序时通常有用的一些测试实用程序类被打包为spring-boot.

8.4.1. ConfigDataApplicationContextInitializer

ConfigDataApplicationContextInitializer是一个ApplicationContextInitializer你可以应用到你的测试来加载 Spring Bootapplication.properties文件的工具。当您不需要 提供的全套功能时,您可以使用它,@SpringBootTest如下例所示:

java
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;

@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {

    // ...

}
科特林
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer
import org.springframework.test.context.ContextConfiguration

@ContextConfiguration(classes = [Config::class], initializers = [ConfigDataApplicationContextInitializer::class])
class MyConfigFileTests {

    // ...

}
单独使用ConfigDataApplicationContextInitializer不支持@Value("${…​}")注入。它唯一的工作是确保将application.properties文件加载到 Spring 的Environment. 为了获得@Value支持,您需要另外配置 aPropertySourcesPlaceholderConfigurer或 use @SpringBootTest,它会为您自动配置一个。

8.4.2. 测试属性值

TestPropertyValues让您快速将属性添加到 aConfigurableEnvironmentConfigurableApplicationContext. 您可以使用key=value字符串调用它,如下所示:

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

}
科特林
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")
    }

}

8.4.3. 输出捕获

OutputCaptureExtension是一个可用于捕获System.outSystem.err输出的 JUnit 。要使用 add@ExtendWith(OutputCaptureExtension.class)和 injectionCapturedOutput作为测试类构造函数或测试方法的参数,如下所示:

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

}
科特林
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")
    }

}

8.4.4. 测试休息模板

TestRestTemplate是 Spring 的一种方便替代方案,RestTemplate在集成测试中很有用。您可以获得一个普通模板或发送基本 HTTP 身份验证(带有用户名和密码)的模板。在任何一种情况下,模板都是容错的。这意味着它以一种测试友好的方式运行,不会在 4xx 和 5xx 错误上抛出异常。相反,可以通过返回ResponseEntity的及其状态码检测此类错误。

Spring Framework 5.0 提供了一个新功能WebTestClient,适用于WebFlux 集成测试以及WebFlux 和 MVC 端到端测试。与TestRestTemplate.

建议使用 Apache HTTP 客户端(版本 4.3.2 或更高版本),但不是强制性的。如果您的类路径中有它,则TestRestTemplate通过适当地配置客户端来响应。如果您确实使用 Apache 的 HTTP 客户端,则会启用一些额外的测试友好功能:

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

  • Cookie 被忽略(因此模板是无状态的)。

TestRestTemplate可以在集成测试中直接实例化,如下例所示:

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

}
科特林
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.test.web.client.TestRestTemplate

class MyTests {

    private val template = TestRestTemplate()

    @Test
    fun testRequest() {
        val headers = template.getForEntity("https://myhost.example.com/example", String::class.java)
        assertThat(headers.headers.location).hasHost("other.example.com")
    }

}

或者,如果您将@SpringBootTest注释与WebEnvironment.RANDOM_PORTor一起使用WebEnvironment.DEFINED_PORT,您可以注入一个完全配置的TestRestTemplate并开始使用它。如有必要,可以通过RestTemplateBuilderbean 应用额外的定制。任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如下例所示:

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

    }

}
科特林
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))
        }

    }

}

9. 创建您自己的自动配置

如果您在开发共享库的公司工作,或者如果您在开源或商业库中工作,您可能希望开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,并且仍然可以被 Spring Boot 拾取。

自动配置可以与提供自动配置代码以及您将使用的典型库的“启动器”相关联。我们首先介绍了构建您自己的自动配置所需了解的内容,然后我们继续介绍创建自定义启动器所需的典型步骤

一个演示项目可用于展示如何逐步创建启动器。

9.1。了解自动配置的 Bean

在幕后,自动配置是通过@AutoConfiguration注释实现的。这个注释本身是用 元注释的@Configuration,使自动配置成为标准@Configuration类。附加@Conditional注释用于限制何时应用自动配置。通常,自动配置类使用@ConditionalOnClass@ConditionalOnMissingBean注释。这确保了自动配置仅在找到相关类并且您没有声明自己的类时适用@Configuration

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

9.2. 定位自动配置候选

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsSpring Boot 检查发布的 jar中是否存在文件。该文件应列出您的配置类,如以下示例所示:

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以通过#在此文件中使用注释。
自动配置只能 以这种方式加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应启用组件扫描以查找其他组件。@Import应该使用 特定的 s 来代替。

如果您的配置需要按特定顺序应用,您可以使用@AutoConfigureAfteror注释。@AutoConfigureBefore例如,如果您提供特定于 Web 的配置,您的类可能需要在WebMvcAutoConfiguration.

如果您使用@AutoConfiguration注解,则可以使用、 和before属性beforeName别名来代替专用注解。 afterafterName

如果您想订购某些彼此不应该有任何直接了解的自动配置,您也可以使用@AutoConfigureOrder. 该注释与常规注释具有相同的语义,@Order但为自动配置类提供了专用顺序。

与标准@Configuration类一样,应用自动配置类的顺序只影响定义它们的 bean 的顺序。随后创建这些 bean 的顺序不受影响,由每个 bean 的依赖关系和任何@DependsOn关系决定。

9.3. 条件注释

您几乎总是希望@Conditional在您的自动配置类中包含一个或多个注释。注解是一个常见的@ConditionalOnMissingBean例子,如果开发人员对你的默认设置不满意,它可以让他们覆盖自动配置。

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

9.3.1。班级条件

@ConditionalOnClass@ConditionalOnMissingClass注释允许根据@Configuration特定类的存在与否来包含类。由于注释元数据是使用ASM解析的,因此您可以使用该value属性来引用真实的类,即使该类实际上可能不会出现在正在运行的应用程序类路径中。name如果您更喜欢使用值指定类名,也可以使用该属性String

此机制不适用于@Bean通常返回类型是条件目标的方法:在方法上的条件适用之前,JVM 将加载类和可能处理的方法引用,如果类不加载,这些方法引用将失败当下。

为了处理这种情况,可以使用一个单独的@Configuration类来隔离条件,如下例所示:

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

    }

}
科特林
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
// Some conditions ...
class MyAutoConfiguration {

    // Auto-configured beans ...
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(SomeService::class)
    class SomeServiceConfiguration {

        @Bean
        @ConditionalOnMissingBean
        fun someService(): SomeService {
            return SomeService()
        }

    }

}
如果您使用@ConditionalOnClass@ConditionalOnMissingClass作为元注释的一部分来组成您自己的组合注释,则必须name在不处理这种情况下使用 as 引用类。

9.3.2. 豆条件

@ConditionalOnBean和注释允许根据@ConditionalOnMissingBean特定 bean 的存在或不存在来包含 bean。您可以使用该value属性按类型name指定bean 或按名称指定bean。该search属性允许您限制ApplicationContext在搜索 bean 时应考虑的层次结构。

放置在@Bean方法上时,目标类型默认为方法的返回类型,如下例所示:

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

}
科特林
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    fun someService(): SomeService {
        return SomeService()
    }

}

在前面的示例中,someService如果.SomeServiceApplicationContext

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

9.3.3. 物业条件

@ConditionalOnProperty注释允许基于 Spring Environment 属性包含配置。使用prefixname属性指定应检查的属性。默认情况下,false匹配任何存在但不等于的属性。您还可以使用havingValuematchIfMissing属性创建更高级的检查。

9.3.4。资源条件

@ConditionalOnResource注释允许仅在存在特定资源时才包含配置。可以使用通常的 Spring 约定来指定资源,如下例所示file:/home/user/test.dat

9.3.5。网络申请条件

@ConditionalOnWebApplication和注释允许根据@ConditionalOnNotWebApplication应用程序是否为“Web 应用程序”来包含配置。基于 servlet 的 Web 应用程序是任何使用 Spring WebApplicationContext、定义session范围或具有ConfigurableWebEnvironment. 反应式 Web 应用程序是任何使用ReactiveWebApplicationContext或具有ConfigurableReactiveWebEnvironment.

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

9.3.6。SpEL 表达条件

@ConditionalOnExpression注释允许基于SpEL 表达式的结果包含配置。

在表达式中引用 bean 将导致该 bean 在上下文刷新处理中很早就被初始化。结果,bean 将不适合进行后处理(例如配置属性绑定),并且其状态可能不完整。

9.4。测试您的自动配置

自动配置可能会受到许多因素的影响:用户配置(@Bean定义和Environment自定义)、条件评估(特定库的存在)等。具体来说,每个测试都应该创建一个定义良好ApplicationContext的,代表这些定制的组合。 ApplicationContextRunner提供了实现这一目标的好方法。

ApplicationContextRunner通常被定义为测试类的一个字段,用于收集基本的、通用的配置。以下示例确保MyServiceAutoConfiguration始终调用它:

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

每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置 ( UserConfiguration) 并检查自动配置是否正确退出。Invokingrun提供了一个回调上下文,可以与AssertJ.

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

}
科特林
@Test
fun defaultServiceBacksOff() {
    contextRunner.withUserConfiguration(UserConfiguration::class.java)
        .run { context: AssertableApplicationContext ->
            assertThat(context).hasSingleBean(MyService::class.java)
            assertThat(context).getBean("myCustomService")
                .isSameAs(context.getBean(MyService::class.java))
        }
}

@Configuration(proxyBeanMethods = false)
internal class UserConfiguration {

    @Bean
    fun myCustomService(): MyService {
        return MyService("mine")
    }

}

也可以轻松自定义Environment,如以下示例所示:

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");
    });
}
科特林
@Test
fun serviceNameCanBeConfigured() {
    contextRunner.withPropertyValues("user.name=test123").run { context: AssertableApplicationContext ->
        assertThat(context).hasSingleBean(MyService::class.java)
        assertThat(context.getBean(MyService::class.java).name).isEqualTo("test123")
    }
}

跑步者也可用于显示ConditionEvaluationReport. 报告可以在INFODEBUG水平打印。以下示例显示如何使用ConditionEvaluationReportLoggingListener打印自动配置测试中的报告。

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

}
科特林
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? -> }
    }

}

9.4.1。模拟 Web 上下文

如果您需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请分别使用WebApplicationContextRunnerReactiveWebApplicationContextRunner

9.4.2. 覆盖类路径

还可以测试在运行时不存在特定类和/或包时会发生什么。Spring Boot 附带一个FilteredClassLoaderrunner 可以轻松使用的。在以下示例中,我们断言如果MyService不存在,则自动配置被正确禁用:

java
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
    this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
            .run((context) -> assertThat(context).doesNotHaveBean("myService"));
}
科特林
@Test
fun serviceIsIgnoredIfLibraryIsNotPresent() {
    contextRunner.withClassLoader(FilteredClassLoader(MyService::class.java))
        .run { context: AssertableApplicationContext? ->
            assertThat(context).doesNotHaveBean("myService")
        }
}

9.5。创建自己的启动器

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

具体来说,自定义启动器可以包含以下内容:

  • autoconfigure包含“acme”的自动配置代码的模块。

  • 提供对模块starter的依赖关系的autoconfigure模块以及“acme”和通常有用的任何其他依赖关系。简而言之,添加启动器应该提供开始使用该库所需的一切。

两个模块中的这种分离绝不是必要的。如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的事实。此外,您还可以制作一个启动器来提供有关这些可选依赖项的意见。同时,其他人只能依靠autoconfigure模块,制作自己的不同意见的starter。

如果自动配置相对简单并且没有可选功能,那么在启动器中合并两个模块绝对是一种选择。

9.5.1。命名

您应该确保为您的启动器提供适当的命名空间。不要以 . 开头的模块名称spring-boot,即使您使用不同的 Maven groupId。我们将来可能会为您自动配置的内容提供官方支持。

根据经验,您应该在启动器之后命名组合模块。例如,假设您正在为“acme”创建一个启动器,并且您命名自动配置模块acme-spring-boot和启动器acme-spring-boot-starter。如果您只有一个模块将两者结合起来,请将其命名为acme-spring-boot-starter.

9.5.2. 配置键

如果您的启动器提供配置键,请为它们使用唯一的命名空间。特别是,不要将您的键包含在 Spring Boot 使用的命名空间中(例如servermanagementspring等)。如果您使用相同的命名空间,我们将来可能会以破坏您的模块的方式修改这些命名空间。根据经验,在所有键前面加上您拥有的命名空间(例如acme)。

确保通过为每个属性添加字段 javadoc 来记录配置键,如以下示例所示:

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

}
科特林
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 的纯文本@ConfigurationProperties,因为它们在添加到 JSON 之前不会被处理。

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

  • 不要以“The”或“A”开始描述。

  • 对于boolean类型,以“是否”或“启用”开始描述。

  • 对于基于集合的类型,以“逗号分隔列表”开始描述

  • 如果默认单位与毫秒不同,则使用java.time.Duration而不是long描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。

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

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

9.5.3。“自动配置”模块

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

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

Spring Boot 使用注解处理器来收集元数据文件 ( META-INF/spring-autoconfigure-metadata.properties) 中的自动配置条件。如果该文件存在,它将用于急切地过滤不匹配的自动配置,这将缩短启动时间。建议在包含自动配置的模块中添加以下依赖项:

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

如果您在应用程序中直接定义了自动配置,请确保配置spring-boot-maven-plugin以防止repackage目标将依赖项添加到 fat jar 中:

<project>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-autoconfigure-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

对于 Gradle 4.5 及更早版本,应在compileOnly配置中声明依赖项,如以下示例所示:

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

对于 Gradle 4.6 及更高版本,应在annotationProcessor配置中声明依赖项,如下例所示:

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

9.5.4。入门模块

启动器实际上是一个空罐子。它的唯一目的是提供必要的依赖项以使用该库。您可以将其视为对入门所需内容的固执己见。

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

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

10. Kotlin 支持

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

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

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

10.1。要求

Spring Boot 至少需要 Kotlin 1.3.x,并通过依赖管理来管理合适的 Kotlin 版本。要使用 Kotlin,org.jetbrains.kotlin:kotlin-stdlib并且org.jetbrains.kotlin:kotlin-reflect必须存在于类路径中。也可以使用变kotlin-stdlibkotlin-stdlib-jdk7和。kotlin-stdlib-jdk8

由于Kotlin 类默认是 final 的,您可能需要配置kotlin-spring插件以自动打开带有 Spring 注释的类,以便它们可以被代理。

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

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

10.2. 零安全

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

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

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

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

10.3. Kotlin API

10.3.1. 运行应用程序

Spring Boot 提供了一种惯用的方式来运行应用程序,runApplication<MyApplication>(*args)如以下示例所示:

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). 它还允许自定义应用程序,如以下示例所示:

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

10.3.2. 扩展

Kotlin扩展提供了使用附加功能扩展现有类的能力。Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利。

TestRestTemplate提供了类似于 Spring Framework 为RestOperationsSpring Framework 提供的扩展。除其他外,这些扩展使利用 Kotlin 实体化类型参数成为可能。

10.4. 依赖管理

为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。

使用 Maven,可以通过设置kotlin.version属性来自定义 Kotlin 版本,并为kotlin-maven-plugin. 使用 Gradle,Spring Boot 插件会自动与kotlin.versionKotlin 插件的版本对齐。

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

org.jetbrains.kotlinx:kotlinx-coroutines-reactor如果一个 Kotlin 项目在start.spring.io上至少有一个反应性依赖项,则默认情况下会提供依赖项。

10.5。@ConfigurationProperties

@ConfigurationProperties当与@ConstructorBinding具有不可变属性的支持类结合使用时val,如下例所示:

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

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

10.6。测试

虽然可以使用 JUnit 4 来测试 Kotlin 代码,但 JUnit 5 默认提供并推荐使用。JUnit 5 允许一个测试类被实例化一次,并被重用于该类的所有测试。这使得在非静态方法上使用@BeforeAll@AfterAll注释成为可能,这非常适合 Kotlin。

要模拟 Kotlin 类,建议使用MockK。如果您需要Mockk等效的 Mockito 特定@MockBean@SpyBean注释,您可以使用提供类似和注释的SpringMockK@MockkBean@SpykBean

10.7。资源

10.7.2. 例子

11. What to Read Next

如果您想了解有关本节中讨论的任何类的更多信息,请参阅Spring Boot API 文档,或者您可以直接浏览源代码。如果您有具体问题,请参阅操作方法部分。

如果您对 Spring Boot 的核心功能感到满意,您可以继续阅读有关生产就绪功能的信息。


1. see XML Configuration