# “操作指南”

这一部分提供了一些常见的“我如何做到这一点……”问题的答案,这些问题在使用 Spring Boot 时经常会出现。它的覆盖范围并不全面,但确实涵盖了相当多的内容。

如果你有一个我们在此未讨论的特定问题,你可能想要检查stackoverflow.com (opens new window),以查看是否有人已经提供了答案。这也是一个提出新问题的好地方(请使用spring-boot标签)。

我们也非常乐意扩展这一部分。如果你想添加“how-to”,请给我们发送拉请求 (opens new window)

# 1. Spring 引导应用程序

本节包括与 Spring 引导应用程序直接相关的主题。

# 1.1.创建自己的故障分析器

[FailureAnalyzer](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/diagnostics/failureanalyzer.html)是一种很好的方法,可以在启动时拦截异常,并将其转换为人类可读的消息,包装在[FailureAnalysis](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/springfailureframework/api/api/org/diagnostics.html/failureanalys Spring Boot 为应用程序上下文相关的异常、JSR-303 验证等提供了这样的分析器。你也可以创建自己的。

AbstractFailureAnalyzerFailureAnalyzer的一个方便的扩展,用于检查要处理的异常中是否存在指定的异常类型。你可以从中进行扩展,以便你的实现只有在异常实际存在时才有机会处理该异常。如果由于任何原因无法处理异常,则返回null,以便给另一个实现一个处理异常的机会。

FailureAnalyzer实现必须在META-INF/spring.factories中注册。以下示例寄存器ProjectConstraintViolationFailureAnalyzer:

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
如果你需要访问BeanFactoryEnvironment,则你的FailureAnalyzer可以分别实现BeanFactoryAwareEnvironmentAware

# 1.2.故障排除自动配置

Spring 引导自动配置尽力“做正确的事情”,但有时事情会失败,而且很难讲出原因。

有一个真正有用的ConditionEvaluationReport可用于任何 Spring 引导ApplicationContext。如果启用DEBUG日志输出,就可以看到它。如果使用spring-boot-actuator(参见《执行器》一章),还会有一个conditions端点在 JSON 中呈现该报告。使用该端点调试应用程序,并查看在运行时 Spring 启动时添加了哪些特性(哪些特性尚未添加)。

通过查看源代码和 Javadoc,可以回答更多的问题。在阅读代码时,请记住以下经验法则:

  • 查找名为*AutoConfiguration的类,并阅读它们的源代码。请特别注意@Conditional*注释,以了解它们启用了哪些功能以及何时启用。将--debug添加到命令行或系统属性-Ddebug,可以在控制台上获得在应用程序中做出的所有自动配置决策的日志。在启用了执行器的正在运行的应用程序中,查看conditions端点(/actuator/conditions或等效的 JMX)以获得相同的信息。

  • 查找@Configuration属性的类(例如[Server属性](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/autofork/autofigure/web/serverproperties.java)),并从那里读取可用的外部配置选项。@Configuration属性注释具有name属性,该属性充当外部属性的前缀。因此,Server属性具有prefix="server",并且其配置属性是server.portserver.address和其他属性。在启用了执行器的正在运行的应用程序中,查看configprops端点。

  • Binder上查找bind方法的使用情况,以轻松的方式显式地从Environment中提取配置值。它常与前缀一起使用。

  • 查找直接绑定到Environment@Value注释。

  • 查找响应 SPEL 表达式打开和关闭特性的@ConditionalOnExpression注释,通常使用从Environment解析的占位符进行评估。

# 1.3.在启动前自定义环境或应用程序上下文

aSpringApplication具有ApplicationListenersApplicationContextInitializers,它们用于对上下文或环境应用自定义。 Spring 引导从META-INF/spring.factories加载许多这样的自定义以供内部使用。注册额外定制的方法不止一种:

  • 从编程的角度来看,通过在运行SpringApplication之前调用addListenersaddInitializers方法,每个应用程序都可以这样做。

  • 声明性地,通过设置context.initializer.classescontext.listener.classes属性,为每个应用程序设置。

  • 声明地,对于所有应用程序,通过添加META-INF/spring.factories并打包一个 jar 文件,应用程序都将其用作库。

SpringApplication将一些特殊的ApplicationEvents发送给侦听器(有些甚至在创建上下文之前),然后为ApplicationContext发布的事件注册侦听器。有关完整列表,请参见“ Spring 引导功能”部分中的“应用程序事件和监听器”。

在使用EnvironmentPostProcessor刷新应用程序上下文之前,还可以自定义Environment。每个实现都应该在META-INF/spring.factories中注册,如下例所示:

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

该实现可以加载任意文件并将其添加到Environment中。例如,以下示例从 Classpath 加载 YAML 配置文件:

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Resource path = new ClassPathResource("com/example/myapp/config.yml");
        PropertySource<?> propertySource = loadYaml(path);
        environment.getPropertySources().addLast(propertySource);
    }

    private PropertySource<?> loadYaml(Resource path) {
        Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist");
        try {
            return this.loader.load("custom-resource", path).get(0);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
        }
    }

}

Environment已经与所有常用的属性源一起准备好了 Spring 默认情况下引导加载。
因此可以从环境中获取文件的位置。
前面的示例在列表的末尾添加了custom-resource属性源,所以在任何通常的其他位置中定义的键都具有优先权。
一个自定义实现可以定义另一个顺序。
虽然在@SpringBootApplication上使用@PropertySource似乎是在Environment中加载自定义资源的一种方便方法,我们不推荐这样做。
这样的属性源在应用程序上下文被刷新之前不会被添加到Environment
这对于配置某些属性(如logging.*spring.main.*)来说太晚了,因为这些属性在开始刷新之前就已读取。

# 1.4.构建应用程序上下文层次结构(添加父上下文或根上下文)###

你可以使用ApplicationBuilder类来创建父/子ApplicationContext层次结构。有关更多信息,请参见“ Spring 引导功能”部分中的“features.html”。

# 1.5.创建一个非 Web 应用程序

Spring 并非所有的应用程序都必须是 Web 应用程序(或 Web 服务)。如果你希望在main方法中执行一些代码,而且还需要引导一个应用程序来设置要使用的基础设施,那么你可以使用SpringApplication Spring 引导的功能。一个SpringApplication会改变它的ApplicationContext类,这取决于它是否认为需要一个 Web 应用程序。你可以做的第一件事是将与服务器相关的依赖关系(例如 Servlet API)从 Classpath 中去除。如果你不能这样做(例如,你从相同的代码库运行两个应用程序),那么你可以在SpringApplication实例上显式调用setWebApplicationType(WebApplicationType.NONE)或设置applicationContextClass属性(通过 Java API 或使用外部属性)。希望作为业务逻辑运行的应用程序代码可以实现为CommandLineRunner,并作为@Bean定义放入上下文。

# 2. 属性和配置

本节包括有关设置和读取属性、配置设置及其与 Spring 引导应用程序的交互的主题。

# 2.1.在构建时自动展开属性

你可以使用现有的构建配置,而不是对项目的构建配置中也指定的一些属性进行硬编码,从而自动扩展它们。这在 Maven 和 Gradle 中都是可能的。

# 2.1.1.使用 Maven #### 进行自动属性扩展

通过使用资源筛选,你可以从 Maven 项目中自动扩展属性。如果使用spring-boot-starter-parent,则可以使用@[[email protected]](/cdn-cgi/l/email-protection)占位符引用 Maven“项目属性”,如以下示例所示:

属性

[email protected]@
[email protected]@

Yaml

app:
  encoding: "@[email protected]"
  java:
    version: "@[email protected]"
只有生产配置是以这种方式进行筛选的(换句话说,在src/test/resources上不应用筛选)。
如果启用addResources标志,spring-boot:run目标可以直接将src/main/resources添加到 Classpath 中(用于热重载目的)。
这样做会绕过资源过滤和此功能。,相反,
,你可以使用exec:java目标或自定义插件的配置。
有关更多详细信息,请参见插件使用页面 (opens new window)

如果不使用启动器父元素,则需要在你的pom.xml<build/>元素中包含以下元素:

<resources>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
    </resource>
</resources>

你还需要在<plugins/>中包含以下元素:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.7</version>
    <configuration>
        <delimiters>
            <delimiter>@</delimiter>
        </delimiters>
        <useDefaultDelimiters>false</useDefaultDelimiters>
    </configuration>
</plugin>
如果在配置中使用标准 Spring 占位符(例如${placeholder}),则useDefaultDelimiters属性很重要。
如果该属性未设置为false,则可以通过构建对其进行扩展。

# 2.1.2.使用 Gradle #### 进行自动属性扩展

通过配置 Java 插件的processResources任务,你可以从 Gradle 项目中自动扩展属性,如下例所示:

processResources {
    expand(project.properties)
}

然后,你可以使用占位符来引用你的 Gradle 项目的属性,如以下示例所示:

属性

app.name=${name}
app.description=${description}

Yaml

app:
  name: "${name}"
  description: "${description}"
Gradle 的expand方法使用 Groovy 的SimpleTemplateEngine,它转换${..}令牌。
${..}样式与 Spring 自己的属性占位符机制冲突。
将 Spring 属性占位符与自动展开一起使用,将 Spring 属性占位符转义如下:\${..}

# 2.2.将 SpringApplication 的配置外部化

SpringApplication具有 Bean 属性设置器,因此你可以在创建应用程序时使用其 Java API 来修改其行为。或者,你可以通过在spring.main.*中设置属性来外部化配置。例如,在application.properties中,你可能有以下设置:

属性

spring.main.web-application-type=none
spring.main.banner-mode=off

Yaml

spring:
  main:
    web-application-type: "none"
    banner-mode: "off"

然后 Spring 启动横幅不会在启动时打印,并且应用程序不会启动嵌入式 Web 服务器。

在外部配置中定义的属性覆盖并替换用 Java API 指定的值,但主要源是一个明显的例外。主源是提供给SpringApplication构造函数的那些源:

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

@SpringBootApplication
public class MyApplication {

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

}

或将sources(…​)方法转换为SpringApplicationBuilder:

import org.springframework.boot.Banner;
import org.springframework.boot.builder.SpringApplicationBuilder;

public class MyApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder()
            .bannerMode(Banner.Mode.OFF)
            .sources(MyApplication.class)
            .run(args);
    }

}

给出了上面的示例,如果我们有以下配置:

属性

spring.main.sources=com.example.MyDatabaseConfig,com.example.MyJmsConfig
spring.main.banner-mode=console

Yaml

spring:
  main:
    sources: "com.example.MyDatabaseConfig,com.example.MyJmsConfig"
    banner-mode: "console"

实际的应用程序将显示横幅(被配置覆盖),并为ApplicationContext使用三个源。应用程序来源如下:

  1. MyApplication(来自代码)

  2. MyDatabaseConfig(来自外部配置)

  3. MyJmsConfig(来自外部配置)

# 2.3.更改应用程序外部属性的位置

默认情况下,来自不同来源的属性以定义的顺序添加到 Spring Environment中(具体顺序请参见“ Spring 引导功能”部分中的“features.html”)。

你还可以提供以下系统属性(或环境变量)来更改行为:

  • spring.config.nameSPRING_CONFIG_NAME):默认为application作为文件名的根。

  • spring.config.locationSPRING_CONFIG_LOCATION):要加载的文件(例如 Classpath 资源或 URL)。为该文档设置了一个单独的Environment属性源,它可以被系统属性、环境变量或命令行覆盖。

无论你在环境中设置了什么, Spring 引导总是加载application.properties,如上文所述。默认情况下,如果使用 YAML,那么扩展名为“.yml”的文件也会添加到列表中。

Spring 引导日志记录在DEBUG级别加载的配置文件和在TRACE级别未找到的候选文件。

参见[ConfigFileApplicationListener](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot/SRC/main/java/org/springframework/boot/context/config/configfileapplicationlistener.java)了解更多细节。

# 2.4.使用“short”命令行参数

有些人喜欢使用(例如)--port=9000而不是--server.port=9000来在命令行上设置配置属性。你可以通过在application.properties中使用占位符来启用此行为,如下例所示:

属性

server.port=${port:8080}

Yaml

server:
  port: "${port:8080}"
如果继承自spring-boot-starter-parent POM,则maven-resources-plugins的默认筛选器标记已从${*}更改为@(即,@[[email protected]](/cdn-cgi/l/email-protection)而不是${maven.token}),以防止与 Spring 样式的占位符发生冲突。
如果你已经直接为application.properties启用了 Maven 筛选,那么你可能还希望将默认的筛选标记更改为使用其他分隔符 (opens new window)
在这种特定的情况下,端口绑定工作在 PaaS 环境中,例如 Heroku 或 Cloud Foundry。
在这两个平台中,PORT环境变量被自动设置,并且 Spring 可以绑定到Environment属性的大写同义词。

# 2.5.对外部属性使用 YAML

YAML 是 JSON 的超集,因此是一种方便的语法,用于以层次结构格式存储外部属性,如以下示例所示:

spring:
  application:
    name: "cruncher"
  datasource:
    driver-class-name: "com.mysql.jdbc.Driver"
    url: "jdbc:mysql://localhost/test"
server:
  port: 9000

创建一个名为application.yml的文件,并将其放在 Classpath 的根目录中。然后将snakeyaml添加到你的依赖项( Maven 坐标org.yaml:snakeyaml,如果你使用spring-boot-starter)。将 YAML 文件解析为 JavaMap<String,Object>(类似于 JSON 对象),并且 Spring 引导将映射变平,使其具有一层深度并具有周期分隔的键,就像许多人习惯于使用 Java 中的属性文件一样。

前面的示例 YAML 对应于下面的application.properties文件:

spring.application.name=cruncher
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000

有关 YAML 的更多信息,请参见“ Spring 引导功能”部分中的“features.html”。

# 2.6.设置活动的 Spring 配置文件

Spring Environment对此有一个 API,但通常需要设置一个系统属性(spring.profiles.active)或一个 OS 环境变量(SPRING_PROFILES_ACTIVE)。此外,你还可以使用-D参数启动应用程序(请记住将其放在主类或 jar 归档文件之前),如下所示:

$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar

在 Spring 引导中,还可以在application.properties中设置活动配置文件,如以下示例所示:

属性

spring.profiles.active=production

Yaml

spring:
  profiles:
    active: "production"

这种方式的值集被系统属性或环境变量设置代替,而不是被SpringApplicationBuilder.profiles()方法代替。因此,可以使用后一个 Java API 来增强配置文件,而无需更改默认值。

有关更多信息,请参见“ Spring 引导功能”部分中的“features.html”。

# 2.7.设置默认配置文件名

默认配置文件是一个配置文件,如果没有配置文件处于活动状态,则启用该配置文件。默认情况下,默认配置文件的名称是default,但可以使用系统属性(spring.profiles.default)或 OS 环境变量(SPRING_PROFILES_DEFAULT)更改它。

在 Spring 引导中,还可以在application.properties中设置默认配置文件名,如下例所示:

属性

spring.profiles.default=dev

Yaml

spring:
  profiles:
    default: "dev"

有关更多信息,请参见“ Spring 引导功能”部分中的“features.html”。

# 2.8.根据环境 ### 更改配置

Spring 启动支持多文档 YAML 和属性文件(详见features.html),这些文件可以基于活动配置文件有条件地被激活。

如果文档包含spring.config.activate.on-profile键,那么配置文件值(用逗号分隔的配置文件列表或配置文件表达式)将被输入 Spring Environment.acceptsProfiles()方法。如果配置文件表达式匹配,那么该文档将包含在最终的合并中(否则不包括),如以下示例所示:

属性

server.port=9000
#---
spring.config.activate.on-profile=development
server.port=9001
#---
spring.config.activate.on-profile=production
server.port=0

Yaml

server:
  port: 9000
---
spring:
  config:
    activate:
      on-profile: "development"
server:
  port: 9001
---
spring:
  config:
    activate:
      on-profile: "production"
server:
  port: 0

在前面的示例中,缺省端口是 9000。然而,如果称为“开发”的 Spring 配置文件是活动的,那么端口是 9001。如果“production”是活动的,那么端口是 0。

文档按照遇到它们的顺序合并。
后面的值覆盖前面的值。

# 2.9.发现外部属性的内置选项

Spring 引导在运行时将application.properties(或.yml文件和其他地方)的外部属性绑定到应用程序中。在单个位置中不存在(从技术上讲也不可能有)所有受支持的属性的详尽列表,因为贡献可以来自你的 Classpath 上的其他 jar 文件。

具有执行器功能的正在运行的应用程序具有configprops端点,该端点通过@Configuration属性显示所有可用的绑定和绑定属性。

附录包括一个[application.properties](application-properties.html#application.application-properties)示例,其中列出了 Spring boot 支持的最常见的属性。最终的列表来自搜索@Configuration属性@Value注释的源代码,以及偶尔使用Binder。有关加载属性的确切顺序的更多信息,请参见“features.html”。

# 3. 嵌入式 Web 服务器

Spring 每个引导 Web 应用程序包括嵌入式 Web 服务器。这个特性导致了许多操作问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。这一节回答了这些问题。

# 3.1.使用另一台 Web 服务器

Spring 许多启动程序都包含默认的嵌入式容器。

  • 对于 Servlet 堆栈应用程序,spring-boot-starter-web通过包括spring-boot-starter-tomcat而包括 Tomcat,但是你可以使用spring-boot-starter-jettyspring-boot-starter-undertow来代替。

  • 对于反应性堆栈应用程序,spring-boot-starter-webflux通过包括spring-boot-starter-reactor-netty而包括了反应器网,但是你可以使用spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow来代替。

当切换到不同的 HTTP 服务器时,你需要将缺省依赖项交换为你需要的依赖项。 Spring 为了帮助完成这一过程,Boot 为每个受支持的 HTTP 服务器提供了一个单独的启动器。

下面的 Maven 示例显示了如何排除 Tomcat 并包括 Jetty 用于 Spring MVC 的方法:

<properties>
    <servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- Exclude the Tomcat dependency -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Servlet API 的版本已被重写,因为与 Tomcat 9 和 Undertow 2 不同, Jetty 9.4 不支持 Servlet 4.0。
如果你希望使用 Jetty 10,它确实支持 Servlet 4.0,请覆盖jetty.version属性,而不是servlet-api.version属性。

下面的 Gradle 示例配置了必要的依赖关系和模块更换 (opens new window),以使用 Undertow 代替 Spring WebFlux 的反应堆网络:

dependencies {
    implementation "org.springframework.boot:spring-boot-starter-undertow"
    implementation "org.springframework.boot:spring-boot-starter-webflux"
    modules {
        module("org.springframework.boot:spring-boot-starter-reactor-netty") {
            replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty")
        }
    }
}
spring-boot-starter-reactor-netty是使用WebClient类所必需的,因此即使需要包含不同的 HTTP 服务器,也可能需要保持对 Netty 的依赖。

# 3.2.禁用 Web 服务器

如果你的 Classpath 包含启动 Web 服务器所需的位, Spring 启动将自动启动它。要禁用此行为,请在你的application.properties中配置WebApplicationType,如以下示例所示:

属性

spring.main.web-application-type=none

Yaml

spring:
  main:
    web-application-type: "none"

# 3.3.更改 HTTP 端口

在独立应用程序中,主 HTTP 端口默认为8080,但可以设置为server.port(例如,在application.properties中或作为系统属性)。由于放松了Environment值的绑定,你还可以使用SERVER_PORT(例如,作为 OS 环境变量)。

要完全关闭 HTTP 端点,但仍然创建WebApplicationContext,请使用server.port=-1(这样做有时对测试很有用)。

有关更多详细信息,请参见“ Spring 启动特性”部分中的“web.html”,或[Server属性](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/org/springframework/autoframework/autofconfigure/web/serverproperties.java)源代码

# 3.4.使用随机未分配的 HTTP 端口

要扫描自由端口(使用 OS native 来防止冲突),请使用server.port=0

# 3.5.在运行时发现 HTTP 端口

你可以从日志输出或从WebServerApplicationContext通过其WebServer访问服务器运行的端口。获得该结果并确保其已被初始化的最佳方法是添加@Bean类型的ApplicationListener<WebServerInitializedEvent>,并在事件发布时将容器从事件中拉出。

使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)的测试也可以通过使用@LocalServerPort注释将实际端口注入字段,如以下示例所示:

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {

    @LocalServerPort
    int port;

    // ...

}

@LocalServerPort@Value("${local.server.port}")的元注释。
不要尝试在常规的应用程序中注入端口。
正如我们刚才看到的,只有在容器被初始化之后才会设置该值。
与测试相反,应用程序代码回调是早期处理的(在该值实际可用之前)。

# 3.6.启用 HTTP 响应压缩

Jetty、 Tomcat 和 Undertow 支持 HTTP 响应压缩。它可以在application.properties中启用,如下所示:

属性

server.compression.enabled=true

Yaml

server:
  compression:
    enabled: true

默认情况下,响应的长度必须至少为 2048 字节,才能执行压缩。你可以通过设置server.compression.min-response-size属性来配置此行为。

默认情况下,只有当响应的内容类型是以下类型之一时,响应才会被压缩:

  • text/html

  • text/xml

  • text/plain

  • text/css

  • text/javascript

  • application/javascript

  • application/json

  • application/xml

你可以通过设置server.compression.mime-types属性来配置此行为。

# 3.7.配置 SSL

可以通过设置各种server.ssl.*属性来声明性地配置 SSL,通常是在application.propertiesapplication.yml中。下面的示例显示了在application.properties中设置 SSL 属性:

属性

server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret

Yaml

server:
  port: 8443
  ssl:
    key-store: "classpath:keystore.jks"
    key-store-password: "secret"
    key-password: "another-secret"

有关所有支持的属性的详细信息,请参见[Ssl](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot/SRC/main/java/org/springframework/boot/web/server/ssl.java)。

使用像前面示例那样的配置意味着应用程序不再支持端口 8080 的普通 HTTP 连接器。 Spring 引导不支持通过application.properties同时配置 HTTP 连接器和 HTTPS 连接器。如果你想同时拥有这两种功能,那么你需要以编程方式配置其中的一种。我们建议使用application.properties来配置 HTTPS,因为 HTTP 连接器是两个中更容易通过编程进行配置的。

# 3.8.配置 HTTP/2

你可以使用server.http2.enabled配置属性在 Spring 引导应用程序中启用 HTTP/2 支持。同时支持h2(http/2over TLS)和h2c(http/2over TCP)。若要使用h2,还必须启用 SSL。当未启用 SSL 时,将使用h2ch2支持的细节取决于所选的 Web 服务器和应用程序环境,因为所有 JDK8 版本都不支持该协议。

# 3.8.1.HTTP/2with Tomcat

Spring 默认情况下,在使用 JDK9 或更高版本时,Boot 附带 Tomcat 9.0.x 支持h2c开箱即用和h2开箱即用。或者,如果libtcnative库及其依赖项安装在主机操作系统上,则h2可以在 JDK8 上使用。

必须使库目录对 JVM 库路径可用(如果不是已经可用的话)。你可以使用 JVM 参数(如-Djava.library.path=/usr/local/opt/tomcat-native/lib)来实现此目的。在official Tomcat documentation (opens new window)中有更多关于此的内容。

在启用了 HTTP/2 和 SSL 的情况下,在 JDK8 上启动 Tomcat 9.0.x,但如果没有本机支持,则会记录以下错误:

ERROR 8787 --- [           main] o.a.coyote.http11.Http11NioProtocol      : The upgrade handler [org.apache.coyote.http2.Http2Protocol] for [h2] only supports upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"] connector that does not support ALPN.

这个错误并不是致命的,应用程序仍然以 HTTP/1.1SSL 支持启动。

# 3.8.2.HTTP/2with Jetty

对于 HTTP/2 支持, Jetty 需要额外的org.eclipse.jetty.http2:http2-server依赖关系。要使用h2c,不需要其他依赖项。要使用h2,还需要根据你的部署选择以下依赖项之一:

  • org.eclipse.jetty:jetty-alpn-java-server用于在 JDK9+ 上运行的应用程序

  • org.eclipse.jetty:jetty-alpn-openjdk8-server用于在 JDK8U252+ 上运行的应用程序

  • org.eclipse.jetty:jetty-alpn-conscrypt-server和不需要 JDK 的Conscrypt 图书馆 (opens new window)

# 3.8.3.带有反应堆网络的 HTTP/2

spring-boot-webflux-starter默认情况下使用 reactor netty 作为服务器。使用 JDK8 或更高版本,Reactor Netty 支持h2c,没有额外的依赖关系。使用 JDK9 或更高版本的 JDK 支持,Reactor Netty 支持h2。对于 JDK8 环境或最佳运行时性能,此服务器还支持带有本机库的h2。要实现这一点,你的应用程序需要有一个额外的依赖关系。

Spring 引导管理用于io.netty:netty-tcnative-boringssl-static“UBER jar”的版本,其中包含用于所有平台的本机库。开发人员可以选择只使用分类器导入所需的依赖项(参见Netty 官方文档 (opens new window))。

# 3.8.4.HTTP/2with Undertow

截至 Undertow 1.4.0+,h2h2c在 JDK8 上都得到了支持,没有任何额外的依赖关系。

# 3.9.配置 Web 服务器

通常,你应该首先考虑使用许多可用配置键中的一个,并通过在application.propertiesapplication.yml文件中添加新条目来定制 Web 服务器。见“发现外部属性的内置选项”)。在这里,server.*命名空间非常有用,对于服务器特定的特性,它包括server.tomcat.*server.jetty.*等命名空间。参见应用程序-properties.html列表。

前面的部分已经涵盖了许多常见的用例,例如压缩、SSL 或 HTTP/2。但是,如果你的用例不存在配置键,那么你应该查看[WebServerFactoryCustomizer](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/web/server/webserverfactorycustomizer.html)。你可以声明这样的组件并获得对与你的选择相关的服务器工厂的访问:你应该为所选择的服务器( Tomcat、 Jetty、反应器网络、 Undertow)和所选择的 Web 堆栈( Servlet 或反应式)选择变体。

下面的示例用于使用spring-boot-starter-web( Servlet 堆栈)的 Tomcat:

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        // customize the factory here
    }

}

Spring boot 在内部使用该基础设施来自动配置服务器。
自动配置的WebServerFactoryCustomizerbean 具有0的顺序,并且将在任何用户定义的定制程序之前进行处理,除非它具有明确的顺序,该顺序另有规定。

一旦你使用 Customizer 访问了WebServerFactory,就可以使用它来配置特定的部分,例如连接器、服务器资源或服务器本身——所有这些都使用特定于服务器的 API。

此外, Spring Boot 还提供:

Server Servlet stack 反应式堆栈
Tomcat TomcatServletWebServerFactory TomcatReactiveWebServerFactory
Jetty JettyServletWebServerFactory JettyReactiveWebServerFactory
Undertow UndertowServletWebServerFactory UndertowReactiveWebServerFactory
Reactor N/A NettyReactiveWebServerFactory

作为最后的手段,你还可以声明你自己的WebServerFactory Bean,这将覆盖由 Spring Boot 提供的那个。当你这样做时,自动配置的自定义程序仍然应用于你的自定义工厂,因此请小心使用该选项。

# 3.10.向应用程序添加 Servlet、过滤器或侦听器

在 Servlet 堆栈应用程序中,即使用spring-boot-starter-web的情况下,有两种方法可以向你的应用程序添加ServletFilterServletContextListener以及 Servlet API 支持的其他侦听器:

# 3.10.1.通过使用 Spring Bean #### 来添加 Servlet、过滤器或侦听器

要通过使用 Spring Bean 来添加ServletFilter或 Servlet *Listener,你必须为它提供一个@Bean定义。当你想要注入配置或依赖项时,这样做会非常有用。但是,你必须非常小心,不要让它们引起太多其他 bean 的急于初始化,因为它们必须在应用程序生命周期的早期安装到容器中。(例如,让它们依赖于你的DataSource或 JPA 配置不是一个好主意。)你可以通过在首次使用 bean 时而不是在初始化时懒洋洋地初始化 bean 来解决这些限制。

在过滤器和 servlet 的情况下,你还可以通过添加FilterRegistrationBeanServletRegistrationBean来添加映射和 init 参数,而不是添加或添加到基础组件中。

如果在过滤器注册中没有指定dispatcherType,则使用REQUEST
这与 Servlet 规范的默认 Dispatcher 类型一致。

与任何其他 Spring Bean 一样,你可以定义 Servlet 过滤器 bean 的顺序;请确保检查“web.html”部分。

# 禁用 Servlet 或过滤器的注册

作为前面描述的,任何ServletFilterbean 都会自动注册到 Servlet 容器中。要禁用特定FilterServlet Bean 的注册,请为其创建注册 Bean 并将其标记为禁用,如以下示例所示:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {

    @Bean
    public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
        registration.setEnabled(false);
        return registration;
    }

}

# 3.10.2.使用 Classpath 扫描 ##### 添加 servlet、过滤器和侦听器

@WebServlet@WebFilter,和@WebListener注释类可以通过注解一个@Configuration类和@ServletComponentScan类,并指定包含你想要注册的组件的包,自动地在嵌入式 Servlet 容器中注册。默认情况下,@ServletComponentScan从带注释的类的包中扫描。

# 3.11.配置访问日志

访问日志可以通过 Tomcat、 Undertow 和 Jetty 各自的名称空间进行配置。

例如,下面的设置使用自定义模式 (opens new window)日志访问 Tomcat。

属性

server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a %r %s (%D ms)

Yaml

server:
  tomcat:
    basedir: "my-tomcat"
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D ms)"
日志的默认位置是相对于 Tomcat 基本目录的logs目录,
默认情况下,logs目录是一个临时目录,因此你可能希望修复 Tomcat 的基本目录,或者对日志使用一个绝对路径,
在前面的示例中,相对于应用程序的工作目录,日志在my-tomcat/logs中可用。

Undertow 的访问日志记录可以以类似的方式进行配置,如以下示例所示:

属性

server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a %r %s (%D ms)

Yaml

server:
  undertow:
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D ms)"

日志相对于应用程序的工作目录存储在logs目录中。你可以通过设置server.undertow.accesslog.dir属性来定制此位置。

最后, Jetty 的访问日志也可以配置如下:

属性

server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log

Yaml

server:
  jetty:
    accesslog:
      enabled: true
      filename: "/var/log/jetty-access.log"

默认情况下,日志被重定向到System.err。有关更多详细信息,请参见 Jetty 文档。

# 3.12.运行在前端代理服务器后面

如果你的应用程序运行在代理、负载均衡器或云中,那么请求信息(如主机、端口、方案……)可能会在此过程中发生变化。你的应用程序可能运行在10.10.10.10:8080上,但是 HTTP 客户端应该只看到example.org

RFC7239“转发头” (opens new window)定义了ForwardedHTTP 报头;代理可以使用这个报头来提供关于原始请求的信息。你可以将你的应用程序配置为读取这些标题,并在创建链接并将其发送到 HTTP302 响应、JSON 文档或 HTML 页面中的客户端时自动使用这些信息。也有非标准的标题,如X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-Ssl,和X-Forwarded-Prefix

如果代理添加了常用的X-Forwarded-ForX-Forwarded-Proto头,则将server.forward-headers-strategy设置为NATIVE就足以支持这些。有了这个选项,Web 服务器本身就支持这个特性;你可以查看它们的特定文档来了解特定的行为。

如果这还不够, Spring 框架提供了ForwardedHeaderFilter (opens new window)。通过将server.forward-headers-strategy设置为FRAMEWORK,可以将其注册为应用程序中的 Servlet 过滤器。

如果你正在使用 Tomcat 并在代理上终止 SSL,则server.tomcat.redirect-context-root应该设置为false
这允许在执行任何重定向之前执行X-Forwarded-Proto头。
如果你的应用程序在 Cloud Foundry 或 Heroku 中运行,则server.forward-headers-strategy属性默认为NATIVE。在所有其他实例中,它默认为

# 3.12.1.自定义 Tomcat 的代理配置

如果使用 Tomcat,则可以另外配置用于携带“转发”信息的标题的名称,如以下示例所示:

属性

server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header
server.tomcat.remoteip.protocol-header=x-your-protocol-header

Yaml

server:
  tomcat:
    remoteip:
      remote-ip-header: "x-your-remote-ip-header"
      protocol-header: "x-your-protocol-header"

Tomcat 还配置有与要被信任的内部代理相匹配的默认正则表达式。默认情况下,10/8192.168/16169.254/16127/8中的 IP 地址是受信任的。你可以通过向application.properties添加一个条目来定制阀门的配置,如下例所示:

属性

server.tomcat.remoteip.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}

Yaml

server:
  tomcat:
    remoteip:
      internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"
可以通过将internal-proxies设置为空来信任所有代理(但在生产过程中不要这样做)。

通过关闭自动关闭(为此,请设置server.forward-headers-strategy=NONE)并使用WebServerFactoryCustomizer Bean 添加一个新的阀实例,可以完全控制 Tomcat 的RemoteIpValve的配置。

# 3.13. Tomcat 启用多个连接器

你可以将org.apache.catalina.connector.Connector添加到TomcatServletWebServerFactory,这可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如以下示例所示:

import java.io.IOException;
import java.net.URL;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;

@Configuration(proxyBeanMethods = false)
public class MyTomcatConfiguration {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> sslConnectorCustomizer() {
        return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createSslConnector());
    }

    private Connector createSslConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
        try {
            URL keystore = ResourceUtils.getURL("keystore");
            URL truststore = ResourceUtils.getURL("truststore");
            connector.setScheme("https");
            connector.setSecure(true);
            connector.setPort(8443);
            protocol.setSSLEnabled(true);
            protocol.setKeystoreFile(keystore.toString());
            protocol.setKeystorePass("changeit");
            protocol.setTruststoreFile(truststore.toString());
            protocol.setTruststorePass("changeit");
            protocol.setKeyAlias("apitester");
            return connector;
        }
        catch (IOException ex) {
            throw new IllegalStateException("Fail to create ssl connector", ex);
        }
    }

}

# 3.14.使用 Tomcat 的 LegacyCookieProcessor

默认情况下, Spring 引导所使用的嵌入式 Tomcat 不支持 cookie 格式的“版本 0”,因此你可能会看到以下错误:

java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value

如果可能的话,你应该考虑更新你的代码,以便只存储符合后来的 Cookie 规范的值。但是,如果不能更改 cookie 的编写方式,则可以将 Tomcat 配置为使用LegacyCookieProcessor。要切换到LegacyCookieProcessor,请使用一个WebServerFactoryCustomizer Bean,它会添加一个TomcatContextCustomizer,如以下示例所示:

import org.apache.tomcat.util.http.LegacyCookieProcessor;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyLegacyCookieProcessorConfiguration {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
        return (factory) -> factory
                .addContextCustomizers((context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
    }

}

# 3.15.启用 Tomcat 的 MBean 注册中心

默认情况下,嵌入式 Tomcat 的 MBean 注册中心被禁用。这最大限度地减少了 Tomcat 的内存占用。例如,如果你想使用 Tomcat 的 MBean,以便 Micrometer 可以使用它们来公开度量,那么你必须使用server.tomcat.mbeanregistry.enabled属性来这样做,如以下示例所示:

属性

server.tomcat.mbeanregistry.enabled=true

Yaml

server:
  tomcat:
    mbeanregistry:
      enabled: true

# 3.16.使用 Undertow 启用多个侦听器

UndertowBuilderCustomizer添加到UndertowServletWebServerFactory,并将侦听器添加到Builder,如以下示例所示:

import io.undertow.Undertow.Builder;

import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyUndertowConfiguration {

    @Bean
    public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
        return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
    }

    private Builder addHttpListener(Builder builder) {
        return builder.addHttpListener(8080, "0.0.0.0");
    }

}

# 3.17.使用 @serverendPoint## 创建 WebSocket 端点

如果要在使用嵌入式容器的 Spring 引导应用程序中使用@ServerEndpoint,则必须声明单个ServerEndpointExporter``@Bean,如以下示例所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration(proxyBeanMethods = false)
public class MyWebSocketConfiguration {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

Bean 在前面的示例中所示的任何@ServerEndpoint注释的 bean 与底层 WebSocket 容器一起注册。当部署到独立的 Servlet 容器时,该角色由 Servlet 容器初始化器执行,并且不需要ServerEndpointExporter Bean。

# 4. Spring MVC

Spring 启动具有包括 Spring MVC 的许多启动器。请注意,一些启动器包含对 Spring MVC 的依赖,而不是直接包含它。这一部分回答了关于 Spring MVC 和 Spring Boot 的常见问题。

# 4.1.编写 JSON REST 服务

Spring 在 Spring 引导应用程序中的任何 Spring 默认情况下都应该呈现 JSON 响应,只要 Jackson2 在 Classpath 上,如以下示例所示:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @RequestMapping("/thing")
    public MyThing thing() {
        return new MyThing();
    }

}

只要MyThing可以被 Jackson2 序列化(对于正常的 POJO 或 Groovy 对象为真),那么[localhost:8080/thing](http://localhost:8080/thing)默认情况下就提供了它的 JSON 表示。请注意,在浏览器中,你有时可能会看到 XML 响应,因为浏览器倾向于发送更喜欢 XML 的 Accept 头。

# 4.2.编写 XML REST 服务

如果在 Classpath 上有 Jackson 的 XML 扩展(jackson-dataformat-xml),则可以使用它来呈现 XML 响应。我们为 JSON 使用的前一个示例将起作用。要使用 Jackson 的 XML 渲染器,请向项目添加以下依赖项:

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

如果 Jackson 的 XML 扩展是不可用的,而 JAXB 是可用的,则可以使用附加的要求将MyThing注释为@XmlRootElement来呈现 XML,如以下示例所示:

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class MyThing {

    private String name;

    // getters/setters ...

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

    public void setName(String name) {
        this.name = name;
    }

}

JAXB 只能在 Java8 的开箱即用中使用。如果你使用的是新一代的 Java,那么可以在项目中添加以下依赖项:

<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
</dependency>
要让服务器呈现 XML 而不是 JSON,你可能必须发送Accept: text/xml头(或使用浏览器)。

# 4.3.自定义 JacksonObjectMapper

Spring MVC(客户端和服务器端)使用HttpMessageConverters在 HTTP 交换中协商内容转换。如果 Jackson 在 Classpath 上,则你已经获得了由Jackson2ObjectMapperBuilder提供的默认转换器,其实例是为你自动配置的。

ObjectMapper(或XmlMapper用于 JacksonXML 转换器)实例(默认情况下创建)具有以下定制属性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION已禁用

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES已禁用

  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS已禁用

Spring 引导还具有一些功能,以使其更容易自定义这种行为。

你可以通过使用环境来配置ObjectMapperXmlMapper实例。Jackson 提供了一套广泛的 On/Off 功能,可用于配置其处理的各个方面。这些特征以六个枚举(在 Jackson 中)进行描述,这些枚举映射到环境中的属性上:

Enum Property 价值观
com.fasterxml.jackson.databind.DeserializationFeature spring.jackson.deserialization.<feature_name> true, false
com.fasterxml.jackson.core.JsonGenerator.Feature spring.jackson.generator.<feature_name> true, false
com.fasterxml.jackson.databind.MapperFeature spring.jackson.mapper.<feature_name> true, false
com.fasterxml.jackson.core.JsonParser.Feature spring.jackson.parser.<feature_name> true, false
com.fasterxml.jackson.databind.SerializationFeature spring.jackson.serialization.<feature_name> true, false
com.fasterxml.jackson.annotation.JsonInclude.Include spring.jackson.default-property-inclusion always, non_null, non_absent, non_default, non_empty

例如,要启用 pretty print,请设置spring.jackson.serialization.indent_output=true。注意,由于使用了松弛结合indent_output的情况不必匹配对应的枚举常数的情况,即INDENT_OUTPUT

这种基于环境的配置应用于自动配置的Jackson2ObjectMapperBuilder Bean,并应用于通过使用构建器创建的任何映射器,包括自动配置的ObjectMapper Bean。

上下文的Jackson2ObjectMapperBuilder可以由一个或多个Jackson2ObjectMapperBuilderCustomizerbean 自定义。可以订购这样的定制程序 bean(Boot 自己的定制程序的订单为 0),从而在 Boot 的定制之前和之后都可以应用额外的定制程序。

类型com.fasterxml.jackson.databind.Module的任何 bean 都会自动注册到自动配置的Jackson2ObjectMapperBuilder中,并应用于它创建的任何ObjectMapper实例。这提供了一种全局机制,用于在向应用程序添加新特性时贡献自定义模块。

如果要完全替换缺省的ObjectMapper,可以定义该类型的@Bean并将其标记为@Primary,或者,如果你更喜欢基于构建器的方法,可以定义Jackson2ObjectMapperBuilder``@Bean。请注意,在这两种情况下,这样做都会禁用ObjectMapper的所有自动配置。

如果你提供任何@Beans类型的MappingJackson2HttpMessageConverter,它们将替换 MVC 配置中的默认值。此外,还提供了HttpMessageConverters类型的方便 Bean(如果使用默认的 MVC 配置,则始终可用)。它有一些有用的方法来访问默认的和用户增强的消息转换器。

参见“自定义 @responsebody 呈现”部分和[WebMvcAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/autofconfigure/web/[ Servlet/ Servlet/webmvcautofconfiguration.java])源

# 4.4.自定义 @responsebody 呈现

Spring 使用HttpMessageConverters来呈现@ResponseBody(或来自@RestController的响应)。你可以通过在 Spring 引导上下文中添加适当类型的 bean 来贡献额外的转换器。如果你添加的 Bean 是一种无论如何默认情况下都会包含的类型(例如,对于 JSON 转换,MappingJackson2HttpMessageConverter),那么它将替换默认值。 Bean 提供了HttpMessageConverters类型的便利,并且如果使用默认的 MVC 配置,这种便利总是可用的。它有一些有用的方法来访问默认的和用户增强的消息转换器(例如,如果你想手动将它们注入到自定义的RestTemplate中,它可能会很有用)。

正如在正常的 MVC 使用中一样,你提供的任何WebMvcConfigurerbean 也可以通过覆盖configureMessageConverters方法来贡献转换器。然而,与普通的 MVC 不同,你只能提供所需的额外转换器(因为 Spring Boot 使用相同的机制来贡献其默认值)。最后,如果你通过提供你自己的@EnableWebMvc配置来 OPT 出 Spring 引导默认的 MVC 配置,那么你可以通过使用getMessageConvertersfromWebMvcConfigurationSupport来完全控制并手动执行所有操作。

参见[WebMvcAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/web/ Servlet/webmvcautofconfiguration.javation)源代码了解更多详细信息。

# 4.5.处理多部分文件上传

Spring 引导包含 Servlet 3javax.servlet.http.PartAPI 以支持上载文件。默认情况下, Spring 引导配置 Spring MVC,在单个请求中,每个文件的最大大小为 1MB,文件数据的最大大小为 10MB。你可以使用Multipart属性类中公开的属性重写这些值、存储中间数据的位置(例如,存储到/tmp目录)以及将数据刷新到磁盘的阈值。例如,如果你想指定文件是无限的,那么将spring.servlet.multipart.max-file-size属性设置为-1

当你希望在 Spring MVC 控制器处理程序方法中以@RequestParam类型的MultipartFile注释参数的形式接收多部分编码的文件数据时,多部分支持是有帮助的。

参见[MultipartAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/web/ Servlet/multipartauconfiguration.java)源代码获取更多详细信息。

建议对多部分上传使用容器的内置支持,而不是引入额外的依赖关系,如 Apache Commons 文件上传。

# 4.6.关闭 Spring MVC DispatcherServlet

默认情况下,所有内容都是从应用程序的根目录(/)提供的。如果你更愿意映射到另一条路径,那么可以按以下方式配置一条路径:

Properties

spring.mvc.servlet.path=/mypath

Yaml

spring:
  mvc:
    servlet:
      path: "/mypath"

如果你有额外的 servlet,你可以为每个 servlet 声明@BeanServletRegistrationBean类型的@Bean,并且 Spring 引导将透明地将它们注册到容器中。因为 servlet 是以这种方式注册的,所以它们可以映射到DispatcherServlet的子上下文,而无需调用它。

自己配置DispatcherServlet是不寻常的,但是如果你确实需要这样做,则必须提供类型DispatcherServletPath@Bean,以提供自定义DispatcherServlet的路径。

# 4.7.关闭默认的 MVC 配置

对 MVC 配置进行完全控制的最简单的方法是使用@EnableWebMvc注释提供你自己的@Configuration。这样做会让所有的 MVC 配置都在你的手中。

# 4.8.自定义视解析程序

aViewResolver是 Spring MVC 的核心组件,将@Controller中的视图名称转换为实际的View实现。注意,ViewResolvers主要用于 UI 应用程序,而不是 REST 风格的服务(aView不用于呈现 a@ResponseBody)。有许多ViewResolver的实现方式可供选择,并且 Spring 本身并不确定应该使用哪些实现方式。 Spring 另一方面,引导为你安装一个或两个,这取决于它在 Classpath 上和在应用程序上下文中发现的内容。DispatcherServlet使用它在应用程序上下文中找到的所有解析器,依次尝试每个解析器,直到得到一个结果。如果你添加了你自己的,那么你必须知道你的解析程序添加的顺序和位置。

WebMvcAutoConfiguration将以下ViewResolvers添加到上下文中:

  • 名为“defaultViewResolver”的<gtr="602"/>。这一个定位了可以通过使用DefaultServlet呈现的物理资源(包括静态资源和 JSP 页面,如果你使用它们的话)。它将前缀和后缀应用到视图名称,然后在 Servlet 上下文中查找具有该路径的物理资源(默认值都是空的,但是可以通过spring.mvc.view.prefixspring.mvc.view.suffix进行外部配置)。你可以通过提供相同类型的 Bean 来覆盖它。

  • 一个名为“BeannameViewResolver”的BeanNameViewResolver。这是视图解析器链中的一个有用的成员,并获取与正在解析的View同名的任何 bean。不需要重写或替换它。

  • 只有当存在Are类型的 bean 时,才会添加名为ContentNegotiatingViewResolver的 viewresolver。这是一个复合解析器,将其委托给所有其他解析器,并试图找到与客户机发送的“Accept”HTTP 头匹配的项。有一个有用的[blog aboutContentNegotiatingViewResolver](https:// Spring.io/blog/2013/06/03/content-consultation-using-views),你可能想学习更多信息,也可能会查看源代码了解详细信息。你可以通过定义一个名为“ViewResolver”的 Bean 来关闭自动配置的ContentNegotiatingViewResolver

  • 如果你使用 ThymeLeaf,那么你还有一个名为“ThymeLeafViewResolver”的ThymeleafViewResolver。它通过使用前缀和后缀环绕视图名来查找资源。前缀是spring.thymeleaf.prefix,后缀是spring.thymeleaf.suffix。前缀和后缀的值分别默认为“ Classpath:/templates/”和“.html”。你可以通过提供同名的 Bean 来覆盖ThymeleafViewResolver

  • 如果使用 freemarker,还会有一个名为“freemarkerviewresolver”的FreeMarkerViewResolver。通过使用前缀和后缀围绕视图名,它在加载程序路径(外部化为spring.freemarker.templateLoaderPath,并具有默认值‘ Classpath:/templates/’)中查找资源。前缀外部化为spring.freemarker.prefix,后缀外部化为spring.freemarker.suffix。前缀和后缀的默认值分别为空和“.ftlh”。你可以通过提供同名的 Bean 来覆盖FreeMarkerViewResolver

  • 如果你使用 Groovy 模板(实际上,如果groovy-templates在你的 Classpath 上),那么你还有一个名为“GroovyMarkupViewResolver”的GroovyMarkupViewResolver。它通过使用前缀和后缀(外部化为spring.groovy.template.prefixspring.groovy.template.suffix)包围视图名称,在加载程序路径中查找资源。前缀和后缀的默认值分别为“ Classpath:/templates/”和“.TPL”。你可以通过提供同名的 Bean 来覆盖GroovyMarkupViewResolver

  • 如果你使用 Mustache,你也有一个MustacheViewResolver名为“MustacheViewResolver”。它通过使用前缀和后缀环绕视图名来查找资源。前缀是spring.mustache.prefix,后缀是spring.mustache.suffix。前缀和后缀的值分别默认为“ Classpath:/templates/”和“.mustache”。你可以通过提供同名的 Bean 来覆盖MustacheViewResolver

有关更多详细信息,请参见以下部分:

  • [WebMvcAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/web/ Servlet/webmvcautofconfiguration.java)

  • [ThymeleafAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/tofigure/thymeleautofconfiguration.java)

  • [FreeMarkerAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/org/springframework/boot/autofigure/freemmarkautofconfiguration.java)

  • [GroovyTemplateAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofconfigure/groovy/groovy/templateautofconfiguration.java)

# 5. 泽西岛

# 5.1.使用 Spring 安全性保护 Jersey 端点

Spring 安全性可用于保护基于 Jersey 的 Web 应用程序,其方式与其可用于保护基于 Spring MVC 的 Web 应用程序的方式大致相同。但是,如果你希望在 Jersey 中使用 Spring Security 的方法级安全性,则必须将 Jersey 配置为使用setStatus(int)而不是sendError(int)。这可以防止 Jersey 在 Spring Security 有机会向客户端报告身份验证或授权失败之前提交响应。

在应用程序的ResourceConfig Bean 上,jersey.config.server.response.setStatusOverSendError属性必须设置为true,如以下示例所示:

import java.util.Collections;

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {

    public JerseySetStatusOverSendErrorConfig() {
        register(Endpoint.class);
        setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true));
    }

}

# 5.2.在使用另一个 Web 框架的同时使用 Jersey

要将 Jersey 与另一个 Web 框架(例如 Spring MVC)一起使用,应该对其进行配置,以便允许另一个框架处理它无法处理的请求。首先,通过配置spring.jersey.type值为filter的应用程序属性,将 Jersey 配置为使用过滤器而不是 Servlet。其次,将你的ResourceConfig配置为转发会导致 404 的请求,如下面的示例所示。

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletProperties;

import org.springframework.stereotype.Component;

@Component
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(Endpoint.class);
        property(ServletProperties.FILTER_FORWARD_ON_404, true);
    }

}

# 6. HTTP 客户端

Spring Boot 提供了许多与 HTTP 客户端一起工作的启动器。本节回答与使用它们有关的问题。

# 6.1.配置 RESTTemplate 以使用代理

io.html中所述,可以使用RestTemplateCustomizerRestTemplateBuilder来构建定制的RestTemplate。这是创建配置为使用代理的RestTemplate的推荐方法。

代理配置的确切细节取决于所使用的底层客户机请求工厂。

# 6.2.配置由基于反应堆网络的 WebClient 使用的 TCPClient###

Classpath 基于反应器网络的WebClient在反应器网络上时是自动配置的。要定制客户机对网络连接的处理,请提供ClientHttpConnector Bean。下面的示例配置了 60 秒的连接超时,并添加了ReadTimeoutHandler:

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import reactor.netty.http.client.HttpClient;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.client.reactive.ReactorResourceFactory;

@Configuration(proxyBeanMethods = false)
public class MyReactorNettyClientConfiguration {

    @Bean
    ClientHttpConnector clientHttpConnector(ReactorResourceFactory resourceFactory) {
        HttpClient httpClient = HttpClient.create(resourceFactory.getConnectionProvider())
                .runOn(resourceFactory.getLoopResources())
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
                .doOnConnected((connection) -> connection.addHandlerLast(new ReadTimeoutHandler(60)));
        return new ReactorClientHttpConnector(httpClient);
    }

}

注意对连接提供程序和事件循环资源使用ReactorResourceFactory
这确保了接收请求的服务器和发出请求的客户端的资源的有效共享。

# 7. Logging

Spring 启动没有强制的日志依赖性,除了 Commons 日志 API,它通常由 Spring Framework 的spring-jcl模块提供。要使用Logback (opens new window),需要在 Classpath 上包含它和spring-jcl。推荐的实现方法是通过启动器,所有启动器都依赖于spring-boot-starter-logging。对于 Web 应用程序,你只需要spring-boot-starter-web,因为它在传递上依赖于日志启动器。如果你使用 Maven,以下依赖项将为你添加日志记录:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring 引导具有LoggingSystem的抽象,该抽象尝试基于 Classpath 的内容来配置日志记录。如果可以登录,它是第一个选择。

如果你需要对日志记录进行的唯一更改是设置各种日志记录器的级别,那么你可以在application.properties中使用“logging.level”前缀进行设置,如下例所示:

Properties

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

Yaml

logging:
  level:
    org.springframework.web: "debug"
    org.hibernate: "error"

还可以使用logging.file.name设置要将日志写入的文件的位置(除了控制台)。

要配置日志系统的更细粒度的设置,你需要使用所讨论的LoggingSystem所支持的本机配置格式。默认情况下, Spring boot 从其系统的默认位置(例如classpath:logback.xml用于回传)获取本机配置,但是你可以通过使用logging.config属性设置配置文件的位置。

# 7.1.配置日志记录的回录

如果你需要在application.properties所能实现的范围之外,对回录应用自定义,那么你将需要添加一个标准的回录配置文件。你可以将logback.xml文件添加到你的 Classpath 的根目录中,以便进行检索。如果你想使用Spring Boot Logback extensions,也可以使用logback-spring.xml

在一些细节上,回登文档有一个包含配置的专用部分 (opens new window)

Spring Boot 提供了许多从你自己的配置中执行included的回录配置。这些包括被设计为允许某些共同的引导约定被重新应用。

以下文件在org/springframework/boot/logging/logback/下提供:

  • defaults.xml-提供转换规则、模式属性和公共记录器配置。

  • console-appender.xml-使用CONSOLE_LOG_PATTERN添加ConsoleAppender

  • file-appender.xml-在适当的设置下使用FILE_LOG_PATTERNROLLING_FILE_NAME_PATTERN添加RollingFileAppender

此外,还提供了一个遗留的base.xml文件,用于与 Spring 引导的早期版本兼容。

一个典型的自定义logback.xml文件看起来是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
    <logger name="org.springframework.web" level="DEBUG"/>
</configuration>

你的回录配置文件还可以使用LoggingSystem为你创建的系统属性:

  • ${PID}:当前进程 ID。

  • ${LOG_FILE}:在 Boot 的外部配置中是否设置了logging.file.name

  • ${LOG_PATH}:在 Boot 的外部配置中是否设置了logging.file.path(代表用于保存日志文件的目录)。

  • ${LOG_EXCEPTION_CONVERSION_WORD}:在 Boot 的外部配置中是否设置了logging.exception-conversion-word

  • ${ROLLING_FILE_NAME_PATTERN}:在 Boot 的外部配置中是否设置了logging.pattern.rolling-file-name

Spring 启动还通过使用定制的回录转换器在控制台上(但不是在日志文件中)提供一些不错的 ANSI 彩色终端输出。有关示例,请参见defaults.xml配置中的CONSOLE_LOG_PATTERN

如果 Groovy 位于 Classpath 上,那么你也应该能够使用logback.groovy配置回发。如果存在,此设置将被优先考虑。

Spring Groovy 配置不支持扩展名。
任何logback-spring.groovy文件都不会被检测到。

# 7.1.1.为仅文件输出配置回录

如果要禁用控制台日志记录并只将输出写到文件,则需要一个自定义的logback-spring.xml,它导入file-appender.xml,但不导入console-appender.xml,如以下示例所示:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
    <root level="INFO">
        <appender-ref ref="FILE" />
    </root>
</configuration>

你还需要将logging.file.name添加到你的application.propertiesapplication.yaml中,如以下示例所示:

Properties

logging.file.name=myapplication.log

Yaml

logging:
  file:
    name: "myapplication.log"

# 7.2.为日志配置 log4j

Spring 如果在 Classpath 上,则启动支持Log4j 2 (opens new window)用于日志配置。如果你使用启动器来组装依赖项,那么你必须排除注销,然后包括 log4j2。如果不使用启动器,则除了 log4j2 之外,还需要提供(至少)spring-jcl

推荐的路径是通过启动器,尽管它需要进行一些微调。下面的示例显示了如何在 Maven 中设置启动器:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Gradle 提供了几种不同的方式来设置启动器。一种方法是使用模块更换 (opens new window)。为此,在 log4j2starter 上声明一个依赖项,并告诉 Gradle 默认的日志启动器的任何出现都应该被 log4j2starter 替换,如下面的示例所示:

dependencies {
    implementation "org.springframework.boot:spring-boot-starter-log4j2"
    modules {
        module("org.springframework.boot:spring-boot-starter-logging") {
            replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
        }
    }
}
log4j 启动器将常见日志需求的依赖关系收集在一起(例如, Tomcat 使用java.util.logging,但使用 log4j2 配置输出)。
要确保使用java.util.logging执行的调试日志被路由到 log4j2 中,可以通过将java.util.logging.manager系统属性设置为org.apache.logging.log4j.jul.LogManager来配置其JDK 日志适配器 (opens new window)

# 7.2.1.使用 YAML 或 JSON 配置 log4j2

除了其默认的 XML 配置格式,Log4j2 还支持 YAML 和 JSON 配置文件。要将 log4j2 配置为使用替代的配置文件格式,请向 Classpath 添加适当的依赖项,并将配置文件命名为与所选文件格式匹配的文件,如以下示例所示:

Format 依赖关系 File names
YAML com.fasterxml.jackson.core:jackson-databind + com.fasterxml.jackson.dataformat:jackson-dataformat-yaml log4j2.yaml + log4j2.yml
JSON com.fasterxml.jackson.core:jackson-databind log4j2.json + log4j2.jsn

# 7.2.2.使用复合配置配置来配置 log4j2

log4j2 支持将多个配置文件合并为一个复合配置。要在 Spring 引导中使用这种支持,可以使用一个或多个辅助配置文件的位置配置logging.log4j2.config.override。辅助配置文件将与主配置合并,无论主配置的源是 Spring boot 的默认值、标准位置(如log4j.xml),还是由logging.config属性配置的位置。

# 8. 数据访问

Spring 引导包括许多用于处理数据源的启动器。本节回答与此相关的问题。

# 8.1.配置自定义数据源

要配置自己的DataSource,请在配置中定义该类型的@Bean。 Spring 启动在需要的任何地方重用你的DataSource,包括数据库初始化。如果需要外部化某些设置,可以将DataSource绑定到环境(请参见“features.html”)。

下面的示例展示了如何在 Bean 中定义数据源:

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

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "app.datasource")
    public SomeDataSource dataSource() {
        return new SomeDataSource();
    }

}

下面的示例展示了如何通过设置属性来定义数据源:

Properties

app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30

Yaml

app:
  datasource:
    url: "jdbc:h2:mem:mydb"
    username: "sa"
    pool-size: 30

假设SomeDataSource具有 URL、用户名和池大小的常规 JavaBean 属性,则在DataSource对其他组件可用之前,将自动绑定这些设置。

Spring Boot 还提供了一种实用程序生成器类,称为,其可用于创建标准数据源之一(如果它在 Classpath 上)。构建器可以基于在 Classpath 上可用的内容来检测要使用的一个。它还基于 JDBC URL 自动检测驱动程序。

下面的示例展示了如何使用DataSourceBuilder创建数据源:

import javax.sql.DataSource;

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

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

}

要运行带有DataSource的应用程序,你所需要的只是连接信息。还可以提供特定于池的设置。检查将在运行时使用的实现,以获得更多详细信息。

下面的示例展示了如何通过设置属性来定义 JDBC 数据源:

Properties

app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30

Yaml

app:
  datasource:
    url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

然而,有一个问题。由于连接池的实际类型未公开,因此在你的自定义DataSource的元数据中不会生成任何键,并且 IDE 中也不会提供任何补全功能(因为DataSource接口不公开任何属性)。另外,如果你碰巧在 Classpath 上有 hikari,则此基本设置将不工作,因为 hikari 没有url属性(但是有jdbcUrl属性)。在这种情况下,你必须按以下方式重写配置:

Properties

app.datasource.jdbc-url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30

Yaml

app:
  datasource:
    jdbc-url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

可以通过强制连接池使用并返回一个专用的实现(而不是DataSource)来解决此问题。你无法在运行时更改实现,但选项列表将是显式的。

下面的示例显示了如何使用DataSourceBuilder创建HikariDataSource:

import com.zaxxer.hikari.HikariDataSource;

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

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource")
    public HikariDataSource dataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

}

你甚至可以通过利用DataSourceProperties为你做的事情来更进一步——也就是说,如果没有提供 URL,则提供一个默认的嵌入式数据库,该数据库具有合理的用户名和密码。你可以轻松地从任何DataSourceProperties对象的状态初始化DataSourceBuilder,因此你还可以插入 Spring boot 自动创建的数据源。但是,这将把你的配置拆分成两个名称空间:urlusernamepasswordtypedriverspring.datasource上,其余的在你的自定义名称空间上(app.datasource)。为了避免这种情况,你可以在自定义名称空间上重新定义自定义DataSourceProperties,如下例所示:

import com.zaxxer.hikari.HikariDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("app.datasource.configuration")
    public HikariDataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

}

该设置将你的同步与 Spring boot 在默认情况下为你提供的功能相同,只是选择了一个专用的连接池(在代码中),并且其设置在app.datasource.configuration子名称空间中公开。因为DataSourceProperties正在为你处理url/jdbcUrl转换,所以你可以将其配置如下:

Properties

app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30

Yaml

app:
  datasource:
    url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    configuration:
      maximum-pool-size: 30
Spring Boot 将向spring.datasource.hikari公开特定于光的设置。
该示例使用更通用的configuration子名称空间,因为该示例不支持多个数据源实现。
因为你的自定义配置选择使用 hikari,所以app.datasource.type没有任何效果。
在实践中,构建器是用你可能在那里设置的任何值初始化的,然后通过调用.type()来重写。

请参阅“ Spring 引导功能”部分中的“data.html”,以及[DataSourceAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/autofork/autoform/autoforigation/jdbc/dasourceautofconfiguration.java)类

# 8.2.配置两个数据源

如果需要配置多个数据源,则可以应用上一节中描述的相同技巧。但是,你必须将DataSource实例之一标记为@Primary,因为未来的各种自动配置都希望能够根据类型获得一个实例。

如果你创建了自己的DataSource,那么自动配置就会后退。在下面的示例中,我们提供了确切与在主数据源上提供的自动配置相同的功能集:

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    public DataSourceProperties firstDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first.configuration")
    public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
        return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    public BasicDataSource secondDataSource() {
        return DataSourceBuilder.create().type(BasicDataSource.class).build();
    }

}

firstDataSourceProperties必须标记为@Primary,以便数据库初始化器功能使用你的副本(如果你使用初始化器)。

这两个数据源也都是为高级定制而设计的。例如,你可以将它们配置如下:

Properties

app.datasource.first.url=jdbc:mysql://localhost/first
app.datasource.first.username=dbuser
app.datasource.first.password=dbpass
app.datasource.first.configuration.maximum-pool-size=30

app.datasource.second.url=jdbc:mysql://localhost/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30

Yaml

app:
  datasource:
    first:
      url: "jdbc:mysql://localhost/first"
      username: "dbuser"
      password: "dbpass"
      configuration:
        maximum-pool-size: 30

    second:
      url: "jdbc:mysql://localhost/second"
      username: "dbuser"
      password: "dbpass"
      max-total: 30

你也可以将相同的概念应用到次要的DataSource,如下例所示:

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyCompleteDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    public DataSourceProperties firstDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first.configuration")
    public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
        return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    public DataSourceProperties secondDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second.configuration")
    public BasicDataSource secondDataSource(
            @Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
        return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build();
    }

}

前面的示例在自定义名称空间上配置两个数据源,其逻辑与 Spring 引导在自动配置中使用的逻辑相同。请注意,每个configuration子名称空间根据所选的实现提供高级设置。

# 8.3.使用 Spring 数据存储库

Spring 数据可以创建各种类型的@Repository接口的实现方式。 Spring 启动为你处理所有这些,只要那些@Repositories包含在你的@EnableAutoConfiguration类的同一个包(或子包)中。

对于许多应用程序,你所需要的只是将正确的 Spring 数据依赖关系放在你的 Classpath 上。对于 JPA 有spring-boot-starter-data-jpa,对于 MongoDB 有spring-boot-starter-data-mongodb,对于受支持的技术还有其他各种启动器。要开始,请创建一些存储库接口来处理@Entity对象。

Spring Boot 试图根据它找到的@EnableAutoConfiguration来猜测你的@Repository定义的位置。要获得更多的控制,使用@EnableJpaRepositories注释(来自 Spring data JPA)。

有关 Spring 数据的更多信息,请参见Spring Data project page (opens new window)

# 8.4.将 @Entity 定义与 Spring 配置 ### 分开

Spring Boot 试图根据它找到的@EnableAutoConfiguration来猜测你的@Entity定义的位置。要获得更多的控制,可以使用@EntityScan注释,如以下示例所示:

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {

    // ...

}

# 8.5.配置 JPA 属性

Spring 数据 JPA 已经提供了一些独立于供应商的配置选项(例如用于 SQL 日志记录的那些选项),并且 Spring 启动公开了这些选项以及用于 Hibernate 的其他一些选项作为外部配置属性。其中一些是根据上下文自动检测的,因此你不必设置它们。

spring.jpa.hibernate.ddl-auto是一种特殊情况,因为根据运行时条件,它有不同的默认值。如果使用了嵌入式数据库,并且没有模式管理器(例如 Liquibase 或 Flyway)处理DataSource,则默认为create-drop。在所有其他情况下,它默认为none

JPA 提供程序检测要使用的方言。如果你希望自己设置方言,请设置spring.jpa.database-platform属性。

下面的示例显示了最常见的设置选项:

Properties

spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=true

Yaml

spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: "com.example.MyPhysicalNamingStrategy"
    show-sql: true

此外,当创建本地spring.jpa.properties.*时,spring.jpa.properties.*中的所有属性都将作为正常的 JPA 属性(前缀被剥离)通过。

你需要确保在spring.jpa.properties.*下定义的名称与你的 JPA 提供程序所期望的名称完全匹配,
Spring boot 将不会尝试对这些条目进行任何放松的绑定。

,例如,如果要配置 Hibernate 的批处理大小,则必须使用spring.jpa.properties.hibernate.jdbc.batch_size
如果使用其他形式,例如batchSizebatch-size,则 Hibernate 将不应用该设置。
如果你需要对 Hibernate 属性应用高级定制,请考虑注册一个HibernatePropertiesCustomizer Bean,它将在创建EntityManagerFactory之前被调用。
这优先于自动配置所应用的任何内容。

# 8.6.配置 Hibernate 命名策略

Hibernate 使用两种不同的命名策略 (opens new window)将名称从对象模型映射到相应的数据库名称。通过分别设置spring.jpa.hibernate.naming.physical-strategyspring.jpa.hibernate.naming.implicit-strategy属性,可以配置物理实现和隐式策略实现的完全限定类名。或者,如果ImplicitNamingStrategyPhysicalNamingStrategybean 在应用程序上下文中可用, Hibernate 将自动配置为使用它们。

默认情况下, Spring 引导使用CamelCaseToUnderscoresNamingStrategy配置物理命名策略。使用这种策略,所有的点都会被下划线所代替,而驼峰外壳也会被下划线所代替。此外,默认情况下,所有的表名都是用小写字母生成的。例如,将TelephoneNumber实体映射到telephone_number表。如果你的模式需要混合大小写标识符,请定义自定义CamelCaseToUnderscoresNamingStrategy Bean,如下例所示:

import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {

    @Bean
    public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
        return new CamelCaseToUnderscoresNamingStrategy() {

            @Override
            protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
                return false;
            }

        };
    }

}

如果你更喜欢使用 Hibernate 5 的默认值,请设置以下属性:

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

或者,你可以配置以下内容 Bean:

import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

    @Bean
    PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
        return new PhysicalNamingStrategyStandardImpl();
    }

}

参见[HibernateJpaAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofconfigure/SRC/more/java/org/Springframework/boot/autoframework/HibernatejaAutoConfiguration.jconfiguration)和[<JpaBaseConfiguration](https:///github.com/[[ Spring-projb/ Spring-projects/ Spring-tree/v2.6.4/

# 8.7.配置 Hibernate 二级缓存

Hibernate 二级缓存 (opens new window)可以被配置为用于缓存提供程序的范围。 Hibernate 与其配置以再次查找缓存提供程序,不如尽可能提供上下文中可用的那个。

要使用 JCache 执行此操作,首先要确保org.hibernate:hibernate-jcache在 Classpath 上可用。然后,添加一个HibernatePropertiesCustomizer Bean,如以下示例所示:

import org.hibernate.cache.jcache.ConfigSettings;

import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {

    @Bean
    public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
        return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
    }

}

这个定制程序将配置 Hibernate 使用与应用程序使用的相同的CacheManager。也可以使用单独的CacheManager实例。详见the Hibernate user guide (opens new window)

# 8.8.在 Hibernate 组件中使用依赖注入

默认情况下, Spring 启动注册了BeanContainer实现,该实现使用BeanFactory,以便转换器和实体侦听器可以使用常规的依赖注入。

你可以通过注册删除或更改hibernate.resource.beans.container属性的HibernatePropertiesCustomizer来禁用或调优此行为。

# 8.9.使用自定义 EntityManagerFactory

要完全控制EntityManagerFactory的配置,你需要添加一个名为“EntityManagerFactory”的@Bean。 Spring 在存在 Bean 该类型的实体管理器的情况下,启动自动配置关闭其实体管理器。

# 8.10.使用多个 EntityManagerFactory

如果需要对多个数据源使用 JPA,则每个数据源可能需要一个EntityManagerFactory。 Spring ORM 中的LocalContainerEntityManagerFactoryBean允许你为你的需要配置EntityManagerFactory。你还可以重用JpaProperties来绑定每个EntityManagerFactory的设置,如以下示例所示:

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

@Configuration(proxyBeanMethods = false)
public class MyEntityManagerFactoryConfiguration {

    @Bean
    @ConfigurationProperties("app.jpa.first")
    public JpaProperties firstJpaProperties() {
        return new JpaProperties();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource,
            JpaProperties firstJpaProperties) {
        EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
        return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build();
    }

    private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
        JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
        return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
    }

    private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
        // ... map JPA properties as needed
        return new HibernateJpaVendorAdapter();
    }

}

上面的示例使用名为firstDataSourceDataSource Bean 创建EntityManagerFactory。它扫描位于与Order相同的包中的实体。可以使用app.first.jpa名称空间映射额外的 JPA 属性。

当你自己为LocalContainerEntityManagerFactoryBean创建 Bean 时,在创建自动配置的LocalContainerEntityManagerFactoryBean期间应用的任何自定义都会丢失。
例如,在 Hibernate 的情况下,在spring.jpa.hibernate前缀下的任何属性都不会自动应用到你的LocalContainerEntityManagerFactoryBean
如果你依赖这些属性来配置诸如命名策略或 DDL 模式之类的东西,那么在创建LocalContainerEntityManagerFactoryBean Bean 时,你将需要显式地配置这些属性。

你应该为需要 JPA 访问的任何其他数据源提供类似的配置。要完成这幅图,你还需要为每个EntityManagerFactory配置一个JpaTransactionManager。或者,你也可以使用跨越这两个部分的 JTA 事务管理器。

如果使用 Spring 数据,则需要相应地配置@EnableJpaRepositories,如以下示例所示:

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
public class OrderConfiguration {

}

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {

}

# 8.11.使用传统的 persistence.xml 文件

Spring 默认情况下,引导不会搜索或使用META-INF/persistence.xml。如果你更喜欢使用传统的persistence.xml,则需要定义你自己的@Bean类型的LocalEntityManagerFactoryBean(ID 为“EntityManagerFactory”)并在此设置持久性单元名称。

参见[JpaBaseConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/orm/ JPA/jpabaseconfighimation.java)的默认设置。

# 8.12.使用 Spring 数据 JPA 和 Mongo 存储库

Spring Data JPA 和 Spring Data Mongo 都可以自动为你创建Repository实现。如果它们都存在于 Classpath 上,那么你可能需要做一些额外的配置来告诉 Spring 引导要创建哪些存储库。最明确的方法是使用标准 Spring 数据@EnableJpaRepositories@EnableMongoRepositories注释并提供你的Repository接口的位置。

还有一些标志(spring.data.*.repositories.enabledspring.data.*.repositories.type),你可以使用它们在外部配置中打开和关闭自动配置的存储库。这样做是有用的,例如,如果你想关闭 Mongo 存储库,并且仍然使用自动配置的MongoTemplate

Spring 对于其他自动配置的数据存储库类型(ElasticSearch、SOLR 和其他),存在相同的障碍和相同的特征。要使用它们,相应地更改注释和标志的名称。

# 8.13.自定义 Spring 数据的 Web 支持

Spring 数据提供了 Web 支持,该支持简化了 Spring 数据存储库在 Web 应用程序中的使用。 Spring Boot 在spring.data.web命名空间中提供了用于定制其配置的属性。请注意,如果使用 Spring 数据 REST,则必须使用spring.data.rest名称空间中的属性。

# 8.14.公开 Spring 数据存储库作为 REST 端点

Spring 数据 REST 可以公开实现作为你的 REST 端点,前提是 Spring 已经为应用程序启用了 MVC。

Spring 引导公开了一组有用的属性(来自spring.data.rest命名空间),这些属性定制了[RepositoryRestConfiguration](https://DOCS. Spring.io/ Spring-data/rest/DOCS/3.6.2/api/org/springframework/data/rest/core/core/config/restoryrestconfiguration.html)。如果需要提供额外的定制,则应该使用[RepositoryRestConfigurer](https://DOCS. Spring.io/ Spring-data/rest/DOCS/3.6.2/api/org/springframework/data/rest/webmvc/config/repositoryrestconfigrer.html) Bean。

如果你没有在你的自定义RepositoryRestConfigurer上指定任何订单,则它将在 Spring 引导内部使用的命令之后运行。
如果你需要指定订单,请确保其高于 0。

# 8.15.配置 JPA ### 使用的组件

如果要配置 JPA 使用的组件,那么需要确保在 JPA 之前初始化该组件。当组件被自动配置时, Spring Boot 将为你处理这个问题。例如,当 Flyway 被自动配置时, Hibernate 被配置为依赖于 Flyway,这样 Flyway 就有机会在 Hibernate 尝试使用数据库之前初始化数据库。

如果你自己配置一个组件,你可以使用EntityManagerFactoryDependsOnPostProcessor子类作为设置必要的依赖关系的一种方便的方式。例如,如果使用 Hibernate Search with ElasticSearch 作为其索引管理器,则任何EntityManagerFactorybean 都必须配置为依赖于elasticsearchClient Bean,如以下示例所示:

import javax.persistence.EntityManagerFactory;

import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.stereotype.Component;

/**
 * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
 * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
 */
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
        extends EntityManagerFactoryDependsOnPostProcessor {

    public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
        super("elasticsearchClient");
    }

}

# 8.16.用两个数据源配置 Jooq

如果需要对多个数据源使用 Jooq,那么应该为每个数据源创建自己的DSLContext。有关更多详细信息,请参见JooqAutoConfiguration (opens new window)

特别是,JooqExceptionTranslatorSpringTransactionProvider可以重用,以提供与自动配置使用单个DataSource所做的类似的功能。

# 9. 数据库初始化

SQL 数据库可以根据堆栈的不同以不同的方式初始化。当然,如果数据库是一个单独的过程,你也可以手动进行此操作。建议使用单一的机制来生成模式。

# 9.1.使用 JPA 初始化数据库

JPA 具有用于生成 DDL 的功能,并且这些功能可以设置为在启动时针对数据库运行。这是通过两个外部属性控制的:

  • spring.jpa.generate-ddl(布尔)会打开和关闭该功能,并且是独立于供应商的。

  • spring.jpa.hibernate.ddl-auto是一个 Hibernate 特性,它以更细粒度的方式控制行为。这一特性将在本指南的后面进行更详细的描述。

# 9.2.使用 Hibernate 初始化数据库

可以显式地设置spring.jpa.hibernate.ddl-auto,标准 Hibernate 属性值为nonevalidateupdatecreatecreate-drop。 Spring Boot 根据它是否认为你的数据库是嵌入式的,为你选择一个默认值。如果没有检测到模式管理器,则默认为create-drop,或者在所有其他情况下,默认为none。通过查看Connection类型和 JDBC URL 来检测嵌入式数据库。hsqldbh2derby是候选项,其他不是候选项。在从内存中切换到“真实”数据库时要小心,不要对新平台中的表和数据的存在做出假设。你必须显式地设置ddl-auto,或者使用其他机制之一来初始化数据库。

你可以通过启用org.hibernate.SQL记录器来输出模式创建。
如果你启用debug mode,这将自动为你完成。

此外,如果 Hibernate 从头创建模式(即,如果ddl-auto属性设置为createcreate-drop),则在启动时执行 Classpath 根中名为import.sql的文件。这对于演示和测试是有用的,如果你非常小心的话,但这可能不是你希望在生产 Classpath 中使用的东西。这是一个 Hibernate 特性(与 Spring 无关)。

# 9.3.使用基本 SQL 脚本初始化数据库

Spring 引导可以自动创建你的 JDBCDataSource或 R2DBCConnectionFactory的模式(DDL 脚本)并对其进行初始化(DML 脚本)。它从标准的根 Classpath 位置加载 SQL:分别是schema.sqldata.sql。此外, Spring 引导处理schema-${platform}.sqldata-${platform}.sql文件(如果存在),其中platformspring.sql.init.platform的值。这允许你在必要时切换到特定于数据库的脚本。例如,你可以选择将其设置为数据库的供应商名称(hsqldbh2oraclemysqlpostgresql,以此类推)。默认情况下,SQL 数据库初始化仅在使用嵌入式内存数据库时执行。要始终初始化 SQL 数据库(无论其类型如何),请将spring.sql.init.mode设置为always。类似地,要禁用初始化,请将spring.sql.init.mode设置为never。默认情况下, Spring 启动支持其基于脚本的数据库初始化器的抗故障特性。这意味着,如果脚本导致异常,则应用程序将无法启动。你可以通过设置spring.sql.init.continue-on-error来调整该行为。

默认情况下,在创建任何 JPA EntityManagerFactorybean 之前,执行基于脚本的DataSource初始化。schema.sql可用于为 JPA 管理的实体创建模式,而data.sql可用于填充它。虽然我们不推荐使用多个数据源初始化技术,但如果你希望基于脚本的DataSource初始化能够构建在 Hibernate 执行的模式创建上,请将spring.jpa.defer-datasource-initialization设置为true。这将推迟数据源的初始化,直到任何EntityManagerFactorybean 被创建和初始化之后。schema.sql然后可用于对 Hibernate 执行的任何模式创建进行添加,而data.sql可用于填充它。

如果你使用高级数据库迁移工具,比如 Flyway 或 Liquibase,那么你应该单独使用它们来创建和初始化模式。不建议在 Flyway 或 Liquibase 旁边使用基本的schema.sqldata.sql脚本,并且在未来的版本中将取消支持。

# 9.4.初始化 Spring 批处理数据库

如果你使用 Spring batch,那么它会预先打包为大多数流行的数据库平台的 SQL 初始化脚本。 Spring 启动可以检测到你的数据库类型,并在启动时执行这些脚本。如果使用嵌入式数据库,默认情况下会发生这种情况。你还可以为任何数据库类型启用它,如以下示例所示:

属性

spring.batch.jdbc.initialize-schema=always

Yaml

spring:
  batch:
    jdbc:
      initialize-schema: "always"

你还可以通过将spring.batch.jdbc.initialize-schema设置为never来显式地关闭初始化。

# 9.5.使用更高级的数据库迁移工具

Spring Boot 支持两个更高级别的迁移工具:Flyway (opens new window)Liquibase (opens new window)

# 9.5.1.在启动时执行 Flyway 数据库迁移

若要在启动时自动运行 Flyway 数据库迁移,请将org.flywaydb:flyway-core添加到 Classpath 中。

通常,迁移是V<VERSION>__<NAME>.sql形式的脚本(带有<VERSION>的下划线分隔版本,例如“1”或“2_1”)。默认情况下,它们位于一个名为classpath:db/migration的目录中,但是你可以通过设置spring.flyway.locations来修改该位置。这是一个以逗号分隔的列表,其中包含一个或多个classpath:filesystem:位置。例如,以下配置将在缺省 Classpath 位置和/opt/migration目录中搜索脚本:

属性

spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration

Yaml

spring:
  flyway:
    locations: "classpath:db/migration,filesystem:/opt/migration"

你还可以添加一个特殊的{vendor}占位符来使用特定于供应商的脚本。假设如下:

属性

spring.flyway.locations=classpath:db/migration/{vendor}

Yaml

spring:
  flyway:
    locations: "classpath:db/migration/{vendor}"

前面的配置不使用db/migration,而是根据数据库的类型设置要使用的目录(例如,对于 MySQL,db/migration/mysql)。支持的数据库列表可在[DatabaseDriver](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot/SRC/main/java/org/springframework/boot/jdbc/datasedriver.java)中找到。

迁移也可以用 Java 编写。Flyway 将使用实现JavaMigration的任何 bean 自动配置。

[Flyway属性](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofconfigure/SRC/main/java/org/org/springframework/boot/autofigure/flyway/flywayproperties.java)提供了 Flyway 的大部分设置和一小部分附加属性,可用于禁用迁移或关闭位置检查。如果需要对配置进行更多控制,可以考虑注册FlywayConfigurationCustomizer Bean。

Spring 引导调用Flyway.migrate()来执行数据库迁移。如果你想要更多的控制,请提供一个@Bean,它实现了[FlywayMigrationStrategy](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofconfigure/autofigure/flyway/flywaymigrationstrateg

Flyway 支持 SQL 和 Javacallbacks (opens new window)。要使用基于 SQL 的回调,请将回调脚本放置在classpath:db/migration目录中。要使用基于 Java 的回调,请创建一个或多个实现Callback的 bean。任何这样的 bean 都会自动注册为Flyway。它们可以通过使用@Order或实现Ordered来订购。也可以检测到实现不推荐的FlywayCallback接口的 bean,但是它们不能与Callbackbean 一起使用。

默认情况下,Flyway 在上下文中自动连接(@PrimaryDataSource,并将其用于迁移。如果你希望使用不同的DataSource,则可以创建一个,并将其@Bean标记为@FlywayDataSource。如果你这样做并且需要两个数据源,请记住创建另一个数据源并将其标记为@Primary。或者,你可以通过在外部属性中设置spring.flyway.[url,user,password]来使用 Flyway 的本机DataSource。设置spring.liquibase.userspring.flyway.user就足以使 Flyway 使用其自己的DataSource。如果没有设置这三个属性中的任何一个,则将使用其等效的spring.datasource属性的值。

你还可以使用 Flyway 为特定场景提供数据。例如,你可以在src/test/resources中放置特定于测试的迁移,并且仅在应用程序开始测试时才运行这些迁移。此外,你还可以使用配置文件特定的配置文件来定制spring.flyway.locations,以便仅在特定配置文件处于活动状态时才运行某些迁移。例如,在application-dev.properties中,你可以指定以下设置:

属性

spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration

Yaml

spring:
  flyway:
    locations: "classpath:/db/migration,classpath:/dev/db/migration"

在这种设置下,只有当dev配置文件处于活动状态时,dev/db/migration中的迁移才会运行。

# 9.5.2.在启动时执行 Liquibase 数据库迁移

若要在启动时自动运行 Liquibase 数据库迁移,请将org.liquibase:liquibase-core添加到你的 Classpath 中。

当你将org.liquibase:liquibase-core添加到你的 Classpath 中时,默认情况下,在应用程序启动期间和测试运行之前都会运行数据库迁移,
可以通过使用spring.liquibase.enabled属性来定制此行为,在maintest配置中设置不同的值。
不可能使用两种不同的方式来初始化数据库(例如,用于应用程序启动的 Liquibase,用于测试运行的 JPA)。

默认情况下,从db/changelog/db.changelog-master.yaml读取主更改日志,但是你可以通过设置spring.liquibase.change-log来更改位置。除了 YAML,Liquibase 还支持 JSON、XML 和 SQL 更改日志格式。

默认情况下,Liquibase 在上下文中自动连接(@PrimaryDataSource,并将其用于迁移。如果需要使用不同的DataSource,则可以创建一个,并将其@Bean标记为@LiquibaseDataSource。如果这样做,并且需要两个数据源,请记住创建另一个数据源,并将其标记为@Primary。或者,你可以通过在外部属性中设置spring.liquibase.[driver-class-name,url,user,password]来使用 Liquibase 的本机DataSource。设置spring.liquibase.urlspring.liquibase.user就足以使 Liquibase 使用自己的DataSource。如果没有设置这三个属性中的任何一个,则将使用其等效的spring.datasource属性的值。

参见[LiquibaseProperties](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/liquibase/liquibase/liquibaseproperties.java),以获取关于上下文、默认模式等可用设置的详细信息。

# 9.6.依赖于初始化的数据库

作为应用程序上下文刷新的一部分,在应用程序启动时执行数据库初始化。为了允许在启动过程中访问已初始化的数据库,将自动检测充当数据库初始化器的 bean 和要求该数据库已初始化的 bean。初始化依赖于已初始化的数据库的 bean 被配置为依赖于初始化它的 bean。如果在启动过程中,你的应用程序试图访问数据库,但尚未对其进行初始化,则可以配置额外的 bean 检测,以初始化数据库并要求对数据库进行初始化。

# 9.6.1.检测数据库初始化器

Spring 启动将自动检测初始化 SQL 数据库的以下类型的 bean:

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

如果你使用第三方启动器进行数据库初始化库,则它可能会提供一个检测器,从而也会自动检测到其他类型的 bean。要检测到其他 bean,请在META-INF/spring-factories中注册DatabaseInitializerDetector的实现。

# 9.6.2.检测依赖于数据库初始化的 Bean

Spring 启动将自动检测依赖于数据库初始化的以下类型的 bean:

  • AbstractEntityManagerFactoryBean(除非spring.jpa.defer-datasource-initialization设置为true

  • DSLContext

  • EntityManagerFactory(除非spring.jpa.defer-datasource-initialization设置为true

  • JdbcOperations

  • NamedParameterJdbcOperations

如果你使用的是第三方启动器数据访问库,那么它可能会提供一个检测器,使得其他类型的 bean 也会被自动检测到。要检测到其他 bean,请在META-INF/spring-factories中注册spring-boot-devtools的实现。或者,用@DependsOnDatabaseInitialization注释 Bean 的类或其@Bean方法。

# 10. 消息传递

Spring Boot 提供了许多启动器来支持消息传递。本节回答了在 Spring 引导下使用消息传递所产生的问题。

# 10.1.禁用已处理的 JMS 会话

如果你的 JMS 代理不支持事务会话,那么你必须完全禁用事务的支持。如果你创建自己的JmsListenerContainerFactory,则无需做任何事情,因为默认情况下无法进行交易。如果你想使用DefaultJmsListenerContainerFactoryConfigurer来重用 Spring boot 的默认值,可以禁用已处理的会话,如下所示:

import javax.jms.ConnectionFactory;

import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;

@Configuration(proxyBeanMethods = false)
public class MyJmsConfiguration {

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory listenerFactory = new DefaultJmsListenerContainerFactory();
        configurer.configure(listenerFactory, connectionFactory);
        listenerFactory.setTransactionManager(null);
        listenerFactory.setSessionTransacted(false);
        return listenerFactory;
    }

}

前面的示例覆盖了缺省工厂,并且应该将其应用到应用程序定义的任何其他工厂(如果有的话)。

# 11. 批处理应用程序

当人们在 Spring 引导应用程序中使用 Spring 批处理时,经常会出现许多问题。本节讨论这些问题。

# 11.1.指定批处理数据源

默认情况下,批处理应用程序需要DataSource来存储作业详细信息。 Spring 批处理默认情况下期望单个DataSource。要使用应用程序的主DataSource以外的DataSource,请声明DataSource Bean,并用@BatchDataSource注释其@Bean方法。如果你这样做并且需要两个数据源,请记住标记另一个@Primary。要获得更大的控制权,请实现BatchConfigurer。有关更多详细信息,请参见[@EnableBatchProcessing的 Javadoc](https://DOCS. Spring.io/ Spring-batch/DOCS/4.3.5/api/org/springframework/batch/core/configuration/annotation/enablebatchprocessing.html)。

有关 Spring 批处理的更多信息,请参见Spring Batch project page (opens new window)

# 11.2.在启动时运行 Spring 批处理作业

Spring 通过将@EnableBatchProcessing添加到你的一个@Configuration类,可以启用批处理自动配置。

默认情况下,它在启动时在应用程序上下文中执行全部Jobs(详见[JobLauncherApplicationRunner](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/main/java/org/applingframework/boot/autofoot/autocutorunner.java))。你可以通过指定spring.batch.job.names(它接受一个以逗号分隔的作业名称模式列表),将范围缩小到一个或多个特定的作业。

有关更多详细信息,请参见BatchaAutoConfiguration (opens new window)@enableBatchProcessing (opens new window)

# 11.3.从命令行运行

Spring Boot 将以--开头的任何命令行参数转换为要添加到Environment中的属性,请参见。这不应用于将参数传递给批处理作业。要在命令行上指定批处理参数,请使用常规格式(即不使用--),如下例所示:

$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue

如果在命令行上指定Environment的属性,则作业将忽略该属性。考虑以下命令:

$ java -jar myapp.jar --server.port=7070 someParameter=someValue

这只为批处理作业提供一个参数:someParameter=someValue

# 11.4.存储作业存储库

Spring 批处理需要用于Job存储库的数据存储。如果使用 Spring 引导,则必须使用实际的数据库。请注意,它可以是内存中的数据库,请参见配置作业存储库 (opens new window)

# 12. 执行器

Spring 引导包括 Spring 引导致动器。这一节回答了在使用中经常出现的问题。

# 12.1.更改执行器端点的 HTTP 端口或地址

在独立的应用程序中,Actuator HTTP 端口默认与主 HTTP 端口相同。要使应用程序侦听不同的端口,请设置外部属性:management.server.port。要侦听完全不同的网络地址(例如,当你有用于管理的内部网络和用于用户应用程序的外部网络时),还可以将management.server.address设置为服务器能够绑定的有效 IP 地址。

有关更多详细信息,请参见[DataSource](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-actuator-autofigure/SRC/main/java/org/springframework/boot/actuate/actuate/autofconfigure/web/server/managementserverproperties.java)源代码和“actuator.html”部分的“production-re

# 12.2.自定义“白标”错误页面

Spring 如果遇到服务器错误(使用 JSON 和其他媒体类型的机器客户端应该看到具有正确错误代码的合理响应),则启动安装你在浏览器客户端中看到的“WhiteLabel”错误页面。

设置server.error.whitelabel.enabled=false关闭默认的错误页。
这样做会恢复你正在使用的 Servlet 容器的默认值。
注意, Spring boot 仍然尝试解析错误视图,因此你可能应该添加自己的错误页,而不是完全禁用它。

使用自己的模板技术覆盖错误页面,这取决于你使用的模板技术。例如,如果使用 ThymeLeaf,则可以添加error.html模板。如果使用 freemarker,则可以添加error.ftlh模板。通常,需要一个名为ViewView解析,或者一个处理@Controller路径的@Controller解析。除非你替换了一些默认配置,否则你应该在ApplicationContext中找到一个BeanNameViewResolver,所以一个名为@Beanerror将是这样做的一种方式。参见[ErrorMvcAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/SpringFramework/boot/autofigure/autofigure/web/ Servlet/error/errormvcautofconfiguration.javoration)以获取更多选项。

有关如何在 Servlet 容器中注册处理程序的详细信息,请参见“错误处理”一节。

# 12.3.清除敏感值

envconfigprops端点返回的信息可能比较敏感,因此默认情况下匹配某些模式的键将被清除(即它们的值被******代替)。 Spring 对于这样的密钥,引导使用合理的默认值:任何以单词“password”、“secret”、“key”、“token”、“vcap_services”、“sun.java.command”结尾的密钥都是完全净化的。此外,将单词credentials(配置为正则表达式,即.*credentials.*)作为键的一部分的任何键也将被完全清除。

此外, Spring 引导使用以下结尾之一来清除键的类 URI 值的敏感部分:

  • address

  • addresses

  • uri

  • uris

  • url

  • urls

URI 的敏感部分使用<scheme>://<username>:<password>@<host>:<port>/格式标识。例如,对于属性myclient.uri=http://user1:[[email protected]](/cdn-cgi/l/email-protection):8081,生成的经过净化的值是http://user1:******@localhost:8081

envconfigprops端点使用的默认模式可以分别使用management.endpoint.env.keys-to-sanitizemanagement.endpoint.configprops.keys-to-sanitize替换。或者,可以使用management.endpoint.env.additional-keys-to-sanitizemanagement.endpoint.configprops.additional-keys-to-sanitize来配置其他模式。

# 12.4.将健康指标映射到千分尺指标

Spring 引导健康指示器返回一个Status类型,以指示整个系统的健康状况。如果你想要监视或提醒特定应用程序的健康水平,那么可以使用 Micrometer 将这些状态导出为度量。默认情况下,状态代码“上”、“下”、“Out_of_Service”和“Unknown”用于 Spring 引导。要导出这些值,你需要将这些状态转换为一组数字,以便它们可以与千分尺Gauge一起使用。

下面的示例展示了一种编写这种输出器的方法:

import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;

import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHealthMetricsExportConfiguration {

    public MyHealthMetricsExportConfiguration(MeterRegistry registry, HealthEndpoint healthEndpoint) {
        // This example presumes common tags (such as the app) are applied elsewhere
        Gauge.builder("health", healthEndpoint, this::getStatusCode).strongReference(true).register(registry);
    }

    private int getStatusCode(HealthEndpoint health) {
        Status status = health.health().getStatus();
        if (Status.UP.equals(status)) {
            return 3;
        }
        if (Status.OUT_OF_SERVICE.equals(status)) {
            return 2;
        }
        if (Status.DOWN.equals(status)) {
            return 1;
        }
        return 0;
    }

}

# 13. 安全

本节讨论了使用 Spring 引导时的安全性问题,包括在 Spring 引导时使用 Spring 安全性所产生的问题。

有关 Spring 安全性的更多信息,请参见Spring Security project page (opens new window)

# 13.1.关闭 Spring 启动安全配置

如果在应用程序中使用WebSecurityConfigurerAdapterSecurityFilterChain Bean 定义@Configuration,则会在 Spring 引导中关闭默认的 WebApp 安全设置。

# 13.2.更改 UserDetailsService 并添加用户帐户

如果提供AuthenticationManagerAuthenticationProviderUserDetailsService类型的View,则不会创建InMemoryUserDetailsManager的默认。这意味着你拥有 Spring 安全性的完整功能集(例如各种身份验证选项 (opens new window))。

添加用户帐户的最简单方法是提供你自己的UserDetailsService Bean。

# 13.3.在代理服务器后面运行时启用 HTTPS

对于任何应用程序来说,确保你的所有主要端点仅在 HTTPS 上可用都是一项重要的工作。如果你使用 Tomcat 作为 Servlet 容器,那么 Spring 启动将自动添加 Tomcat 自己的RemoteIpValve,如果它检测到某些环境设置,并且你应该能够依赖UserDetailsService来报告它是否安全(甚至在处理真正的 SSL 终止的代理服务器的下游)。标准行为是由某些请求头的存在或不存在决定的(x-forwarded-forx-forwarded-proto),它们的名称是常规的,因此它应该与大多数前端代理一起工作。可以通过向application.properties添加一些条目来打开阀门,如以下示例所示:

Properties

server.tomcat.remoteip.remote-ip-header=x-forwarded-for
server.tomcat.remoteip.protocol-header=x-forwarded-proto

Yaml

server:
  tomcat:
    remoteip:
      remote-ip-header: "x-forwarded-for"
      protocol-header: "x-forwarded-proto"

(在阀门上存在这两种属性中的任何一种的开关。或者,你可以通过使用WebServerFactoryCustomizer Bean 自定义TomcatServletWebServerFactory来添加RemoteIpValve。)

要将 Spring 安全性配置为需要针对所有(或某些)请求的安全通道,请考虑添加你自己的SecurityFilterChain Bean,该配置添加以下HttpSecurity配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class MySecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // Customize the application security ...
        http.requiresChannel().anyRequest().requiresSecure();
        return http.build();
    }

}

# 14. 热交换

Spring 启动支持热交换。这一部分回答了有关它如何工作的问题。

# 14.1.重新加载静态内容

热重装有几种选择。推荐的方法是使用[spring-boot-devtools](使用.html#using.devtools),因为它提供了额外的开发时功能,例如支持快速应用程序重启和 LiveReload 以及合理的开发时配置(例如模板缓存)。DevTools 通过监视 Classpath 的变化来工作。这意味着静态资源更改必须“构建”以使更改生效。默认情况下,在 Eclipse 中,当你保存更改时,这会自动发生。在 IntelliJ IDEA 中,make 项目命令触发了必要的构建。由于默认重启排除项,对静态资源的更改不会触发应用程序的重新启动。然而,它们确实会触发一次实时重装。

或者,在 IDE 中运行(尤其是在调试的情况下)是进行开发的一种好方法(所有现代 IDE 都允许重新加载静态资源,并且通常还允许对 Java 类的更改进行热交换)。

最后,Maven and Gradle plugins可以配置(参见addResources属性)以支持从命令行运行,并直接从源重新加载静态文件。如果你使用更高级的工具编写代码,那么你可以将其与外部 CSS/JS 编译器进程一起使用。

# 14.2.在不重新启动容器的情况下重新加载模板

Spring Boot 支持的大多数模板化技术都包括禁用缓存的配置选项(本文将在后面描述)。如果你使用spring-boot-devtools模块,那么在开发时,这些属性对你来说就是自动配置

# 14.2.1.百香叶模板

如果使用 thymeleaf,请将spring.thymeleaf.cache设置为false。参见[ThymeleafAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/thymeleautofconfiguration.javoration)了解其他 thymeleaf 定制选项。

# 14.2.2.自由标记模板

如果使用 freemarker,请将spring.freemarker.cache设置为false。参见[FreeMarkerAutoConfiguration](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/autofigure/fremark/freemortification.java)了解其他自由标记定制选项。

# 14.2.3.Groovy 模板

如果使用 Groovy 模板,请将spring.groovy.template.cache设置为false。参见[false](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofconfigure/groovy/templatautofconfiguration.javis)以获取其他 Groovy 定制选项。

# 14.3.快速应用程序重启

spring-boot-devtools模块包括对应用程序自动重启的支持。虽然没有JRebel (opens new window)等技术那么快,但它通常比“冷启动”快得多。在研究本文后面讨论的一些更复杂的重新加载选项之前,你可能应该尝试一下。

有关更多详细信息,请参见using.html部分。

# 14.4.在不重新启动容器的情况下重新加载 Java 类

许多现代 IDE(Eclipse、IDEA 和其他)支持字节码的热交换。因此,如果你所做的更改不会影响类或方法签名,那么它应该干净地重新加载,而不会产生任何副作用。

# 15. 测试

Spring 引导包括许多测试实用程序和支持类,以及提供公共测试依赖关系的专用启动器。这一节回答了有关考试的常见问题。

# 15.1.用 Spring 安全性进行测试

Spring 安全性为作为特定用户运行测试提供了支持。例如,下面代码片段中的测试将与具有ADMIN角色的经过身份验证的用户一起运行。

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@WebMvcTest(UserController.class)
class MySecurityTests {

    @Autowired
    private MockMvc mvc;

    @Test
    @WithMockUser(roles = "ADMIN")
    void requestProtectedUrlWithUser() throws Exception {
        this.mvc.perform(get("/"));
    }

}

Spring 安全性提供了与 Spring MVC 测试的全面集成,并且这也可以在使用@WebMvcTest切片和MockMvc测试控制器时使用。

有关 Spring Security 的测试支持的更多详细信息,请参见 Spring Security 的main

# 15.2.使用 TestContainers 进行集成测试

测试容器 (opens new window)库提供了一种管理在 Docker 容器中运行的服务的方法。它集成了 JUnit,允许你编写一个测试类,可以在运行任何测试之前启动一个容器。TestContainers 对于编写与真正的后端服务(如 MySQL、MongoDB、Cassandra 等)对话的集成测试特别有用。 Spring 启动测试中可以使用 TestContainers,如下所示:

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

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

@SpringBootTest
@Testcontainers
class MyIntegrationTests {

    @Container
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:4.2");

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

}

这将在运行任何测试之前启动运行 NEO4j 的 Docker 容器(如果 Docker 在本地运行)。在大多数情况下,你将需要使用正在运行的容器中的详细信息来配置应用程序,例如容器 IP 或端口。

这可以通过静态@DynamicPropertySource方法来完成,该方法允许向 Spring 环境添加动态属性值。

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@SpringBootTest
@Testcontainers
class MyIntegrationTests {

    @Container
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:4.2");

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

    @DynamicPropertySource
    static void neo4jProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
    }

}

上述配置允许应用程序中与 NEO4J 相关的 bean 与运行在 TestContainers 管理的 Docker 容器内的 NEO4J 通信。

# 16. 建立

Spring 引导包括用于 Maven 和 Gradle 的构建插件。本节回答有关这些插件的常见问题。

# 16.1.生成构建信息

Maven 插件和 Gradle 插件都允许生成包含项目的坐标、名称和版本的构建信息。插件也可以被配置为通过配置添加额外的属性。当存在这样的文件时, Spring 引导自动配置BuildProperties Bean。

要用 Maven 生成构建信息,请为build-info目标添加一个执行,如以下示例所示:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.6.4</version>
            <executions>
                <execution>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
有关更多详细信息,请参见全部

下面的示例对 Gradle 执行相同的操作:

springBoot {
    buildInfo()
}
有关更多详细信息,请参见Spring Boot Gradle Plugin documentation (opens new window)

# 16.2.生成 Git 信息

Maven 和 Gradle 都允许生成一个git.properties文件,该文件包含有关在构建项目时你的git源代码库的状态的信息。

对于 Maven 用户,spring-boot-starter-parent POM 包括一个预先配置的插件以生成git.properties文件。要使用它,请将[Git Commit Id Plugin](https://github.com/git-commit-id/git-commit-id- Maven-plugin)的以下声明添加到你的 POM:

<build>
    <plugins>
        <plugin>
            <groupId>pl.project13.maven</groupId>
            <artifactId>git-commit-id-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Gradle 用户可以通过使用[gradle-git-properties](https://plugins. Gradle.org/plugin/com.gorylenko. Gradle-git-properties)插件来实现相同的结果,如以下示例所示:

plugins {
    id "com.gorylenko.gradle-git-properties" version "2.3.2"
}

Maven 和 Gradle 插件都允许配置git.properties中包含的属性。

git.properties中的提交时间预计将匹配以下格式:yyyy-MM-dd’T’HH:mm:ssZ
这是上面列出的两个插件的默认格式。
使用这种格式,当序列化为 JSON 时,可以将时间解析为Date及其格式,由 Jackson 的日期序列化配置设置控制。

# 16.3.自定义依赖版本

spring-boot-dependencies POM 管理公共依赖关系的版本。 Maven 和 Gradle 的 Spring 引导插件允许使用构建属性定制这些托管依赖版本。

Spring 每个启动版本都是针对这一组特定的第三方依赖关系进行设计和测试的。
重写版本可能会导致兼容性问题。

要用 Maven 重写依赖版本,请参见 Maven 插件文档中的本节 (opens new window)

要重写 Gradle 中的依赖关系版本,请参见 Gradle 插件文档中的本节 (opens new window)

# 16.4.用 Maven 创建可执行文件 jar

spring-boot-maven-plugin可用于创建可执行的“fat” jar。如果你使用spring-boot-starter-parent POM,你可以声明该插件,并且你的 JAR 被重新打包如下:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

如果你不使用父 POM,你仍然可以使用该插件。但是,你必须另外添加<executions>部分,如下所示:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>{spring-boot-version}</version>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

有关完整的使用详细信息,请参见插件文档 (opens new window)

# 16.5.使用 Spring 引导应用程序作为依赖项

与 WAR 文件一样, Spring 引导应用程序不打算用作依赖项。如果你的应用程序包含希望与其他项目共享的类,那么推荐的方法是将该代码移动到单独的模块中。然后,你的应用程序和其他项目可以依赖于单独的模块。

如果不能按照上面的建议重新排列代码,则必须对 Spring 引导的 Maven 和 Gradle 插件进行配置,以生成一个单独的工件,该工件适合用作依赖项。可执行归档文件不能作为executable jar format中的BOOT-INF/classes包应用程序类的依赖项使用。这意味着当可执行文件 jar 用作依赖项时,无法找到它们。

要生成两个工件(一个可用作依赖项,另一个可执行),必须指定一个分类器。此分类器应用于可执行归档的名称,将默认归档作为依赖项使用。

要在 Maven 中配置exec的分类器,可以使用以下配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <classifier>exec</classifier>
            </configuration>
        </plugin>
    </plugins>
</build>

# 16.6.当可执行文件 jar 运行 ### 时提取特定的库

jar 可执行文件中的大多数嵌套库不需要解压缩才能运行。然而,某些图书馆可能会有问题。例如,JRuby 包含其自己的嵌套 jar 支持,它假定jruby-complete.jar始终可以直接作为文件使用。

为了处理任何有问题的库,你可以标记特定的嵌套 JAR 应该在可执行文件 jar 首次运行时自动解压缩。这样的嵌套 JAR 写在由java.io.tmpdir系统属性标识的临时目录之下。

应该注意确保配置了你的操作系统,以便在应用程序仍在运行时,它不会删除已解压缩到临时目录的 JAR。

例如,为了表明应该使用 Maven 插件标记 JRuby 以进行解包,你需要添加以下配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <requiresUnpack>
                    <dependency>
                        <groupId>org.jruby</groupId>
                        <artifactId>jruby-complete</artifactId>
                    </dependency>
                </requiresUnpack>
            </configuration>
        </plugin>
    </plugins>
</build>

# 16.7.创建带有排除项的不可执行文件 jar

jar 通常,如果将可执行文件和不可执行文件作为两个单独的构建产品,则可执行版本具有库 jar 中不需要的附加配置文件。例如,application.yml配置文件可能被从非可执行文件 jar 中排除。

在 Maven 中,可执行文件 jar 必须是主要工件,并且可以为库添加一个分类 jar,如下所示:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <executions>
                <execution>
                    <id>lib</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                    <configuration>
                        <classifier>lib</classifier>
                        <excludes>
                            <exclude>application.yml</exclude>
                        </excludes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

# 16.8.用 Maven 启动的 Spring 启动应用程序的远程调试

要将远程调试器附加到用 Maven 启动的 Spring 引导应用程序,可以使用jvmArguments属性的maven plugin (opens new window)

有关更多详细信息,请参见这个例子 (opens new window)

# 16.9.在不使用 Spring-boot-antlib### 的情况下,从 Ant 构建一个可执行的归档文件

要使用 Ant 进行构建,你需要获取依赖项、进行编译,然后创建 jar 或 WAR 归档。要使其可执行,你可以使用spring-boot-antlib模块,也可以遵循以下说明:

  1. 如果正在构建 jar,则将应用程序的类和资源打包到嵌套的BOOT-INF/classes目录中。如果正在构建 WAR,则像往常一样将应用程序的类打包到嵌套的WEB-INF/classes目录中。

  2. 在嵌套的BOOT-INF/lib目录中为 jar 或WEB-INF/lib为 WAR 添加运行时依赖项。记住不是来压缩归档中的条目。

  3. 在嵌套的BOOT-INF/lib目录中添加provided(嵌入式容器)依赖项,用于 jar 或用于 WAR 的WEB-INF/lib-provided。记住不是来压缩归档中的条目。

  4. 在归档的根目录中添加spring-boot-loader类(这样Main-Class就可用了)。

  5. 在清单中使用适当的启动器(例如 jar 文件的JarLauncher)作为Main-Class属性,并指定它需要的其他属性作为清单条目——主要是通过设置Start-Class属性。

下面的示例展示了如何使用 Ant 构建可执行归档文件:

<target name="build" depends="compile">
    <jar destfile="target/${ant.project.name}-${spring-boot.version}.jar" compress="false">
        <mappedresources>
            <fileset dir="target/classes" />
            <globmapper from="*" to="BOOT-INF/classes/*"/>
        </mappedresources>
        <mappedresources>
            <fileset dir="src/main/resources" erroronmissingdir="false"/>
            <globmapper from="*" to="BOOT-INF/classes/*"/>
        </mappedresources>
        <mappedresources>
            <fileset dir="${lib.dir}/runtime" />
            <globmapper from="*" to="BOOT-INF/lib/*"/>
        </mappedresources>
        <zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
        <manifest>
            <attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" />
            <attribute name="Start-Class" value="${start-class}" />
        </manifest>
    </jar>
</target>

# 17. 传统部署

Spring 引导支持传统的部署以及更现代的部署形式。本节回答有关传统部署的常见问题。

# 17.1.创建一个可部署的 WAR 文件

Spring 由于 WebFlux 并不严格依赖于 Servlet API,并且应用程序是默认部署在嵌入式反应堆 Netty 服务器上的,因此 WebFlux 应用程序不支持 WAR 部署。

生成可部署 WAR 文件的第一步是提供SpringBootServletInitializer子类并覆盖其configure方法。这样做利用了 Spring Framework 的 Servlet 3.0 支持,并允许你在 Servlet 容器启动应用程序时对其进行配置。通常,你应该更新应用程序的主类以扩展SpringBootServletInitializer,如下例所示:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MyApplication.class);
    }

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

}

下一步是更新你的构建配置,以便你的项目生成一个 WAR 文件,而不是一个 jar 文件。如果你使用 Maven 和spring-boot-starter-parent(它为你配置了 Maven 的 WAR 插件),那么你所需要做的就是修改pom.xml以将包装更改为 WAR,如下所示:

<packaging>war</packaging>

如果使用 Gradle,则需要修改build.gradle以将 WAR 插件应用到项目中,如下所示:

apply plugin: 'war'

该过程中的最后一步是确保所嵌入的 Servlet 容器不会干扰部署了 WAR 文件的 Servlet 容器。要做到这一点,你需要将嵌入的 Servlet 容器依赖标记为已提供的。

如果使用 Maven,则以下示例将 Servlet 容器( Tomcat,在本例中)标记为被提供的:

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- ... -->
</dependencies>

如果使用 Gradle,以下示例将 Servlet 容器( Tomcat,在这种情况下)标记为被提供的:

dependencies {
    // ...
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    // ...
}
providedRuntime优于 Gradle 的compileOnly配置。
在其他限制中,compileOnly依赖项不在测试 Classpath 上,因此任何基于 Web 的集成测试都会失败。

如果使用Spring Boot build tools,将嵌入的 Servlet 容器依赖项标记为提供的,则生成一个可执行的 WAR 文件,其所提供的依赖项打包在lib-provided目录中。这意味着,除了可以部署到 Servlet 容器之外,还可以通过在命令行上使用java -jar来运行应用程序。

# 17.2.将现有的应用程序转换为 Spring 引导

要将现有的非 Web Spring 应用程序转换为 Spring 引导应用程序,请替换创建ApplicationContext的代码,并将其替换为对SpringApplicationSpringApplicationBuilder的调用。 Spring MVC Web 应用程序通常适于首先创建可部署的 WAR 应用程序,然后将其稍后迁移到可执行的 WAR 或 jar。参见Getting Started Guide on Converting a jar to a war (opens new window)

要通过扩展SpringBootServletInitializer(例如,在一个名为Getting Started Guide on Converting a jar to a war (opens new window)的类中)并添加 Spring boot@SpringBootApplication注释来创建可部署的 WAR,请使用类似于以下示例中所示的代码:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        // Customize the application or call application.sources(...) to add sources
        // Since our example is itself a @Configuration class (via @SpringBootApplication)
        // we actually do not need to override this method.
        return application;
    }

}

请记住,无论你在sources中放入什么,都只是一个 Spring ApplicationContext。通常情况下,任何已经起作用的东西都应该在这里起作用。可能有一些 bean 可以稍后删除,并让 Spring boot 为它们提供自己的默认值,但是在你需要这样做之前,应该有可能使某些工作正常进行。

静态资源可以在 Classpath 根中移动到/public(或/staticGetting Started Guide on Converting a jar to a war (opens new window)/META-INF/resources)。这同样适用于messages.properties( Spring boot 在 Classpath 的根中自动检测)。

Spring DispatcherServlet和 Spring 安全性的普通用法应该不需要进一步的更改。如果你的应用程序中有其他功能(例如,使用其他 servlet 或过滤器),则可能需要通过替换web.xml中的那些元素,向Application上下文添加一些配置,如下所示:

  • 类型为ServletServletRegistrationBean@Bean将 Bean 安装到容器中,就好像它是<servlet/><servlet/>中的容器一样。

  • 类型FilterFilterRegistrationBean@Bean的行为类似(如<filter/><filter-mapping/>)。

  • XML 文件中的ApplicationContext可以通过@ImportResource中的Application添加。或者,注释配置已经被大量使用的情况可以用几行重新创建@Bean定义。

一旦 WAR 文件开始工作,你就可以通过向你的Application添加main方法使其可执行,如以下示例所示:

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

如果你打算以 WAR 或可执行应用程序的形式启动你的应用程序,你需要在一个方法中共享构建器的自定义,该方法对SpringBootServletInitializer回调和main方法都是可用的,其类类似于:

应用程序可以分为多个类别:

  • Servlet 3.0+noweb.xml的应用程序。

  • 带有web.xml的应用程序。

  • 具有上下文层次结构的应用程序。

  • 没有上下文层次结构的应用程序。

所有这些都应该易于翻译,但每一种可能需要略有不同的技术。

Servlet 3.0+ 应用程序如果已经使用了 Spring Servlet 3.0+ 初始化器支持类,可能会非常容易地进行转换。通常,来自现有WebApplicationInitializer的所有代码都可以移动到SpringBootServletInitializer中。如果你现有的应用程序有多个ApplicationContext(例如,如果它使用AbstractDispatcherServletInitializer),那么你可以将所有的上下文源合并到一个main中。你可能遇到的主要复杂情况是,如果合并不起作用,并且需要维护上下文层次结构。有关示例,请参见关于建立层次结构的条目。包含特定于 Web 的特性的现有父上下文通常需要分解,以便所有ServletContextAware组件都在子上下文中。

Spring 应用程序可能可转换为 Spring 引导应用程序,而前面提到的指导可能会有所帮助。然而,你可能还会遇到问题。在这种情况下,我们建议[使用spring-boot标记在堆栈溢出上提出问题](https://stackoverflow.com/questions/tagged/ Spring-boot)。

# 17.3.向 WebLogic 部署一场战争

要将 Spring 引导应用程序部署到 WebLogic,你必须确保你的 Servlet 初始化器直接实现了WebApplicationInitializer(即使你从已经实现它的基类进行了扩展)。

WebLogic 的典型初始化器应该类似于以下示例:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.WebApplicationInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {

}

如果你使用注销,你还需要告诉 WebLogic 更喜欢打包的版本,而不是服务器预装的版本。可以通过添加具有以下内容的WEB-INF/weblogic.xml文件来实现此目的:

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
    xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        https://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
        http://xmlns.oracle.com/weblogic/weblogic-web-app
        https://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
    <wls:container-descriptor>
        <wls:prefer-application-packages>
            <wls:package-name>org.slf4j</wls:package-name>
        </wls:prefer-application-packages>
    </wls:container-descriptor>
</wls:weblogic-web-app>