Springboot Feature翻译

shell本节将深入介绍 Spring Boot 的详情,在这里你能了解到你可以会使用或者自定义的关键特性。如果你还没有准备好,你也许需要去阅读 “getting-started.html”“using-spring-boot.html” 章节来打下好的基础。

1. SpringApplication

SpringApplication 类提供了一种便捷的方法来引导从 main() 方法启动的 Spring 应用。在很多情况下,你能够委托静态的 SpringApplication.run 方法,就像下面的例子一样:

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

当你的应用启动时,你应该会看到类似下面的输出:

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

2019-04-31 13:09:54.117  INFO 56603 --- [           main] o.s.b.s.app.SampleApplication            : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb)
2019-04-31 13:09:54.166  INFO 56603 --- [           main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy
2019-04-01 13:09:56.912  INFO 41370 --- [           main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080
2019-04-01 13:09:57.501  INFO 41370 --- [           main] o.s.b.s.app.SampleApplication            : Started SampleApplication in 2.992 seconds (JVM running for 3.658)

默认情况下,将会打印 INFO 级别的日志消息,包括一些有关启动的详细信息,比如开启应用的用户,你也可以修改日志级别,具体的可以查看 [Log Levels](./Log Levels.md)。应用版本是由具体实现的主程序的类包的版本所决定的。你可以将 spring.main.log.startup-info 设为 false 来关闭日志信息,此操作也会关闭应用中确定模式下的日志消息。

你可以重写 SpringApplication 子类的 logStartupInfo(boolean) 来添加额外的启动日志。

1.1 Startup Failure

如果你的应用启动失败,注册的 FailureAnalyzers 将会提供对应的错误信息和修复这个问题的具体操作。比如,如果你在 8080 端口启动一个 web 应用,但这个端口已经被使用了,你将会看到类似下面的信息:

***************************
APPLICATION FAILED TO START
***************************

Description:

Embedded servlet container failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Spring Boot 提供了很多 FailureAnlyzer 实现,并且你可以实现你自己的

如果没有能够解决问题的错误分析,你也可以显示报告的所有信息来更好地理解该错误。为了达到这一小锅,你需要为 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener 启动 debug 属性或启动 debug 的日志级别。

比如,如果你是用 java -jar 来运行你的应用,你能像下面这样开启 debug 属性:

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

1.2 Lazy Initialization

SpringApplication 允许应用的初始化进行懒加载,当懒加载开启后,bean 会在它们被需要的时候创建而不是应用一开启就创建。开启懒加载能减少应用的启动时间,在 web 应用中,开启懒加载会导致很多与 web 相关的 bean 不会被初始化,直到接收了 HTTP 请求。

懒加载的一个缺点是它会导致应用中问题的发现杯延迟,如果一个没有被配置的 bean 是懒加载的,这个错误可能在启动的时候不会显示,直到该 bean 被加载才会暴露出来。还必须确保 JVM 有足够的内存使用应用所有的 bean,而不仅仅是启动时的 bean。基于以上原因,懒加载初始化默认情况下没有开启,并且推荐在开启懒加载之前对 JVM 堆大小进行调整。

可以使用编程的方法在 SpringApplicationBuilderlazyInitation 方法或 SpringApplicationsetLazyInitation 方法开启懒加载,此外,也可以如下使用 spring.main.lazy-initation 属性开启:

spring.main.lazy-initialization=true

如果你禁用应用中一些 bean 的懒加载,而对剩下的开启懒加载,可以使用 @Lazy(false) 注解显式地将它们的懒加载设置为 false。

1.3 Customizing the Banner

banner 是在启动时输出的样式,可以通过在类路径下创建 banner.txt 文件或者设置 spring.banner.location 属性的文件路径来修改它,如果文件不是 UTF-8 编码的,你可以设置 spring.banner.charset。除了文本文件,你也可以在类路径下添加 banner.gif、banner.jpg 或 banner.png 图片文件或者设置 spring.banner.image.location 属性。图片会被转换为 ASCII 形式并打印为文本 banner。

banner.txt 中,你能使用下面的占位符:

变量描述
${application.version}你应用的版本号,就像在 MANIFEST.MF 中声明的。例如 Implementation-Version: 1.0 会输出 1.0
${application.formatted-version}你应用的版本号,就像在 MANIFEST.MF 中声明的并格式化表示(用圆括号括起来,并加上前缀 v)。例如 (v1.0)
${spring-boot.version}你使用的 Spring Boot 版本号,比如 2.2.7.RELEASE
${spring-boot.formatted-version}你使用的 Spring Boot 版本号,格式化输出(用圆括号括起来,并加上前缀 v)。比如 (v2.2.7.RELEASE)
${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME})NAME 是 ANSI 转义码的满足,具体查看 AnsiPropertySource
${application.title}你应用中的主题,想 MANIFEST.MF 中声明的一样。比如 Implementation-Title: MyApp 输出 MyApp

如果你想编程式创建一个 banner,可以使用 SpringApplication.setBanner(...) 方法。使用 org.springframework.boot.Banner 接口并实现你自己的 printBanner() 方法。

你可以使用 spring.main.banner-mode 属性来确认是否将 banner 打印在 System.outconsole)、发送到指定的日志(log)、或者不输出(off)。

banner 被注册为一个名字为 springBanner 的单例 bean。

1.4 Customizing SpringApplication

如果 SpringApplication 默认的构造不符合你的要求,你可以创建一个本地实例并自定义它。比如,你可以通过这样来关闭 banner:

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

传给 SpringApplication 的参数是 Spring bean 的配置源。在大多数新情况下,它们都是 @Configuration 类的引用,不过它们也可以是对 XML 配置的引用或者是被扫描的包的引用。

可以使用 application.properties 文件对 SpringApplication 进行配置,详情可查看 Externalized Configuration

查看 SpringApplication Java 文档 获取配置的选项。

1.5 Fluent Builder API

如果你想构建一个层级 ApplicationContext(父/子 关系的多个上下文)或者你想使用链式 API 进行构建,你可以通过 SpringApplicationBuilder 实现。

SpringApplicationBuilder 可以将多个方法链式调用,并且包括 parentchild 方法来创建层次结构,正如下面例子所示:

new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

创建层次 ApplicationContext 存在一些限制,比如 web 组件必须被包含在子上下文中,并且父子上下文的 Environment 必须一样,具体的可以查看 SpringApplicationbuilder Java 文档 获取信息。

1.6 Application Events and Listeners

除了常见的 Spring Framework 事件,比如 ContextRefreshedEventSpringApplication 会发送一些附加的应用事件。

有些事件是在 ApplicationContext 被创建前触发的,所以你不能将监听器使用 @Bean 进行注册,你可以使用 SpringApplication.addLlisterners(...) 方法或 SpringApplicationBuilder.listerners(...) 方法进行注册。

如果你想要监听器自动注册在应用上,无论应用是否被创建。你可以在你的项目下创建一个 META-INF/spring.factories 文件并使用 org.springframework.context.ApplicationListener 键引用你的监听器,如下面所示:

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

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

  1. ApplicationStartingEvent 在运行开始时,任何处理之前派送,除了注册监听器和初始化器之外。
  2. ApplicationEnvironmentPreparedEvent 在已知 Envirment 之后,未创建上下文之前派送。
  3. ApplicationContextInitializedEventApplicationContext 准备好,ApplicationContextInitializers 被调用,但是所有 bean 定义没有被加载的时候派送。
  4. ApplicationPreparedEvent 在 refresh 之前,bean 定义被加载之后派送。
  5. ApplicationStartedEvent 在上下文被刷新之后,调用应用程序和命令行运行之前派送。
  6. ApplicationReadyEvent 在调用任意应用程序和命令行运行程序之后派送,它表示应用程序已准备好为请求提供服务。
  7. ApplicationFailedEvent 在程序出现异常时派送。

上面的列表只包括绑定到 SpringApplicationSpringApplicationEvents。除此之外,还将在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前发布以下事件:

  1. ContextRefreshedEventApplicationContext 被刷新的时候派送。
  2. WebServerInitializedEventWebServer 准备好后派送。ServletWebServerInitializedEventReactiveWebServerInitializedEvent 分别是 servletreactive 变体。

你通常不需要使用应用事件,但是了解它们对于程序处理很有帮助,在内部,Spring Boot 通过事件来处理各种任务。

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

为了让监听器区分其上下文事件和子上下文事件,它应该注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。上下文可以通过实现 ApplicationContextAware 注入,如果监听器是 bean,则可以使用 @Autowired 注入。

1.7 Web Environment

SpringApplication 根据你的代码尝试创建正确类型的 ApplicationContext,用于确定 WebApplicationType 的算法很简单:

  • 如果 Spring MVC 存在,就使用 AnnotationConfigServletWebServerApplicationContext
  • 如果 Spring MVC 不存在,Spring WebFlux 存在,就使用 AnnotationConfigReactiveWebServerApplicationContext
  • 否则使用 AnnotationConfigApplicationContext

以上意味着如果你在同一个应用中使用了 Spring MVC 并从 Spring WebFlux 创建了 WebClient,默认使用 Spring MVC。你可以通过调用 setWebApplicationType(WebApplicationType) 来轻松覆盖它。

也可以通过调用 setApplicationContextClass(...) 来完全控制 ApplicationContext 类型。

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

1.8 Accessing Application Arguments

如果你想访问传入 SpringApplication.run(...) 的应用参数,你可以注入 org.springframework.boot.ApplicationArguments bean,ApplicationArguments 接口提供对访问原始字符串数组 String[] 参数以及解析的 optionno-option 参数,如下面所示:

import org.springframework.boot.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;

@Component
public class MyBean {

    @Autowired
    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
    }

}

Spring Boot 还向 Spring Envirment 注册了 CommandLinePropertySource。这还允许使用 @Value 注解注入单个应用程序参数。

1.9 Using the ApplicationRunner or CommandLineRunner

如果你需要在 SpringApplication 启动的时候运行一些代码,你可以实现 ApplicationRunnnerCommandLineRunner 接口,这两个接口都以相同的方式工作,并提供一个单独的 run 方法,在 SpringApplication.run(...) 完成前调用。

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

import org.springframework.boot.*;
import org.springframework.stereotype.*;

@Component
public class MyBean implements CommandLineRunner {

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

}

如果定义了一些必须按特定顺序调用的 CommandLineRunnerApplicationRunner bean,则可以另外实现 org.springframework.core.Ordered 接口或使用 org.springframework.core.annotation.order 注解。

1.10 Application Exit

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

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

@SpringBootApplication
public class ExitCodeApplication {

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

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

}

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

1.11 Admin Features

通过指定 spring.application.admin.enable 属性的值可以开启应用汇总有关管理的功能,这将在平台 MBeanServer 上展示 SpringApplicationAdminMXBean。你可以使用这些管理特性来远程管理你的应用,这个特性对于任何服务包装器实现都很有用。

如果你想知道应用运行的端口,可以获取 local.server.port 的值来查看。

2. Externalized Configuration

Spring Boot 允许在外部进行配置,以便在不同的环境中使用相同的应用程序代码。你可以使用 properties 文件、YAML 文件、环境变量和命令行参数来进行外部配置。属性值可以通过使用 @Value 直接注入到bean中,可以通过 Spring Environment 抽象,也可以通过@ConfigurationProperties 绑定到结构化对象

Spring Boot 使用一个非常特殊的 PropertySource 顺序,它被设计为允许对值进行合理的重写。属性按以下顺序设置:

  1. home目录下的devtools全局设置属性(~/.spring-boot-devtools.properties,如果devtools激活)。
  2. 测试中的 @TestPropertySource 注解。
  3. 测试中的属性,在 @SpringBootTest测试注解中提供,用于测试应用程序的特定部分
  4. 命令行参数。
  5. 来自 SPRING_APPLICATION_JSON(内嵌在环境变量或系统属性中的 JSON)的属性。
  6. ServletConfig 初始化参数。
  7. ServletContext 初始化参数。
  8. 来自 java:comp/env 的 JNDI 属性。
  9. Java 系统属性(System.getProperties())。
  10. 操作系统环境变量。
  11. RandomValuePropertySource,其属性仅为 random.*
  12. jar 包之外特定于概要文件(Profile-specific application properties)的应用程序属性(application-{Profile}.propertiesYAML 变量)。
  13. jar 包中的特定于概要文件(Profile-specific application properties)的应用程序属性(application-{Profile}.propertiesYAML 变量)。
  14. jar 包之外的应用程序属性(application.propertiesYAML 变体)。
  15. jar 包中的应用程序属性(application.propertiesYAML 变体)。
  16. @Configuration 类上的 @PropertySource 注释。注意,在刷新应用程序上下文之前,这些属性源不会添加到环境中。此时配置某些属性(如 logging.*spring.main.* 等)太迟了,这些属性在刷新开始之前就会被读取。
  17. 默认属性(通过设置 SpringApplication.setDefaultProperties 指定)。

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

import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;

@Component
public class MyBean {

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

    // ...

}

在应用的类路径(例如,在 jar )上,可以有一个 application.properties 文件,该文件为每个 name 提供合理的默认属性值。当运行在一个新环境中,可以在 jar 外部提供 application.properties 文件覆盖原文件。对于一次性的测试,可以使用特定的命令行开关进行启动启动(例如,java-jar app.jar--name="Spring")。

SPRING)APPLICATION_JSON 属性的值能在带有环境参数的命令行上提供,比如,你可以在 UN*X shell 上使用以下:

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

在前面的例子中,你最后在 Spring Environment 中使用 acme.name=test。还可以将在系统属性中将 JSON 作为 spring.application.json 提供,如下例所示:

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

还可以使用命令行参数提供 JSON,如下例所示:

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

还可以将 JSON 作为 JNDI 变量提供,如下所示:java:comp/env/spring.application.json

2.1 Configuring Random Values

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

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

random.int* 的语法是 OPEN value (,max) CLOSEOPEN, CLOSE 是任意字符, value, max 是数字。如果提供了 max,那么 value 是最小值,max 是最大值(独占)。

2.2 Accessing Command Line Properties

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

如果你不想让命令行属性添加到 Environment,你可以使用 SpringApplication.setAddCommandLineProperties(false) 关闭。

2.2 Application Property Files

SpringApplicationapplication.properties 文件加载属性。属性文件位于以下位置,并将其添加到 Spring Environment中:

  1. 当前目录的 /config 子目录
  2. 当前目录
  3. 类路径的 /config
  4. 类路径的根目录下

该列表按优先级排序(在列表中较高位置定义的属性覆盖在较低位置定义的属性)。

你也可以使用 YAML(’.yml’) 文件 代替 ‘.properties’。

如果你不喜欢将 application.properties 作为配置文件名,可以通过指定 spring.config.name 环境属性切换为另一个文件名。还可以使用 spring.config.location 环境属性(目录位置或文件路径的逗号分隔列表)显示指定位置。以下示例演示如何指定其他文件名:

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

下面的例子展示了如何指定两个位置:

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

spring.config.namespring.config.location 很早就用于确定必须加载哪些文件。它们必须定义为环境属性(通常是 OS 环境变量、系统属性或命令行参数)。

如果 spring.config.location 包含目录(而不是文件),则它们应该以 /(并且,在运行时,在加载之前,应该附加从 spring.config.name 生成的名称,包括 profile-specific 文件的文件名)。在 spring.config.location 中指定的文件按原样使用,不支持 profile-specific 变量,并且由任何特定于 profile-specific 的属性重写。

配置的位置以相反的顺序搜索。默认情况下,配置的位置是 classpath:/classpath:/config/file:./file:./config/。搜索结果如下:

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

当使用 spring.config.location 配置自定义配置位置时,它们将替换默认位置。例如,如果使用值 classpath:/custom config/,file:./custom-config/ 配置 spring.config.location,则搜索顺序如下:

  1. file:./custom-config/
  2. classpath:custom-config/

此外,当使用 spring.config.additional-location 配置自定义配置位置时,除了默认位置之外,还将使用它们。在默认位置之前搜索其他位置。例如,如果配置了classpath:/custom config/、file:./custom-config/ 的其他位置,则搜索顺序如下:

  1. file:./custom-config/
  2. classpath:custom-config/
  3. file:./config/
  4. file:./
  5. classpath:/config/
  6. classpath:/

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

如果使用环境变量而不是系统属性,大多数操作系统不允许使用句点分隔的键名,但是可以使用下划线(例如,使用 SPRING_CONFIG_NAME 而不是 SPRING.CONFIG.NAME)。有关详细信息,参见 环境变量绑定

如果应用程序在容器中运行,那么可以使用 JNDI 属性(在 java:comp/env 中)或 servlet 上下文初始化参数来代替环境变量或系统属性,或者也可以使用环境变量或系统属性。

2.4 Profile-specific Properties

除了 application.properties 文件,profile-specific 属性也能通过命名惯例 application-{profile}.properties定义。Environment有个默认 profiles 集合(默认情况为 [default]),在没有设置激活的 profiles 时会被使用(例如,如果没有明确指定激活的 profiles,application-default.properties 中的属性会被加载)。

Profile-specific 属性加载路径和标准的 application.properties 相同,并且 profile-specific 文件总是会覆盖 non-specific 文件,不管 profile-specific 文件是否被打包到 jar 中。

如果定义多个 profiles,使用的是最后一个。例如,spring.profiles.active 定义的 profiles 被添加到通过 SpringApplication API 定义的 profiles 后面,因此优先级更高。

如果你已经在 spring.config.location 下定义所有文件(非目录),那些 profile-specific 的文件将不被考虑。如果想使用 profile-specific 属性,那就在spring.config.location 下使用目录。

2.5 Placeholders in Properties

application.properties 中的值在使用时通过现有环境进行查找,因此你可以引用以前定义的值(例如,从系统属性)。

app.name=MyApp
app.description=${app.name} is a Spring Boot application

你可以使用此技术创建现有 Spring Boot 属性的 “短” 变体。有关详细信息,参阅 how to.html

2.6 Encrypting Properties

Spring Boot不提供任何内置的对加密属性值的支持,但是,它提供了修改 Spring 环境中包含的值所必需的挂接点。EnvironmentPostProcessor 接口允许在应用程序启动之前操作环境。有关详细信息,参见 howto.html

如果你正在寻找一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目将为在 HashiCorp Vault 中存储外部化配置提供支持。

2.7 Using YAML Instead of Properties

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

如果你使用 “Starters”,spring-boot-starter 将自动提供 SnakeYAML 。

2.7.1 Loading YAML

Spring Framework 提供了两个转换类用于加载 YAML 文档,YamlPropertiesFactoryBean 将 YAML 加载为 PropertiesYamlMapFactoryBean 将 YAML 加载为 Map

比如,思考以下 YAML 文档:

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

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

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
The preceding example would be transformed 

前面示例转为以下属性:

my.servers[0]=dev.example.com
my.servers[1]=another.example.com

要通过使用 Spring Boot 的 Binder 实用程序(这就是 @ConfigurationProperties 所做的)绑定到类似的属性,需要在目标 bean 中具有 java.util.List(或 Set)类型的属性,并且需要提供 setter 或使用可变值初始化它。例如,以下示例绑定到前面显示的属性:

@ConfigurationProperties(prefix="my")
public class Config {

    private List<String> servers = new ArrayList<String>();

    public List<String> getServers() {
        return this.servers;
    }
}

2.7.2 Exposing YAML as Properties in the Spring Environment

YamlPropertySourceLoader 类可用于在 Spring Environment中将 YAML 暴露为 PropertySource。这样做可以使用 @Value注释和占位符语法来访问 YAML 属性。

2.7.3 Multi-profile YAML Documents

你可以通过 spring.profiles 键指定文档何时应用,可以在单个文件中指定多个特定于配置文件的 YAML 文档,如下例所示:

server:
    address: 192.168.1.100
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: production & eu-central
server:
    address: 192.168.1.120

在前面的示例中,如果 development 配置处于启动状态,则 server.address 属性为127.0.0.1。类似地,如果 productioneu-central 配置处于启动状态,则 server.address 属性为 192.168.1.120。如果未启用 developmentproductioneu-central 配置,则属性值为 192.168.1.100

因此,spring.profiles 可以包含一个简单的 profile 名(例如 production)或 profile 表达式。profile 表达式允许表达更复杂的 profile 逻辑,例如 production&(eu-central | eu west)。查看 参考指南 了解更多详细信息。

如果应用程序上下文启动时没有显式激活配置,则将激活默认配置。因此,在下面的 YAML 中,我们为 spring.security.user.password 设置了一个仅在 “default” 配置文件中可用的值:

server:
  port: 8000
---
spring:
  profiles: default
  security:
    user:
      password: weak

但是,在下面的示例中,密码将始终被设置好了,因为它没有附加到任何配置文件,并且必须在所有其他配置文件中根据需要显式重置密码:

server:
  port: 8000
spring:
  security:
    user:
      password: weak

使用 spring.profiles 元素指定的 Spring 配置文件可以通过使用 ! 字符设置否定配置文件。如果为单个文档同时指定了否定配置文件和非否定配置文件,则必须至少有一个非否定配置文件匹配,并且不能有否定配置文件匹配。

2.7.4 YAML Shortcomings

YAML文件不能使用 @PropertySource 注解加载。因此,如果需要以这种方式加载值,则需要使用属性文件。

在 profile-specific YAML文件中使用多个 YAML文档语法可能会导致意外行为。例如,考虑文件中的以下配置:

application-dev.yml

server:
  port: 8000
---
spring:
  profiles: "!test"
  security:
    user:
      password: "secret"

如果你使用参数 spring.profiles.active=dev 运行应用程序,你可能希望 security.user.password 设置为 “secret”,但事实并非如此。

因为主文件名为 application-dev.yml。它已经被认为是 profile-specific,因此嵌套文档将被忽略。

建议不要混合使用 profile-specific YAML文件和多个 YAML 文档。坚持只使用其中一个。

2.8 Type-safe Configuration Properties

使用 @Value()${property}) 注解注入配置属性有时会很麻烦,特别是在处理多个属性或数据本质上是分层的情况下。Spring Boot 提供了另一种处理属性的方法,这种方法允许强类型 bean 控制和验证应用程序的配置。

参见 @Value和类型安全配置属性之间的区别

2.8.1 JavaBean properties binding

可以绑定一个标准 JavaBean 属性的 bean,如下面的例子所示:

package com.example;

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

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

@ConfigurationProperties("acme")
public class AcmeProperties {

    private boolean enabled;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    public boolean isEnabled() { ... }

    public void setEnabled(boolean enabled) { ... }

    public InetAddress getRemoteAddress() { ... }

    public void setRemoteAddress(InetAddress remoteAddress) { ... }

    public Security getSecurity() { ... }

    public static class Security {

        private String username;

        private String password;

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

        public String getUsername() { ... }

        public void setUsername(String username) { ... }

        public String getPassword() { ... }

        public void setPassword(String password) { ... }

        public List<String> getRoles() { ... }

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

    }
}

前面的 POJO 定义了以下属性:

  • acme.enabled,默认值为 false。

  • acme.remote-address,其类型可以从字符串强制转换。

  • acme.security.username,具有嵌套的 “security” 对象,其名称由属性的名称确定。特别是,返回类型根本没有在那里使用,可能是 SecurityProperties

  • acme.security.password:密码。

  • acme.security.roles,默认为 USER 的字符串集合。

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

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

  • Maps,只要被初始化,就需要一个 getter,但不一定是 setter,因为绑定器可以对它们进行修改。

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

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

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

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

2.8.2 Constructor binding

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

package com.example;

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("acme")
public class AcmeProperties {

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

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

    public boolean isEnabled() { ... }

    public InetAddress getRemoteAddress() { ... }

    public Security getSecurity() { ... }

    public static class Security {

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

        public String getUsername() { ... }

        public String getPassword() { ... }

        public List<String> getRoles() { ... }

    }

}

在此设置中,@ConstructorBinding 注解用于指示应使用构造函数绑定。这意味着绑定器将期望找到一个具有希望绑定的参数的构造函数。

@ConstructorBinding 类的嵌套成员(如上例中的 Security )也将通过其构造函数绑定。

可以使用 @DefaultValue 指定默认值,并应用相同的转换服务将字符串值强制为缺少属性的目标类型。默认情况下,如果没有属性绑定到 Security ,那么 AcmeProperties 实例将包含一个空的 Security 实例 。如果希望返回一个非空的 Security 实例,即使没有属性绑定到它,也可以使用空的 @DefaultValue 注解来执行此操作:

package com.example;
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("acme")
public class AcmeProperties {

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

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

要使用构造函数绑定,必须使用 @EnableConfigurationProperties 或配置属性扫描启用类。你不能对由常规 Spring 机制创建的 bean 使用构造函数绑定(例如@Component bean、通过 @Bean 方法创建的 bean 或使用 @Import 加载的 Bean)。

如果类有多个构造函数,也可以直接在应该绑定的构造函数上使用@ConstructorBinding

2.8.3 Enabling @ConfigurationProperties-annotated types

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

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

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

要使用配置属性扫描,需要把 @configurationpropertiescan 注解添加到应用中。通常,它被添加到用 @Springbootsapplication 注解的主应用程序类中,但可以添加到任何 @Configuration 类中。默认情况下,扫描将从注解声明的包中开始。如果要定义扫描特定的包,可以按以下示例所示执行操作:

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

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

上面例子中的 bean 是 acme-com.example.AcmeProperties

建议 @ConfigurationProperties 只处理环境,特别是不要从上下文中注入其他 bean。对于角点情况(corner cases),可以使用 setter 注入或框架提供的任意 *Aware(例如,如果需要访问 Environment,则使用 EnvironmentAware)。如果仍要使用构造函数注入其他 bean,则必须使用 @Component 注解配置属性 bean,并使用基于 JavaBean 的属性绑定。

2.8.4 Using @ConfigurationProperties-annotated types

这种类型的配置在 SpringApplication 外部 YAML 配置中特别适用,如下例所示:

 application.yml

acme:
    remote-address: 192.168.1.1
    security:
        username: admin
        roles:
          - USER
          - ADMIN

# additional configuration as required

要使用 @ConfigurationProperties bean,可以以用与任何其他 bean 相同的方式注入它们,如下例所示:

@Service
public class MyService {

    private final AcmeProperties properties;

    @Autowired
    public MyService(AcmeProperties properties) {
        this.properties = properties;
    }

    //...

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

}

2.8.5 Third-party Configuration

除了使用 @ConfigurationProperties 注解类之外,还可以在公开的 @Bean 方法上使用它。如果要将属性绑定到不在你控制范围内的第三方组件,那么这样做特别有用。

要从环境属性配置 bean,将 @ConfigurationProperties添加到其 bean 注册中,如下例所示:

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
    ...
}

another 前缀定义的任何 JavaBean 属性都映射到 AnotherComponent bean上 ,映射方式类似于前面的 AcmeProperties 示例。

2.8.6 Relaxed Binding

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

例如,思考以下 @ConfigurationProperties 类:

@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {

    private String firstName;

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

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

}

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

属性注意
acme.my-project.person.first-nameKebab 案例,建议在 .properties.yml 文件中使用。
acme.myProject.person.firstName标准的大小写语法。
acme.my_project.person.first_name下划线表示法,这是 .properties.yml 文件中使用的另一种格式。
ACME_MYPROJECT_PERSON_FIRSTNAME大写格式,在使用系统环境变量时推荐使用。

注解的 orefix 值必须采用 kebab 表示(小写并以-分隔,如 acme.my-project.person)。

属性源的绑定规则

属性源SimpleList
Properties File驼峰表示、kebab 表示、下划线表示使用 [] 或逗号分隔值的标准列表语法
YAML File驼峰表示、kebab 表示、下划线表示标准的YAML列表语法或逗号分隔的值
Environment Variables用下划线作为分隔符的大写格式(参见环境变量绑定由下划线包围的数值(参见环境变量绑定
System properties驼峰表示、kebab 表示、下划线表示使用 [] 或逗号分隔值的标准列表语法

建议在可能的情况下,以小写的 kebab 格式存储属性,例如 my.property-name=acme

Binding Maps

绑定到 map 属性时,如果键包含除小写字母数字字符或 - 以外的任何字符,则需要使用括号表示法,以便保留原始值。如果键没有被 [] 包围,则将删除不是字母数字或 - 的任何字符。例如,考虑将以下属性绑定到 map

acme:
  map:
    "[/key1]": value1
    "[/key2]": value2
    /key3: value3

上面的属性将绑定到以 /key1/key2key3 作为 map 中的键的映射。

对于 YAML 文件,方括号需要用引号括起来,以便正确地解析键。

Binding from Environment Variables

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

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

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

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

  • 去掉所有扩折号(-)。

  • 转换为大写。

例如,配置属性 spring.main.log-startup-info 将是一个名为 SPRING_MAIN_LOGSTARTUPINFO 的环境变量。

下划线不能用于替换属性名称中的扩折号。如果尝试在上面的示例中使用 SPRING_MAIN_LOG_STARTUP_INFO,则不会绑定任何值。

当绑定到对象列表时,也可以使用环境变量。要绑定到列表,应该在变量名中用下划线包围元素编号。

例如,配置属性 my.acme[0].other 将使用一个名为 MY_ACME_0_OTHER 的环境变量。

2.8.7 Merging Complex Types

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

例如,假设 MyPojo 对象的名称和描述属性在默认情况下为 null。下面的例子展示了来自 AcmeProperties 的一个 MyPojo 对象列表:

@ConfigurationProperties("acme")
public class AcmeProperties {

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

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

}

思考以下配置:

acme:
  list:
    - name: my name
      description: my description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

如果 dev 配置未处于启用状态,则 AcmeProperties.list 包含一个 MyPojo 条目,像前面定义一样。但是,如果启用了 dev 配置,则列表仍然只包含一个条目(名称为 my another name,并且 description 为 null)。此配置不会向列表中添加第二个 MyPojo 实例,也不会合并项。

在多个配置文件中指定列表时,将使用优先级最高的配置文件(且仅使用该配置文件)。思考以下示例:

acme:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

在前面的示例中,如果 dev 配置处于启用状态,那么 AcmeProperties.list 包含一个 MyPojo 条目(名称为 my another name,并且 description 为 null)。对于 YAML,逗号分隔列表和YAML 列表都可以用于完全重写列表的内容。

对于 map 属性,可以使用从多个源的属性值进行绑定。但是,对于多个源中的同一属性,将使用优先级最高的属性。以下示例来自 AcmePropertiesMap<String,MyPojo>

@ConfigurationProperties("acme")
public class AcmeProperties {

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

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

}

思考以下配置:

acme:
  map:
    key1:
      name: my name 1
      description: my description 1
---
spring:
  profiles: dev
acme:
  map:
    key1:
      name: dev name 1
    key2:
      name: dev name 2
      description: dev description 2

如果 dev 配置未处于活动状态,则 AcmeProperties.map 包含一个键为 key1 的条目(名称为 my name 1,description 为 my description 1)。但是,如果启用了dev 配置,那么 map 包含两个条目,键 key1(名称为 dev name 1,description 为 my description 1)和 key2(名称为 dev name 2,description 为 dev description 2)。

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

2.8.8 Properties Conversion

当 Spring Boot 绑定到 @ConfigurationProperties bean时,它尝试将外部应用程序属性强制转换为正确的类型。如果需要自定义类型转换,可以提供 ConversionService bean(使用名为 ConversionService 的 bean)或自定义属性编辑器(通过 CustomEditorConfigurer bean)或自定义转换器(使用注解为 @ConfigurationPropertiesBinding 的 bean 定义)。

由于该 bean 在应用程序生命周期的早期被使用,因此要确保限制 ConversionService 正在使用的依赖项。通常,你需要的依赖项在创建时可能无法完全初始化。如果配置键的强制转换不需要重命名自定义转换服务,并且只依赖于使用 @ConfigurationPropertiesBinding 限定的自定义转换,那么你可能希望重命名自定义 ConversionService

Converting durations

SpringBoot对表示时间有专门的支持。如果你公开了 java.time.Duration 属性,则可用应用程序属性中的以下格式:

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

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

  • 一种更可读的格式,其中值和单位是耦合的(例如,10s 表示10 秒)

思考以下示例:

@ConfigurationProperties("app.system")
public class AppSystemProperties {

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

    private Duration readTimeout = Duration.ofMillis(1000);

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

}

要指定 30 秒的会话超时,30PT30S30s 都是等效的。读取超时 500ms可以用以下任何形式指定:500PT0.5S500ms

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

  • ns 纳秒

  • us 微秒

  • ms 毫秒

  • s

  • m 分钟

  • h 小时

  • d

默认单位是毫秒,可以使用 @DurationUnit重写,如上面的示例所示。

如果你要从简单使用 Long 来表示持续时间的以前版本升级,如果它不是毫秒,确保在切换到 Duration 时间的同时定义单元(使用@DurationUnit)。这样做提供了一个透明的升级路径,同时支持更丰富的格式。

Converting Data Sizes

Spring 框架有一个 DataSize 的值类型,它以字节表示大小。如果你暴露了 DataSize 属性,则可用应用属性中的以下格式:

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

  • 一种更可读的格式,其中值和单位是耦合的(例如,10MB 表示 10 兆字节)

思考以下示例:

@ConfigurationProperties("app.io")
public class AppIoProperties {

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

    private DataSize sizeThreshold = DataSize.ofBytes(512);

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

}

要指定 10 兆字节的缓冲区大小,1010MB 是等效的。256 字节的大小阈值可以指定为 256256B

也可以使用任意受支持的单元:

  • B 表示字节

  • KB 表示千字节

  • MB 表示兆字节

  • GB 表示千兆字节

  • TB(兆字节)

默认单位是字节,可以使用 @DataSizeUnit 重写,如上面的示例所示。

如果你要从以前的版本升级,而以前的版本只是使用 Long 来表示大小,确保定义单元(使用 @DataSizeUnit),如果它不是字节,则切换到 DataSize。这样做提供了一个透明的升级路径,同时支持更丰富的格式。

### 2.8.9 @ConfigurationProperties Validation

每当使用 Spring 的 @Validated 注解对 @ConfigurationProperties 类进行注解时,Spring Boot 就会尝试验证它们。你可以直接在配置类上使用 JSR-303 javax.validation 约束注释。为此,应确保类路径上有一个兼容的J SR-303 实现,然后将约束注释添加到字段中,如下例所示:

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

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

为了确保对嵌套属性也能触发验证,即使找不到属性,也必须用 @Valid 注解关联的字段。以下示例基于前面 的 AcmeProperties示例:

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private InetAddress remoteAddress;

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

    // ... getters and setters

    public static class Security {

        @NotEmpty
        public String username;

        // ... getters and setters

    }

}

还可以通过创建名为 configurationPropertiesValidator 的 bean 定义来添加自定义Spring Validator@Bean 方法应该声明为静态的。配置属性验证器是在应用程序生命周期的早期创建的,将 @Bean 方法声明为静态的话就可以创建Bean,而无需实例化@Configuration 类。这样做可以避免任何可能由早期实例化引起的问题。

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

2.8.10 @ConfigurationProperties vs. @Value

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

特性@ConfigurationProperties@Value
松散绑定YesLimited
元数据支持YesNo
SpEL 表达式NoYes

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

如果你确实想使用 @Value,建议使用规范形式引用属性名(kebab case——只使用小写字母)。这将允许 Spring Boot 使用与松散绑定 @ConfigurationProperties 时相同的逻辑。例如,@Value("{demo.item price}")将从 application.properties 文件中获取 demo.item-pricedemo.itemPrice 表单,并从系统环境中获取 demo_itemPrice 表单。如果改用 @Value("{demo.item price}"),则不考虑 demo.item-pricedemo_itemPrice

最后,虽然可以在 @Value 中编写 SpEL 表达式,但此类表达式不会从应用程序属性文件中处理。

3. Profiles

Spring Profile 提供了一个方法来将应用配置分离,并在不同的环境中使用它们。任意的 @Component@Configuration 或者 @ConfigurationProperties 都可以被标记为 @Profile 来限制它的加载,如下面的例子所示:

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

    // ...

}

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

你可以使用 spring.profiles.active 指定 Environment 属性哪些配置是生效的,也可以使用本章前面描述的任何方式指定属性。比如,你可以写在你的 properties.properties 文件,如下所示:

spring.profiles.active=dev,hsqldb

你也可以在命令行上指定:--spring.profiles.active=dev,hsqldb

3.1 Adding Active Profiles

spring.profile.active 属性遵循着其它属性的顺序规则:最高的 PropertrySource 获胜。这意味着你可以在 application.properties 中指定启动的配置,然后在命令行中修改它。

有时,将 rofile-specific 属性添加到启用配置比替换它们更很有用的。spring.profiles.include 属性可用于无条件添加启用配置。SpringApplication 入口还有一个用于设置其他配置的Java API(即在 spring.profiles.active 属性激活配置之上)。可参阅 SpringApplication 中的 setAdditionalProfiles() 方法。

例如,--spring.profiles.active=prod 运行具有以下属性的应用程序时,proddb 和p rodmq 配置也会被激活:

---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include:
  - proddb
  - prodmq

记住,可以在 YAML 文档中定义 spring.profiles 属性,以确定配置中何时包含此特定文档。有关详细信息,参见 howto.html

3.2 Programmatically Setting Profiles

在你的应用启动前,你可以调用 SpringApplication.setAdditionalProfiles(...) 方法来通过编程设置启动的配置,也可以通过使用 Spring 的 ConfigurableEnvironment 接口来启动配置。

3.3 Profile-specific Configuration Files

application.properties(或 application.yml)和通过 @ConfigurationProperties 引用的文件的 Profile-specific 变量都被视为文件并加载。有关详细信息,参阅 “特定于配置文件的属性"。

4. Logging

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 Log Format

Spring Boot 默认情况下的输入格式类似于下面:

2019-03-05 10:57:51.112  INFO 45469 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.52
2019-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1358 ms
2019-03-05 10:57:51.698  INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2019-03-05 10:57:51.702  INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]

输出项包括下面内容:

  • 日期和时间:毫秒精度,易于分类。
  • 日志级别:ERRORWARNINFOTRACE
  • 进程 ID。
  • 一个 --- 分隔符区分日志内容的开始
  • 线程名字:用方括号括起来(可能会被截断以用于控制台输出)。
  • 日志器名字:这通常是源类名(通常是缩写)。
  • 日志消息。

Logback 没有 FATAL 级别,它被映射Wie ERROR

4.2 Console Output

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

$ java -jar myapp.jar --debug

你也可以在你的 application.properties 中指定 debug=true

启用调试模式后,将配置一组核心记录器(嵌入式容器、HibernateSpring Boot)以输出更多信息。启用调试模式不会将应用程序配置为使用 DEBUG 级别记录所有消息。

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

4.2.1 Color-coded Output

如果你的终端支持 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 File Output

默认情况下,Spring Boot 日志输出到控制台,不写入日志文件。如果希望在控制台输出之外写入日志文件,则需要设置一个 log.file.namelog .file.path 属性(例如,在你的 application.properties)。

下表展示了能被一起使用的 logging.* 属性:

logging.file.namelogging.file.path例子描述
(none)(none)只输出到控制台
指定文件(none)my.log写入指定的日志文件。名称可以是确切的位置或相对于当前目录。
(none)指定目录/var/logspring.log 写入指定的目录。名称可以是确切的位置或相对于当前目录。

日志文件在达到 10 MB时会自动覆盖前面的,与控制台输出一样,默认情况下会记录 ERROR 级别、WARN 级别和 INFO 级别的消息。可以使用 logging.file.max-size 属性更改大小限制。除非设置了 logging.file.max-history 属性,否则默认情况下将保留最近 7 天的日志文件。可以使用 logging.file.total-size-cap 限制日志存档的总大小。当日志存档的总大小超过该阈值时,将删除备份。要在应用程序启动时强制清除日志存档,使用 logging.file.clean-history-on-start 属性。

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

4.4 Log Levels

Spring Boot 所有支持的日志系统的日志级别都以 logging.level.<logger-name>=<level> 的形式写在 Environment 中(比如在 application.properties),其中 level 是 TRACE/DEBUG/INFO/WARN/ERROR/FATAL/OFF 中的一种,root 日志器能使用 logging.level.root 进行配置。

如下例展示了 application.properties 中部分日志设置:

logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error

也可以使用环境变量来设置日志级别,比如 LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG 能将 org.springframework 设置为 DEBUG

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

4.5 Log Groups

将相关的日志记录器分组在一起通常是很有用的,这样就可以同时对它们进行配置。例如,你通常可能会更改所有与 Tomcat 相关的日志记录程序的日志级别,但是你无法轻松记住顶级包。

为了帮助实现这一点,Spring Boot 允许在 Spring Environment中定义日志组。例如,下面是如何定义一个 “tomcat” 组,方法是将其添加到你的 application.properties 中:

logging.group.tomcat=org.apache.catalina, org.apache.coyote, org.apache.tomcat

一旦被定义了,你就可以使用一行代码修改组中所有记录器的级别:

logging.level.tomcat=TRACE

Spring Boot 包含以下这些开箱即用的预定义记录组:

名字记录器
weborg.springframework.core.codec, org.springframework.http, org.springframework.web, org.springframework.boot.actuate.endpoint.web, org.springframework.boot.web.servlet.ServletContextInitializerBeans
sqlorg.springframework.jdbc.core, org.hibernate.SQL, org.jooq.tools.LoggerListener

4.6 Custom Log Configuration

可以通过在类路径上安装适当的库来激活各种日志系统,还可以通过在类路径的根目录中或在由以下 Spring Environment 属性指定的位置提供适当的配置文件来进一步自定义日志系统:logging.config

通过使用 org.springframework.Boot.loggingSystem 属性,可以强制 Spring Boot 使用特定的日志系统。该值应为 LoggingSystem 实现的完全限定类名。你还可以使用值 none 来完全禁用 Spring Boot 的日志配置。

由于日志记录是在创建 ApplicationContext 之前初始化的,所以不可能控制来自Spring @Configuration 文件中的 @PropertySources 的日志记录。更改日志系统或完全禁用它的唯一方法是通过系统属性。

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

日志系统自定义文件
Logbacklogback-spring.xml, logback-spring.groovy, logback.xml, 或logback.groovy
Log4j2log4j2-spring.xmllog4j2.xml
JDK (Java Util Logging)logging.properties

如果可能,建议在日志配置中使用 -spring 变体(例如,使用 logback-spring.xml 而不是 logback.xml)。如果使用标准的配置位置,Spring无法完全控制日志初始化。

Java Util Logging 中存在一些已知的类加载问题,这些问题会在从 “可执行jar” 运行时造成错误。建议尽可能在从 “可执行jar” 运行时避免使用它。

为了帮助自定义,一些其他属性从 Spring 环境转移到了系统属性,如下表所示:

Spring EnvironmentSystem PropertyComments
logging.exception-conversion-wordLOG_EXCEPTION_CONVERSION_WORD记录异常时使用的转换字。
logging.file.clean-history-on-startLOG_FILE_CLEAN_HISTORY_ON_START是否在启动时清除存档日志文件(如果启用了 LOG_FILE)。(仅支持默认的 Logback设置。)
logging.file.nameLOG_FILE如果已定义,则在默认日志配置中使用。
logging.file.max-sizeLOG_FILE_MAX_SIZE最大日志文件大小(如果启用了 LOG_FILE)。(仅支持默认的 Logback 设置。)
logging.file.max-historyLOG_FILE_MAX_HISTORY要保留的归档日志文件的最大数量(如果启用了 LOG_FILE)。(仅支持默认的 Logback 设置。)
logging.file.pathLOG_PATH如果已定义,则在默认日志配置中使用。
logging.file.total-size-capLOG_FILE_TOTAL_SIZE_CAP要保留的日志备份的总大小(如果启用了 LOG_FILE)。(仅支持默认的 Logback 设置。)
logging.pattern.consoleCONSOLE_LOG_PATTERN要在控制台(stdout)上使用的日志模式。(仅支持默认的 Logback 设置。)
logging.pattern.dateformatLOG_DATEFORMAT_PATTERN日志日期格式的追加器模式。(仅支持默认的 Logback 设置。)
logging.pattern.fileFILE_LOG_PATTERN在文件中使用的日志模式(如果启用了LOG_FILE)。(仅支持默认的 Logback设置。)
logging.pattern.levelLOG_LEVEL_PATTERN呈现日志级别时使用的格式(默认为 %5p)。(仅支持默认的 Logback 设置。)
logging.pattern.rolling-file-nameROLLING_FILE_NAME_PATTERN模式用于滚动的日志文件名(默认的 ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz)。(仅支持默认的Logback设置。)
PIDPID当前进程ID(如果可能,在还没有定义为OS环境变量时发现)。

所有支持的日志系统在解析配置文件时都可以参考系统属性。查看 spring-boot.jar 中的默认配置示例:

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

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

2019-08-30 12:30:04.031 user:someone INFO 22174 --- [  nio-8080-exec-0] demo.Controller
Handling authenticated request

4.7 Logback Extensions

Spring Boot 包含许多 Logback 扩展,可以帮助进行高级配置。你可以在 logbackspring .xml 配置文件中使用这些扩展。

因为标准的 log.xml 配置文件加载得太早,所以不能在其中进行扩展。你需要使用logback-spring.xml 或定义 logging.config 属性。

这些扩展不能与 Logback 的配置扫描一起使用。如果你尝试这样做,对配置文件进行更改将导致与以下记录类似的错误:

ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]

4.7.1 Profile-specific Configuration

<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.7.2 Environment Properties

<springProperty> 标记允许你暴露 Spring Environment 的属性,以便在 Logback 中使用。这样的话,如果你想在你的 Logback 配置中访问你的 application.properties 文件的变量很有用。标记的工作方式与 Logback 的标准 <property> 标记类似。但是,不是指定一个直接值,而是指定属性的源(来自 Environment)。如果需要将该属性存储在本地范围之外的其他地方,可以使用 scope属性。如果需要回退值(如果未在环境中设置属性),则可以使用 defaultValue 属性。下面的示例演示如何公开属性以在 Logback 中使用:

<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
        defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
    <remoteHost>${fluentHost}</remoteHost>
    ...
</appender>

source 指定方式必须为 kabab(比如 my.property-name)。然而,可以使用松散的规则将属性添加到 Environment 中。

5. Internationalization

Spring Boot 支持本地化消息,因此你的应用可以满足不同语言的用户。默认情况下,Spring Boot 会在类路径的根目录中查找 message 资源包的存在。

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

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

spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false

spring.message.basename 支持以逗号分隔的位置列表,可以是包限定符,也可以是从类路径根解析的资源。

查看 MessageSourceProperties 获取详情。

6. JSON

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

  • Gson
  • Jackson
  • JSON-B

Jackson 是首选的和默认的库。

6.1 Jackson

提供了Jackson 的自动配置,Jackson 是 spring-boot-starter-json 的一部分。当Jackson 在类路径上时,会自动配置一个 ObjectMapper bean。提供了几个配置属性来定制 ObjectMapper 的配置

6.2 Gson

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

6.3 JSON-B

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

7. Developing Web Applications

Spring Boot 非常适合 web 应用程序开发。您可以使用嵌入的 Tomcat、Jetty、Undertow 或 Netty 来创建一个自包含的 HTTP 服务器。大多数 web 应用程序都使用 spring-boot-starter-web 模块来快速启动和运行。您你可以选择使用 spring-boot-starter-webflux 模块来构建响应式 web 应用程序。

如果你还没有开发一个 Spring Boot web 应用程序,你可以遵循入门部分中的 “Hello World!” 示例。

7.1 Spring Web MVC Framework

Spring Web MVC Framwork(通常简称为 “Spring MVC”)是一个丰富的 “模型-视图-控制器(model view controller)” Web框架。Spring MVC 允许你创建特殊的 @Controller@RestController bean来处理传入的 HTTP 请求。控制器中的方法通过使用@RequestMapping 注解映射到 HTTP。

下面的代码显示了一个典型的服务 JSON 数据的 @RestController

@RestController
@RequestMapping(value="/users")
public class MyRestController {

    @RequestMapping(value="/{user}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long user) {
        // ...
    }

    @RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
    List<Customer> getUserCustomers(@PathVariable Long user) {
        // ...
    }

    @RequestMapping(value="/{user}", method=RequestMethod.DELETE)
    public User deleteUser(@PathVariable Long user) {
        // ...
    }

}

Spring MVC 是 Spring 核心框架的一部分,详细信息可以在参考文档中找到。在 Spring .io/guides 中还有一些介绍Spring MVC的指南。

7.1.1 Spring MVC Auto-configuration

Spring Boot 为 Spring MVC 提供了自动配置,它可以很好地与大多数应用一起工作。

自动配置在 Spring 默认设置的基础上添加了以下功能:

  • 包含 ContentNegotingViewResolverBeanNameViewResolver bean。

  • 对服务静态资源的支持,包括对 WebJars 的支持。

  • 自动注册 ConverterGenericConverter 和 ``Formatter bean。

  • 支持 HttpMessageConverter

  • 自动注册 MessageCodesResolver

  • 静态 index.html 支持。

  • 自定义 Favicon 支持。

  • 自动使用可配置的 WebBindingInitializerbean

如果你想在保留这些 Spring Boot MVC 自定义的同时进行更多 MVC 自定义(拦截器——interceptors、格式化——formatters、视图控制器——view controllers 和其他功能),可以添加自己的 @ConfigurationwebMvcConfiguer 类型,但不需要 @EnableWebMvc

如果要提供 RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver 的自定义实例,并且仍然保留 Spring Boot MVC 自定义,则可以声明 WebMVCregistration 类型的 bean,并使用它来提供这些组件的自定义实例。

如果想完全控制 Spring MVC,可以添加自己的 @Configuration,并用 @EnableWebMvc 注解,或者添加自己的 @Configuration 注解 DelegatingWebMvcConfiguration 配置,像@EnableWebMvc 的文档说的那样。

7.1.2 HttpMessageConverters

Spring MVC 使用 HttpMessageConverter 接口来转换 HTTP 请求和响应。合理的默认行为是开箱即用。例如,对象可以自动转换为 JSON(通过使用 Jackson 库)或 XML(通过使用Jackson XML 扩展(如果可用),或者通过使用 JAXB(如果 Jackson XML 扩展不可用)。默认情况下,字符串是用 UTF-8 编码的。

如果需要添加或自定义转换器,可以使用 Spring Boot 的 HttpMessageConverters 类,如下所示:

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }

}

上下文中存在的所有 HttpMessageConverter bean 都将添加到转换器列表中。你也可以用同样的方法覆盖默认的转换器。

7.1.3 Custom JSON Serializers and Deserializers

如果你使用 Jackson 来序列化和反序列化 JSON 数据,你可能想编写自己的 JsonSerializerJsonDeserializer 类。自定义序列化器通常通过模块在 Jackson 中注册,但是 Spring Boot 提供了一个用于替代的 @JsonComponent 注释,这让它更容易注册到 Spring Beans 中。

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

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;

@JsonComponent
public class Example {

    public static class Serializer extends JsonSerializer<SomeObject> {
        // ...
    }

    public static class Deserializer extends JsonDeserializer<SomeObject> {
        // ...
    }

}

ApplicationContext 中的所有的 @JsonComponent bean 都自动注册到 Jackson 中。因为 @JsonComponent 是用 @Component 作为元注解的,所以可以进行通常的组件扫描。

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

7.1.4 MessageCodesResolver

Spring MVC 有一个生成错误代码的策略,用于渲染来自绑定的错误的消息: MessageCodesResolver。如果设置了 spring.mvc.message-codes-resolver-format 属性为 PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE,则 Spring Boot 会为你创建一个(参阅DefaultMessageCodesResolver.Format 中的枚举)。

7.1.5 Static Content

默认情况下,Spring Boot 服务的静态内容来自类路径下的 /static(或 /public/resource/META-INF/resource)目录或 ServletContext 的根路径,它使用来自Spring MVC 的 ResourceHttpRequestHandler,因此你可以通过添加自己的 WebMvcConfigurer和重写 addResourceHandlers 方法来修改它的行为。

在一个独立的web应用程序中,容器中的默认servlet也被启用,并充当备用,如果 Spring 决定不处理它,则从 ServletContext 的根目录提供内容。大多数情况下,这种情况不会发生(除非修改默认的 MVC 配置),因为 Spring 总是可以通过DispatcherServlet处理请求。

默认情况下,资源映射在 /**,但可以使用 spring.mvc.static-path-pattern 属性对其进行调整。例如,可以将所有资源重新定位到 /resources/** 中,如下所示:

spring.mvc.static-path-pattern=/resources/**

你可以使用 spring.resources.static-locations 自定义静态资源的位置(用目录位置列表替代默认值)。根 Servlet 上下文路径 / 也被自动作为一个位置添加。

如果你的应用是一个 jar 包,不要使用 src/main/webapp 目录。虽然这个目录是一个常用标准,但是它只在 war 包下起作用,如果你创建的是一个 jar 包,那么它在大多数的构建工具下都会被忽略。

Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用诸如 cache-busting 静态资源或对 Webjars 使用版本不可知的 URL 等。

要对 Webjars 使用版本不可知的 URL,添加 webjars-locator-core 依赖项。然后声明你的 Webjar。以 jQuery 为例,添加 "/webjars/jQuery/jQuery.min.js" 将导致 "/webjars/jQuery/x.y.z/jQuery.min.js",其中 x.y.z 是Webjar版本。

如果使用 JBoss,则需要声明 webjar-locator-Jjboss-vfs 依赖项,而不是 webjar-locator-core。否则,所有 Webjars 都会被解析为 404

要使用 cache busting,以下配置将为所有静态资源配置 cache busting 解决方案,有效地在URL中添加内容哈希,例如 <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css/>

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

由于 ResourceUrlEncodingFilter 是为 Thymeleaf 和 FreeMarker 自动配置的,所以在运行时可以在模板中重写到资源的链接。在使用 JSP 时,你应该手动声明此过滤器。其他模板引擎目前还不被支持自动配置,但是可以使用自定义模板宏/帮助程序和 ResourceUrlProvider

当使用比如 JavaScript 模块加载器动态加载资源时,不可以重命名文件。这就是为什么其他策略也受到支持,并且可以结合使用。“fixed” 策略在 URL 中添加一个不改变文件名的静态版本字符串,如下例所示:

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12

通过这种配置,"/js/lib/" 下的 JavaScript 模块使用一种固定的版本控制策略(""/v12/js/lib/mymodule.js"),而其他资源仍然使用内容哈希策略(link://css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css“/)。

有关更多支持的选项,参阅 ResourceProperties

这个特性在一篇专门的 博文Spring Framework 的参考文档中有详细的描述。

7.1.6 Welcome Page

Spring Boot 同时支持静态和模板化欢迎页面。它首先在配置的静态内容位置中查找i index.html 文件。如果没有找到,则查找 index 模板。如果找到其中之一,它将自动用作应用程序的欢迎页面。

7.1.7 Custom Favicon

与其他静态资源一样,Spring Boot 在配置的静态内容位置中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的图标。

7.1.8 Path Matching and Content Negotiation

Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,控制器方法上的 @GetMapping 注解)匹配,将传入的 HTTP 请求映射到处理程序。

Spring Boot 默认选择禁用后缀模式匹配,这意味着 "GET/projects/Spring Boot.json" 之类的请求将不会与 @GetMapping("/projects/Spring Boot") 映射匹配。这被认为是 Spring MVC 应用程序的最佳实践。这个特性在过去对于没有发送正确的 “Accept” 请求头的 HTTP 客户端非常有用;我们需要确保向客户端发送正确的内容类型。如今,内容协商机制更加可靠。

还有其他方法来处理HTTP客户端,它们不会一致地发送适当的 “Accept” 请求头的 HTTP 客户端。我们应该使用查询参数而不是后缀匹配来确保像 "GET /projects/spring-boot?format=json 这样的请求被映射到 @GetMapping("/projects/spring-boot")

spring.mvc.contentnegotiation.favor-parameter=true

# We can change the parameter name, which is "format" by default:
# spring.mvc.contentnegotiation.parameter-name=myparam

# We can also register additional file extensions/media types with:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown

后缀模式匹配是不建议使用的,并将在未来的版本中删除。如果你理解这些注意事项,并且仍然希望应用程序使用后缀模式匹配,则需要进行以下配置:

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true

此外,与其打开所有后缀模式,不如只支持注册后缀模式更安全:

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true

# You can also register additional file extensions/media types with:
# spring.mvc.contentnegotiation.media-types.adoc=text/asciidoc

7.1.9 ConfigurableWebBindingInitializer

Spring MVC 使用 WebBindingInitializer 来为特定的请求初始化 WebDataBinder。如果你创建了自己的 ConfigurableWebBindingInitializer @Bean, Spring Boot 会自动配置 Spring MVC 来使用它。

7.1.10 Template Engines

与 REST web 服务一样,你也可以使用 Spring MVC 来提供动态 HTML 内容。Spring MVC 支持各种模板技术,包括T hymeleaf、FreeMarker 和 JSP。此外,许多其他模板引擎也包含自己的Spring MVC集成。

Spring Boot 包含对下列模板引擎的自动配置支持:

如果可能,应该避免使用 JSP。它在与嵌入式 servlet 容器一起使用时,有几个已知的限制

当你使用这些带有默认配置的模板引擎时,你的模板将自动从 src/main/resources/templates 中获取。

根据应用程序的运行方式,IntelliJ IDEA 对类路径的顺序不同。在 IDE 中从主方法运行应用程序的顺序与使用 Maven 或 Gradle 或其打包的 jar 运行应用程序的顺序不同。这可能会导致 Spring Boot 无法在类路径上找到模板。如果遇到此问题,可以在 IDE 中重新排序类路径,以将模块的类和资源放在第一位。或者,你可以将模板前缀配置为搜索类路径上的每个 templates 目录,如下所示:classpath*:/templates/

7.1.11 Error Handling

默认情况下,Spring Boot 提供了一个 /error 映射,它以合理的方式处理所有错误,并在 servlet 容器中将其注册为 “global” 错误页。对于机器客户端,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户端,有一个 “whitelabel” 错误视图,它以 HTML 格式呈现相同的数据(要自定义它,添加一个解析为 errorView)。要完全替换默认行为,可以实现 ErrorController 并注册该类型的 bean 定义,或者添加 ErrorAttributes 类型的 bean 来使用现有机制,仅仅替换内容。

BasicErrorController 可以用作自定义 ErrorController 的基类。如果你想为新的内容类型添加一个处理程序(默认情况下是专门处理 text/html 并为其他所有内容提供一个后备处理程序),那么这一点特别有用。为此,扩展 BasicErrorController,添加一个带有 @RequestMapping 的公共方法,该方法具有一个 produces 属性,并创建一个新类型的 bean。

还可以定义一个带 @ControllerAdvice 注解的类,来自定义J SON 文档,使其返回特定的控制器和/或异常类型,如下面的示例所示:

@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}

在前面的示例中,如果与 AcmeController 在同一个包中定义的控制器抛出了 YourException,则使用 CustomErrorType POJO 的 JSON 表示,而不是 ErrorAttributes 表示。

Custom Error Pages

如果你想要显示给定状态代码的自定义 HTML 错误页,你可以将文件添加到 /error 文件夹。错误页面可以是静态 HTML(即添加到任何静态资源文件夹下),也可以使用模板构建。文件的名称应该是确切的状态码或系列掩码。

例如,要将 404 映射到静态 HTML 文件,你的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 FreeMarker 模板来映射所有 5xx 的错误,你的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- <other templates>

对于更复杂的映射,你可以通过添加实现 ErrorViewResolver 接口的 bean,如下面的示例所示:

public class MyErrorViewResolver implements ErrorViewResolver {

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request,
            HttpStatus status, Map<String, Object> model) {
        // Use the request or status to optionally return a ModelAndView
        return ...
    }

}

还可以使用常规的 Spring MVC 特性,比如 @ExceptionHandler 方法@ControllerAdvicehttps://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/web.html#mvc-ann-controller-advice。然后 ErrorController 获取其它未处理的异常。

Mapping Error Pages outside of Spring MVC

对于不使用 Spring MVC 的应用,可以使用 ErrorPageRegistrar 接口直接注册错误页面。这个抽象可以直接与底层的嵌入的 servlet 容器一起工作,即使你没有 Spring MVC DispatcherServlet 也可以进行工作。

@Bean
public ErrorPageRegistrar errorPageRegistrar(){
    return new MyErrorPageRegistrar();
}

// ...

private static class MyErrorPageRegistrar implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }

}

如果你注册了一个 ErrorPage,其路径最终由一个过滤器处理(这在一些非 Spring web 框架中很常见,如 Jersey 和 Wicket),那么过滤器必须显式地注册为一个 ERROR 调度程序,如下面的示例所示:

@Bean
public FilterRegistrationBean myFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter());
    ...
    registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
    return registration;
}

注意,默认的 FilterRegistrationBean 不包括 ERROR 调度程序类型。

注意:当部署到 servlet 容器时,Spring Boot 使用其错误页过滤器将带有错误状态的请求转发到适当的错误页。如果尚未提交响应,则只能将请求转发到正确的错误页面。默认情况下,WebSphere Application Server 8.0 及以后在成功完成 servlet 的服务方法后提交响应。你应该通过设置 com.ibm.ws.webcontainer.invokeFlushAfterService=false 来禁用此行为。

7.1.12 Spring HATEOAS

如果你开发了一个使用超媒体的 RESTful API,那么 Spring Boot 为 Spring HATEOAS 提供了自动配置,它可以很好地与大多数应用一起工作。自动配置取代了使用 @EnableHypermediaSupport 的需要,并注册了许多 bean 来简化基于超媒体应用的构建,包括一个 LinkDiscoverers(用于客户端支持)和一个 ObjectMapper,它们被配置为将响应正确地封送到所需的表示中。ObjectMapper 是通过设置各种 spring.jackson.* 属性来定制的,或者可以通过 Jackson2ObjectMapperBuilder bean来定制。

你可以使用 @EnableHypermediaSupport 来控制 Spring HATEOAS 的配置。注意,这样做会禁用前面描述的 ObjectMapper 自定义。

7.1.13 CORS Support

跨源资源共享(Cross-origin resource sharing, CORS)是一个由大多数浏览器实现的 W3C规范,它允许你以灵活的方式指定授权的跨域请求的类型。,而不是使用一些不那么安全、不那么强大的方法,如 IFRAME 或 JSONP。

从4.2版开始,Spring MVC 就支持 CORS。在 Spring Boot 应用程序中使用带有 @CrossOrigin 注解的控制器方法 CORS 不需要任何特定的配置。可以通过使用定制的addcorsm(CorsRegistry) 方法注册 WebMvcConfigurer bean 来定义全局 CORS 配置,如下所示:

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }
        };
    }
}

7.2 The “Spring WebFlux Framework”

Spring WebFlux是 在 Spring framework 5.0 中引入的新的响应式性 web 框架。与 Spring MVC 不同的是,它不需要 Servlet API,完全异步且非阻塞,并通过 Reactor 实现响应式流规范。

Spring WebFlux 有两种风格:函数式和基于注解的。基于注解的模式非常接近于 Spring MVC 模式,如下例所示:

@RestController
@RequestMapping("/users")
public class MyRestController {

    @GetMapping("/{user}")
    public Mono<User> getUser(@PathVariable Long user) {
        // ...
    }

    @GetMapping("/{user}/customers")
    public Flux<Customer> getUserCustomers(@PathVariable Long user) {
        // ...
    }

    @DeleteMapping("/{user}")
    public Mono<User> deleteUser(@PathVariable Long user) {
        // ...
    }

}

“WebFlux.fn” 是函数变量,它将路由配置从实际的请求处理中分离出来,如下面的例子所示:

Configuration(proxyBeanMethods = false)
public class RoutingConfiguration {

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
        return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
                .andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
                .andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
    }

}

@Component
public class UserHandler {

    public Mono<ServerResponse> getUser(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        // ...
    }
}

WebFlux 是 Spring 框架的一部分,详细的信息可以在它的参考文档中找到。

你可以定义任意多的 RouterFunction bean来模块化路由器的定义。如果需要进行优先级设置,可以对 bean 进行排序。

在你的应用中添加 spring-boot-starter-webflux 模型就可以开始了。

应用程序中同时添加 spring-boot-starter-webspring-boot-starter-webflux 模块,会导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。之所以选择这种行为,是因为许多 Spring 开发人员将 spring-boot-starter-webflux 添加到他们的Spring MVC 应用程序中,以使用响应式 WebClient。你仍然可以通过将选择的应用程序类型设置为 SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 来执行你的选择。

7.2.1 Spring WebFlux Auto-configuration

Spring Boot 为 Spring WebFlux 提供了自动配置,它可以很好地与大多数应用程序一起工作。

自动配置在 Spring 默认设置的基础上添加了以下功能:

  • HttpMessageReaderHttpMessageWriter 实例配置编解码器。

  • 对服务静态资源的支持,包括对 WebJars 的支持。

如果你想保留 Spring Boot WebFlux 特性,并且想添加额外的 WebFlux 配置,那么可以添加自己的 @Configuration 类,类型为 WebFluxConfigurer,但不需要 @EnableWebFlux

如果你想完全控制 Spring WebFlux,可以添加自己的 @Configuration,并用 @EnableWebFlux 注释。

7.2.2 HTTP Codecs with HttpMessageReaders and HttpMessageWriters

Spring WebFlux 使用 HttpMessageReaderHttpMessageWriter 接口来转换 HTTP 请求和响应。它们是用 CodecConfigurer 配置的,通过查看类路径中可用的库来获得合理的默认值。

Spring Boot为编解码器 Spring.codec.* 提供专用配置属性。它还通过使用 CodecCustomizer 实例应用进一步的自定义。例如,spring.jackson.* 配置键应用于 Jackson 编解码器。

如果需要添加或自定义编解码器,可以创建自定义编解码器自定义程序组件,如下例所示:

import org.springframework.boot.web.codec.CodecCustomizer;

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public CodecCustomizer myCodecCustomizer() {
        return codecConfigurer -> {
            // ...
        };
    }

}

你还可以定制 JSON 序列化器和反序列化器

7.2.3. Static Content

和 REST web 服务一样,你可以使用 Spring WebFlux 来提供动态 HTML 内容。Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。

Spring Boot 包含对下列模板引擎的自动配置支持:

当你使用这些带有默认配置的模板引擎时,你的模板将自动从 src/main/resources/templates 中获取。

7.2.5. Error Handling

Spring Boot 提供了一个 WebExceptionHandler,它以一种合理的方式处理所有错误。它在处理顺序中位置紧接在 WebFlux 提供的处理程序之前,后者被认为是最后一个处理程序。对于机器客户端,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户机,有一个 “whitelabel” 错误处理,它以 HTML 格式呈现相同的数据。你还可以提供自己的 HTML 模板来显示错误。

自定义此功能的第一步通常涉及使用现有机制,替换或增加错误内容。为此,可以添加 ErrorAttributes 类型的 bean。

要更改错误处理行为,可以实现 ErrorWebExceptionHandler 并注册该类型的 bean 定义。由于 WebExceptionHandler 是非常底层的,Spring Boot 还提供了一个方便的 AbstractErrorWebExceptionHandler,使你能够以 WebFlux 的函数式来处理错误,如下例所示:

public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    // Define constructor here

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {

        return RouterFunctions
                .route(aPredicate, aHandler)
                .andRoute(anotherPredicate, anotherHandler);
    }

}

对于更完整的实例,你还可以直接子类化 DefaultErrorWebExceptionHandler 并覆盖特定的方法。

Custom Error Pages

如果你想要展示给定状态代码的自定义的 HTML 错误页,你可以将文件添加到 /error 文件夹。错误页面可以是静态 HTML(即添加到任何静态资源文件夹下),也可以是用模板构建的。文件的名称应该是确切的状态码或系列掩码。

例如,要将 404 映射到静态 HTML 文件,你的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 Mustache 模板来映射所有 5xx 错误,你的文件夹结构应该如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.mustache
             +- <other templates>

7.2.6 Web Filters

Spring WebFlux提供了一个 WebFilter 接口,可以实现这个接口来过滤 HTTP request-response 交换。在应用程序上下文中找到的 WebFilter bean将自动用于过滤每个交换。

如果要指定过滤器的顺序,它们可以实现 Ordered 或使用 @Order 注解。Spring Boot 自动配置可以为你配置 we b过滤器。当它这样做时,将使用下表所示的顺序:

Web FilterOrder
MetricsWebFilterOrdered.HIGHEST_PRECEDENCE + 1
WebFilterChainProxy (Spring Security)-100
HttpTraceWebFilterOrdered.LOWEST_PRECEDENCE - 10

7.3 JAX-RS and Jersey

如果您更喜欢 REST 类型的 JAX-RS 编程模型,那么可以使用其中一个可用的实现,而不是 Spring MVC。JerseyApache CXF 在开箱即用的情况下工作得很好。CXF 要求你在应用程序上下文中将其 ServletFilter 注册为 @Bean。Jersey有 一些本地的 Spring 支持,因此也在 Spring Boot 中为它提供自动配置支持,同时还提供了一个 starter。

要开始使用J ersey,将 spring-boot-starter-jersey 作为依赖项,然后需要一个 ResourceConfig 类型的 @Bean,在其中注册所有端点,如下例所示:

@Component
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(Endpoint.class);
    }

}

Jersey 对扫描可执行文件的支持相当有限。例如,当运行可执行 war 文件时,它无法扫描完全可执行 jar 文件WEB-INF/classes 中的包中的端点。为了避免这种限制,不应该使用 packages 方法,而应该使用 register 方法单独注册端点,如前一个示例所示。

对于更高级的定制,你还可以注册任意数量的 ResourceConfigCustomizer 实现类的 bean。

所有注册的端点都应该是带有 HTTP 资源注解的 @Components@GET 等)。如下例所示:

@Component
@Path("/hello")
public class Endpoint {

    @GET
    public String message() {
        return "Hello";
    }

}

7.4 Embedded Servlet Container Support

Spring Boot 包含对嵌入式T omcat、Jetty 和 Undertow 服务器的支持。大多数开发人员使用对应的 “Starter” 来获得完全配置的实例。默认情况下,嵌入式服务器监听端口 8080 上的 HTTP 请求。

7.4.1 Servlets, Filters, and listeners

当使用嵌入式 servlet 容器时,你可以注册 servlet 规范中的 servlet、filter 和所有 listener(如 HttpSessionListener),方法是使用 Spring bean或扫描 Servlet 组件。

Registering Servlets, Filters, and Listeners as Spring Beans

任何作为 Spring bean 的 ServletFilter 或 servlet *Listener 实例都会注册到嵌入式容器中。如果要在配置过程中引用 application.properties 中的值,这会特别方便。

默认情况下,如果上下文只包含一个Servlet,则将其映射到 /。在多个 servlet bean的情况下,bean 名称用作路径前缀。过滤器映射到 /*

如果基于约定(convention-based)的映射不够灵活,则可以使用 ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean 类进行完全控制。

通常情况下,无序的 Filter bean 是安全的。如果需要特定的顺序,则应使用 @Order 注解 Filter,或使其实现 Ordered。不能通过用 @Order 注解的 bean 方法来配置 Filter 的顺序。如果不能将 Filter 类添加 @Order 或实现 Ordered,则必须为 Filter定义 FilterRegistration bean,并使用 setOrder(int) 方法设置注册 bean 的顺序。避免配置以 Ordered.HIGHEST_PRECEDENCE 读取请求正文的 Filter,因为它可能违反应用程序的字符编码配置。如果Servlet过滤器包装了请求,则应使用小于或等于 OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER 的顺序对其进行配置。

要查看应用程序中每个 Filter 的顺序,可为 web 日志记录组启用调试级别日志记录(logging.level.web=debug)。注册过滤器的详细信息,包括它们的顺序和 URL 模式,将在启动时记录下来。

注册 Filter bean时要小心,因为它们在应用程序生命周期的早期就被初始化了。如果需要注册与其他 bean 交互的过滤器,请考虑改用 DelegatingFilterProxyRegistrationBean

7.4.2 Servlet Context Initialization

嵌入式 servlet 容器不会直接执行 Servlet3.0+ javax.servlet.ServletContainerInitializer 接口或Spring的 org.springframework.web.WebApplicationInitializer 接口。这是一个有意的设计决策,旨在降低设计用于在 war 中运行的第三方库可能会破坏 Spring 引导应用程序的风险。

如果需要在 Spring Boot 应用程序中执行 servlet上 下文初始化,则应注册实现 org.springframework.Boot.web.servlet.ServletContextInitializer 接口的 bean。 onStartup 方法提供对 ServletContext 的访问,如果需要,可以很容易地用作现有 WebApplicationInitializer 的适配器。

Scanning for Servlets, Filters, and listeners

使用嵌入式容器时,可以使用 @ServletComponentScan 启用对 @WebServlet@WebFilter@WebListener 注解的类的自动注册。

ServletComponentScan 在单个独立的容器中不起作用,因为它使用了容器内置的发现机制。可查看 https://blog.csdn.net/vipzyj/article/details/105622524

7.4.3 The ServletWebServerApplicationContext

在底层,Spring Boot 使用不同类型的 ApplicationContext 来支持嵌入式 servlet 容器。ServletWebServerApplicationContext 是一种特殊类型的 WebApplicationContext,它通过搜索单个 ServletWebServerFactory bean来引导自己。通常,TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory 都是自动配置的。

你通常不需要知道这些实现类。大多数应用程序都是自动配置的,会为你配置合适的 ApplicationContextServletWebServerFactory

7.4.4 Customizing Embedded Servlet Containers

通用的servlet容器设置可以使用 Spring Environment 属性配置。通常,你可以在 application.properties 文件中定义属性。

常用服务器设置包括:

  • 网络设置:接收HTTP请求的侦听端口(server.port)、要绑定到的 server.address 接口地址,等等。

  • 会话设置:会话是否持久(server.servlet.Session.persistent)、会话超时(server.servlet.Session.timeout)、会话数据位置(server.servlet.Session.store dir)和会话 cookie 配置(server.servlet.Session.cookie.*)。

  • 错误管理:错误页的位置(server.Error.path)等。

  • SSL

  • HTTP compression

Spring Boot 尽可能多地暴露公开的常用设置,但这不是万全之策。对于这些情况,专用名称空间提供了 server-specific 定制服务(查看 server.tomcatserver.undertow)。例如,可以使用嵌入式 servlet 容器的特定特性来配置访问日志

查看 ServerProperties 获取完整数据。

Programmatic Customization

如果你需要以编程式的方法配置嵌入式 servlet 容器,可以注册实现 WebServerFactoryCustomizer 接口的 Spring bean。WebServerFactoryCustomizer 提供对 ConfigurableServletWebServerFactory 的访问,其中包括许多自定义的 setter 方法。以下示例展示了以编程式设置端口:

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactoryConfigurableServletWebServerFactory 的变体,它们分别为 Tomcat、Jetty 和 Undertow 提供了额外定制的 setter 方法。

Customizing ConfigurableServletWebServerFactory Directly

如果认为前面的定制技术存在限制,你可以自己注册 TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory bean:

@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.setPort(9000);
    factory.setSessionTimeout(10, TimeUnit.MINUTES);
    factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
    return factory;
}

它为许多配置选项提供了 setter。如果你需要做一些更特别的事情,还提供了一些受保护的钩子函数。有关详细信息,参阅 源代码文档

7.4.5 JSP Limitations

当使用嵌入式 servlet 容器(打包为可执行归档文件)的 Sprin Boot 应用程序时,JSP 支持会受到一些限制。

  • 可以使用 war 打包 Jetty 和 Tomcat。一个可执行的 war 在用 java -jar 启动时可以工作,并且可以部署到任何标准容器。但使用可执行 jar 时不支持 JSP。

  • Undertow 不支持 JSP。

  • 创建自定义 error.jsp 页面不会覆盖错误处理的默认视图,应改用自定义错误页

7.5 Embedded Reactive Server Support

Spring Boot 包括对以下嵌入式响应式 web 服务器的支持:Reactor Netty、Tomcat、Jetty 和 Undertow。大多数开发人员使用适当的 “Starter” 来获得完整配置的实例。默认情况下,嵌入式服务器侦听端口 8080 上的 HTTP 请求。

7.6 Reactive Server Resources Configuration

当自动配置 Reactor Netty 或 Jetty 服务器时,Spring Boot 将创建特定的bean,为服务器实例提供 HTTP 资源:ReactorResourceFactoryJettyResourceFactory

默认情况下,这些资源还将与Reactor Netty 和 Jetty 客户端共享,以获得最佳性能,前提是:

  • 服务器和客户端使用相同的技术

  • 客户机实例是使用由 Spring Boot 自动配置的 WebClient.Builder bean构建的

开发人员可以通过提供自定义的 ReactorResourceFactoryJettyResourceFactory bean来覆盖 Jetty 和 Reactor Netty 的资源配置——这将应用于客户端和服务器。

你可以在 WebClient 运行时部分中了解有关客户端资源配置的更多信息。

8. RSocket

RSocket 是一种用于比特流传输的二进制协议,它通过在单个连接上异步传输消息来开启对称的交互模型。

Spring Framework 提供了 spring-messaging 模型用于支持 RSocket 服务端和客户端的请求和响应,有关更多细节(包括RSocket协议的概述),参见 Spring 框架参考的 RSocket部分

8.1 RSocket Strategies Auto-configuration

Spring Boot 会自动配置一个 RSocketStrategies bean,它提供了编码和解码 RSocket 有效负载所需的所有基础设置。默认情况下,自动配置将尝试配置以下(按照顺序):

  • Jackson 的 CBOR 编解码器

  • Jackson 的 JSON 编解码器

spring-boot-starter-rsocket 提供了所有依赖,可以查看 Jackson 支持部分了解更多的可用于自定义的组件。

开发人员可以通过创建实现 RSocketStrategies customizer 接口的 bean 来定制 RSocketStrategies 组件。注意它们的 @Order 很重要,因为它决定了编解码器的顺序。

8.2 RSocket server Auto-configuration

Spring Boot 提供 RSocket 服务的自动配置。所需的依赖项由 spring-boot-starter-rsocket 提供。

Spring Boot 允许通过 WebFlux 服务在 WebSocket 上暴露 RSocket,或者建立一个独立的 RSocket 服务。这取决于应用程序的类型及其配置。

对于 WebFlux 应用程序(即 WebApplicationType.REACTIVE 类型),只有在下列属性匹配时,RSocket 服务才会插入到 Web 服务:

spring.rsocket.server.mapping-path=/rsocket # a mapping path is defined
spring.rsocket.server.transport=websocket # websocket is chosen as a transport
#spring.rsocket.server.port= # no port is defined

只有 Reactor Netty 支持将 RSocket 插入web服务器,,因为 RSocket 本身就是用这个库构建的。

此外,RSocket TCP 或 websocket 服务可以作为独立的嵌入式服务器启动。除了依赖需求,唯一需要的配置是为该服务器定义一个端口:

spring.rsocket.server.port=9898 # the only required configuration
spring.rsocket.server.transport=tcp # you're free to configure other properties

8.3 Spring Messaging RSocket support

Spring Boot将为 RSocket 自动配置 Spring Messaging 基础组件。

这意味着 Spring Boot 将创建一个 RSocketMessageHandlerbean,用于处理对应用程序的 RSocket 请求。

8.4 Calling RSocket Services with RSocketRequester

一旦在服务器和客户端之间建立了 RSocket 通道,任何一方都可以向另一方发送或接收请求。

作为服务器,可以在 RSocket @Controller 的任何处理程序方法上注入 RSocketRequester 实例。作为客户端,你需要首先配置并建立 RSocket 连接。Spring Boot 使用预期的编解码器自动为此类情况配置 RSocketRequester.Builder

RSocketRequester.Builder 实例是一个原型 bean,这意味着每个注入点都将为你提供一个新实例。这是有目的的,因为这个构建器是有状态的,你不应该使用同一个实例创建具有不同设置的请求程序。

以下代码显示了一个典型示例:

@Service
public class MyService {

    private final Mono<RSocketRequester> rsocketRequester;

    public MyService(RSocketRequester.Builder rsocketRequesterBuilder) {
        this.rsocketRequester = rsocketRequesterBuilder
                .connectTcp("example.org", 9898).cache();
    }

    public Mono<User> someRSocketCall(String name) {
        return this.rsocketRequester.flatMap(req ->
                    req.route("user").data(name).retrieveMono(User.class));
    }

}

9. Security

如果 Spring Security 在类路径上,那么 web 应用程序在默认情况下是安全的。Spring Boot 依赖于 Spring Security 的内容协商策略来决定是使用 httpBasic 还是 formLogin。要向 web 应用程序添加方法级的安全性,还可以使用所需的设置添加 @EnableGlobalMethodSecurity。其他信息可以在 Spring Security 参考指南 中找到。

UserDetailsService 默认只有一个用户。用户名是 user,密码是随机的,并且会在应用程序启动时在 INFO 级别打印,如下例所示:

Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35

如果你对日志配置进行了调整,要确保 org.springframework.boot.autoconfigure 的类别设置为 INFO 级别日志。否则,将不会打印默认的随机密码。

你可以通过提供 spring.security.user.namespring.security.user.password 来更改用户名和密码。

默认情况下,web 应用程序中的基本功能包括:

  • 一个具有内存存储的 UserDetailsService (或者在 WebFlux 应用程序的情况下是 ReactiveUserDetailsService)bean和一个具有生成的密码的用户(有关用户的属性,参阅 SecurityProperties.User )。

  • 整个应用程序都基于表单的登录或 HTTP Basic 安全性(取决于请求中的 Accept 头)(如果执行器位于类路径上,则包括执行器端点)。

  • 用于发布身份验证事件的 DefaultAuthenticationEventPublisher

你可以通过添加 bean 来提供不同的 AuthenticationEventPublisher

9.1 MVC Security

默认的安全配置在 SecurityAutoConfigurationUserDetailsServiceAutoConfiguration 中实现。SecurityAutoConfiguration 导入 SpringBootWebSecurityConfiguration 用于 web 安全和 UserDetailsServiceAutoConfiguration 用于配置身份验证,这在非 web 应用程序中也是差不多的。要完全关闭默认的 web 应用程序安全配置或组合多个Spring Security 组件(如 OAuth2 客户端和资源服务器),要添加 WebSecurityConfigurerAdapter 类型的 bean(这样做并不会禁用 UserDetailsService 配置或执行器的安全性)。

要同时关闭 UserDetailsService 配置,可以添加 UserDetailsServiceAuthenticationProviderAuthenticationManager 类型的 bean。

访问规则可以通过添加自定义 WebSecurity 配置适配器来重写。Spring Boot 提供了一些方便的方法,可以用来覆盖执行器端点和静态资源的访问规则。EndpointRequest 可用于创建基于 management.endpoints.web.base-path 属性的 RequestMatcherPathRequest 可用于为常用位置的资源创建 RequestMatcher

9.2 WebFlux Security

与 Spring MVC 应用程序类似,可以通过添加 spring-boot-starter-security 依赖项来保护 WebFlux 应用程序。默认的安全配置在 ReactiveSecurityAutoConfigurationUserDetailsServiceAutoConfiguration 中实现。ReactiveSecurityAutoConfiguration 导入 WebFluxSecurityConfiguration 用于 web 安全和 UserDetailsServiceAutoConfiguration 用于配置身份验证,这在非web应用程序中也是差不多的。要完全关闭默认的 we b应用程序安全配置,可以添加 WebFilterChainProxy 类型的 bean(这样做不会禁用 UserDetailsService 配置或执行器的安全性)。

要同时关闭 UserDetailsService 配置,可以添加 ReactiveUserDetailsServiceReactiveAuthenticationManager 类型的bean。

访问规则多个 Spring Security 组件(如 OAuth2 客户端和资源服务器)的使用可以通过添加自定义 SecurityWebFilterChain bean 进行配置。Spring Boot 提供了一些方便的方法,可以用来覆盖执行器端点和静态资源的访问规则。EndpointRequest 可用于创建基于 management.endpoints.web.base-path 属性的 ServerWebExchangeMatcher

PathRequest 可用于为常用位置的资源创建 ServerWebExchangeMatcher

例如,可以通过添加以下内容自定义安全配置:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    return http
        .authorizeExchange()
            .matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            .pathMatchers("/foo", "/bar")
                .authenticated().and()
            .formLogin().and()
        .build();
}

9.3 OAuth2

OAuth2 是一个 Spring 支持的,被广泛用于用户验证的框架。

9.3.1 客户端

如果类路径上有 spring-security-oauth2-client,那么可以利用一些自动配置来轻松地设置 OAuth2/Open ID Connect 客户端。这个配置使用了 OAuth2ClientProperties 下的属性。servlet 和响应式应用都是使用相同的属性。

你可以在 spring.security.oauth2 前缀下注册多个 OAuth2 客户端和服务端,如下例所示:

pring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri=https://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri=https://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server/oauth/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server/oauth/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server/userinfo
spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header
spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server/token_keys
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name

对于支持 OpenID Connect 发现机制 的OpenID Connect 提供者,可以进一步简化配置。提供者需要配置一个 issuer-uri,该 URI 是它断言为其发布方的标识符。例如,如果提供 issuer-uri 是 “https://example.com”,那么将向 “https://example.com/.well-known/OpenID-Configuration” 发送 OpenID Provider Configuration Request。结果应该是获得一个 OpenID Provider Configuration Response。以下示例显示如何使用 issuer-uri 配置 OpenID Connector 提供者:

spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/

默认情况下,Spring Security 的 OAuth2LoginAuthenticationFilter 只处理匹配 /login/oauth2/code/* 的 URL。如果要自定义重定向 URI 以使用不同的模式,则需要提供配置来处理该自定义模式。例如,对于 servlet 应用程序,可以添加自己的 WebSecurity 配置适配器,类似于以下内容:

public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .oauth2Login()
                .redirectionEndpoint()
                    .baseUri("/custom-callback");
    }
}
OAuth2 client registration for common providers

对于常见的 OAuth2 和 OpenID 提供者,包括 Google、Github、Facebook 和 Okta,Spring Boot 提供了一组提供者的缺省值(分别为 googlegithubfacebookokta)。

如果你不需要自定义这些提供这,你可以将提供者属性设置为你所需要的推断默认值的属性。此外,如果客户端注册的键与默认的受支持的提供程序匹配,Spring Boot 也会推断出这一点。

换句话说,下面示例中的两个配置使用了 google 提供者:

spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.my-client.provider=google

spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.google.client-secret=password

9.3.2 Resource Server

如果类路径上有 spring-security-oauth2-resource-server, Spring Boot 可以设置一个 OAuth2 资源服务器。对于 JWT 配置,需要指定 JWK Set URI 或 OIDC Issuer URI,如下例所示:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/

如果授权服务器不支持 JWK Set URI,则可以使用用于验证 JWT 签名的公钥配置资源服务器。此可以使用 spring.security.oauth2.resourceserver.jwt.public-key-location 属性完成,其中的值需要指向包含 PEM-encoded x509 格式的公钥的文件。

相同的属性适用于 servlet 和响应式应用程序。

或者,你可以为 servlet 应用程序定义自己的 JwtDecoder bean,或者为响应式应用程序定义 ReactiveJwtDecoder

在使用不透明令牌而不是 JWTs 的情况下,你可以配置以下属性来通过自省来验证令牌:

spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token
spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id
spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret

同样,相同的属性也适用于 servlet 和响应式应用程序。

或者,你可以为 servlet 应用程序定义你自己的 OpaquekenIntrospector bean,或者为响应式应用程序定义一个 ReactiveOpaqueTokenIntrospector

9.3.3 Authorization Server

目前,Spring Security 不支持实现 OAuth2.0 授权服务器。但是,这个功能可以从 Spring Security OAuth 项目获得,Spring Security 最终将完全取代这个项目。在此之前,你可以使用 spring-security-oauth2-autoconfigure 模块轻松设置 OAuth2.0 授权服务器,有关说明,参阅其文档

9.4 SAML 2.0

9.4.1 Relying Party

如果类路径上有 spring-security-saml2-service-provider,那么你可以利用一些自动配置来轻松地设置 SAML2.0 依赖方。此配置使用 Saml2RelyingPartyProperties 下的属性。

依赖方注册表示标识提供者 IDP 和服务提供者 SP 之间的成对配置。可以在spring.security.saml2.relyingparty 前缀下注册多个依赖方,如下例所示:

spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.verification.credentials[0].certificate-location=path-to-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.entity-id=remote-idp-entity-id1
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.sso-url=https://remoteidp1.sso.url

spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.verification.credentials[0].certificate-location=path-to-other-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.entity-id=remote-idp-entity-id2
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.sso-url=https://remoteidp2.sso.url

9.5 Actuator Security

出于安全考虑,默认情况下,除 /health/info 之外的所有执行器都是禁用的。可以使用 management.endpoints.web.exposure.include 属性启用执行器。

如果 Spring Security 在类路径上,并且不存在其他 WebSecurityConfigurerAdapter,那么除了 /health/info 之外的所有执行器都由 Spring 引导自动配置保护。如果你定义了一个自定义 WebSecurityConfigurerAdapter,Spring Boot 自动配置机制将会停止,你将完全控制执行器的访问规则。

设置 management.endpoints.web.exposure 之前。确保暴露的执行器不包含敏感信息 和/或 通过将它们放置在防火墙后或通过类似 Spring Security 的东西来保护它们。

9.5.1 Cross Site Request Forgery Protection

因为 Spring Boot 依赖于 Spring Security 的默认值,所以 CSRF 保护在默认情况下是打开的。这意味着,当使用默认安全配置时,执行器端点进行 POST(关闭和日志记录器端点)、PUTDELETE 将得返回 403。

建议只在创建非浏览器客户端使用的服务时才完全禁用 CSRF 保护。

有关 CSRF 保护的其他信息可以在 Spring Security 参考指南中找到。

10. Working with SQL Databases

Spring Framework 为使用 SQL 数据库提供了广泛的支持,从使用 JdbcTemplate 的直接 JDBC 访问到完成 “对象关系映射(object relational mapping)” 技术,如 Hibernate。Spring Data 提供了附加的功能:直接从接口创建 Repository 实现,并遵循约定从方法名生成查询。

10.1 Configure a DataSource

Java的 javax.sql.DataSource 接口提供了处理数据库连接的标准方法。传统上,“DataSource” 使用一个 URL 和一些凭证来建立数据库连接。

查看 “How-to” 一节获取更多高级的例子,尤其是如何完全靠自己数据源的配置。

10.1.1 Embedded Database Support

使用内存中的嵌入式数据库开发应用程序通常很方便。当然,内存数据库不提供持久存储。你需要在应用程序启动时填充数据库,并准备在应用程序结束时丢弃数据。

“How-to” 一节中包含了 如何初始化数据库

Spring Boot 可以自动配置嵌入的 H2HSQLDerby 数据库。你不需要提供任何连接 URL。只需要包含对要使用的嵌入式数据库的构建依赖项。

如果你在测试中使用这个特性,你可能会注意到,无论你使用多少应用程序上下文,整个测试套件都会重用相同的数据库。如果你想确保每个上下文都有一个单独的嵌入式数据库,你应该设置 spring.datasource.generate-unique-name 为 true。

例如,典型的 POM 依赖关系如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>runtime</scope>
</dependency>

要自动配置嵌入式数据库,需要依赖 spring-jdbc。在本例中,它是通过 spring-boot-starter-data-jpa 间接拉入的。

如果出于任何原因,你为嵌入式数据库配置了连接 URL,要注意确保数据库的自动关闭被禁用。如果使用 H2,则应使用 DB_CLOSE_ON_EXIT=FALSE 来执行此操作。如果使用HSQLDB,则应确保不使用 shutdown=true。禁用数据库的自动关闭让 Spring Boot控制它的关闭,从而确保在在需要访问数据库时再次开启。

10.1.2 Connection to a Production Database

可以使用池数据源自动配置产生的数据库连接。Spring Boot使用以下算法选择特定的实现:

  • 我们更喜欢 {HikariCP](https://github.com/brettwooldridge/HikariCP) 的性能和并发性。如果 HikariCP 可用,我们总是选择它。

  • 否则,如果 Tomcat 池数据源可用,我们就使用它。

  • 如果 HikariCP 和 Tomcat 池数据源都不可用,并且 Commons DBCP2 可用,则使用它。

如果使用 spring-boot-starter-jdbcspring -boot-starter-data-jpa “starters”,则会自动获取对 HikariCP 的依赖。

你可以绕过该算法,通过设置 spring.datasource 指定要使用的连接池。如果在 Tomcat 容器中运行应用程序,这一点尤其重要,因为默认情况下它提供了 tomcat -jdbc

其他连接池始终可以手动配置。如果定义了自己的 DataSource bean,则不会进行自动配置。

数据源配置由 spring.datasource.* 中的外部配置属性控制。例如,你可以在 application.properties 中声明以下部分:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

如果你不设置 spring.datasource-url 属性,Spring Boot 就会尝试自动配置内嵌数据库。

通常不需要指定 driver-class-name,因为 Spring Boot 可以从 url推断大多数数据库的驱动程序类名。

为了创建一个 DataSource,需要能够验证一个有效的驱动程序类是可用的,所以我们在进行数据库操作之前都要检查它。换句话说,如果设置 spring.datasource.driver class name=com.mysql.jdbc.driver,那么该类必须是可加载的。

参阅 DataSourceProperties 查看更多支持的选项,无论实际实现如何,这些都是标准的选项。还可以使用它们各自的前缀(spring.datasource.hikari.*spring.datasource.tomcat.*spring.datasource.dbcp2.*)调整特定于实现的设置。

例如,如果使用 Tomcat连接池,可以自定义许多其他设置,如下例所示:

# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.tomcat.max-wait=10000

# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=50

# Validate the connection before borrowing it from the pool.
spring.datasource.tomcat.test-on-borrow=true

10.1.3 Connection to a JNDI DataSource

如果将 Spring Boot 应用程序部署到应用程序服务器,则可能需要使用应用程序服务器的内置功能配置和管理数据源,并使用 JNDI 访问它。

spring.datasource.jndi-name 属性可以用作 spring.datasource.urlspring.datasource.usernamespring.datasource.password 属性的替代项,以从特定 JNDI 位置访问数据源。例如,application.properties 中的以下部分展示了如何访问JBoss 定义的 DataSource

spring.datasource.jndi-name=java:jboss/datasources/customers

10.2 Using JdbcTemplate

Spring 的 JdbcTemplateNamedParameterJdbcTemplate 类是自动配置的,你可以用 @Autowire 直接将它们放到你自己的 bean 中,如下例所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public MyBean(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // ...

}

你可以使用 spring.jdbc.template.* 属性自定义模板的一些属性,如下例所示:

spring.jdbc.template.max-rows=500

在后台,NamedParameterJdbcTemplate 重用相同的 JdbcTemplate 实例。如果定义了多个 JdbcTemplate 且不存在主候选项,则不会自动配置NamedParameterJdbcTemplate

10.3 JPA and Spring Data JPA

Java 持久化 API是一种标准的技术,允许将对象映射到关系数据库。spring-boot-starter-data-jpa POM 提供了一种快速启动方法。它提供了以下关键依赖:

  • Hibernate:最流行的JPA实现之一。

  • Spring Data JPA:使实现基于JPA的存储库变得很容易。

  • Spring ORM:来自Spring框架的核心 ORM 支持。

不会在这里讨论太多 JPA 或 Spring Data 的细节。你可以遵循 Spring 的使用 JPA 访问数据指南spring.io 和阅读 Spring Data JPAHibernate 参考文档。

10.3.1 Entity Classes

传统上,JPA 实体类是在 persistence.xml 文件中指定的。对于 Spring Boot,不需要这个文件,而是使用实体扫描。默认情况下,将搜索主配置类(用 @EnableAutoConfiguration@SpringBootsApplication 注解包)下面所有的包。

任何用 @Entity@Embeddable@MappedSuperclass 注释的类都将被考虑进去。典型的实体类类似于以下示例:

package com.example.myapp.domain;

import java.io.Serializable;
import javax.persistence.*;

@Entity
public class City implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String state;

    // ... additional members, often include @OneToMany mappings

    protected City() {
        // no-args constructor required by JPA spec
        // this one is protected since it shouldn't be used directly
    }

    public City(String name, String state) {
        this.name = name;
        this.state = state;
    }

    public String getName() {
        return this.name;
    }

    public String getState() {
        return this.state;
    }

    // ... etc

}

可以使用 @EntityScan 注解自定义实体扫描位置。具体可查看 “howto.html” 指南。

10.3.2 Spring Data JPA Repositories

Spring Data JPA 存储库是你可以定义来访问数据的接口。JPA 查询是根据你的方法名自动创建的。例如,CityRepository 接口可以声明一个 findAllByState(String state) 方法来查找给定状态下的所有城市。

对于更复杂的查询,可以使用 Spring Data 的 Quer 注解来标注你的方法。

Spring Data 存储库通常从 RepositoryCrudRepository 接口扩展。如果使用自动配置,存储库将从包含主配置类的包(使用 @EnableAutoConfiguration@SpringBootApplication 注解的类)向下搜索。

下面的例子展示了一个典型的 Spring Data 存储接口定义:

package com.example.myapp.domain;

import org.springframework.data.domain.*;
import org.springframework.data.repository.*;

public interface CityRepository extends Repository<City, Long> {

    Page<City> findAll(Pageable pageable);

    City findByNameAndStateAllIgnoringCase(String name, String state);

}

Spring Data JPA 存储库支持三种不同的引导模式:默认、延迟和懒加载。要启用延迟或懒加载,要将 spring.data.jpa.repositories.bootstrap-mode 属性设置为 deferredlazy。使用延迟或懒加载时,自动配置的 EntityManagerFactoryBuilder 将使用上下文的 AsyncTaskExecutor(如果有)作为引导执行器。如果存在多个,则将使用名为 applicationTaskExecutor 的。

以上仅仅触及了 Spring Data JPA 的皮毛。有关完整的详细信息,参阅 Spring Data JPA参考文档

10.3.3 Creating and Dropping JPA Databases

默认情况下,只有在使用嵌入式数据库(H2、HSQL 或 Derby)时,才会自动创建 JPA 数据库。你可以使用 spring.jpa.* 属性显式地配置 JPA 设置。例如,要创建和删除表,可以将以下行添加到 application.properties

spring.jpa.hibernate.ddl-auto=create-drop

Hibernate 自己的内部属性名是 hibernate.hbm2ddl.auto。你可以使用 spring.jpa.properties.*(在将前缀添加到实体管理器之前去掉前缀)将其与其他 Hibernate 本机属性一起设置。下一行显示了为 Hibernate 设置 JPA 属性的示例:

spring.jpa.properties.hibernate.globally_quoted_identifiers=true

上例中将 hibernate.globally_quoted_identifiers 属性的值 true 传递给 hibernate 实体管理器。

默认情况下,DDL执行(或验证)将延迟到 ApplicationContext 启动。还有一个 spring.jpa.generate-ddl 标志,但如果 Hibernate 自动配置处于启动状态,则不使用该标志,因为 DDL 的自动设置更细粒度。

10.3.4 Open EntityManager in View

如果你在运行一个 web 应用程序,Spring Boot 默认情况下会注册 OpenEntityManagerInViewInterceptor 来使用 “Open EntityManager in View” 模式,以允许在 web 视图中延迟加载。如果不需要此行为,则应在 application.properties 中将 spring.jpa.open-in-view 设置为 false

10.4 Spring Data JDBC

Spring Data 包括对 JDBC 的存储库支持,并将自动为 CrudRepository 上的方法生成 SQL。对于更高级的查询,将提供 @Query 注解。

当必要的依赖项在类路径上时,Spring Boot 将自动配置 Spring Data 的 JDBC 存储库。它们可以通过对 spring-boot-starter-data-jdbc 的单一依赖性添加到你的项目中。如有必要,你可以通过向应用程序添加 @EnableJdbcRepositories 注解或 JdbcConfiguration 子类来控制 Spring Data JDBC 的配置。

查看文档获取 Spring Data JDBC 的详细信息。

10.5 Using H2’s Web Console

H2 dataset提供了一个基于浏览器的控制台,Spring Boot可以为您自动配置。当满足以下条件时,控制台自动配置:

如果你没有使用 Spring Boot 开发工具,仍然向使用 H2 控制台,那么可以将 spring.h2.console.enabled 属性设置为 true

H2 控制台只在开发过程中使用,所以你要注意在生产环境中不要把 spring.h2.console.enabled 设置为 true

10.5.1 Changing the H2 Console’s Path

默认情况下,控制台的路径是在 /h2-console,你可以使用 spring.h2.console.path 自定义控制台路径。

10.6 Using jOOQ

面向对象查询(JOOQ Object Oriented Querying,,jOOQ)是 Data Geekery 的一个流行产品,它从数据库生成 Java 代码,并允许你通过它的流式 API 构建类型安全的 SQL 查询。商业版和开源版都可以与 Spring Boot 一起使用。

10.6.1 Code Generation

为了使用 jOOQ 类型安全查询,需要从数据库模式生成 Java 类。你可以按照 jOOQ 用户手册中的说明进行操作。如果你使用 jooq codegen maven 插件,并且还使用 spring-boot-starter-starter-parent,那么可以安全地省略该插件的 <version> 标记。你还可以使用 Spring Boot 定义的版本变量(例如 h2.version)来声明插件的数据库依赖项。下面的列表显示了一个示例:

<plugin>
    <groupId>org.jooq</groupId>
    <artifactId>jooq-codegen-maven</artifactId>
    <executions>
        ...
    </executions>
    <dependencies>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>${h2.version}</version>
        </dependency>
    </dependencies>
    <configuration>
        <jdbc>
            <driver>org.h2.Driver</driver>
            <url>jdbc:h2:~/yourdatabase</url>
        </jdbc>
        <generator>
            ...
        </generator>
    </configuration>
</plugin>

10.6.2 Using DSLContext

jOOQ 提供的流式 API 是通过 org.jOOQ.DSLContext 接口启动的。Spring Boot 将 DSLContext 自动配置为 Spring Bean,并将其连接到应用程序数据源。要使用 DSLContext,可以通过 @Autowire 使用它,如下例所示:

@Component
public class JooqExample implements CommandLineRunner {

    private final DSLContext create;

    @Autowired
    public JooqExample(DSLContext dslContext) {
        this.create = dslContext;
    }

}

jOOQ 手册中倾向于使用一个名为 create 的变量来保存 DSLContext

然后你可以使用 DSLContext 构造你的查询,如下面的示例所示:

public List<GregorianCalendar> authorsBornAfter1980() {
    return this.create.selectFrom(AUTHOR)
        .where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1)))
        .fetch(AUTHOR.DATE_OF_BIRTH);
}

10.6.3 jOOQ SQL Dialect

可以通过定义自己的 @Bean 定义来实现更高级的定制,在创建 jOOQ 配置时使用 @Bean 定义。你可以为以下 jOOQ 类型定义 bean:

  • ConnectionProvider
  • ExecutorProvider
  • TransactionProvider
  • RecordMapperProvider
  • RecordUnmapperProvider
  • Settings
  • RecordListenerProvider
  • ExecuteListenerProvider
  • VisitListenerProvider
  • TransactionListenerProvider

还可以创建自己的 org.jooq。如果你想完全控制 jOOQ 配置,可以使用 @Bean

11. Working with NoSQL Technologies

Spring Data 提供了额外的项目,帮助你访问各种 NoSQL 技术,包括:

Spring Boot为 Redis、MongoDB、Neo4j、Elasticsearch、Solr Cassandra、Couchbase 和 LDAP 提供了自动配置。你也可以使用其他项目,但是必须自己配置它们。可参考 spring.io/projects/spring-data 中相应的参考文档。

11.1 Redis

Redis 是一个缓存、消息代理和功能丰富的键值存储。Spring Boot为 LettuceJedishttps://github.com/xetorthio/jedis/ 客户端库以及 Spring Data Redis 提供的抽象提供基本的自动配置。

spring-boot-starter-data-redis “Starter” 可以十分方便地收集相关依赖。默认情况下,它使用 Lettuce。该 starter 同时处理传统应用程序和响应式应用程序。

还提供了一个 spring-boot- Starter -data-re -reactive “Starter”,用于与具有响应式支持的其他存储保持一致性。

11.1.1 Connecting to Redis

您可以像注入其他 Spring Bean 一样注入一个自动配置的 RedisConnectionFactoryStringRedisTemplate 或普通的 RedisTemplate 实例。默认情况下,实例尝试连接到 localhost:6379 的Redis服务器。下面的代码显示了这样一个 bean 的例子:

@Component
public class MyBean {

    private StringRedisTemplate template;

    @Autowired
    public MyBean(StringRedisTemplate template) {
        this.template = template;
    }

    // ...

}

你还可以注册实现了 LettuceClientConfigurationBuilderCustomizer,用于更高级的定制的 bean。如果你使用 Jedis,也可以使用 JedisClientConfigurationBuilderCustomizer

如果你添加自己的 @Bean(属于任何自动配置的类型),它将替换默认的(在 RedisTemplate 的情况下除外,当排除基于 Bean 名称的 RedisTemplate 而不是其类型时)。默认情况下,如果 commons-pool2 位于类路径上,你将会得到一个池连接工厂。

11.2 MongoDB

MongoDB 是一个开源的 NoSQL 文档型数据库,它使用类似 JSON 的模式,而不是传统的基于表的关系数据。Spring Boot 为使用 MongoDB 提供了一些便利,包括 spring-boot-starter-data-mongodbspring-boot-starter-data-mongodb-reactive Starters。

11.2.1 Connecting to a MongoDB Database

你可以注入一股自动配置的 org.springframework.data.mongodb.MongoDbFactory 来访问数据库。默认情况下,实例尝试连接到 MongoDB://localhost/test 上的 MongoDB 服务器。下面的例子展示了如何连接到 MongoDB 数据库:

import org.springframework.data.mongodb.MongoDbFactory;
import com.mongodb.DB;

@Component
public class MyBean {

    private final MongoDbFactory mongo;

    @Autowired
    public MyBean(MongoDbFactory mongo) {
        this.mongo = mongo;
    }

    // ...

    public void example() {
        DB db = mongo.getDb();
        // ...
    }

}

你可以设置 spring.data.mongodb.uri 属性来更改 URL 并配置其他设置,如复制集,如下面的示例所示:

spring.data.mongodb.uri=mongodb://user:secret@mongo1.example.com:12345,mongo2.example.com:23456/test

此外,只要你使用了 Mongo 2.x,可以指定主机/端口。比如,你可以在 application.properties 中声明以下设置:

spring.data.mongodb.host=mongoserver
spring.data.mongodb.port=27017

如果你已经定义了你自己的 MongoClient,它将被用来自动配置一个合适的 MongoDbFactory。支持 com.mongodb.MongoClientcom.mongodb.client.MongoClient

如果你使用 Mongo3.0 Java 驱动,则不再支持 spring.data.mongodb.hostspring.data.mongodb.port。在这种情况下,应该使用 spring.data.mongodb.uri 来提供所有的配置。

如果 spring.data.mongodb.port 未指定,使用的默认值为 27017。你可以从前面显示的示例中删除这一行。

如果你不使用 Spring Data Mongo,可以注入 com.mongodb.MongoClient。使用 MongoClient bean 而不是 MongoDbFactory。如果你想完全控制建立 MongoDB 连接,还可以声明自己的 MongoDbFactoryMongoClient bean。

如果你在使用响应式驱动程序,则 SSL 需要 Netty。如果 Netty 可用且工厂尚未定制,则自动配置将自动配置此工厂。

11.2.2 MongoTemplate

Spring Data MongoDB 提供了一个 MongoTemplate 类,它的设计与 Spring 的 JdbcTemplate 非常类似。与 JdbcTemplate 一样,Spring Boot 会自动配置一个 bean 来注入模板,如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final MongoTemplate mongoTemplate;

    @Autowired
    public MyBean(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    // ...

}

查看 MongoOperations Javadoc 获取详情。

11.2.3 Spring Data MongoDB Repositories

Spring Data 包括对 MongoDB 的存储库支持。与前面讨论的 JPA 存储库一样,基本原则是根据方法名自动构造查询。

实际上,Spring Data JPA 和 Spring Data MongoDB 共享相同的公共基础组件。你可以从前面的 JPA 示例开始,假设 City 现在是 Mongo 数据类而不是 JPA @Entity,那么它的工作方式是相同的,如下例所示:

package com.example.myapp.domain;

import org.springframework.data.domain.*;
import org.springframework.data.repository.*;

public interface CityRepository extends Repository<City, Long> {

    Page<City> findAll(Pageable pageable);

    City findByNameAndStateAllIgnoringCase(String name, String state);

}

可以使用 @EntityScan 注解自定义文档扫描位置。

有关 Spring Data MongoDB 的完整细节,包括它的富对象映射技术,参考它的参考文档

11.2.4 Embedded Mongo

Spring Boot 为嵌入式 Mongo 提供了自动配置。要在 Spring Boot 应用程序中使用它,添加对de.flapdoodle.embed:de.flapdoodle.embed.mongo 的依赖。

Mongo 监听的端口可以通过设置 spring.data.mongodb.port 属性进行配置。要使用随机分配的空闲端口,可使用值 0。MongoAutoConfiguration 创建的 MongoClient 会自动配置为使用随机分配的端口。

如果没有配置自定义端口,则默认情况下嵌入式支持使用随机端口(而不是27017)。

如果类路径上有 SLF4J,那么 Mongo 生成的输出将自动路由到名为 org.springframework.boot.autoconfigure.Mongo.embedded.EmbeddedMongo 的记录器。

你可以声明自己的 IMongodConfigIRuntimeConfig bean 来控制 Mongo 实例的配置和日志路由。可以通过声明 DownloadConfigBuilderCustomizer bean 自定义下载配置。

11.3 Neo4j

Neo4j 是一个开源的 NoSQL 图形数据库,它使用了一个由第一级关系连接的节点的富数据模型,这比传统的 RDBMS 方法更适合于大数据连接。Spring Boot 为使用 Neo4j 提供了一些便利,包括 spring-boot-starter-data-neo4j “Starter”。

11.3.1 Connecting to a Neo4j Database

要访问Neo4j服务器,可以插入自动配置的 org.Neo4j.ogm.session.session。默认情况下,该实例尝试使用 Bolt 协议连接到 localhost:7687 上的Neo4j服务器。下面的示例演示如何注入Neo4j Session

@Component
public class MyBean {

    private final Session session;

    @Autowired
    public MyBean(Session session) {
        this.session = session;
    }

    // ...

}

你可以通过设置 spring.data.neo4j.* 来配置要使用的 uri 和凭据属性,如下例所示:

spring.data.neo4j.uri=bolt://my-server:7687
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=secret

你可以通过添加 org.neo4j.ogm.config 来完全控制会话的创建。配置 bean 或 org.neo4j.ogm.session.SessionFactory bean。

11.3.2 Using the Embedded Mode

如果你为你的应用程序添加了 org.neo4j:neo4j-ogm-embedded-driver 依赖,Spring Boot 会自动配置一个进程内的 Neo4j 嵌入式实例,该实例在应用程序关闭时不会保留任何数据。

因为内嵌的 Neo4j OGM 驱动程序本身不提供 Neo4j 内核,所以必须声明 org.neo4j:neo4j 就是依赖你自己。有关兼容版本的列表,参阅 Neo4j OGM 文档

当类路径上有多个驱动程序时,嵌入式驱动程序优先于其他驱动程序。可以通过设置 spring.data.neo4j.embedded.enabled=false 显式禁用嵌入模式。

如果嵌入式驱动程序和 Neo4j 内核位于上述类路径上,则数据 Neo4j 测试会自动使用嵌入式 Neo4j 实例。

你可以通过在配置中提供到数据库文件的路径来启用嵌入式模式的持久性,例如, spring.data.neo4j.uri=file://var/tmp/graph.db

11.3.3 Using Native Types

Neo4j OGM 可以将一些类型(如 java.time.* 中的类型)映射到基于字符串的属性或 Neo4j 提供的本地类型之一。出于向后兼容性的原因,Neo4j OGM 的默认设置是使用基于字符串的表示。若要使用原类型,要添加对 org.neo4j:neo4j-ogm-bolt-native-typesorg.neo4j:neo4j-ogm-embedded-native-types的依赖项,并配置 spring.data.neo4j.use-native-types 属性,如下例所示:

spring.data.neo4j.use-native-types=true

11.3.4 Neo4jSession

默认情况下,如果你运行的是 web 应用程序,则会话将绑定到整个请求处理的线程(即,它使用"在视图中打开会话"模式)。如果不需要此行为,可将以下行添加到 application.properties 文件中:

spring.data.neo4j.open-in-view=false

11.3.5 Spring Data Neo4j Repositories

Spring Data 括对 Neo4j 的存储库支持。

与许多其他 Spring Data 模块一样,Spring Data Neo4j 与 Spring Data JPA 共享公共基础组件。你可以使用前面的 JPA 示例,并将 City 定义为 Neo4j OGM @NodeEntity 而不是 JPA @Entity,并且存储库抽象以相同的方式工作,如下面的示例所示:

package com.example.myapp.domain;

import java.util.Optional;

import org.springframework.data.neo4j.repository.*;

public interface CityRepository extends Neo4jRepository<City, Long> {

    Optional<City> findOneByNameAndState(String name, String state);

}

spring-boot-starter-data-neo4j “Starter” 支持存储库支持和事务管理。你可以通过在 @Configuration-bean上分别使用 @EnableNeo4jRepositories@EntityScan 来自定义查找存储库和实体的位置。

有关 Spring Data Neo4j 的完整细节,包括它的对象映射技术,参考参考文档

11.4 Solr

Apache Solr 是一个搜索引擎。Spring Boot 为 Solr5 客户端库提供了基本的自动配置,并在此基础上提供了 Spring Data Solr 提供的抽象。以 spring-boot-starter-data-solr “Starter” 用于收集依赖项。

11.4.1 Connecting to Solr

可以像注入任何其他 Spring bean 一样注入一个自动配置的 SolrClient 实例。默认情况下,实例尝试连接到 localhost:8983/solr 上的服务器。下面的例子展示了如何注入一个Solr bean:

@Component
public class MyBean {

    private SolrClient solr;

    @Autowired
    public MyBean(SolrClient solr) {
        this.solr = solr;
    }

    // ...

}

如果你添加自己的 SolrClient 类型的 @Bean,那么它将取代默认值。

11.4.2 Spring Data Solr Repositories

Spring Data 包括对 Apache Solr 的存储库支持。与前面讨论的 JPA 存储库一样,基本原则是根据方法名自动构造查询。

事实上,Spring Data JPA 和 Spring Data Solr 共享相同的公共基础组件。你可以从前面的 JPA 示例开始,假设 City 现在是一个 @SolrDocument 类,而不是一个 JPA @Entity,那么它的工作方式是相同的。

有关 Spring Data Solr 的完整详细信息,参阅参考文档

11.5 Elasticsearch

Elasticsearch 是一个开源、分布式、RESTful 风格的搜索和分析引擎。Spring Boot为Elasticsearch提供了基本的自动配置。

Spring Boot支持多个客户端:

  • Java “低级” 和 “高级” REST 客户端

  • Spring Data Elasticsearch 提供的 ReactiveElasticsearchClient

传输客户端仍然可用,但它的支持在 Spring Data Elasticsearch 和 Elasticsearch 本身中已被弃用。它将在以后的版本中删除。Spring Boot 提供了一个专用的 “Starter”,spring-boot-starter-data-elasticsearch

Jest 客户端也被弃用,因为 Elasticsearch 和 Spring Data Elasticsearch 都为 REST 客户机提供了官方支持。

11.5.1 Connecting to Elasticsearch using REST clients

Elasticsearch 提供了 两个不同的 REST 客户端,你可以使用"低级客户端"和"高级客户端"来查询集群。

如果你在类路径上有 org.elasticsearch.client:elasticsearch-rest-client依赖,Spring Boot 将自动配置并注册一个 RestClientbean,该 bean 的默认目标是 localhost:9200。你可以进一步调整如何配置 RestClient,如下面的示例所示:

spring.elasticsearch.rest.uris=https://search.example.com:9200
spring.elasticsearch.rest.read-timeout=10s
spring.elasticsearch.rest.username=user
spring.elasticsearch.rest.password=secret

你也可以注册任意数量的 bean,这些 bean 实现 RestClientBuilderCustomizer,用于更高级的定制。要完全控制注册,需要定义一个 RestClient bean。

如果你有 org.elasticsearch.client:elasticsearch-rest-high-level-client 依赖于,Spring Boot 将自动配置一个 RestHighLevelClient,该客户端封装了所有现有的 RestClient bean,重用其 HTTP 配置。

11.5.2 Connecting to Elasticsearch using Reactive REST clients

Spring Data Elasticsearch 以响应的方式发送 ReactiveElasticsearchClient 来查询 Elasticsearch 实例。它构建在 WebFlux 的 WebClient 之上,因此 spring-boot-starter-elasticsearchspring-boot-starter-webflux 依赖项都有助于实现这种支持。

默认情况下,Spring Boot 会自动配置并注册一个 ReactiveElasticsearchClient bean,目标是 localhost:9200。你可以进一步调整它的配置,如下面的例子所示:

spring.data.elasticsearch.client.reactive.endpoints=search.example.com:9200
spring.data.elasticsearch.client.reactive.use-ssl=true
spring.data.elasticsearch.client.reactive.socket-timeout=10s
spring.data.elasticsearch.client.reactive.username=user
spring.data.elasticsearch.client.reactive.password=secret

如果配置属性还不够,并且你希望完全控制客户端配置,可以注册一个定制的 ClientConfiguration bean。

11.5.3 Connecting to Elasticsearch using Jest

既然 Spring Boot 支持官方的 RestHighLevelClient,那么对 Jest 的支持是将被弃用。

如果类路径上有 Jest,你可以注入一个自动配置的 JestClient,默认情况下它的目标是 localhost:9200。你可以进一步调整如何配置客户端,如下例所示:

spring.elasticsearch.jest.uris=https://search.example.com:9200
spring.elasticsearch.jest.read-timeout=10000
spring.elasticsearch.jest.username=user
spring.elasticsearch.jest.password=secret

你也可以注册任意数量的 bean,这些 bean 实现 HttpClientConfigBuilderCustomizer 以实现更高级的定制。下面的例子优化额外的HTTP设置:

static class HttpSettingsCustomizer implements HttpClientConfigBuilderCustomizer {

    @Override
    public void customize(HttpClientConfig.Builder builder) {
        builder.maxTotalConnection(100).defaultMaxTotalConnectionPerRoute(5);
    }

}

要完全控制注册,需要定义一个 JestClient bean。

11.5.4 Connecting to Elasticsearch by Using Spring Data

为了连接到 Elasticsearch,必须定义、通过 Spring Boot 自动配置或由应用程序手动提供 RestHighLevelClient bean(参阅前面的部分)。有了这个配置,就可以像注入任何其他 Spring bean 一样注入 ElasticsearchRestTemplate,如下例所示:

@Component
public class MyBean {

    private final ElasticsearchRestTemplate template;

    public MyBean(ElasticsearchRestTemplate template) {
        this.template = template;
    }

    // ...

}

spring-data-elasticsearch 和使用 WebClient(通常是 spring-boot-starter-webflux)所需的依赖项存在的情况下,Spring Boot 还可以将 ReactiveElasticsearchClientReactiveElasticsearchTemplate 自动配置为 bean。它们和其他 REST 客户机的是等效的。

11.5.5 Spring Data Elasticsearch Repositories

Spring Data 包括对 Elasticsearch 的存储库支持。与前面讨论的 JPA 存储库一样,基本原则是根据方法名自动构造查询。

事实上,Spring Data JPA 和 Spring Data Elasticsearch 都共享相同的基础组件。你可以使用前面的 JPA 示例,并假设 City 现在是一个 Elasticsearch @Document 类而不是 JPA @Entity,它以相同的方式工作。

有关 Spring Data Elasticsearch 的完整细节,参考参考文档

Spring Boot 支持经典的和响应式的E lasticsearch 存储库,使用E lasticsearchRestTemplate 或响应式的 ElasticSearchTemplate bean。由于存在所需的依赖项,这些 bean 很可能是由 Spring Boot 自动配置的。

如果希望使用自己的模板来支持 Elasticsearch 存储库,可以添加自己的E lasticsearchRestTemplateElasticsearchOperations @Bean,只要它名为 "elasticsearchTemplate"。这同样适用于 ReactiveElasticsearchTemplateReactiveElasticsearchOperations ,bean 名为 "reactiveElasticsearchTemplate"

你可以选择使用以下属性禁用存储库支持:

spring.data.elasticsearch.repositories.enabled=false

11.6 Cassandra

Cassandra 是一个开源的分布式数据库管理系统,设计用于处理跨服务器的大量数据。Spring Boot 为 Cassandra 提供了自动配置,并在其上提供 Spring Data Cassandra提 供的抽象。以 spring-boot-starter-data-cassandra “Starter” 的方式收集依赖项。

11.6.1 Connecting to Cassandra

你您可以注入一个自动配置的 CassandraTemplate 或 Cassandra Session 实例,就像你注入任何其他 Spring Bean 一样。spring.data.cassandra.* 属性可用于自定义连接。通常,你可以提供 keyspace-namecontract-points 属性,如下面的示例所示:

spring.data.cassandra.keyspace-name=mykeyspace
spring.data.cassandra.contact-points=cassandrahost1,cassandrahost2

你还可以注册任意数量的 bean,这些 bean 实现 ClusterBuilderCustomizer 以实现更高级的定制。

下面的代码展示了如何注入一个 Cassandra bean:

@Component
public class MyBean {

    private CassandraTemplate template;

    public MyBean(CassandraTemplate template) {
        this.template = template;
    }

    // ...

}

如果添加自己的 @Bean类型 CassandraTemplate,它将取代默认值。

11.6.2 Spring Data Cassandra Repositories

Spring Data 包括对 Cassandra 的基本存储库支持。目前,这比前面讨论的 JPA 存储库更受限制,需要使用 @Query 注解查找方法。

查看 参考文档 了解 Spring Data Cassandra。

11.7 Couchbase

Couchbase 是一个开源、分布式、多模型的 NoSQL 面向文档的数据库,它针对交互式应用程序进行了优化。Spring Boot 提供了对 Couchbase 的自动配置,以及 Spring Data Couchbase 在其上提供的抽象。有 spring-boot-starter-data-couchbasespring-boot-starter-data-couchbase-reactive “Starts” 收集依赖项。

11.7.1 Connecting to Couchbase

可以通过添 加Couchbase SDK 和一些配置获得 BucketClusterspring.couchbase.* 属性可用于自定义连接。一般情况下,需要提供地址、bucket名称和密码,如下例所示:

spring.couchbase.bootstrap-hosts=my-host-1,192.168.1.123
spring.couchbase.bucket.name=my-bucket
spring.couchbase.bucket.password=secret

你至少需要提供地址,在这种情况下,bucket 名称是默认的,密码是一个空字符串。或者,可以定义自己的 org.springframework.data.couchbase.config.CouchbaseConfigurer @Bean 来控制整个配置。

还可以定制一些 CouchbaseEnvironment 设置。例如,下面的配置更改了用于打开新桶并启用 SSL 支持的超时:

spring.couchbase.env.timeouts.connect=3000
spring.couchbase.env.ssl.key-store=/location/of/keystore.jks
spring.couchbase.env.ssl.key-store-password=secret

检查 spring.couchbase.env.* 属性获取更多详细信息。

11.7.2 Spring Data Couchbase Repositories

Spring Data 包括对 Couchbase 的存储库支持。有关 Spring Data Couchbase 的完整细节,参阅参考文档

你可以像使用任何其他 Spring Bean 一样注入一个自动配置的 CouchbaseTemplate 实例,只要有一个可用的缺省 CouchbaseConfigurer (在启用 Couchbase 支持时发生,如前所述)。

下面的例子展示了如何注入一个 Couchbase bean:

@Component
public class MyBean {

    private final CouchbaseTemplate template;

    @Autowired
    public MyBean(CouchbaseTemplate template) {
        this.template = template;
    }

    // ...

}

你可以在自己的配置中定义一些 bean 来覆盖这些由自动配置提供的 bean:

  • 名为 couchbaseTemplateCouchbaseTemplate @Bean
  • 名为 couchbaseIndexManagerIndexManager @Bean
  • 名为 couchbaseCustomConversionsCustomConversions @Bean

为了避免在自己的配置中硬编码这些名称,可以重用 Spring Data Couchbase 提供的 BeanNames。例如,你可以自定义要使用的转换器,如下所示:

@Configuration(proxyBeanMethods = false)
public class SomeConfiguration {

    @Bean(BeanNames.COUCHBASE_CUSTOM_CONVERSIONS)
    public CustomConversions myCustomConversions() {
        return new CustomConversions(...);
    }

    // ...

}

如果你想完全绕过 Spring Data Couchbase 的自动配置,那么要提供自己的 org.springframework.data.couchbase.config.AbstractCouchbaseDataConfiguration 的实现。

11.8 LDAP

LDAP(轻量级目录访问协议)是一种开放的、与供应商无关的行业标准应用程序协议,用于通过 IP 网络访问和维护分布式目录信息服务。Spring Boot 为兼容的 LDAP 服务器提供自动配置,并支持来自 UnboundID 的嵌入式内存 LDAP 服务器。

LDAP 抽象是由 Spring Data LDAP 提供的。用 spring-boot-starter-data-ldap “Starter” 收集依赖项。

11.8.1 Connecting to an LDAP Server

为了连接到 LDAP 服务器,确保加载了 spring-boot-starter-data-ldapspring-ldap-core 的依赖,然后在应用程序中声明服务器的 URL 属性,如下例所示:

spring.ldap.urls=ldap://myserver:1235
spring.ldap.username=admin
spring.ldap.password=secret

如果你需要自定义连接设置,可以使用 spring.ldap.basespring.ldap.base-environment 属性。

LdapContextSource 是根据这些设置自动配置的。如果需要对其进行自定义,例如使用 PooledContextSource ,则仍然可以注入自动配置的 LdapContextSource。确保将定制的 ContextSource 标记为 @Primary,以便自动配置的 LdapTemplate使用它。

11.8.2 Spring Data LDAP Repositories

Spring Data 包括对 LDAP 的存储库支持。有关 Spring Data LDAP 的完整细节,参阅参考文档

你还可以像注入任何其他Spring Bean一样注入一个自动配置的 LdapTemplate ,如下例所示:

@Component
public class MyBean {

    private final LdapTemplate template;

    @Autowired
    public MyBean(LdapTemplate template) {
        this.template = template;
    }

    // ...

}

11.8.3 Embedded In-memory LDAP Server

出于测试目的,Spring Boot 支持从 UnboundID 自动配置内存中的 LDAP 服务器。要配置服务器,要添加一个 com.unboundid:unboundid-ldapsdk 依赖项。并声明一个 spring.ldap.embedded.base-dn 属性,如下:

spring.ldap.embedded.base-dn=dc=spring,dc=io

可以定义多个 base-dn 值,但是,由于专有名称通常包含逗号,因此必须使用正确的符号来定义它们。

在 yaml 文件中,可以使用 yaml 列表符号:

spring.ldap.embedded.base-dn:
  - dc=spring,dc=io
  - dc=pivotal,dc=io

在 properties 文件中,必须包含索引作为属性名称的一部分:

spring.ldap.embedded.base-dn[0]=dc=spring,dc=io
spring.ldap.embedded.base-dn[1]=dc=pivotal,dc=io

默认情况下,服务器在一个随机端口上启动,并触发常规的 LDA P支持。不需要指定 spring.ldap.urls 属性。

如果有一个 schema.ldif 文件在你的类路径上,那么它会被用于初始化服务器。如果希望从不同的资源加载初始化脚本,还可以使用 spring.ldap.embedded.ldif 属性。

默认情况下,使用标准模式验证 LDIF 文件。你可以通过设置 spring.ldap.embedded.validation.enabled 来完全关闭验证。如果有自定义属性,可以使用 spring.ldap.embedded.validation.schema 来定义自定义属性类型或对象类。

1.9 InfluxDB

InfluxDB 是一个开放源码的时间序列数据库,用于快速、高可用性存储和检索时间序列数据,如操作监视、应用程序指标、物联网传感器数据和实时分析。

11.9.1 Connecting to InfluxDB

Spring Boot自动配置一个 InfluxDB 数据库实例,前提是 influxdb-java 客户端位于类路径上,并且设置了数据库的 URL,如下例所示:

spring.influx.url=https://172.0.0.1:8086

如果 InfluxDB 的连接需要用户和密码,可以设置 spring.influx.userspring.influx.password 属性。

InfluxDB 库依赖于 OkHttp。如果你需要调优 InfluxDB 使用的 http 客户端,你可以注册一个 InfluxDbOkHttpClientBuilderProvider bean。

12. Caching

Spring Framework 提供对透明添加缓存到应用中的支持。抽象的核心是将缓存应用于方法,从而减少了基于缓存中可用信息的执行次数。缓存逻辑是透明地应用的,对调用程序没有任何干扰。只要通过 @EnableCaching 注注解用缓存支持,Spring Boot 就会自动配置缓存基础组件。

有关更多细节,参阅 Spring 框架参考 的相关部分。

简而言之,将缓存添加到服务的操作中就像将相关注解添加到其方法中一样简单,如下面的示例所示:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class MathService {

    @Cacheable("piDecimals")
    public int computePiDecimal(int i) {
        // ...
    }

}

该示例演示了在一个可能开销很大的操作上使用缓存。在调用 computePiDecimal 之前,抽象会在 piDecimals 缓存中查找与 i 参数匹配的条目。如果找到一个条目,缓存中的内容会立即返回给调用者,而方法不会被调用。否则,将调用该方法,并在返回值之前更新缓存。

你还可以透明地使用标准 JSR-107 (JCache)注解(比如 @CacheResult)。但是,强烈建议你不要混合使用 Spring 缓存和 JCache 注释。

你如果不添加任何特定的缓存库,Spring Boot 会自动配置一个在内存中使用并发映射的简单提供程序。当需要缓存时(如上例中的 piDecimals),此提供程序将为你创建缓存。该提供程序并不真正推荐用于生产,但它对于入门和确保你了解这些特性非常有用。当你决定使用缓存提供程序时,要先阅读其文档以了解如何配置应用程序使用的缓存。几乎所有提供程序都要求你显式配置在应用程序中使用的每个缓存。有些提供了一种自定义由 spring.cache.cache-names 属性定义的默认缓存的方法。

还可以从缓存中透明地更新删除数据。

12.1 Supported Cache Providers

抽象缓存不提供实际的存储,而是依赖于 org.springframework.cache.Cache 实现的抽象和 org.springframework.cache.CacheManager 接口。

如果你还没有定义类型为 CacheManager 的 bean 或名为 cacheResolverCacheResolver (参阅 CachingConfigurer), Spring Boot 将尝试检测以下提供程序(按照指定的顺序):

  1. Generic
  2. JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, 其它)
  3. EhCache 2.x
  4. Hazelcast
  5. Infinispan
  6. Couchbase
  7. Redis
  8. Caffeine
  9. Simple

可以通过设置 spring.cache.type 来强制特定的缓存提供程序。类型属性。如果需要在某些环境(如测试)中完全禁用缓存,使用此属性。

使用 spring-boot-starter-cache “Starter” 快速添加基本缓存依赖项。启动器引入了 spring-context-support。如果手动添加依赖项,则必须包含 spring-context-support,以便使用 JCache EhCache2.x,或 Caffeine 支持。

如果 CacheManager 是由 Spring Boot 自动配置的,则可以通过公开实现 CacheManagerCustomizer 接口的 bean,在完全初始化之前进一步优化其配置。以下示例设置了一个标志,表示应将空值传递给基础映射:

@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
    return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
        @Override
        public void customize(ConcurrentMapCacheManager cacheManager) {
            cacheManager.setAllowNullValues(false);
        }
    };
}

在前面的示例中,需要自动配置的 ConcurrentMapCacheManager。如果不是这样(你提供了自己的配置或自动配置了其他缓存提供程序),则不会调用自定义程序。你可以拥有任意数量的自定义项,也可以使用 @orderOrdered 对它们排序。

12.1.1 Generic

如果上下文中定义了不止一个 org.springframework.cache.Cache bean,那么就会使用通用缓存。Spring Boot 将会创建一个包装所有 bean 的 CacheManager 类型。

12.1.2 JCache(JSR-107)

JCache 是通过类路径上的 javax.cache.spi.CachingProvider(即类路径上存在一个符合 JSR-107 的缓存库)来引导的,JCacheCacheManagerspring-boot-starter-cache “starter” 提供。提供了各种兼容的库,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供了依赖性管理。也可以添加任何其他兼容库。

可能会出现多个提供程序,在这种情况下,必须显式指定提供程序。即使 JSR-107 标准没有强制使用标准化的方式来定义配置文件的位置,Spring Boot 也会尽其所能地使用实现细节来设置缓存,如下例所示:

# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.acme.MyCachingProvider
spring.cache.jcache.config=classpath:acme.xml

当缓存库同时提供本地实现和 JSR-107 支持时,Spring Boot 更喜欢使用 JSR-107 支持,因此如果切换到不同的 JSR-107 实现,也可以使用相同的特性。

Spring Boot 一般支持 Hazelcast。如果有一个 HazelcastInstance 可用,那么它会自动被 CacheManager 重用,除非指定了 spring.cache.jcache.config 属性。

有两种方法可以自定义底层的 javax.cache.cacheManager

  • 可以通过设置 spring.cache.cache-names 属性让缓存在启动时被创建,如果你定义了一个 javax.cache.configuration.Configuration bean,则使用它来自定义它们。
  • 使用 CacheManager的引用调用 org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer bean 进行完全定制。

如果定义了一个标准的 javax.cache.CacheManager bean,它将自动包装在抽象所期望的 org.springframework.cache.CacheManager 实现中。不会对其应用进行进一步的自定义。

12.1.3 EhCache 2.x

如果可以在类路径的根目录中找到名为 EhCache.xml 的文件,则使用 EhCache2.x 缓存。如果找到 EhCache 2.x,则使用 spring-boot-starter-cache “starter” 提供的 EhCacheCacheManager 来引导缓存管理器。此外还可以提供备用配置文件,如下例所示:

spring.cache.ehcache.config=classpath:config/another-config.xml

12.1.4 Hazelcast

Spring Boot 一般支持 Hazelcast,如果 HazelcastInstance 已经被自动配置,那么它将被自动包装在 CacheManager 中。

12.1.5 Infinispan

Infinispan 没有默认的配置文件位置,因此必须显式地指定它。否则,将使用默认的引导程序。

spring.cache.infinispan.config=infinispan.xml

通过设置 spring.cache.cache-names 在启动时创建缓存。如果定义了自定义的 ConfigurationBuilder bean,则使用它定制缓存。

在 Spring Boot 中对 Infinispan 的支持仅限于嵌入式模式,这是非常基本的。如果你想要更多的选项,应该使用官方的 Infinispan Spring Boot starter。有关更多细节,参见 Infinispan 文档

12.1.6 Couchbase

如果 Couchbase Java 客户端和 couchbase-spring-cache 实现可用并且 Couchbase 已配置,则 Couchbas e缓存管理器将自动配置。还可以通过设置 spring.cache.cache-names 属性在启动时创建其他缓存。这些缓存在自动配置的 Bucke t上操作。还可以使用自定义程序在另一个 Bucket 上创建其他缓存。假设你需要主 Bucket 上的两个缓存(cache1cache2)和其它 Bucket 上的一个(cache3)缓存(自定义生存时间为2秒)。你可以通过配置创建前两个缓存,如下所示:

spring.cache.cache-names=cache1,cache2

然后可以定义一个 @Configuration 类来配置额外的 Bucketcache3 缓存,如下所示:

@Configuration(proxyBeanMethods = false)
public class CouchbaseCacheConfiguration {

    private final Cluster cluster;

    public CouchbaseCacheConfiguration(Cluster cluster) {
        this.cluster = cluster;
    }

    @Bean
    public Bucket anotherBucket() {
        return this.cluster.openBucket("another", "secret");
    }

    @Bean
    public CacheManagerCustomizer<CouchbaseCacheManager> cacheManagerCustomizer() {
        return c -> {
            c.prepareCache("cache3", CacheBuilder.newInstance(anotherBucket())
                    .withExpiration(2));
        };
    }

}

这个示例配置重用了通过自动配置创建的 Cluster

12.1.7 Redis

如果 Redis 可用且已配置,则自动配置 RedisCacheManager。通过设置 spring.cache,可以在启动时创建额外的缓存。可以使用 spring.cache.redis.* 配置 spring.cache.cache-names 和缓存默认值。例如,下面的配置创建了时间为 10分钟 的 cache1cache2 缓存:

spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=600000

默认情况下,添加一个键前缀,这样,如果两个单独的缓存使用相同的键,Redis 也没有重复的键,也不会返回无效的值。如果你创建自己的 RedisCacheManager,强烈建议保持启用此设置。

你可以通过添加自己的 RedisCacheConfiguration @Bean 来完全控制缺省配置。如果你正在寻找自定义默认序列化策略的方法,那么这将非常有用。

如果需要对配置进行更多控制,可以考虑注册一个 RedisCacheManagerBuilderCustomizerbean。下面的例子显示了一个定制器,它为 cache1cache2 配置了一个特定的生存时间:

@Bean
public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
    return (builder) -> builder
            .withCacheConfiguration("cache1",
                    RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)))
            .withCacheConfiguration("cache2",
                    RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)));

}

12.1.8 Caffeine

Caffeine 是 Java8 对 Guava 缓存的重写,取代了对 Guava 的支持。如果有 Caffeine,Spring Boot 会自动配置 CaffeineCacheManager (由 spring-boot-starter-cache “Starter” 提供)。通过设置 spring.cache.cache-names ,可以在启动时创建缓存,而且该属性可以通过以下方式之一(按指定的顺序)进行自定义:

  • spring.cache.caffine.spec 定义的缓存规范
  • 定义的一个 com.github.benmanes.caffine.cache.CaffeineSpec bean
  • 定义的一个 com.github.benmanes.caffee.cache.Caffeine bean

例如,下面的配置创建了最大大小为 500、生存时间为 10分钟 的 cache1cache2 缓存:

spring.cache.cache-names=cache1,cache2
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s

如果定义了 com.github.benmanes.caffine.cache.CacheLoader bean,它将自动关联到 CaffeineCacheManager。由于 CacheLoader 将与缓存管理器管理的所有缓存关联,因此必须将其定义为 CacheLoader<Object,Object>,因为自动配置忽略任何其他泛型类型。

12.1.9 Simple

如果找不到其他的缓存提供程序,则可以配置一个使用 ConcurrentHashMap 作为缓存存储的简单实现。如果应用程序中没有缓存库,这是默认设置。默认情况下,缓存是根据需要创建的,但是你可以通过设置 cache-names 属性来限制可用缓存的列表。例如,如果你只想要 cache1cache 2缓存,可以设置 cache-names 属性如下:

spring.cache.cache-names=cache1,cache2

如果你这么做,并且你的应用程序使用了未列出的缓存,那么它在运行时需要缓存时就会失败,但在启动时不会失败。这与使用未声明的缓存时真正的缓存提供程序的行为类似。

12.1.10 None

当配置中出现 @EnableCaching 时,也需要适当的缓存配置。如果需要在某些环境中完全禁用缓存,则强制将缓存类型设置为 none 以使用无操作实现,如下面的示例所示:

spring.cache.type=none

13. Messaging

Spring Frameword 为消息传递系统的集成提供了广泛的支持,从使用 JmsTemplate 的 JMS API 的简化使用到异步接收消息的完整基础结构。Spring AMQP 为高级消息队列协议提供了类似的功能集。Spring Boot 还为 RabbitTemplate 和 RabbitMQ 提供了自动配置选项。Spring WebSocket 包含了对 STOMP 消息的支持,Spring Boot 通过启动器和少量的自动配置支持这一点。Spring Boot 还支持 Apache Kafka。

13.1 JMS

javax.jms.ConnectionFactory 接口提供了创建 javax.jms.Connection 与 JMS 代理交互的标准方法。尽管 Spring 需要一个 ConnectionFactory 来处理 JMS,但你通常不需要自己直接使用它,而是可以依赖于更高级别的消息传递抽象。(有关详细信息,参阅 Spring 框架参考文档的相关部分。)Spring Boot 还自动配置发送和接收消息所需的基础组件。

13.1.1 ActiveMQ Support

ActiveMQ 可用时,Spring Boot 还可以配置 ConnectionFactory。如果存在代理,则会自动启动并配置嵌入式代理(假设没有通过配置指定代理 URL)。

如果使用了 spring-boot-starter-activemq,则会提供连接或嵌入 ActiveMQ 实例所需的依赖项,以及与 JMS 集成的 Spring 基础组件。

ActiveM Q配置由 spring.activemq.* 中的外部配置属性控制。例如,你可以在 application.properties 中声明以下部分:

spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret

默认情况下,CachingConnectionFactory 将本地 ConnectionFactory 包装起来,并使用一些合理的设置,你可以通过 spring.jms.* 中的外部配置属性来控制这些设置。

spring.jms.cache.session-cache-size=5

如果你希望使用本机池,可以通过向 org.messaginghub:pooled-jms 添加一个依赖项来实现,并相应地配置 JmsPoolConnectionFactory,如下面的示例所示:

spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50

有关更多受支持的选项,参见 ActiveMQProperties。你还可以注册任意数量的 bean,这些 bean 实现 ActiveMQConnectionFactoryCustomizer 以实现更高级的定制。

默认情况下,ActiveMQ 会创建一个目的地(如果它还不存在),以便根据它们提供的名称解析目的地。

13.1.2 Artemis Support

当 Spring Boot 检测到类路径下的 Attemis 可用时,它会自动配置好 ConnectionFactory 。如果存在代理,则会自动启动并配置嵌入式代理(除非已显式设置了 mode 属性)。受支持的模式包括嵌入式模式(为了明确说明需要嵌入式代理,如果代理在类路径上不可用,则会出现错误)和本地模式(为了使用 netty 传输协议连接到代理)。配置后一个代理时,Spring Boot 会配置一个 ConnectionFactory,它使用默认设置连接本地机器上运行的代理。

spring-boot-starter=antemis 提供了连接到现有的 Artemis 实例的必要依赖项,以及与 JMS 集成的 Spring 基础组件。将 org.apache.activemq:artemis-jms-server 提交到你的应用中可以允许你使用嵌入式模式。

可以使用 spring.artemis.* 属性控制 Artemis 配置。比如,你可以在 application.properties 上声明以下内容:

spring.artemis.mode=native
spring.artemis.host=192.168.1.210
spring.artemis.port=9876
spring.artemis.user=admin
spring.artemis.password=secret

在嵌入式模式中,你可以启动持久化和列出可用的代理之间的距离。可以将它们指定为用逗号分隔的列表项,然后用默认选项创建它们,或者你可以定义一个 org.apache.activemq.artemis.jms.server.config.JMSQueueConfigurationorg.apache.activemq.artemis.jms.server.config.TopicConfiguration 类型的 bean分别用于高级队列和主题配置。

默认情况下,CachingConnectionFactory 会通过 spring.jms.* 中合理的值来封装本地的 ConnectionFactory

spring.jms.cache.session-cache-size=5

如果你想使用本地池,可以添加 org.messaginghub:pooled-jms 依赖,并且配置好对应的 JmsPollConecctionFactory ,如下例所示:

spring.artemis.pool.enabled=true
spring.artemis.pool.max-connections=50

可以查看 ArtemisProperties 获取更多支持的选项参数。

目的地根据它们的名称解析,并不涉及 JNDI 的查找,可以使用 Artemis 配置中的 name 属性或通过配置提供的名称。

13.1.3 Using a JNDI ConnectionFactory

如果你在应用服务器上运行应用程序,Spring Boot 会尝试使用 JNDI 来定位 JMS 的 ConnectionFactory。默认情况下,它会检查 java:/JmsXA 和j ava:/XAConnectionFactory 位置。你可以使用 spring.jms-jndi-name 指定另一个位置如下例所示:

spring.jms.jndi-name=java:/MyConnectionFactory

13.1.4 Send a Message

Spring 的 JmsTemplate 是自动配置的,你可以直接将其自动装配到自己的 bean 中,如下面的示例所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final JmsTemplate jmsTemplate;

    @Autowired
    public MyBean(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    // ...

}

可以以类似的方式注入 JmsMessagingTemplate。如果定义了 DestinationResolverMessageConverte r bean,它将自动关联到自动配置的 JmsTemplate

13.1.5 Receiving a Message

当 JMS 基础组件存在时,任何 bean 都可以使用 @JmsListener 注解创建监听器端点。如果没有 JmsListenerContainerFactory 被定义,则会自动配置默认的 JmsListenerContainerFactory 。如果定义了 DestinationResolverMessageConverte r bean,它将自动关联到默认工厂。

默认情况下,默认工厂是事务性的。如果你在一个存在 JtaTransactionManager 的基础组件中运行,默认情况下它与监听器容器相关联。如果没有,则启用 sessionTransacted 标志。在后一种情况下,可以通过在监听器方法(或其委托)上添加 @Transactional 来将本地数据存储事务与传入消息的处理关联起来。这将确保在本地事务完成后确认传入消息,还包括发送在同一 JMS 会话上执行的响应消息。

下面的组件在 someQueue 目的地创建一个监听器端点:

@Component
public class MyBean {

    @JmsListener(destination = "someQueue")
    public void processMessage(String content) {
        // ...
    }

}

查看 @EnableJms 文档 获取更多信息。

如果你需要创建更多的 JmsListenerContainerFactory 实例或者覆盖默认实例,Spring Boot将提供一个 DefaultJmsListenerContainerFactoryConfigurer,可以使用与自动配置的设置相同的设置来初始化 DefaultJmsListenerContainerFactory

例如,下面的示例展示了另一个使用特定 MessageConverter 的工厂:

@Configuration(proxyBeanMethods = false)
static class JmsConfiguration {

    @Bean
    public DefaultJmsListenerContainerFactory myFactory(
            DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, connectionFactory());
        factory.setMessageConverter(myMessageConverter());
        return factory;
    }

}

然后你可以使用工厂在任何 @JmsListener 注解方法:

@Component
public class MyBean {

    @JmsListener(destination = "someQueue", containerFactory="myFactory")
    public void processMessage(String content) {
        // ...
    }

}

13.2 AMQP

高级消息队列协议(AMQP)是一种与平台无关的、面向消息中间件的线程级别协议。Spring AMQP 项目将核心 Spring 概念应用于基于 AMQP 的消息传递解决方案的开发。Spring Boot 为通过 RabbitMQ 使用 AMQP 提供了一些便利,包括 spring-boot-starter-amqp “Starter”。

13.2.1 RabbitMQ support

RabbitMQ 是一种基于 AMQP 协议的轻量级、可靠、可伸缩和可移植的消息代理。Spring 使用 RabbitMQ 通过 AMQP 协议进行通信。

外部配置 spring.rabbitmq.* 控制 RabbitMQ 配置。比如,你可以在 application.properties 中声明以下属性:

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=secret

此外,你也可以使用 address 属性定义连接:

spring.rabbitmq.addresses=amqp://admin:secret@localhost

如果上下文中存在 ConnectionNameStrategy bean,它将被自动配置的 ConnectionFactory 用于创建连接。有关更多受支持的选项,参见 RabbitProperties

查看 理解使用 RabbitMQ 协议的 AMQP 获取更多内容。

13.2.2 Sending a Message

Spring 的 AmqpTemplateAmqpAdmin 是自动配置的,您你以直接将它们自动装配到自己的 bean 中,如下面的示例所示:

import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final AmqpAdmin amqpAdmin;
    private final AmqpTemplate amqpTemplate;

    @Autowired
    public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) {
        this.amqpAdmin = amqpAdmin;
        this.amqpTemplate = amqpTemplate;
    }

    // ...

}

可以以类似的方式注入 RabbitMessagingTemplate。如果定义了 MessageConverter bean,它将自动关联到自动配置的 AmqpTemplate

如果需要,任何定义为 bean 的 org.springframework.amqp.core.Queue 都会自动用于在 RabbitMQ 实例上声明相应的队列。

如果要重复操作,可以在 AmqpTemplate 上启用重试(例如,在代理连接丢失的情况下):

spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=2s

默认情况下重试是被禁止的。你还可以通过声明一个 RabbitRetryTemplateCustomizer bean 以编程的方式定制 RetryTemplate

13.2.3 Receiving a Message

当 Rabbit 基本组件存在时,任何 bean 都可以用 @RabbitListener 注解来创建监听器端点。如果尚未定义 RabbitListenerContainerFactory,则会自动配置默认的 SimpleRabbitListenerContainerFactory,你可以使用 spring.rabbitmq.listener.type 属性切换到直接容器。如果定义了 MessageConverterMessageRecoverer bean,它将自动与默认工厂关联。

下列展示了 someQueue 队列创建了一个监听器端点的简单组件:

@Component
public class MyBean {

    @RabbitListener(queues = "someQueue")
    public void processMessage(String content) {
        // ...
    }

}

查看 @EnableRabbit 文档 获取更多内容。

如果你需要创建多个 RabbitListenerContainerFactory 实例或者你想覆盖默认的,Spring Boot 提供了SimpleRabbitListenerContainerFactoryConfigurerDirectRabbitListenerContainerFactoryConfigurer,你可以用它来初始化一个与 SimpleRabbitListenerContainerFactoryDirectRabbitListenerContainerFactory 相同的设置工厂使用的自动配置。

选择哪种容器类型并不重要。这两个 bean 会通过自动配置暴露出来。

例如,下面的配置类公开了另一个使用特定 MessageConverter 的工厂:

@Configuration(proxyBeanMethods = false)
static class RabbitConfiguration {

    @Bean
    public SimpleRabbitListenerContainerFactory myFactory(
            SimpleRabbitListenerContainerFactoryConfigurer configurer) {
        SimpleRabbitListenerContainerFactory factory =
                new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        factory.setMessageConverter(myMessageConverter());
        return factory;
    }

}

然后你可以在任何 @RabbitListener 注解方法中使用该工厂,如下所示:

@Component
public class MyBean {

    @RabbitListener(queues = "someQueue", containerFactory="myFactory")
    public void processMessage(String content) {
        // ...
    }

}

你可以启用重试来处理侦啊今天器引发异常的情况。默认情况下,使用 RejectAndDontRequeueRecoverer,但你可以定义自己的消息恢复程序。当重试次数用尽时,消息将被拒绝,如果代理配置这样做,则消息将被丢弃或路由到死信交换。默认情况下,禁用重试。你还可以通过声明 RabbitRetryTemplateCustomizer bean以编程的方式自定义 RetryTemplate

默认情况下,如果禁用重试并且监听器引发异常,则会无限期重试传递。你可以通过两种方式修改此行为:将 defaultRequeueRejected 属性设置为false,进行零次重新传递,或者抛出AmqpRejectAndDontRequeueException,以表示应拒绝邮件。后者是启用重试并达到最大传递尝试次数时使用的机制。

13.3 Apache Kafka Support

spring-kafka 项目支持 Apache KafKa 的自动配置。

外部配置 spring.kafka.* 控制 RabbitMQ 配置。比如,你可以在 application.properties 中声明以下属性:

spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup

要在启动时创建主题,添加 NewTopic 类型的 bean。如果主题已经存在,则忽略该 bean。

查看 KafkaProperties 获取更多支持的选项参数。

13.3.1 Sending a Message

Spring 的 KafkaTemplate 是自动配置的,你可以直接在你自己的 bean 中自动装配它,如下面的例子所示:

@Component
public class MyBean {

    private final KafkaTemplate kafkaTemplate;

    @Autowired
    public MyBean(KafkaTemplate kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    // ...

}

如果定义了属性 spring.kafka.producer.transaction-id-prefix,则会自动配置 KafkaTransactionManager。此外,如果定义了 RecordMessageConverter bean,它将自动与自动配置的 KafkaTemplate 关联。

13.3.2 Receiving a Message

当 Apache Kafka 基本组件存在时,任何 bean 都可以使用 @KafkaListener 进行注释,以创建监听器端点。如果没有定义 KafkaListenerContainerFactory,则会自动配置默认值,并在 spring.kafka.listener.* 中定义键。

下面的组件在 someTopic 主题上创建一个监听器端点:

@Component
public class MyBean {

    @KafkaListener(topics = "someTopic")
    public void processMessage(String content) {
        // ...
    }

}

如果定义了 KafkaTransactionManager bean,它将自动与容器工厂关联。类似地,如果定义了 ErrorHandlerAfterRollbackProcessorConsumerAwareRebalanceListener bean,它将自动关联到默认工厂。

根据监听器类型,RecordMessageConverterBatchMessageConverter bean与默认工厂关联。如果批处理监听器只存在 RecordMessageConverter bean,则它将被包装在BatchMessageConverter中。

自定义的 ChainedKafkaTransactionManager 必须标记为 @Primary,因为它通常引用自动配置的 KafkaTransactionManager bean。

13.3.3 Kafka Streams

Spring 为 Kafka 提供了一个工厂 bean 来创建 StreamsBuilder 对象并管理其流的生命周期。只要 kafka-streams 在类路径上并且 Kafka 流通过 @EnableKafkaStreams 注解启用,Spring Boot就会自动配置所需的 KafkaStreamsConfiguration bean。

启用 Kafka 流意味着必须设置应用程序 id 和引导服务器。前者可以使用 spring.kafka.streams.application-id 配置,如果未设置,则默认为 spring.application.name 。后者可以全局设置,也可以专门为流重写。

使用专用属性时可以使用几个附加属性;可以使用 spring.Kafka.streams.properties 命名空间设置其他任意 Kafka 属性。有关详细信息,参见其他 Kafka 属性

要使用工厂 bean,只需将 StreamsBuilder 连接到 @Bean,如下例所示:

@Configuration(proxyBeanMethods = false)
@EnableKafkaStreams
public static class KafkaStreamsExampleConfiguration {

    @Bean
    public KStream<Integer, String> kStream(StreamsBuilder streamsBuilder) {
        KStream<Integer, String> stream = streamsBuilder.stream("ks1In");
        stream.map((k, v) -> new KeyValue<>(k, v.toUpperCase())).to("ks1Out",
                Produced.with(Serdes.Integer(), new JsonSerde<>()));
        return stream;
    }

}

默认情况下,将自动启动由它创建的 StreamBuilder 对象管理的流。可以使用 spring.kafka.streams.auto-startup 其行为。

13.3.4 Additional Kafka Properties

自动配置支持的属性记录在 appendix-application-properties.html 中。注意,在大多数情况下,这些属性(连字符或驼峰字符)直接映射到 Apache Kafka 点式属性(dotted properties)。有关详细信息,参阅 Apache Kafka文档。

前几个属性应用于所有组件(生产者、消费者、管理者和流),但如果希望使用不同的值,则可以在组件级别指定。Apache Kafka 指定重要性分别为 HIGH、MEDIUM 或 LOW 的属性。Spring Boot 自动配置支持所有高重要性属性、某些选定的中、低属性以及任何没有默认值的属性。

只有 Kafka 支持的属性的一个子集可以通过 KafkaProperties 类直接使用。如果要使用不直接支持的其他属性配置生产者或消费者,请使用以下属性:

spring.kafka.properties.prop.one=first
spring.kafka.admin.properties.prop.two=second
spring.kafka.consumer.properties.prop.three=third
spring.kafka.producer.properties.prop.four=fourth
spring.kafka.streams.properties.prop.five=fifth

上面将公共的 prop.one Kafka 属性设置为 first(适用于生产者、消费者和管理者),prop.two admin属性设置为 secondprop.three consumer 属性设置为 thirdprop.four producer属性设置为 fourthprop.five 流属性设置为 fifth

你还可以配置Spring Kafka JsonDeserializer ,如下所示:

spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.value.default.type=com.example.Invoice
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example,org.acme

类似地,你可以禁用 JsonSerializer 在 header 中发送类型信息的默认行为:

spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.properties.spring.json.add.type.headers=false

以这种方式设置的属性覆盖 Spring Boot 显式支持的任何配置项。

13.3.5 Testing with Embedded Kafka

Spring 为 Apache Kafka 提供了一种使用嵌入式 Apache Kafka 代理测试项目的便捷方法。要使用此功能,要在测试类上添加来自 spring-kafka-test 模块的 EmbeddedKafka 注解。有关更多信息,参阅 Spring For Apache Kafka 参考手册。

要使 Spring Boot 自动配置与前面提到的嵌入式 Apache Kafka 代理一起工作,需要将嵌入式代理地址(由 EmbeddedKafkaBroker 填充)的系统属性重新映射到 Apache Kafka 的 Spring Boot 配置属性中。有几种方法可以做到这一点:

  • 提供一个系统属性来将嵌入式代理地址映射到测试类中的 spring.kafka.bootstrap-servers

    static {
        System.setProperty(EmbeddedKafkaBroker.BROKER_LIST_PROPERTY, "spring.kafka.bootstrap-servers");
    }
    
  • @EmbeddedKafka 注解上配置属性名:

    @EmbeddedKafka(topics = "someTopic",
            bootstrapServersProperty = "spring.kafka.bootstrap-servers")
    
  • 在配置属性中使用占位符:

    spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}
    

14. Calling REST Services with RestTemplate

如果需要从应用程序调用远程 REST 服务,可以使用 Spring Framework 的 RestTemplate 类。由于在使用 RestTemplate 实例之前通常需要定制,Spring Boot 不提供任何单独的自动配置的 RestTemplate bean。但是,它会自动配置 RestTemplateBuilder,可以在需要时使用它创建 RestTemplate 实例。自动配置的 RestTemplateBuilder 确保将敏感的 HttpMessageConverters 应用于 RestTemplate 实例。

下面的代码展示了一个典型的例子:

@Service
public class MyService {

    private final RestTemplate restTemplate;

    public MyService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    public Details someRestCall(String name) {
        return this.restTemplate.getForObject("/{name}/details", Details.class, name);
    }

}

RestTemplateBuilder 包含许多有用的方法,可用于快速配置 RestTemplate 。例如,要添加基本的 auth 支持,可以使用 builder.basicAuthentication("user", "password").build()

14.1 RestTemplate Customization

有三种主要方法来自定义 RestTemplate,取决于你希望定制应用的范围。

要尽可能缩小任何自定义的范围,可插入自动配置的 RestTemplateBuilder,然后根据需要调用其方法。每个方法调用都返回一个新的 RestTemplateBuilder 实例,因此自定义只影响这个 builder 的使用。

要在应用程序范围内进行附加自定义,使用 Restemplatecustomizer bean。所有这些bean 都自动注册到自动配置的 RestTemplateBuilder 中,并应用到用它构建的任何模板。

以下示例显示一个自定义程序,该自定义程序为除 192.168.0.5 以外的所有主机配置代理的使用:

static class ProxyCustomizer implements RestTemplateCustomizer {

    @Override
    public void customize(RestTemplate restTemplate) {
        HttpHost proxy = new HttpHost("proxy.example.com");
        HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {

            @Override
            public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context)
                    throws HttpException {
                if (target.getHostName().equals("192.168.0.5")) {
                    return null;
                }
                return super.determineProxy(target, request, context);
            }

        }).build();
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
    }

}

最后,最极端(且很少使用)的选项是创建自己的 RestTemplateBuilder bean。这样做会关闭 RestTemplateBuilder 的自动配置,并阻止使用任何 RestTemplateCustomizer bean。

15. Calling REST Service with WebClient

如果类路径上有 Spring WebFlux,也可以选择使用 WebClient 调用远程 REST 服务。与 RestTemplate相比,这个客户端具有更高的功能性,并且是响应式的。你可以在 Spring Framework 文档的专用部分了解更多关于 WebClient 的信息。

SpringBoot 为你创建并预配置了一个 WebClient.Builder;强烈建议你将它注入到组件中并使用它创建 WebClient 实例。Spring Boot 将配置该构建器以共享 HTTP 资源、以与服务器相同的方式进行编解码器设置(参阅 WebFlux HTTP 编解码器自动配置)等等。

下例是一个典型例子:

@Service
public class MyService {

    private final WebClient webClient;

    public MyService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://example.org").build();
    }

    public Mono<Details> someRestCall(String name) {
        return this.webClient.get().uri("/{name}/details", name)
                        .retrieve().bodyToMono(Details.class);
    }

}

15.1 WebClient Runtime

根据应用程序类路径上可用的库,Spring Boot 将自动检测要用于驱动 WebClientClientHttpConnector 。目前,Reactor Netty 和 Jetty 的客户机是受支持的。

spring-boot-starter-webflux 默认依赖于 io.projectreactor.netty:reactor-netty,它同时提供服务器和客户端实现。如果你选择使用 Jetty 作为一个反应性服务器,那么你应该添加对 Jetty 响应式 HTTP 客户端库 org.eclipse.jetty:jetty-reactive-httpclient 的依赖。对服务器和客户机使用相同的技术有这个优点,因为它将在客户机和服务器之间自动共享 HTTP 资源。

开发人员可以通过提供自定义的 ReactorResourceFactoryJettyResourceFactory bean来覆盖 Jetty 和 Reactor Netty 的资源配置——这将应用于客户端和服务器。

如果你希望为客户机重写该选项,则可以定义自己的 ClientHttpConnector bean并完全控制客户机配置。

你可以在 Spring 框架参考文档中了解更多关于 WebClient 配置选项的信息。

15.2 WebClient Customization

WebClient自定义的方法主要有三种,这取决于你希望定制应用的范围。

要尽可能缩小任何自定义的范围,可插入自动配置的 WebClient.Builder,然后根据需要调用其方法。Builder 实例是有状态的:对 Builder 的任何更改都反映在随后用它创建的所有客户端中。如果要使用同一生成器创建多个客户端,还可以考虑使用 WebClient.Builder other = builder.clone();

要对所有 WebClient.Builder 实例进行应用程序范围的附加自定义,可以声明 WebClientCustomizer bean,并在注入点本地更改 WebClient.Builder

最后,你可以返回到使用原始 API 并使用 WebClient.create()。在这种情况下,不会使用自动配置或 WebClientCustomizer

16. Validation

只要在类路径上有 JSR-303实现(如 Hibernate 验证器),就会自动启用 Bean validation 1.1支持的方法验证功能。这样就可以用 javax.validation 约束对 bean 方法的参数和/或返回值进行注解。具有此类带注解方法的目标类需要在类型级别使用 @Validated 注解进行注释,以便在其方法中搜索内联约束注释。

例如,以下服务触发第一个参数的验证,确保其大小在 8 到 10 之间:

@Service
@Validated
public class MyBean {

    public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code,
            Author author) {
        ...
    }

}

17. Sending Email

Spring Framework 通过使用 JavaMailSender 接口为发送电子邮件提供了一个简单的抽象,Spring Boo t为其提供了自动配置以及一个启动模块。

有关如何使用 JavaMailSender 的详细说明,参阅参考文档

如果 spring.mail.host 和相关库(由 spring-boot-starter-mail 定义)可用,那么要是不存在 JavaMailSender,就创建默认的 JavaMailSender。可以通过 spring.mail 命名空间中的配置项进一步自定义发件人。有关详细信息,参阅 MailProperties

特别是,某些默认超时值是无限的,你可能需要更改该值,以避免线程被无响应的邮件服务器阻塞,如下例所示:

spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000

也可以使用 JNDI中 的现有 Session 来配置 JavaMailSender

spring.mail.jndi-name=mail/Session

当设置 jndi-name 后,它优先于所有其他与会话相关的设置。

18. Distributed Transactions with JTA

Spring Boot 通过使用 AtomikosBitronix 嵌入式事务管理器支持跨多个 XA 资源的分布式 JTA 事务。在部署到合适的 Java EE 应用服务器时,也支持 JTA 事务。

当检测到 JTA 环境时,Spring 的 JtaTransactionManager 被用于管理事务。自动配置的 JMS、DataSource 和 JPA bean被升级为支持 XA 事务。你可以使用标准的 Spring 用法(如@Transactional)来参与分布式事务。如果你在 JTA 环境中,仍然希望使用本地事务,可以将 spring.jta.enabled 属性设置为 false 以禁用 JTA 自动配置。

18.1 Using an Atomikos Transaction Manage

Atomikos 是一个流行的开源事务管理器,可以嵌入到你的 Spring Boot 应用程序中。可以使用 spring-boot-starter-jta-atomikos 拉入相应的 Atomikos 库。Spring Boot 自动配置Atomikos,并确保对 Spring beans 应用适当的 depends-on 设置,以获得正确的启动和关闭顺序。

默认情况下,Atomikos 事务日志被写入应用程序主目录(应用程序 jar文件所在的目录)中的 transaction-logs 目录。通过在 application.properties 文件中设置 spring.jta.log-dir 属性,可以自定义此目录的位置。从 spring.jta.atomikos.properties 开始的属性也可以用于自定义 Atomikos UserTransactionServiceImp。有关完整的详细信息,参阅A tomikosProperties 文档

为了确保多个事务管理器可以安全地协调同一个资源管理器,每个 Atomikos 实例都必须配置一个唯一的 ID。默认情况下,此 ID 是运行 Atomikos 的计算机的 IP 地址。为了确保生产中的唯一性,应该为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,使其具有不同的值。

18.2 Using a Bitronix Transaction Manager

Bitronix 是一种流行的开源 的JTA 事务管理器实现。可以使用 spring-boot-starter-jta-bitronix 将 Bitronix 依赖项添加到项目中。与 Atomikos 一样,Spring Boot 会自动配置 Bitronix 并对 bean 进行后置处理,以确保启动和关闭顺序正确。

默认情况下,Bitronix 事务日志文件(part1.btmpart2.btm)将写入应用程序主目录中的事务日志目录。可以通过设置 spring.jta.log-dir 属性自定义此目录的位置。以 spring.jta.bitronix.properties 开头的属性也绑定到 bitronix.tm.Configuration bean,允许完全自定义。有关详细信息,参阅 Bitronix 文档

为了确保多个事务管理器可以安全地协调相同的资源管理器,必须为每个 Bitronix 实例配置唯一的 ID。默认情况下,此 ID 是运行 Bitronix 的计算机的IP地址。为了确保生产中的唯一性,应该为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,使其具有不同的值。

18.3 Using a Java EE Managed Transaction Manager

如果将 Spring Boot 应用程序打包为 war 或 jar 文件并将其部署到 Java EE 应用服务器,则可以使用应用服务器的内置事务管理器。Spring Boot 试图通过查看常见的 JNDI 位置(java:comp/UserTransactionjava:comp/TransactionManager 等等)来自动配置事务管理器。如果使用应用服务器提供的事务服务,通常还需要确保所有资源都由服务器管理并通过 JNDI 公开。Spring Boot 尝试通过在 JNDI 路径(java:/JmsXAjava:/XAConnectionFactory)中查找 ConnectionFactory 来自动配置 JMS,你可以使用 spring.datasource.jndi-name property 属性来配置 DataSource

18.4 Mixing XA and Non-XA JMS Connections

当使用 JTA 时,主JMS ConnectionFactory bean是 XA-aware,并参与分布式事务。在某些情况下,你可能希望使用非XA ConnectionFactory 来处理某些 JMS 消息。例如,JMS 处理逻辑可能需要比 XA 超时更长的时间。

如果要使用非XA ConnectionFactory,可以注入非XA jmsConnectionFactory bean,而不是 @Primary jmsConnectionFactory bean。为了保持一致性,jmsConnectionFactory bean 也通过使用 bean 别名 xaJmsConnectionFactory 提供。

下面的示例演示如何注入ConnectionFactory实例:

// Inject the primary (XA aware) ConnectionFactory
@Autowired
private ConnectionFactory defaultConnectionFactory;

// Inject the XA aware ConnectionFactory (uses the alias and injects the same as above)
@Autowired
@Qualifier("xaJmsConnectionFactory")
private ConnectionFactory xaConnectionFactory;

// Inject the non-XA aware ConnectionFactory
@Autowired
@Qualifier("nonXaJmsConnectionFactory")
private ConnectionFactory nonXaConnectionFactory;

18.5 Supporting an Alternative Embedded Transaction Manager

XAConnectionFactoryWrapperXADataSourceWrapper 接口可用于支持其他嵌入式事务管理器。接口负责包装 XAConnectionFactoryXADataSource bean,并将它们公开为常规的 ConnectionFactoryDataSource bean,它们将被透明地注册到分布式事务中。数据源和 JMS 自动配置使用会 JTA 变量,前提是在 ApplicationContext中注册了JtaTransactionManagerbean和合适的 XA 包装 bean。

BitronixXAConnectionFactoryWrapperBitronixXADataSourceWrapper 提供了如何编写 XA 包装器的好例子。

19. Hazelcast

如果 Hazelcast 在类路径上,并且找到了合适的配置,那么 Spring Boot会自动配置一个 Hazelcast 实例,你可以在应用程序中注入它。

如果你定义了一个 com.hazelcast.config.Config bean,那么Spring Boot将使用它。如果你的配置定义了一个实例名,那么 Spring Boot 将尝试定位一个现有实例,而不是创建一个新实例。

你还可以指定要通过配置使用的 Hazelcast 配置文件,如下例所示:

spring.hazelcast.config=classpath:config/my-hazelcast.xml

否则,Spring Boot将尝试从默认位置查找 Hazelcast 配置:工作目录中的 hazelcast.xml 或类路径的根目录中的 hazelcast.xml ,或在相同位置的 .yaml 对应项。还会还检查是否设置了 hazelcast.config 系统属性。有关更多详细信息,参见 Hazelcast 文档

如果类路径上存在 hazelcast-client,Spring Boot 将首先通过检查以下配置选项来尝试创建客户端:

  • 存在 com.hazelcast.client.config.ClientConfig bean。

  • spring.hazelcast.config 属性定义的配置文件。

  • hazelcast.client.config 系统属性的存在。

  • 工作目录或类路径根目录中的 hazelcast-client.xml

  • 工作目录或类路径根目录中的 hazelcast-client.yaml

Spring Boot 还对 Hazelcast 提供了显式的缓存支持。如果启用了缓存,HazelcastInstance 将自动包装在 CacheManager 实现中。

20. Quartz Scheduler

Spring Boot 为使用 Quartz 调度程序提供了一些便利,包括 spring-boot-starter-quartz “starter”。如果 Quartz 可用,则会自动配置调度程序(通过 SchedulerFactoryBean 抽象)。

以下类型的 bean 将自动与调度程序关联:

  • JobDetail:定义特定任务。可以使用JobBuilder API构建JobDetail实例。

  • Calendar

  • Trigger:定义触发特定任务的时间。

默认情况下,使用内存中的 JobStore。但是,如果应用程序中有可用的Datasource bean,并且相应地配置了 spring.quartz.job-store-type 属性,则可以配置基于 JDBC 的存储,如下例所示:

spring.quartz.job-store-type=jdbc

当使用 JDBC 存储时,模式可以在启动时初始化,如下面的例子所示:

spring.quartz.jdbc.initialize-schema=always

默认情况下,会使用 Quartz 库提供的标准脚本检测和初始化数据库。这些脚本会删除现有的表,在每次重新启动时删除所有触发器。还可以通过设置 spring.quartz.jdbc.schema 来提供自定义脚本。

要让 Quartz 使用应用程序主数据源以外的数据源,需要声明一个 Datasource bean,用 @QuartDataSource 注解其 @Bean 方法。这样做可以确保 SchedulerFactoryBean 和模式初始化都使用 Quartz 特定的数据源。

默认情况下,由配置创建的任务不会覆盖已从持久任务存储读取的已注册任务。要启用覆盖现有任务定义,需要设置 spring.quartz.overwrite-existing-jobs 属性。

Quartz 调度程序配置可以使用 spring.quartz 属性和 SchedulerFactoryBeanCustomizer bean 进行自定义,后者允许编程式自定义 SchedulerFactoryBean。高级 Quartz 配置属性可以使用 spring.Quartz.properties.* 自定义。

Executor bean与调度器没有关联,因为 Quartz 提供了一种通过 spring.quartz.properties 配置调度器的方法。如果需要自定义任务执行器,考虑实现 SchedulerFactoryBeanCustomizer

任务可以定义 setter 来注入数据映射属性。常规的 bean 也可以以类似的方式注入,如下例所示:

public class SampleJob extends QuartzJobBean {

    private MyService myService;

    private String name;

    // Inject "MyService" bean
    public void setMyService(MyService myService) { ... }

    // Inject the "name" job data property
    public void setName(String name) { ... }

    @Override
    protected void executeInternal(JobExecutionContext context)
            throws JobExecutionException {
        ...
    }

}

21. Task Execution and Scheduling

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

如果你已经在上下文中定义了一个自定义 Executor ,那么常规任务执行(即@EnableAsync)将直接使用它,但是 Spring MVC 支持将不会配置,因为它需要一个 AsyncTaskExecutor 实现(名为 applicationTaskExecutor)。根据你的目的,你可以将执行程序更改为 ThreadPoolTaskExecutor,或者定义 ThreadPoolTaskExecutor 和包装 ExecutorAsyncConfigurer

自动配置的 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

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

如果需要与计划的任务执行相关联,还可以自动配置 ThreadPoolTaskScheduler@ EnableScheduling)。线程池默认使用一个线程,这些设置可以使用 spring.task.scheduling进行调整。

如果需要创建自定义执行器或调度器,则可以在上下文中使用 TaskExecutorBuilder bean 和 TaskSchedulerBuilder bean。

22. Spring Integration

Spring Boot 为使用 Spring Integeration 提供了一些便利,包括 spring-boot-starter-integration “starter”。Spring Integration 提供了对消息传递和其他传输(如HTTP、TCP等)的抽象。如果你的类路径上有 Spring Integration,那么它将通过 @EnableIntegration 注解进行初始化。

Spring Boot 还配置了一些由其他 Spring Integration 模块触发的特性。如果 spring-integration-jmx 也在类路径上,那么消息处理统计信息将通过 JMX 发布。如果 spring-integration-jdbc可用,则可以在启动时创建默认数据库架构,如下所示:

spring.integration.jdbc.initialize-schema=always

参见 IntegrationAutoConfigurationIntegrationProperties 类了解更多细节。

默认情况下,如果存在 Micrometer meterRegistry bean, Spring Integration 指标将由 Micrometer 管理。如果你希望使用遗留的 Spring Integration 指标,要在应用程序上下文中添加一个 DefaultMetricsFactory bean。

23. Spring Session

Spring Boot为各种数据存储提供了 Spring Session 自动配置。在构建 Servlet web 应用程序时,可以自动配置以下存储:

  • JDBC
  • Redis
  • Hazelcast
  • MongoDB

在构建响应式 web 应用程序时,可以自动配置以下存储:

  • Redis

  • Mongodb

如果类路径上存在单个 Spring Session 模型,Spring Boot 将自动使用该存储实现。如果有多个实现,则必须选择要用于存储会话的存储类型。例如,要使用 JDBC 作为后端存储,可以对应用程序进行如下配置:

spring.session.store-type=jdbc

你可以通过将 store-type 设置为 none 来禁用 Spring Session。

每个存储都有特定的附加设置。例如,可以为 JDBC 存储指定表的名称,如下面的例子所示:

spring.session.jdbc.table-name=SESSIONS

可以使用 spring.session.timeout 设置会话的超时时间。如果未设置该属性,则自动配置将回落到 server.servlet.session.timeout 的值。

24. Monitoring and Management over JMX

Java Management Extensions(JMX)提供了监控和管理应用程序的标准机制。Spring Boot 将最合适的 MBeanServer 公开为一个 ID 为 MBeanServer 的 bean。使用Spring JMX 注解(@ManagedResource@ManagedAttribute@ManagedOperation)任何 bean 都将公开给它。

如果平台提供了一个标准的 MBeanServer,那么 Spring Boot 将使用它,并且在必要时默认为 VM MBeanServer。如果所有这些都失败了,将创建一个新的 MBeanServer

有关更多详细信息,参阅 JmxAutoConfiguration 类

25. Testing

Spring Boot 在测试应用程序时提供了许多实用的程序和注解。测试支持由两个模块提供:spring-boot-test 包含核心项,spring-boot-test-autoconfigure 支持对测试进行自动配置。

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

该 starter 还带来了老式的引擎,因此你可以同时运行JUnit4 和 JUnit5测试。如果你已经将测试迁移到JUnit5,那么应该排除 JUnit4 支持,如下例所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

25.1 Test Scope Dependencies

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

  • JUnit 5 (包括用于向后兼容 JUnit 4的老式引擎):Java 应用程序单元测试的实际标准。
  • Spring Test & Spring Boot Test:对 Spring Boot 应用程序的实用程序和集成测试支持。
  • AssertJ:流式的断言库。
  • Hamcrest:匹配器对象的库(也称为约束或谓词)。
  • Mockito: 一个 Java mock 框架。
  • JSONassert:JSON的断言库。
  • JsonPath:JSON 的 XPath。

Spring Boot 发现这些公共库在编写测试时非常有用。如果这些库不适合你的需要,你可以添加你自己的附加测试依赖项。

25.2 Testing Spring Applications

依赖注入的一个主要优点是它使代码更容易进行单元测试。你可以使用 new 操作符实例化对象,甚至不涉及 Spring。也可以使用 mock 对象而不是实际依赖项。

通常,你需要超越单元测试,来进行集成测试(使用 Spring ApplicationContext)。Spring Test 是你能够在不需要部署应用程序或连接到其他基础结构的情况下执行集成测试。

Spring Framework 包含了一个专门的集成测试模块。你可以直接向 org.springframework:spring 测试声明一个依赖项,或者使用 ``spring-boot-starter-test` “starter” 以传递方式将其拉入。

如果以前没有使用过 spring-test 模块,那么应该从阅读 Spring Framework 框架参考文档的相关部分开始。

25.3 Testing Spring Boot Applications

Spring Boot 应用程序实际上是一个 Spring ApplicationContext,所以除了通常使用普通的 Spring 上下文所做的事情,不需要做什么特别的事情来测试它。

默认情况下,只有在使用 SpringApplication 创建时,才会在上下文中安装 Spring Boot 的外部属性、日志记录和其他特性。

Spring Boot 提供了一个 @SpringBootTest 注解,当你测试时需要 Spring Boot 特性时,它可以作为标准 spring-test @ContextConfiguration 注解的替代。注解的工作方式是通过 SpringApplication 创建测试中使用的 ApplicationContext 。除了 @SpringBootTest 之外,还提供了一些其他注解,用于测试应用程序的更具体的部分。

如果您正在使用 JUnit4,不要忘记在测试中添加 @RunWith(SpringRunner.class),否则该注解将被忽略。如果你正在使用 JUnit 5,则不需要添加与之等价的 @ExtendWith(SpringExtension.class) 作为 @SpringbootTest,而其他的 @Test 注解已经用它进行了注解。

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

  • MOCK(默认):加载 web ApplicationContext 并提供 mock web 环境。使用此注解时,嵌入式服务器不会启动。如果类路径上没有可用的 web 环境,则此模式会透明地返回到创建常规的非 web ApplicationContext。它可以与 @AutoConfigureMockMvc@AutoConfigurewebTestClient 结合使用,对 web 应用程序进行基于 mock 的测试。
  • RANDOM_PORT:加载WebServerApplicationContext 并提供真实的 web 环境。嵌入式服务器在一个随机端口上启动和监听。
  • DEFINED_PORT:加载 WebServerApplicationContext 并提供真实的 web 环境。嵌入式服务器在一个定义的端口(来自application.properties)或默认端口 8080 上启动和监听。
  • NONE:通过使用 SpringApplication 加载 ApplicationContex t,但不提供任何 web 环境(mock 或其他)。

如果你的测试是 @Transactional,那么默认情况下,它会在每个测试方法结束时回滚事务。然而,由于对随机端口或定义的端口隐式地提供了一个真正的 servlet 环境,HTTP 客户机和服务器在单独的线程中运行,因此在单独的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。

使用 webEnvironment=webEnvironment.RANDOM_PORT@SpringBootTest 也将在单独的随机端口上启动管理服务器,如果你的应用程序对管理服务器使用不同的端口。

25.3.1 Detecting Web Application Type

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

如果两者都存在,则 Spring MVC 优先。如果要在此场景中测试响应性 web 应用程序,必须设置 spring.main.web-application-type 属性:

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

25.3.2 Detecting Test Configuration

如果你熟悉 Spring 测试框架,那么可以使用 @ContextConfiguration(classes=...) 来指定要加载哪个 Spring @Configuration。或者,你可能经常在测试中使用的嵌套 @Configuration 类。

在测试 Spring Boot 应用程序时,这通常不是必需的。Spring Boot的 @*Test 注解在你没有显式定义主配置时会自动搜索你的主配置。

搜索算法从包含测试的包开始工作,直到找到用 @SpringBootApplication@SpringBootConfiguration 注解的类为止。只要以合理的方式构造代码,通常都会找到主配置。

如果你使用测试注解测试应用程序的更特殊的部分,则应避免添加特别于主方法的应用程序类上特定区域的配置设置。

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

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

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

25.3.3 Excluding Test Configuration

如果你的应用使用组件扫描(例如,如果使用 @SpringBootApplication@ComponentScan),你可能会发现,你仅为特定测试创建的顶级配置类会意外地在任何地方出现。

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

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

    @Test
    void exampleTest() {
        ...
    }

}

如果你直接使用 @ComponentScan(不通过 @SpringBootApplication),则需要注册 TypeExcludeFilter。有关详细信息,参见 java 文档

25.3.4 Using Application Arguments

如果应用程序需要参数,可以使用 args 属性让 @SpringBootTest 注入参数。

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

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

}

25.3.5 Testing with a mock environment

默认情况下,@SpringBootTest 不会启动服务器。如果你有想要在这个 mock 环境中测试的 web 端点,还可以配置 MockMvc,如下面的示例所示:

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.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 MockMvcExampleTests {

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

}

如果你只关注 web 层,而不想启动一个完整的 ApplicationContext,那么可以考虑使用 @WebMvcTest

此外,你可以配置一个 WebTestClient,如下面的例子所示:

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 MockWebTestClientExampleTests {

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

}

在 mock 环境中进行测试通常比使用完整的 Servlet 容器运行要快。但是,由于 mocking 发生在 Spring MVC 层,依赖于低级 Servlet 容器行为的代码不能直接用 MockMvc 测试。

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

25.3.6 Testing with a running server

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

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

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 RandomPortWebTestClientExampleTests {

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

}

这个设置需要类路径上的 spring-webflux。如果你不能或者不愿意添加webflux, Spring Boot 也提供了一个T estRestTemplate 工具:

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 RandomPortTestRestTemplateExampleTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");
    }

}

25.3.7 Customizing WebTestClient

自定义 WebTestClient bean,需要配置 WebTestClientBuilderCustomizer bean。使用用于创建 WebTestClientWebTestClient.Builder 调用此类的 bean。

25.3.8 Using JMX

当测试上下文框架缓存上下文时,JMX 在默认情况下是禁用的,以防止相同的组件注册在相同的域中。如果这样的测试需要访问 MBeanServer,考虑将它标记:

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

    @Autowired
    private MBeanServer mBeanServer;

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

}

25.3.9 Mocking and Spying Beans

运行测试时,有时需要在应用程序上下文中模拟某些组件。例如,可能有一个表象覆盖了一些在开发期间不可用的远程服务。当你希望模拟在实际环境中可能难以触发的故障时,mock 也很有用。

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

如果你的测试使用了 Spring Boot 的一个测试注释(例如 @SpringBootTest),则会自动启用此功能。要在不同的安排中使用此功能,必须显式地添加一个监听器,如下面的示例所示:

@TestExecutionListeners(MockitoTestExecutionListener.class)

下面的示例使用模拟实现替换现有的 RemoteService bean:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

@SpringBootTest
class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

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

此外,你可以使用 @SpyBean 将任何现有的 bean 与 Mockito spy 打包在一起。参阅 java 文档以获得完整的细节。

CGLib 代理,例如为作用域 bean 创建的代理,将代理方法声明为 final。这会阻止 Mockito 正常工作,因为它无法在默认配置中模拟或监视最终方法。如果想模拟或监视这样的 bean,可以通过将 org.mockito:mockito-inline 添加到应用程序的测试依赖项中来配置 Mockito。这是允许 Mockito 模拟和监视最终方法。

虽然 Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但是使用 @MockBean@SpyBean 会影响缓存键,这很可能会增加上下文的数量。

如果你使用 @SpyBean 监控具有 @Cacheable 方法的 bean,这些方法通过名称引用参数,那么你的应用程序必须使用 -parameters 进行编译。这可以确保一旦 bean 被监视后,参数名对缓存基础组件是可用的。

25.3.10 Auto-configured Tests

Spring Boot 的自动配置机制对应用的运行良好,但有时对测试来说可能有点太多了。它通常有助于只加载测试应用程序部分所需的配置部分。例如,你可能希望测试 Spring MVC 控制器是否正确映射了 URL,并且你不希望在这些测试中涉及数据库调用,或者你可能希望测试 JPA 实体,并且你对运行这些测试时的 web 层不感兴趣。

spring-boot-test-autoconfigure 模块包括许多自己注解,可以用来自动配置这些部分。它们中的每一个都以类似的方式工作,提供一个加载 ApplicationContext@...Test 注解和一个或多个可用于自定义自动配置设置的 @AutoConfigure... 注解。

每个切片将组件扫描限制到合适的组件,并加载一组非常有限的自动配置类。如果需要排除其中一个,大多数 @...Test 注解都提供 excludeAutoConfiguration 属性。或者,可以使用 @ImportAutoConfiguration#exclude

不支持在一个测试中使用多个 @…Test 注解来包含多个切片。 如果你需要多个"切片’’,可选择 @ ...Test 注解之一,并手动添加其他切片的 @AutoConfigure ... 注解。

也可以将 @AutoConfigure...注解与标准的 @SpringBootTest 注解一起使用。如果你对应用程序的切片不感兴趣,但需要一些自动配置的测试 bean,则可以使用此组合。

25.3.11 Auto-configured JSON Tests

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

  • Jackson ObjectMapper、任何 @JsonComponent bean和任何 Jackson 模块

  • Gson

  • Jsonb

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

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

Spring Boot 包括基于 AssertJ 的辅助程序,它们与 JSONAssert 和 JsonPath 库一起工作,以检查 JSON 是否按预期显示。JacksonTesterGsonTesterJsonbTesterBasicJsonTester 类可以分别用于Jackson、Gson、Jsonb 和 String。在使用 @JsonTest 时,可以 @Autowired 测试类中的任何帮助字段。下面的例子显示了Jackson 的一个测试类:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.json.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.json.*;

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

@JsonTest
class MyJsonTests {

    @Autowired
    private JacksonTester<VehicleDetails> json;

    @Test
    void testSerialize() 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 testDeserialize() 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");
    }

}

JSON 帮助类也可以直接用于标准单元测试。为此,如果不使用 @JsonTest ,要在 @Before 方法中调用辅助程序的 initFields 方法。

如果使用 Spring Boot 的基于 AssertJ 的辅助程序对给定 JSON 路径上的数值进行断言,则可能无法根据类型使用 isEqualTo。相反,你可以使用 AssertJ 的 satisfies 来断言值与给定条件是否匹配。例如,下面的示例断言实际数字是一个浮动值,接近 0.15,偏移量为 0.01

assertThat(json.write(message))
    .extractingJsonPathNumberValue("@.test.numberValue")
    .satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));

25.3.12 Auto-configured Spring MVC Tests

要测试 Spring MVC 控制器是否按预期工作,使用 @WebMvcTest 注解。@WebMvcTest 自动配置 Spring MVC 基础组件,并将扫描的 bean 限制为 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterFilterHandlerInterceptorWebMvcConfigurerHandlerMethodArgumentResolver。在使用这个注解时,不会扫描常规的 @Component bean。

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

如果你需要注册额外的组件,比如 Jackson 模块,可以在测试中使用 @Import 导入额外的配置类。

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

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

你还可以在非 @WebMvcTest(例如 @SpringBootTest)中使用 @AutoConfigureMockMvcMockMvc 进行注解,从而自动配置 MockMvc 。下面的例子使用了 MockMvc

import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

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

}

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

如果你使用 HtmlUnit 或 Selenium, 自动配置机制还提供了一个 HtmlUnit WebClientbean和/或 Selenium WebDriver bean。下面的例子使用 HtmlUnit:

import com.gargoylesoftware.htmlunit.*;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

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

}

默认情况下,Spring Boot 将 WebDriver bean放在一个特殊的作用域中,以确保驱动程序在每次测试之后退出,并注入一个新实例。如果你不想要这种行为,你可以将 @Scope("singleton") 添加到你的 WebDriver @Bean定义中。

Spring Boot创建的 webDriver 作用域将替换任何用户定义的同名作用域。如果你定义了你自己的 webDriver 范围,你可能会发现当你使用 @WebMvcTest 时它停止了工作。

如果类路径上有Spring Security,@WebMvcTest 还将扫描 WebSecurityConfigurer bean。你可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。有关如何使用Spring Security 的 MockMvc 支持的更多详细信息,参阅 how-to.html 部分。

有时仅仅编写 Spring MVC 测试是不够的,Spring Boot 可以帮助你在实际服务器上运行完整的端到端测试

25.3.13 Auto-configured Spring WebFlux Tests

要测试 Spring WebFlux 控制器是否按预期工作,可以使用 @WebFluxTest 注解。 @WebFluxTest 自动配置 Spring WebFlux 基础组件,并将扫描的 bean 限制为 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterWebFilterWebFluxConfigurer。使用 @WebFluxTest注解时,不会扫描常规的 @Component bean。

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

如果你需要注册额外的组件,比如 Jackson 模块,可以在测试中使用 @Import 导入额外的配置类。

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

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

你还可以在非 @WebFluxTest(例如 @SpringBootTest)中使用@AutoConfigureWebTestClientWebTestClient 进行注解,从而自动配置 WebTestClient。下面的例子展示了一个同时使用 @WebFluxTestWebTestClient的类:

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.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;

@WebFluxTest(UserVehicleController.class)
class MyControllerTests {

    @Autowired
    private WebTestClient webClient;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        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");
    }

}

这个设置只被 WebFlux 应用程序支持,因为在一个模拟的 web 应用程序中使用 WebTestClient 目前只适用于 WebFlux。

@WebFluxTest 无法检测通过函数式 web 框架注册的路由。要在上下文中测试RouterFunction bean,可以考虑自己通过 @Import@SpringBootTest 导入 RouterFunction

@WebFluxTest 无法检测通过 SecurityWebFilterChain 类型的 @Bean 注册的自定义安全配置。要在测试中包含它,需要导入通过 @Import 或使用 @SpringBootTest 注册 bean 的配置。

有时仅仅编写 Spring WebFlux`测试是不够的,Spring Boot可以帮助你在实际服务器上运行完整的端到端测试

25.3.14 Auto-configured Data JPA Tests

26. WebSockets

Spring Boot 为嵌入式 Tomcat、Jetty 和 Undertow 提供 WebSockets 自动配置。如果你将一个 war 文件部署到一个独立的容器中,Spring Boot 假设该容器负责其 WebSocket 支持的配置。

Spring 框架为 MVC web 应用程序提供了丰富的 WebSocket 支持,这些应用程序可以通过 spring-boot-starter-websocket 模块轻松访问。

WebSocket 支持也可用于响应式 web 应用程序,需要在spring-boot-starter-webflux 中包含WebSocket API:

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
</dependency>

27. Web Services

Spring Boot 提供了 Web 服务自动配置,因此你只需定义端点。

使用 spring-boot-starter-webservices 模块可以很容易地访问 Spring Web 服务特性

可以分别为 WSDLs 和 XSD 自动创建SimpleWsdl11Definition 定义和 SimpleXsdSchema bean。为此,要配置它们的位置,如下例所示:

spring.webservices.wsdl-locations=classpath:/wsdl

27.1 Calling Web Services with WebServiceTemplate

如果需要从应用程序调用远程 Web 服务,可以使用 WebServiceTemplate 类。由于 WebServiceTemplate 实例通常需要在使用前进行自定义,所以 Spring Boot 不提供任何自动配置的 WebServiceTemplatebean。但是,它会自动配置 WebServiceTemplateBuilder,需要时可以使用它创建 WebServiceTemplate实例。

如下例所示:

@Service
public class MyService {

    private final WebServiceTemplate webServiceTemplate;

    public MyService(WebServiceTemplateBuilder webServiceTemplateBuilder) {
        this.webServiceTemplate = webServiceTemplateBuilder.build();
    }

    public DetailsResp someWsCall(DetailsReq detailsReq) {
         return (DetailsResp) this.webServiceTemplate.marshalSendAndReceive(detailsReq, new SoapActionCallback(ACTION));
    }

}

默认情况下,WebServiceTemplateBuilder 使用类路径上可用的 HTTP 客户端库检测一个合适的基于 HTTP 的 WebServiceMessageSender。你自定义读取和连接超时如下:

@Bean
public WebServiceTemplate webServiceTemplate(WebServiceTemplateBuilder builder) {
    return builder.messageSenders(new HttpWebServiceMessageSenderBuilder()
            .setConnectTimeout(5000).setReadTimeout(2000).build()).build();
}

28. Creating Your Own Auto-configuration

如果你在开发共享库的公司工作,或者你在开源或商用库上工作,则可能需要开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,仍然可以由 Spring Boot 获取。

自动配置可以关联到一个 “starter”,它提供自动配置代码以及将与之一起使用的典型库。首先介绍构建自己的自动配置所需的知识,然后继续介绍创建自定义启动程序所需的典型步骤

28.1 Understanding Auto-configured Beans

在底层,自动配置是用标准的 Configuration 类实现的。附加的 @Conditional 注解用于约束何时应用自动配置。通常,自动配置类使用 @ConditionalOnClass@ConditionalOnMissingBean 注解。这将确保只有在找到相关类并且尚未声明自己的 @Configuration 时,才应用自动配置。

你可以浏览 spring-boot-auto-configure 的源代码,查看 Spring 提供的 @Configuration类(参见 META-INF/spring.factories 文件)。

28.2 Locating Auto-configuration Candidates

Spring 检查是否存在 META-INF/spring.factories 文件在你发布的 jar 中。该文件应该在 EnableAutoConfiguration 键下列出你的配置类,如下面的示例所示:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

自动配置只能以这种方式加载。确保它们是在特定的包空间中定义的,并且它们绝不是组件扫描的目标。此外,自动配置类不应启用组件扫描以查找其他组件。应该使用特定的 @import

如果需要按特定顺序应用配置,则可以使用 @AutoConfigureAfter@AutoConfigureBefore 注解。例如,如果提供特定于 web 的配置,则可能需要在 WebMvcAutoConfiguration 之后应用类。

如果你想订购某些不应该相互直接了解的自动配置,也可以使用 @AutoConfigureOrder。该注解与常规的 @Order注解具有相同的语义,但为自动配置类提供了专用的顺序。

28.3 Condition Annotations

你几乎总是希望在自动配置类中包含一个或多个 @Conditional 注解。@ConditionalOnMissingBean 注解是一个常见的示例,用于允许开发人员在对默认值不满意时重写自动配置。

Spring Boot 包含许多 @Conditional 注解,你可以通过注解 @Configuration 类或单个 @Bean 方法在自己的代码中重用这些注解。这些注解包括:

28.3.1 Class Conditions

@ConditionalOnClass@ConditionalOnMissingClass 注解允许 @Configuration 类基于特定类的存在或不存在而被包含。由于注解元数据是使用 ASM 解析的,因此可以使用 value 属性来引用实际的类,即使该类可能不会实际出现在正在运行的应用程序类路径上。如果希望使用 String 值指定类名,也可以使用 name 属性。

这种机制对 @Bean 方法的应用方式不同,通常返回类型是条件的目标:在方法上的条件应用之前,JVM 将加载类和可能处理的方法引用,如果类不存在,这些引用将失败。

要处理此场景,可以使用单独的 @Configuration 类来隔离条件,如下例所示:

@Configuration(proxyBeanMethods = false)
// Some conditions
public class MyAutoConfiguration {

    // Auto-configured beans

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(EmbeddedAcmeService.class)
    static class EmbeddedConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public EmbeddedAcmeService embeddedAcmeService() { ... }

    }

}

如果你使用 @ConditionalOnClass@ConditionalOnMissingClass 作为元注解的一部分来组成自己的复合注释,那么在这种情况下,必须使用名称来引用类。

28.3.2 Bean Conditions

@ConditionalOnBean@ConditionalOnMissingBean 注解允许根据特定 bean 的存在或不存在来包含 bean。可以使用 value 属性按类型指定 bean,也可以使用 name 指定bean。 search 属性允许你限制在搜索 bean 时应该考虑的 ApplicationContext层次结构。

当放置在 @Bea n方法上时,目标类型默认为方法的返回类型,如下面的例子所示:

@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService() { ... }

}

在前面的示例中,如果 ApplicationContext 中不包含 MyService 类型的bean,则将创建 myService bean。

你需要非常注意添加 bean 定义的顺序,因为这些条件是基于到目前为止所处理的内容进行评估的。由于这个原因,建议在自动配置类上只使用 @ConditionalOnBean@ConditionalOnMissingBean 注解(因为这些注解保证在添加任何用户定义的 bean 定义之后加载)。

@ConditionalOnBean@ConditionalOnMissingBean 不会阻止创建 @Configuration 类。在类级别使用这些条件和用注解标记每个包含 @Bean 的方法之间的唯一区别是,前者防止在条件不匹配时将 @Configuration 类注册为bean。

28.3.3 Property Conditions

@ConditionalOnProperty 注解允许基于 Spring 环境属性进行配置。使用 prefixname 属性指定应该检查的属性。默认情况下,存在且不等于 false 的任何属性都会被匹配。还可以使用 havingValuematchIfMissing 属性创建更高级的检查。

28.3.4 Resource Conditions

@ConditionalOnResource 注解只允许在存在特定资源时包含配置。可以使用通常的 Spring 约定来指定资源,如下面的示例所示:file:/home/user/test.dat

28.3.5 Web Application Conditions

据应用程序是否为 “web 应用程序”,@ConditionalOnWebApplication@ConditionalOnNotWebApplication 允许是否包括配置。基于 servlet 的 web 应用程序是使用 Spring WebApplicationContext、定义 seesion 范围或具有 ConfigurableWebEnvironment 的任意应用程序。响应式web 应用程序是使用 ReactiveWebApplicationContext 或具有可配置 ReactiveWebApplicationContext 的任意应用程序。

28.3.6 SpEL Expression Conditions

@ConditionalOnExpression 注解允许基于 SpEL 表达式的结果进行配置。

28.4 Testing your Auto-configuration

自动配置可能受到许多因素的影响:用户配置(@Bean 定义和 Environment 自定义)、条件评估(特定库的存在)等等。具体来说,每个测试都应该创建一个定义良好的 ApplicationContext,它表示这些定制的组合。ApplicationContextRunner 提供了一个很好的方法来实现这一点。

ApplicationContextRunner 通常定义为测试类的一个字段,用于收集基本的公共配置。下面的示例确保总是调用 UserServiceAutoConfiguration

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
        .withConfiguration(AutoConfigurations.of(UserServiceAutoConfiguration.class));

如果必须定义多个自动配置,则不需要按照与运行应用程序时完全相同的顺序调用它们的声明。

每个测试都可以使用运行者来表示一个特定的用例。例如,下面的示例调用一个用户配置(UserConfiguration)并检查自动配置是否正确地回退。调用 run 提供了一个可以与 Assert4J 一起使用的回调上下文。

@Test
void defaultServiceBacksOff() {
    this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
        assertThat(context).hasSingleBean(UserService.class);
        assertThat(context).getBean("myUserService").isSameAs(context.getBean(UserService.class));
    });
}

@Configuration(proxyBeanMethods = false)
static class UserConfiguration {

    @Bean
    UserService myUserService() {
        return new UserService("mine");
    }

}

也可以很容易地自定义 Environment,如下例所示:

@Test
void serviceNameCanBeConfigured() {
    this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
        assertThat(context).hasSingleBean(UserService.class);
        assertThat(context.getBean(UserService.class).getName()).isEqualTo("test123");
    });
}

还可以使用运行者展示 ConditionEvaluationReport。报告可以在 INFODEBUG 级别打印。下面的示例显示如何使用 ConditionEvaluationReportLoggingListener 在自动配置测试中打印报告。

@Test
public void autoConfigTest {
    ConditionEvaluationReportLoggingListener initializer = new ConditionEvaluationReportLoggingListener(
            LogLevel.INFO);
    ApplicationContextRunner contextRunner = new ApplicationContextRunner()
            .withInitializer(initializer).run((context) -> {
                    // Do something...
            });
}

28.4.1 Simulating a Web Context

如果需要测试只在 servlet 或响应式 web 应用程序上下文中运行的自动配置,可分别使用 WebApplicationContextRunnerReactiveWebApplicationContextRunner

28.4.2 Overriding the Classpath

还可以测试当特定类和/或包在运行时不存在时会发生什么。Spring Boot 附带一个 FilteredClassLoader ,运行器可以很容易地使用它。在下面的例子中,断言如果 UserService 不存在,自动配置将被正确地禁用:

@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
    this.contextRunner.withClassLoader(new FilteredClassLoader(UserService.class))
            .run((context) -> assertThat(context).doesNotHaveBean("userService"));
}

28.5 Creating Your Own Starter

28.5.1 Naming

你需要确保为启动程序提供正确的名称空间。不要用 spring-boot 启动你的模块名,即使使用的是不同的 Maven groupId。Spring Boot 以后可能会为您自动配置的东西提供官方支持。

根据经验,你应该在启动程序之后命名组合模块。例如,假设你正在为 “acme” 创建一个启动程序,并将自动配置模块命名为 acme-spring-boot-autoconfigure,将启动程序命名为 acme-spring-boot-starter。如果只有一个模块组合了这两个模块,那么将其命名为 acme-spring-boot-starter

28.5.2 Configuration keys

如果启动程序提供了配置键,那么为它们使用唯一的名称空间。特别是,不要在 Spring Boot 使用的名称空间中包含键(如 servermanagementspring 等)。如果你使用相同的名称空间,Spring Boot 将来可能会以破坏模块的方式修改这些名称空间。根据经验,可以在所有键的前面加上自己的名称空间(例如 acme)。

确保通过为每个属性添加字段 Javadoc 来记录配置键,如下面的示例所示:

@ConfigurationProperties("acme")
public class AcmeProperties {

    /**
     * Whether to check the location of acme resources.
     */
    private boolean checkLocation = true;

    /**
     * Timeout for establishing a connection to the acme server.
     */
    private Duration loginTimeout = Duration.ofSeconds(3);

    // getters & setters

}

你应该只使用带有 @ConfigurationProperties 字段 Javadoc 的简单文本,因为它们在被添加到 JSON 之前不会被处理。

下面是一些 Spring Boot 内部遵循的规则,以确保描述是一致的:

  • 不要以 The 或 A 开头。
  • 对于布尔类型,以 Whether 或 Enable 开始描述。
  • 对于基于集合的类型,以逗号分隔列表开始描述
  • 使用 java.time.Duration ,而不是 long,如果它不是毫秒,那么要说明单位。
  • 不要在描述中提供默认值,除非它必须在运行时确定。

要确保触发元数据生成,以便 IDE 插件也可用于你的键。你可能需要查看生成的元数据(META-INF/spring configuration metadata.json),以确保你的键有正确的文档记录。在兼容的 IDE 中使用自己的 starter 也是验证元数据质量的好主意。

28.5.3 autoconfigure Module

autoconfigure 模块包含启动库所需的所有内容。它还可能包含配置键定义(例如 @ConfigurationProperties)和任何回调接口,可以使用这些回调接口进一步定制组件的初始化方式。

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

Spring Boot 使用一个注解处理器来收集元数据文件中关于自动配置的条件(META-INF/ spring-autoconfigure-metada.properties)。如果存在该文件,则使用它来过滤不匹配的自动配置,这将优化启动时间。建议在包含自动配置的模块中添加以下依赖项:

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

在 Gradle4.5 及更早版本中,依赖项应该在 compileOnly 配置中声明,如下例所示:

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

在 Gradle4.6 及更高版本中,依赖项应该在 annotationProcessor 配置中声明,如下例所示:

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

29. Kotlin support

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

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

从 Spring Boot 和 Kotlin 开始最简单的方法就是遵循这个全面的教程。你可以通过start.spring.io 创建新的 Kotlin 项目。如果需要支持,可以加入Kotlin Slack 的 #spring 通道,或者在堆栈溢出时使用 springkotlin 标记进行分析。

29.1 Requirements

Spring Boot支持 Kotlin 1.3.x。要使用 Kotlin,org.jetbrains.Kotlin:kotlin-stdliborg.jetbrains.Kotlin:kotlin-reflect 必须出现在类路径上。也可以使用 kotlin-stdlib变体 kotlin-stdlib-jdk7kotlin-stdlib-jdk8

由于 Kotlin 类在默认情况下是不可变的,所以你可能希望配置 kotlin-spring 插件,来自动打开 Spring 注解类,以便可以代理它们。

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

如果在 start.spring.io 上引导 Kotlin 项目,则默认情况下会提供相关依赖项和插件。

29.2 Null-safety

Kotlin的一个关键特性是空值安全。它在编译时处理 null,而不是将问题推迟到运行时并直到 NullPointerException。这有助于消除常见的错误源,而不必像 Optional 那样支付包装器的成本。Kotlin 还允许使用函数构造,其值可以为空,如 Kotlin 中的空安全性综合指南所述。

尽管 Java 不允许在其类型系统中表示空安全,但是 Spring Framwork、Spring Data 和 Reacto r现在通过工具友好的注解提供了 API 的空安全性。默认情况下,Kotlin 中使用的Java API 中的类型被识别为松散型空检查的平台类型。Kotlin 对 JSR 305 注解和可空性注释的支持为 Kotlin 中相关的 Spring API 提供了空安全性。

JSR 305 检查可以通过添加带有以下选项的 -Xjsr305 编译器标志来配置:-Xjsr305={strict | warn | ignore}。默认行为与 -Xjsr305=warn相同。strict 值要求在从 Spring API 推断的 Kotlin 类型中考虑 空安全性,但应在使用时知道 Spring API 可空性声明可能会在较小的版本之间演变,并且将来可能会添加更多的检查。

目前还不支持泛型类型参数、varargs 和数组元素的可空性。有关最新信息,参阅 SPR-15942

29.3 Kotlin API

29.3.1 runApplication

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

29.3.2 Extensions

Kotlin 扩展提供了用附加功能扩展现有类的能力。Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特有的便利性。

提供了 TestRestTemplate 扩展,类似于 Spring Framework 中用于 RestOperations 的 Spring Framework 提供的扩展。 除其他事项外,这些扩展使利用 Kotlin 修饰类型参数成为可能。

29.4 Dependency management

为了避免在类路径上混合不同版本的Kotlin依赖项,Spring Boot 导入 Kotlin BOM。

使用 Maven,可以通过 kotlin.version 属性自定义 Kotlin 版本,并为 kotlin-maven-plugin 提供插件管理。使用 Gradle,Spring Boot 插件会自动将 kotlin.version 与 Kotlin插件的版本对齐。

Spring Boot 还通过导入 Kotlin 协程 BOM 来管理协程依赖项的版本。可以通过 kotlin-coroutines.version 属性自定义版本。

kotlinx:kotlinx coroutines-reactor 依赖项在默认情况下是提供的,如果一个引导一个 Kotlin 项目,并且至少有一个对 start.spring.io 的对应依赖项。

29.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 模型中的限制,一些特性(如检测默认值或不推荐的项)无法工作。

29.6 Testing

虽然可以使用 JUnit4 测试 Kotlin 代码,但 JUnit5 是默认提供的,建议使用。JUnit5 允许一个测试类被实例化一次,并为该类的所有测试重用。这使得可以在非静态方法上使用 @BeforeClass@AfterClass 注解,这非常适合Kotlin。

JUnit5 是默认的,vintage 引擎是为向后兼容 JUnit4 而提供的。如果不使用它,请排除org.junit.vintange:junit vintage 引擎。你还需要将测试实例生命周期切换到 “pre-class”

为了模拟 Kotlin 类,建议使用 MockK。如果需要 Mockk 等价于 Mockito 特定的 @MockBean@SpyBean 注解,可以使用 SpringMockK,它提供了类似的 @MockBean@SpykBean 注解。

29.7 Resources

29.7.1 Further reading

29.7.2 Examples

30. What to Read Next

如果你想进一步了解本节讨论的任何类,可以查看 Spring Boot API 文档或直接浏览源代码。如果你有特定的问题,可查看“操作”部分。

如果你对 Spring Boot 的核心功能感到满意,那么可以继续阅读有关生产就绪功能的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值