本节提供了使用 Spring Boot 时经常出现的一些常见“我该怎么做……”问题的答案。它的覆盖范围并不详尽,但确实涵盖了很多。

如果您有我们未在此处介绍的特定问题,您可能需要查看stackoverflow.com以查看是否有人已经提供了答案。这也是提出新问题的好地方(请使用spring-boot标签)。

我们也非常乐意扩展本节。如果您想添加“操作方法”,请向我们发送拉取请求

1. Spring Boot 应用程序

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

1.1。创建您自己的故障分析器

FailureAnalyzer是一种在启动时拦截异常并将其转换为人类可读消息的好方法,并以FailureAnalysis. Spring Boot 为应用程序上下文相关的异常、JSR-303 验证等提供了这样的分析器。您也可以创建自己的。

AbstractFailureAnalyzer是一个方便的扩展,FailureAnalyzer它检查要处理的异常中是否存在指定的异常类型。您可以从中扩展,以便您的实现只有在异常实际存在时才有机会处理异常。如果由于某种原因,您无法处理异常,请返回null以给另一个实现处理异常的机会。

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

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

1.2. 自动配置故障排除

Spring Boot 自动配置尽最大努力“做正确的事”,但有时事情会失败,而且很难说出原因。

ConditionEvaluationReport在任何 Spring Boot 中都有一个非常有用的可用ApplicationContext。如果启用DEBUG日志输出,您可以看到它。如果您使用spring-boot-actuator(请参阅执行器章节),还有一个conditions以 JSON 格式呈现报告的端点。使用该端点来调试应用程序,并查看 Spring Boot 在运行时添加了哪些功能(哪些还没有添加)。

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

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

  • 查找类@ConfigurationProperties(例如ServerProperties)并从那里读取可用的外部配置选项。@ConfigurationProperties注释有一个name作为外部属性前缀的属性。因此,ServerPropertieshasprefix="server"及其配置属性是server.portserver.address和其他。在启用执行器的运行应用程序中,查看configprops端点。

  • 寻找该bind方法的用法,以轻松的方式Binder显式地将配置值拉出。Environment它通常与前缀一起使用。

  • 查找@Value直接绑定到Environment.

  • 查找@ConditionalOnExpression响应 SpEL 表达式打开和关闭功能的注释,通常使用从Environment.

1.3. 在启动之前自定义环境或 ApplicationContext

A SpringApplicationhas ApplicationListenersand ApplicationContextInitializersthat 用于将自定义应用到上下文或环境。Spring Boot 从META-INF/spring.factories. 注册附加自定义的方法不止一种:

  • 以编程方式,每个应用程序,通过在运行之前调用addListenersaddInitializers方法。SpringApplication

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

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

向侦听器SpringApplication发送一些特殊ApplicationEvents信息(甚至在创建上下文之前),然后也为侦听器注册发布的事件ApplicationContext。有关完整列表,请参阅“ Spring Boot 特性”部分中的“应用程序事件和侦听器”。

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

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

该实现可以加载任意文件并将它们添加到Environment. 例如,以下示例从类路径加载 YAML 配置文件:

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

}
科特林
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
import java.io.IOException

class MyEnvironmentPostProcessor : EnvironmentPostProcessor {

    private val loader = YamlPropertySourceLoader()

    override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
        val path: Resource = ClassPathResource("com/example/myapp/config.yml")
        val propertySource = loadYaml(path)
        environment.propertySources.addLast(propertySource)
    }

    private fun loadYaml(path: Resource): PropertySource<*> {
        Assert.isTrue(path.exists()) { "Resource $path does not exist" }
        return try {
            loader.load("custom-resource", path)[0]
        } catch (ex: IOException) {
            throw IllegalStateException("Failed to load yaml configuration from $path", ex)
        }
    }

}
Environment已经准备好 Spring Boot 默认加载的所有常用属性源 。因此,可以从环境中获取文件的位置。前面的示例将custom-resource属性源添加到列表的末尾,以便在任何通常的其他位置中定义的键优先。自定义实现可以定义另一个顺序。
虽然使用@PropertySourceon your@SpringBootApplication似乎是一种在 中加载自定义资源的便捷方式Environment,但我们不建议这样做。Environment在刷新应用程序上下文之前,不会将此类属性源添加到 中。配置某些属性(例如在刷新开始之前读取 的logging.*和)为时已晚。spring.main.*

1.4. 构建 ApplicationContext 层次结构(添加父上下文或根上下文)

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

1.5。创建非 Web 应用程序

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

2. 属性和配置

本节包括有关设置和读取属性和配置设置以及它们与 Spring Boot 应用程序交互的主题。

2.1。在构建时自动展开属性

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

2.1.1。使用 Maven 自动扩展属性

您可以使用资源过滤自动扩展 Maven 项目的属性。如果使用spring-boot-starter-parent,则可以使用占位符引用 Maven 的“项目属性” ,如以下示例所示:@[email protected]

yaml
app:
  encoding: "@[email protected]"
  java:
    version: "@[email protected]"
以这种方式仅过滤生产配置(换句话说,不应用过滤src/test/resources)。
如果启用该addResources标志,spring-boot:run目标可以src/main/resources直接添加到类路径(用于热重载目的)。这样做会绕过资源过滤和此功能。相反,您可以使用exec:java目标或自定义插件的配置。有关更多详细信息,请参阅插件使用页面

如果您不使用起始父级,则需要在<build/>您的元素中包含以下元素pom.xml

<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>
useDefaultDelimiters如果您在配置中使用标准 Spring 占位符(例如),则 该属性很重要${placeholder}。如果该属性未设置为false,则这些可能会被构建扩展。

2.1.2. 使用 Gradle 自动扩展属性

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

tasks.named('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 的配置

ASpringApplication具有 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 Boot 横幅,并且应用程序没有启动嵌入式 Web 服务器。

外部配置中定义的属性会覆盖并替换使用 Java API 指定的值,主要来源除外。主要来源是提供给SpringApplication构造函数的来源:

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

@SpringBootApplication
public class MyApplication {

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

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

@SpringBootApplication
object MyApplication {

    @JvmStatic
    fun main(args: Array<String>) {
        val application = SpringApplication(MyApplication::class.java)
        application.setBannerMode(Banner.Mode.OFF)
        application.run(*args)
    }

}

或者到sources(…​)a 的方法SpringApplicationBuilder

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

}
科特林
import org.springframework.boot.Banner
import org.springframework.boot.builder.SpringApplicationBuilder

object MyApplication {

    @JvmStatic
    fun main(args: Array<String>) {
        SpringApplicationBuilder()
            .bannerMode(Banner.Mode.OFF)
            .sources(MyApplication::class.java)
            .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. 更改应用程序外部属性的位置

默认情况下,来自不同来源的属性会Environment按照定义的顺序添加到 Spring 中(有关确切顺序,请参见“Spring Boot 特性”部分中的“features.html”)

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

  • spring.config.name( SPRING_CONFIG_NAME):默认application为文件名的根。

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

无论您在环境中设置什么,Spring Boot 始终application.properties按上述方式加载。默认情况下,如果使用 YAML,则扩展名为“.yml”的文件也会添加到列表中。

Spring Boot 记录在该级别加载的配置文件以及在该DEBUG级别未找到的候选文件TRACE

有关ConfigFileApplicationListener更多详细信息,请参阅。

2.4. 使用“短”命令行参数

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

特性
server.port=${port:8080}
yaml
server:
  port: "${port:8080}"
如果您从spring-boot-starter-parentPOM 继承,则 的默认过滤器标记maven-resources-plugins已从 更改${*}@(即,而不是),以防止与 Spring 样式的占位符发生冲突。如果您已直接启用 Maven 过滤,您可能还需要更改默认过滤器令牌以使用其他分隔符@[email protected]${maven.token}application.properties
在这种特定情况下,端口绑定在 Heroku 或 Cloud Foundry 等 PaaS 环境中工作。在这两个平台中,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并将其放在类路径的根目录中。然后添加snakeyaml到您的依赖项(Maven 坐标org.yaml:snakeyaml,如果您使用,则已包含spring-boot-starter)。YAML 文件被解析为 Java Map<String,Object>(如 JSON 对象),Spring Boot 将映射展平,使其具有一层深度并具有以句点分隔的键,正如许多人习惯于使用PropertiesJava 中的文件一样。

前面的 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 Boot 功能”部分中的“ features.html ”。

2.6. 设置活动弹簧配置文件

SpringEnvironment对此有一个 API,但您通常会设置系统属性 ( spring.profiles.active) 或操作系统环境变量 ( SPRING_PROFILES_ACTIVE)。此外,您可以使用参数启动应用程序-D(请记住将其放在主类或 jar 存档之前),如下所示:

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

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

特性
spring.profiles.active=production
yaml
spring:
  profiles:
    active: "production"

以这种方式设置的值将被系统属性或环境变量设置替换,但不会被SpringApplicationBuilder.profiles()方法替换。因此,后一种 Java API 可用于在不更改默认值的情况下扩充配置文件。

有关更多信息,请参阅“ Spring Boot特性”部分中的“features.html”。

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

默认配置文件是在没有配置文件处于活动状态时启用的配置文件。默认情况下,默认配置文件的名称是default,但可以使用系统属性 ( spring.profiles.default) 或操作系统环境变量 ( SPRING_PROFILES_DEFAULT) 进行更改。

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

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

有关更多信息,请参阅“ Spring Boot特性”部分中的“features.html”。

2.8. 根据环境更改配置

Spring Boot 支持多文档 YAML 和 Properties 文件(有关详细信息,请参阅features.html),这些文件可以根据活动配置文件有条件地激活。

如果文档包含spring.config.activate.on-profile键,则将配置文件值(以逗号分隔的配置文件列表或配置文件表达式)输入 SpringEnvironment.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。但是,如果名为“development”的 Spring 配置文件处于活动状态,则端口为 9001。如果“production”处于活动状态,则端口为 0。

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

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

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

具有 Actuator 功能的正在运行的应用程序具有一个configprops端点,该端点显示所有通过@ConfigurationProperties.

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

3. 嵌入式 Web 服务器

每个 Spring Boot Web 应用程序都包含一个嵌入式 Web 服务器。此功能会导致许多操作方法问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。本节回答了这些问题。

3.1。使用另一个 Web 服务器

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

  • 对于 servlet 堆栈应用程序,通过spring-boot-starter-webinclude 包含 Tomcat spring-boot-starter-tomcat,但您可以使用spring-boot-starter-jettyspring-boot-starter-undertow代替。

  • 对于反应式堆栈应用程序,通过spring-boot-starter-webfluxinclude 包含 Reactor Netty spring-boot-starter-reactor-netty,但您可以使用spring-boot-starter-tomcat,spring-boot-starter-jettyspring-boot-starter-undertow代替。

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

以下 Maven 示例展示了如何排除 Tomcat 并包含 Spring MVC 的 Jetty:

<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。

如果您希望使用支持 servlet 4.0 的 Jetty 10,您可以按照以下示例进行操作:

<properties>
    <jetty.version>10.0.8</jetty.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>
    <exclusions>
        <!-- Exclude the Jetty-9 specific dependencies -->
        <exclusion>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>websocket-server</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>javax-websocket-server-impl</artifactId>
        </exclusion>
    </exclusions>
</dependency>

请注意,除了排除 Tomcat 启动器之外,还需要排除一些特定于 Jetty9 的依赖项。

以下 Gradle 示例配置了必要的依赖项和模块替换,以使用 Undertow 代替 Spring WebFlux 的 Reactor Netty:

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 服务器

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

特性
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(例如,作为操作系统环境变量)。

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

有关更多详细信息,请参阅“ Spring Boot 特性”部分中的“ web.htmlServerProperties ”或源代码。

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

要扫描空闲端口(使用 OS 本机来防止冲突),请使用server.port=0.

3.5. 在运行时发现 HTTP 端口

您可以从日志输出或WebServerApplicationContext通过其WebServer. 获得它并确保它已被初始化的最佳方法是添加一个@Bean类型ApplicationListener<WebServerInitializedEvent>并在发布时将容器从事件中拉出。

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

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

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

    @LocalServerPort
    int port;

    // ...

}
科特林
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.server.LocalServerPort

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

    @LocalServerPort
    var port = 0

    // ...

}

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

3.6. 启用 HTTP 响应压缩

Jetty、Tomcat、Reactor Netty 和 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

SSL 可以通过设置各种server.ssl.*属性以声明方式配置,通常在application.properties或中application.yml。以下示例显示了使用 Java KeyStore 文件设置 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"

以下示例显示使用 PEM 编码的证书和私钥文件设置 SSL 属性:

特性
server.port=8443
server.ssl.certificate=classpath:my-cert.crt
server.ssl.certificate-private-key=classpath:my-cert.key
server.ssl.trust-certificate=classpath:ca-cert.crt
server.ssl.key-store-password=secret
yaml
server:
  port: 8443
  ssl:
    certificate: "classpath:my-cert.crt"
    certificate-private-key: "classpath:my-cert.key"
    trust-certificate: "classpath:ca-cert.crt"
    key-store-password: "secret"

有关Ssl所有受支持属性的详细信息,请参阅。

使用如前面示例的配置意味着应用程序不再支持端口 8080 上的普通 HTTP 连接器。Spring Boot 不支持通过 配置 HTTP 连接器和 HTTPS 连接器application.properties。如果您想同时拥有两者,则需要以编程方式配置其中之一。我们建议使用application.properties来配置 HTTPS,因为 HTTP 连接器是两者中更容易以编程方式配置的。

3.8. 配置 HTTP/2

您可以使用server.http2.enabled配置属性在 Spring Boot 应用程序中启用 HTTP/2 支持。支持h2(基于 TLS 的 HTTP/2)和h2c(基于 TCP 的 HTTP/2)。要使用h2,还必须启用 SSL。未启用 SSL 时,h2c将使用。支持的详细信息h2取决于所选的 Web 服务器和应用程序环境,因为并非所有 JDK 8 版本都开箱即用地支持该协议。

3.8.1. HTTP/2 与 Tomcat

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

库目录必须对 JVM 库路径可用(如果还没有的话)。您可以使用 JVM 参数来执行此操作,例如-Djava.library.path=/usr/local/opt/tomcat-native/lib. 更多信息请参阅Tomcat 官方文档

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

错误 8787 --- [main] oacoyote.http11.Http11NioProtocol:[h2] 的升级处理程序 [org.apache.coyote.http2.Http2Protocol] 仅支持通过 ALPN 升级,但已为 ["https-jsse-nio -8443"] 不支持 ALPN 的连接器。

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

3.8.2. HTTP/2 与 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 库

3.8.3. 带有 Reactor Netty 的 HTTP/2

默认情况下spring-boot-webflux-starter使用 Reactor Netty 作为服务器。Reactor Netty 支持h2c使用 JDK 8 或更高版本,无需额外依赖。Reactor Netty 支持h2在 JDK 9 或更高版本中使用 JDK 支持。对于 JDK 8 环境,或为了获得最佳运行时性能,该服务器还支持h2原生库。要启用它,您的应用程序需要有一个额外的依赖项。

Spring Boot 管理io.netty:netty-tcnative-boringssl-static“uber jar”的版本,包含所有平台的本地库。开发人员可以选择使用分类器仅导入所需的依赖项(参见Netty 官方文档)。

3.8.4. HTTP/2 与 Undertow

从 Undertow 1.4.0+ 开始, JDK 8 支持h2h2c,没有任何额外的依赖项。

3.9. 配置 Web 服务器

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

前面的部分已经涵盖了许多常见用例,例如压缩、SSL 或 HTTP/2。但是,如果您的用例不存在配置密钥,则应查看WebServerFactoryCustomizer. 您可以声明这样一个组件并访问与您的选择相关的服务器工厂:您应该为所选服务器(Tomcat、Jetty、Reactor Netty、Undertow)和所选 Web 堆栈(servlet 或响应式)选择变体。

以下示例适用于带有spring-boot-starter-web(servlet 堆栈)的 Tomcat:

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

}
科特林
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component

@Component
class MyTomcatWebServerCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory?> {

    override fun customize(factory: TomcatServletWebServerFactory?) {
        // customize the factory here
    }

}
Spring Boot 在内部使用该基础架构来自动配置服务器。自动配置的WebServerFactoryCustomizerbean 有一个顺序0并且将在任何用户定义的定制器之前处理,除非它有一个明确的顺序,否则。

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

此外,Spring Boot 还提供:

服务器 Servlet 堆栈 反应堆

雄猫

TomcatServletWebServerFactory

TomcatReactiveWebServerFactory

码头

JettyServletWebServerFactory

JettyReactiveWebServerFactory

暗流

UndertowServletWebServerFactory

UndertowReactiveWebServerFactory

反应堆

不适用

NettyReactiveWebServerFactory

作为最后的手段,您还可以声明自己的WebServerFactorybean,它将覆盖 Spring Boot 提供的 bean。当您这样做时,自动配置的定制器仍会应用于您的自定义工厂,因此请谨慎使用该选项。

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

在 servlet 堆栈应用程序中,即使用spring-boot-starter-web,有两种方法可以将ServletFilterServletContextListener和 Servlet API 支持的其他侦听器添加到您的应用程序:

3.10.1。使用 Spring Bean 添加 Servlet、过滤器或侦听器

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

在过滤器和 servlet 的情况下,您还可以通过添加 aFilterRegistrationBean或 a来添加映射和初始化参数,ServletRegistrationBean而不是添加或添加到底层组件。

如果dispatcherType在过滤器注册上指定 no,REQUEST则使用。这与 servlet 规范的默认调度程序类型一致。

像任何其他 Spring bean 一样,您可以定义 servlet 过滤器 bean 的顺序;请务必检查“ web.html ”部分。

禁用 Servlet 或过滤器的注册

如前所述任何ServletFilterbean 都会自动注册到 servlet 容器中。要禁用特定FilterServletbean 的注册,请为其创建一个注册 bean 并将其标记为禁用,如以下示例所示:

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

}
科特林
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {

    @Bean
    fun registration(filter: MyFilter): FilterRegistrationBean<MyFilter> {
        val registration = FilterRegistrationBean(filter)
        registration.isEnabled = false
        return registration
    }

}

3.10.2. 使用类路径扫描添加 Servlet、过滤器和侦听器

@WebServlet, @WebFilter, 和带注释的类可以通过使用注释类并指定包含要注册的组件的包@WebListener来自动注册到嵌入式 servlet 容器。默认情况下,从注释类的包中扫描。@Configuration@ServletComponentScan@ServletComponentScan

3.11。配置访问日志

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

例如,以下设置使用自定义模式在 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)"
日志的默认位置是logs相对于 Tomcat 基本目录的目录。默认情况下,该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 "Forwarded Headers"定义了ForwardedHTTP 标头;代理可以使用此标头来提供有关原始请求的信息。您可以将应用程序配置为读取这些标头,并在创建链接并将它们发送到 HTTP 302 响应、JSON 文档或 HTML 页面中的客户端时自动使用该信息。还有非标准标题,如X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-Ssl, 和X-Forwarded-Prefix.

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

如果这还不够,Spring Framework 提供了一个ForwardedHeaderFilterserver.forward-headers-strategy您可以通过将其设置为 将其注册为应用程序中的 servlet 过滤器FRAMEWORK

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

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 还配置了一个正则表达式,该表达式匹配要信任的内部代理。有关其默认值,请参见server.tomcat.remoteip.internal-proxies附录中的条目。您可以通过向 中添加条目来自定义阀门的配置,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(但不要在生产中这样做)。

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

3.13。使用 Tomcat 启用多个连接器

您可以在 中添加一个org.apache.catalina.connector.ConnectorTomcatServletWebServerFactory它可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如下例所示:

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

}
科特林
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
import java.io.IOException

@Configuration(proxyBeanMethods = false)
class MyTomcatConfiguration {

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

    private fun createSslConnector(): Connector {
        val connector = Connector("org.apache.coyote.http11.Http11NioProtocol")
        val protocol = connector.protocolHandler as Http11NioProtocol
        return try {
            val keystore = ResourceUtils.getURL("keystore")
            val truststore = ResourceUtils.getURL("truststore")
            connector.scheme = "https"
            connector.secure = true
            connector.port = 8443
            protocol.isSSLEnabled = true
            protocol.keystoreFile = keystore.toString()
            protocol.keystorePass = "changeit"
            protocol.truststoreFile = truststore.toString()
            protocol.truststorePass = "changeit"
            protocol.keyAlias = "apitester"
            connector
        } catch (ex: IOException) {
            throw IllegalStateException("Fail to create ssl connector", ex)
        }
    }

}

3.14。使用 Tomcat 的 LegacyCookieProcessor

默认情况下,Spring Boot 使用的嵌入式 Tomcat 不支持 Cookie 格式的“Version 0”,所以你可能会看到如下错误:

java.lang.IllegalArgumentException:Cookie 值中存在无效字符 [32]

如果可能的话,您应该考虑更新您的代码以仅存储符合以后 Cookie 规范的值。但是,如果您无法更改写入 cookie 的方式,则可以改为将 Tomcat 配置为使用LegacyCookieProcessor. 要切换到LegacyCookieProcessor,请使用WebServerFactoryCustomizer添加 的 bean TomcatContextCustomizer,如以下示例所示:

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

}
科特林
import org.apache.catalina.Context
import org.apache.tomcat.util.http.LegacyCookieProcessor
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer
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)
class MyLegacyCookieProcessorConfiguration {

    @Bean
    fun cookieProcessorCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
        return WebServerFactoryCustomizer { factory: TomcatServletWebServerFactory ->
            factory
                .addContextCustomizers(TomcatContextCustomizer { context: Context ->
                    context.cookieProcessor = 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 启用多个侦听器

添加一个UndertowBuilderCustomizerUndertowServletWebServerFactory并添加一个监听器到Builder,如以下示例所示:

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

}
科特林
import io.undertow.Undertow
import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer
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)
class MyUndertowConfiguration {

    @Bean
    fun undertowListenerCustomizer(): WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
        return WebServerFactoryCustomizer { factory: UndertowServletWebServerFactory ->
            factory.addBuilderCustomizers(
                UndertowBuilderCustomizer { builder: Undertow.Builder -> addHttpListener(builder) })
        }
    }

    private fun addHttpListener(builder: Undertow.Builder): Undertow.Builder {
        return builder.addHttpListener(8080, "0.0.0.0")
    }

}

3.17。使用 @ServerEndpoint 创建 WebSocket 端点

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

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

}
科特林
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.socket.server.standard.ServerEndpointExporter

@Configuration(proxyBeanMethods = false)
class MyWebSocketConfiguration {

    @Bean
    fun serverEndpointExporter(): ServerEndpointExporter {
        return ServerEndpointExporter()
    }

}

前面示例中显示的 bean 将任何@ServerEndpoint带注释的 bean 注册到底层 WebSocket 容器。当部署到独立的 servlet 容器时,此角色由 servlet 容器初始化程序执行,并且ServerEndpointExporter不需要 bean。

4. SpringMVC

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

4.1。编写 JSON REST 服务

只要 Jackson2 在类路径上,Spring Boot 应用程序中的任何 Spring@RestController都应该默认呈现 JSON 响应,如下例所示:

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

}
科特林
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class MyController {

    @RequestMapping("/thing")
    fun thing(): MyThing {
        return MyThing()
    }

}

只要MyThing可以由 Jackson2 序列化(对于普通 POJO 或 Groovy 对象为 true),则localhost:8080/thing默认提供它的 JSON 表示。请注意,在浏览器中,您有时可能会看到 XML 响应,因为浏览器倾向于发送更喜欢 XML 的接受标头。

4.2. 编写 XML REST 服务

如果jackson-dataformat-xml类路径上有 Jackson XML 扩展 ( ),则可以使用它来呈现 XML 响应。我们之前用于 JSON 的示例可以工作。要使用 Jackson XML 渲染器,请将以下依赖项添加到您的项目中:

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

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

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

}
科特林
import javax.xml.bind.annotation.XmlRootElement

@XmlRootElement
class MyThing {

    var name: String? = null

}

JAXB 仅适用于 Java 8。如果您使用较新的 Java 代,请将以下依赖项添加到您的项目中:

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

4.3. 自定义 Jackson ObjectMapper

Spring MVC(客户端和服务器端)用于HttpMessageConverters协商 HTTP 交换中的内容转换。如果 Jackson 在类路径上,您已经获得了由 提供的默认转换器Jackson2ObjectMapperBuilder,它的一个实例是为您自动配置的。

ObjectMapperXmlMapper用于 Jackson XML 转换器)实例(默认创建)具有以下自定义属性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION被禁用

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES被禁用

  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS被禁用

Spring Boot 还具有一些特性,可以更轻松地自定义此行为。

您可以使用环境配置ObjectMapperXmlMapper实例。Jackson 提供了一套广泛的开/关功能,可用于配置其处理的各个方面。这些特性在六个枚举(在 Jackson 中)中描述,它们映射到环境中的属性:

枚举 财产 价值观

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

例如,要启用漂亮打印,请设置spring.jackson.serialization.indent_output=true. 请注意,由于使用了宽松的绑定,因此indent_output不必匹配相应枚举常量的大小写,即INDENT_OUTPUT.

这种基于环境的配置应用于自动配置的Jackson2ObjectMapperBuilderbean,并应用于使用构建器创建的任何映射器,包括自动配置的ObjectMapperbean。

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

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

如果您想ObjectMapper完全替换默认值,请定义@Bean该类型的 a 并将其标记为,@Primary或者,如果您更喜欢基于构建器的方法,请定义 a Jackson2ObjectMapperBuilder @Bean。请注意,在任何一种情况下,这样做都会禁用ObjectMapper.

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

有关更多详细信息,请参阅“自定义 @ResponseBody 渲染”部分和WebMvcAutoConfiguration源代码。

4.4. 自定义 @ResponseBody 渲染

Spring 用于HttpMessageConverters渲染@ResponseBody(或来自 的响应@RestController)。您可以通过在 Spring Boot 上下文中添加适当类型的 bean 来贡献其他转换器。如果您添加的 bean 属于默认情况下会包含的类型(例如MappingJackson2HttpMessageConverter用于 JSON 转换),它将替换默认值。提供了一个便利类型的 bean,HttpMessageConverters如果您使用默认的 MVC 配置,它总是可用的。它有一些有用的方法来访问默认和用户增强的消息转换器(例如,如果你想手动将它们注入到自定义中,它会很有用RestTemplate)。

与正常的 MVC 用法一样,WebMvcConfigurer您提供的任何 bean 也可以通过覆盖该configureMessageConverters方法来贡献转换器。但是,与普通 MVC 不同,您只能提供您需要的其他转换器(因为 Spring Boot 使用相同的机制来贡献其默认值)。最后,如果您通过提供自己的配置来选择退出 Spring Boot 默认 MVC 配置@EnableWebMvc,则可以完全控制并使用getMessageConvertersfrom手动完成所有操作WebMvcConfigurationSupport

有关更多详细信息,请参阅WebMvcAutoConfiguration源代码。

4.5. 处理多部分文件上传

Spring Boot 包含 servlet 3 javax.servlet.http.PartAPI 来支持上传文件。默认情况下,Spring Boot 将 Spring MVC 配置为每个文件的最大大小为 1MB,单个请求中的文件数据最大为 10MB。您可以使用类中公开的属性覆盖这些值、存储中间数据的位置(例如,存储到/tmp目录)以及数据刷新到磁盘的阈值MultipartProperties。例如,如果要指定文件不受限制,请将spring.servlet.multipart.max-file-size属性设置为-1

当您希望在 Spring MVC 控制器处理程序方法中接收多部分编码的文件数据作为@RequestParam类型的 -annotated 参数时,多部分支持很有帮助。MultipartFile

有关更多详细信息,请参阅MultipartAutoConfiguration源代码。

建议使用容器对分段上传的内置支持,而不是引入额外的依赖项,例如 Apache Commons File Upload。

4.6. 关闭 Spring MVC DispatcherServlet

默认情况下,所有内容都从应用程序的根目录 ( /) 提供。如果您希望映射到不同的路径,您可以配置如下:

特性
spring.mvc.servlet.path=/mypath
yaml
spring:
  mvc:
    servlet:
      path: "/mypath"

如果您有额外的 servlet,您可以为每个@Bean类型声明一个ServletServletRegistrationBean,Spring Boot 会将它们透明地注册到容器中。因为 servlet 是以这种方式注册的,所以它们可以映射到 的子上下文DispatcherServlet而不调用它。

配置DispatcherServlet你自己是不寻常的,但如果你真的需要这样做,还必须提供一个@Bean类型来提供你自定义的路径。DispatcherServletPathDispatcherServlet

4.7. 关闭默认 MVC 配置

完全控制 MVC 配置的最简单方法是提供您自己@Configuration@EnableWebMvc注解。这样做会将所有 MVC 配置留在您的手中。

4.8. 自定义 ViewResolver

AViewResolver是 Spring MVC 的核心组件,将视图名称转换@Controller为实际View实现。请注意,ViewResolvers主要用于 UI 应用程序,而不是 REST 样式的服务(aView不用于渲染 a @ResponseBody)。有许多实现ViewResolver可供选择,而 Spring 本身并不认为应该使用哪些实现。另一方面,Spring Boot 会为您安装一个或两个,具体取决于它在类路径和应用程序上下文中找到的内容。使用它在应用程序上下文中找到的DispatcherServlet所有解析器,依次尝试每个解析器,直到得到结果。如果您添加自己的,则必须了解添加解析器的顺序和位置。

WebMvcAutoConfiguration将以下内容添加ViewResolvers到您的上下文中:

  • 一个InternalResourceViewResolver名为“defaultViewResolver”。这个定位可以通过使用来呈现的物理资源DefaultServlet(包括静态资源和 JSP 页面,如果您使用它们)。它将前缀和后缀应用于视图名称,然后在 servlet 上下文中查找具有该路径的物理资源(默认值均为空,但可通过spring.mvc.view.prefix和进行外部配置访问spring.mvc.view.suffix)。您可以通过提供相同类型的 bean 来覆盖它。

  • 一个BeanNameViewResolver名为“beanNameViewResolver”。这是视图解析器链中一个有用的成员,它会拾取任何与View正在解析的名称相同的 bean。没有必要覆盖或替换它。

  • 仅当实际存在类型的 bean 时ContentNegotiatingViewResolver才会添加命名的“viewResolver” 。这是一个复合解析器,委托给所有其他解析器并尝试找到与客户端发送的“接受”HTTP 标头的匹配项。有一个有用的博客,您可能想学习以了解更多信息,您还可以查看源代码以获取详细信息。您可以通过定义一个名为“viewResolver”的 bean来关闭自动配置。ViewContentNegotiatingViewResolverContentNegotiatingViewResolver

  • 如果你使用 Thymeleaf,你还有一个ThymeleafViewResolver名为 'thymeleafViewResolver' 的名字。它通过用前缀和后缀包围视图名称来查找资源。前缀是spring.thymeleaf.prefix,后缀是spring.thymeleaf.suffix。前缀和后缀的值分别默认为 'classpath:/templates/' 和 '.html'。ThymeleafViewResolver您可以通过提供同名的 bean来覆盖。

  • 如果您使用 FreeMarker,您还有一个FreeMarkerViewResolver名为“freeMarkerViewResolver”的工具。spring.freemarker.templateLoaderPath它通过用前缀和后缀包围视图名称来在加载器路径(外部化并具有默认值'classpath:/templates/')中查找资源。前缀外spring.freemarker.prefix化为 ,后缀外化为spring.freemarker.suffix。前缀和后缀的默认值分别为空和“.ftlh”。FreeMarkerViewResolver您可以通过提供同名的 bean来覆盖。

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

  • 如果您使用 Mustache,您还有一个MustacheViewResolver名为“mustacheViewResolver”。它通过用前缀和后缀包围视图名称来查找资源。前缀是spring.mustache.prefix,后缀是spring.mustache.suffix。前缀和后缀的值分别默认为“classpath:/templates/”和“.mustache”。MustacheViewResolver您可以通过提供同名的 bean来覆盖。

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

5. 泽西岛

5.1。使用 Spring Security 保护 Jersey 端点

Spring Security 可用于保护基于 Jersey 的 Web 应用程序,就像它可用于保护基于 Spring MVC 的 Web 应用程序一样。但是,如果您想在 Jersey 中使用 Spring Security 的方法级安全性,则必须将 Jersey 配置为使用setStatus(int)rather sendError(int)。这可以防止 Jersey 在 Spring Security 有机会向客户端报告身份验证或授权失败之前提交响应。

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

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

}
科特林
import org.glassfish.jersey.server.ResourceConfig
import org.springframework.stereotype.Component
import java.util.Collections

@Component
class JerseySetStatusOverSendErrorConfig : ResourceConfig() {

    init {
        register(Endpoint::class.java)
        setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true))
    }

}

5.2. 将 Jersey 与另一个 Web 框架一起使用

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

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

}
科特林
import org.glassfish.jersey.server.ResourceConfig
import org.glassfish.jersey.servlet.ServletProperties
import org.springframework.stereotype.Component

@Component
class JerseyConfig : ResourceConfig() {

    init {
        register(Endpoint::class.java)
        property(ServletProperties.FILTER_FORWARD_ON_404, true)
    }

}

6. HTTP 客户端

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

6.1。配置 RestTemplate 以使用代理

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

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

6.2. 配置基于 Reactor Netty 的 WebClient 使用的 TcpClient

当 Reactor Netty 在类路径上时,WebClient会自动配置基于 Reactor Netty。要自定义客户端对网络连接的处理,请提供一个ClientHttpConnectorbean。以下示例配置 60 秒的连接超时并添加ReadTimeoutHandler

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

}
科特林
import io.netty.channel.ChannelOption
import io.netty.handler.timeout.ReadTimeoutHandler
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
import reactor.netty.http.client.HttpClient

@Configuration(proxyBeanMethods = false)
class MyReactorNettyClientConfiguration {

    @Bean
    fun clientHttpConnector(resourceFactory: ReactorResourceFactory): ClientHttpConnector {
        val httpClient = HttpClient.create(resourceFactory.connectionProvider)
            .runOn(resourceFactory.loopResources)
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
            .doOnConnected { connection ->
                connection.addHandlerLast(ReadTimeoutHandler(60))
            }
        return ReactorClientHttpConnector(httpClient)
    }

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

7. 记录

Spring Boot 没有强制的日志记录依赖,除了 Commons Logging API,它通常由 Spring Framework 的spring-jcl模块提供。要使用Logback,您需要将它包含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 Boot 有一个LoggingSystem抽象,它尝试根据类路径的内容配置日志记录。如果 Logback 可用,它是首选。

如果您需要对日志记录进行的唯一更改是设置各种记录器的级别,则可以application.properties使用“logging.level”前缀来执行此操作,如以下示例所示:

特性
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.xmlLogback)获取本机配置,但您可以使用logging.config属性设置配置文件的位置。

7.1。为日志配置 Logback

如果您需要对 logback 应用超出使用 可以实现的自定义application.properties项,您将需要添加一个标准的 logback 配置文件。您可以将logback.xml文件添加到类路径的根目录以供 logback 查找。logback-spring.xml如果你想使用Spring Boot Logback 扩展,你也可以使用。

Logback 文档有一个专门的部分详细介绍了配置。

Spring Boot 提供了许多included来自您自己的配置的 logback 配置。这些包括旨在允许重新应用某些常见的 Spring Boot 约定。

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

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

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

  • file-appender.xml- 添加了一个RollingFileAppender使用FILE_LOG_PATTERNROLLING_FILE_NAME_PATTERN适当的设置。

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

典型的自定义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>

您的 logback 配置文件还可以使用系统LoggingSystem为您创建的属性:

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

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

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

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

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

Spring Boot 还通过使用自定义 Logback 转换器在控制台(但不在日志文件中)提供了一些不错的 ANSI 颜色终端输出。请参阅配置CONSOLE_LOG_PATTERNdefaults.xml的示例。

如果 Groovy 在类路径上,您应该也可以配置 Logback logback.groovy。如果存在,则优先考虑此设置。

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

7.1.1. 为仅文件输出配置 Logback

如果要禁用控制台日志记录并仅将输出写入文件,则需要一个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.propertiesorapplication.yaml中,如以下示例所示:

特性
logging.file.name=myapplication.log
yaml
logging:
  file:
    name: "myapplication.log"

7.2. 为日志配置 Log4j

如果Spring Boot 在类路径上,则支持Log4j 2进行日志记录配置。如果您使用启动器来组装依赖项,则必须排除 Logback,然后改为包含 log4j 2。spring-jcl如果您不使用启动器,则除了 Log4j 2 之外,您还需要(至少)提供。

推荐的路径是通过启动器,即使它需要一些抖动。以下示例显示了如何在 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 提供了几种不同的方法来设置启动器。一种方法是使用模块更换。为此,请声明对 Log4j 2 启动器的依赖项,并告诉 Gradle 任何出现的默认日志记录启动器都应替换为 Log4j 2 启动器,如以下示例所示:

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但使用 Log4j 2 配置输出)。
要确保使用执行的调试日志记录java.util.logging被路由到 Log4j 2,请通过将系统属性设置为来配置其JDK 日志记录适配器java.util.logging.managerorg.apache.logging.log4j.jul.LogManager

7.2.1. 使用 YAML 或 JSON 配置 Log4j 2

除了默认的 XML 配置格式,Log4j 2 还支持 YAML 和 JSON 配置文件。要将 Log4j 2 配置为使用替代配置文件格式,请将适当的依赖项添加到类路径并命名配置文件以匹配您选择的文件格式,如以下示例所示:

格式 依赖项 文件名

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. 使用复合配置来配置 Log4j 2

Log4j 2 支持将多个配置文件组合成一个复合配置。要在 Spring Boot 中使用此支持,logging.log4j2.config.override请使用一个或多个辅助配置文件的位置进行配置。辅助配置文件将与主配置合并,无论主配置的来源是 Spring Boot 的默认值、标准位置(例如log4j.xml)还是属性配置的位置logging.config

8. 数据访问

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

8.1。配置自定义数据源

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

以下示例显示了如何在 bean 中定义数据源:

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

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

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

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

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "app.datasource")
    fun dataSource(): SomeDataSource {
        return SomeDataSource()
    }

}

以下示例显示了如何通过设置属性来定义数据源:

特性
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

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

Spring Boot 还提供了一个名为 的实用程序构建器类,DataSourceBuilder可用于创建标准数据源之一(如果它位于类路径上)。构建器可以根据类路径上可用的内容来检测要使用的内容。它还根据 JDBC URL 自动检测驱动程序。

以下示例显示了如何使用 a 创建数据源DataSourceBuilder

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

}
科特林
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 javax.sql.DataSource

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

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

}

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

以下示例显示如何通过设置属性来定义 JDBC 数据源:

特性
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接口未公开任何属性)。此外,如果您碰巧在类路径中有 Hikari,则此基本设置不起作用,因为 Hikari 没有url属性(但确实有jdbcUrl属性)。在这种情况下,您必须按如下方式重写您的配置:

特性
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. 您不能在运行时更改实现,但选项列表将是明确的。

以下示例显示了如何创建HikariDataSourcewith DataSourceBuilder

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

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

    @Bean
    @ConfigurationProperties("app.datasource")
    fun dataSource(): HikariDataSource {
        return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
    }

}

您甚至可以利用对您有用的功能走得更远DataSourceProperties——也就是说,如果没有提供 URL,则通过提供具有合理用户名和密码的默认嵌入式数据库。您可以轻松地DataSourceBuilder从任何DataSourceProperties对象的状态初始化 a,因此您还可以注入 Spring Boot 自动创建的 DataSource。但是,这会将您的配置拆分为两个命名空间:urlusernamepasswordtypedriveron spring.datasource,其余在您的自定义命名空间 ( app.datasource) 上。为避免这种情况,您可以DataSourceProperties在自定义命名空间上重新定义自定义,如以下示例所示:

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

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

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource")
    fun dataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

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

}

此设置使您与默认情况下 Spring Boot 为您所做的同步app.datasource.configuration,除了选择了专用连接池(在代码中)并且其设置暴露在子命名空间中。因为DataSourceProperties是为你处理url/jdbcUrl翻译,所以你可以配置如下:

特性
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 会将 Hikari 特定的设置公开到spring.datasource.hikari. 此示例使用更通用的configuration子命名空间,因为该示例不支持多个数据源实现。
因为您的自定义配置选择与 Hikari 一起使用,app.datasource.type所以没有任何效果。在实践中,构建器会使用您可能在此处设置的任何值进行初始化,然后通过对.type().

有关更多详细信息,请参阅“Spring Boot 特性”部分和类中的“ data.html ”。DataSourceAutoConfiguration

8.2. 配置两个数据源

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

如果您创建自己的DataSource,则自动配置会退出。在以下示例中,我们提供了与自动配置在主数据源上提供的完全相同的功能集:

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

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

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    fun firstDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

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

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

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

这两个数据源也都需要进行高级定制。例如,您可以按如下方式配置它们:

特性
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节点,如以下示例所示:

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

}
科特林
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.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyCompleteDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    fun firstDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

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

    @Bean
    @ConfigurationProperties("app.datasource.second")
    fun secondDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @ConfigurationProperties("app.datasource.second.configuration")
    fun secondDataSource(secondDataSourceProperties: DataSourceProperties): BasicDataSource {
        return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource::class.java).build()
    }

}

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

8.3. 使用 Spring 数据存储库

Spring Data 可以创建@Repository各种风格的接口的实现。Spring Boot 会为您处理所有这些,只要@Repositories它们包含在您的类的同一个包(或子包)中@EnableAutoConfiguration

对于许多应用程序,您只需将正确的 Spring Data 依赖项放在您的类路径中。有一个spring-boot-starter-data-jpa用于 JPA、spring-boot-starter-data-mongodbMongodb 和各种其他支持技术的启动器。首先,创建一些存储库接口来处理您的@Entity对象。

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

有关 Spring Data 的更多信息,请参阅Spring Data 项目页面

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

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

java
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 {

    // ...

}
科特林
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])
class MyApplication {

    // ...

}

8.5。配置 JPA 属性

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

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

JPA 提供程序检测到要使用的方言。如果您更喜欢自己设置方言,请设置spring.jpa.database-platform属性。

最常见的设置选项如下例所示:

特性
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.*本地时,所有属性都作为普通 JPA 属性(去掉前缀)传递EntityManagerFactory

您需要确保在下定义的名称spring.jpa.properties.*与您的 JPA 提供者所期望的名称完全匹配。Spring Boot 不会尝试对这些条目进行任何类型的宽松绑定。

例如,如果你想配置 Hibernate 的批量大小,你必须使用spring.jpa.properties.hibernate.jdbc.batch_size. 如果您使用其他形式,例如batchSizebatch-size,Hibernate 将不会应用该设置。

如果您需要对 Hibernate 属性应用高级定制,请考虑注册一个HibernatePropertiesCustomizer将在创建EntityManagerFactory. 这优先于自动配置应用的任何内容。

8.6. 配置休眠命名策略

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

默认情况下,Spring Boot 配置物理命名策略为CamelCaseToUnderscoresNamingStrategy. 使用此策略,所有点都被下划线替换,驼峰式大小写也被下划线替换。此外,默认情况下,所有表名都以小写形式生成。例如,将TelephoneNumber实体映射到telephone_number表。如果您的模式需要混合大小写的标识符,请定义一个自定义CamelCaseToUnderscoresNamingStrategybean,如以下示例所示:

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

        };
    }

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

    @Bean
    fun caseSensitivePhysicalNamingStrategy(): CamelCaseToUnderscoresNamingStrategy {
        return object : CamelCaseToUnderscoresNamingStrategy() {
            override fun isCaseInsensitive(jdbcEnvironment: JdbcEnvironment): Boolean {
                return false
            }
        }
    }

}

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

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

或者,您可以配置以下 bean:

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

}
科特林
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
internal class MyHibernateConfiguration {

    @Bean
    fun caseSensitivePhysicalNamingStrategy(): PhysicalNamingStrategyStandardImpl {
        return PhysicalNamingStrategyStandardImpl()
    }

}

请参阅HibernateJpaAutoConfigurationJpaBaseConfiguration了解更多详情。

8.7. 配置 Hibernate 二级缓存

可以为一系列缓存提供程序配置Hibernate二级缓存。与其将 Hibernate 配置为再次查找缓存提供程序,不如尽可能提供在上下文中可用的提供程序。

要使用 JCache 执行此操作,首先要确保它org.hibernate:hibernate-jcache在类路径中可用。然后,添加一个HibernatePropertiesCustomizerbean,如下例所示:

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

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

    @Bean
    fun hibernateSecondLevelCacheCustomizer(cacheManager: JCacheCacheManager): HibernatePropertiesCustomizer {
        return HibernatePropertiesCustomizer { properties ->
            properties[ConfigSettings.CACHE_MANAGER] = cacheManager.cacheManager
        }
    }

}

这个定制器会将 Hibernate 配置CacheManager为与应用程序使用的相同。也可以使用单独的CacheManager实例。有关详细信息,请参阅Hibernate 用户指南

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

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

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

8.9。使用自定义 EntityManagerFactory

要完全控制 的配置EntityManagerFactory,您需要添加一个@Bean名为“entityManagerFactory”。Spring Boot 自动配置在存在该类型的 bean 时关闭其实体管理器。

8.10。使用多个 EntityManagerFactories

如果您需要针对多个数据源使用 JPA,您可能需要EntityManagerFactory每个数据源一个。LocalContainerEntityManagerFactoryBeanfrom Spring ORM 允许您根据需要配置一个EntityManagerFactory。您还可以重用JpaProperties为每个 绑定设置EntityManagerFactory,如以下示例所示:

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

}
科特林
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
import javax.sql.DataSource

@Configuration(proxyBeanMethods = false)
class MyEntityManagerFactoryConfiguration {

    @Bean
    @ConfigurationProperties("app.jpa.first")
    fun firstJpaProperties(): JpaProperties {
        return JpaProperties()
    }

    @Bean
    fun firstEntityManagerFactory(
        firstDataSource: DataSource?,
        firstJpaProperties: JpaProperties
    ): LocalContainerEntityManagerFactoryBean {
        val builder = createEntityManagerFactoryBuilder(firstJpaProperties)
        return builder.dataSource(firstDataSource).packages(Order::class.java).persistenceUnit("firstDs").build()
    }

    private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
        val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
        return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.properties, null)
    }

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

}

上面的示例EntityManagerFactory使用DataSource名为 的 bean创建了一个firstDataSource。它扫描与 位于同一包中的实体Order。可以使用app.first.jpa命名空间映射其他 JPA 属性。

当您为自己创建 bean 时LocalContainerEntityManagerFactoryBean,在创建自动配置期间应用的任何自定义都将LocalContainerEntityManagerFactoryBean丢失。例如,在 Hibernate 的情况下,spring.jpa.hibernate前缀下的任何属性都不会自动应用于您的LocalContainerEntityManagerFactoryBean. 如果您依赖这些属性来配置命名策略或 DDL 模式等内容,则需要在创建LocalContainerEntityManagerFactoryBeanbean 时显式配置它们。

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

如果使用 Spring Data,则需要进行@EnableJpaRepositories相应的配置,如下例所示:

java
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 = [Order::class], entityManagerFactoryRef = "firstEntityManagerFactory")
class OrderConfiguration
java
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 {

}
科特林
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Customer::class], entityManagerFactoryRef = "secondEntityManagerFactory")
class CustomerConfiguration

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

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

参见JpaBaseConfiguration默认设置。

8.12。使用 Spring Data JPA 和 Mongo 存储库

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

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

其他自动配置的 Spring Data 存储库类型(Elasticsearch、Solr 等)存在相同的障碍和相同的功能。要使用它们,请相应地更改注释和标志的名称。

8.13。自定义 Spring Data 的 Web 支持

Spring Data 提供 Web 支持,简化了在 Web 应用程序中使用 Spring Data 存储库。Spring Boot 在命名空间中提供属性来自spring.data.web定义其配置。请注意,如果您使用 Spring Data REST,则必须改用spring.data.rest命名空间中的属性。

8.14。将 Spring 数据存储库公开为 REST 端点

Spring Data REST 可以Repository为您将实现公开为 REST 端点,前提是已为应用程序启用了 Spring MVC。

Spring Boot 公开了一组有用的属性(来自spring.data.rest命名空间),用于自定义RepositoryRestConfiguration. 如果您需要提供额外的定制,您应该使用RepositoryRestConfigurerbean。

如果您没有在 custom 上指定任何顺序RepositoryRestConfigurer,它将在 Spring Boot 内部使用的一个之后运行。如果需要指定顺序,请确保它大于 0。

8.15。配置 JPA 使用的组件

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

如果您自己配置组件,则可以使用EntityManagerFactoryDependsOnPostProcessor子类作为设置必要依赖项的便捷方式。例如,如果您使用带有 Elasticsearch 作为其索引管理器的 Hibernate Search,则EntityManagerFactory必须将任何 bean 配置为依赖于elasticsearchClientbean,如下例所示:

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

}
科特林
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor
import org.springframework.stereotype.Component

@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
    EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")

8.16。使用两个数据源配置 jOOQ

DSLContext如果您需要将 jOOQ 与多个数据源一起使用,您应该为每个数据源创建自己的。有关更多详细信息,请参阅JooqAutoConfiguration

特别是,JooqExceptionTranslator并且SpringTransactionProvider可以重复使用以提供与自动配置对单个DataSource.

9. 数据库初始化

可以根据堆栈的不同以不同的方式初始化 SQL 数据库。当然,您也可以手动完成,前提是数据库是一个单独的进程。建议使用单一机制生成模式。

9.1。使用 JPA 初始化数据库

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

  • spring.jpa.generate-ddl(布尔值)打开和关闭功能,并且与供应商无关。

  • spring.jpa.hibernate.ddl-auto(enum) 是一个 Hibernate 特性,它以更细粒度的方式控制行为。本指南稍后将更详细地介绍此功能。

9.2. 使用 Hibernate 初始化数据库

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

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

此外,import.sql如果 Hibernate 从头开始​​创建模式(即,如果ddl-auto属性设置为createor create-drop),则在启动时会执行在类路径的根目录中命名的文件。如果您小心的话,这对于演示和测试很有用,但可能不是您希望在生产中的类路径上出现的东西。这是一个 Hibernate 特性(与 Spring 无关)。

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

Spring Boot 可以自动创建 JDBCDataSource或 R2DBC的模式(DDL 脚本)ConnectionFactory并对其进行初始化(DML 脚本)。它分别从标准根类路径位置加载 SQL:schema.sqldata.sql。此外,Spring Boot 处理schema-${platform}.sqldata-${platform}.sql文件(如果存在),platform其中spring.sql.init.platform. 这允许您在必要时切换到特定于数据库的脚本。例如,您可以选择将其设置为数据库的供应商名称(hsqldbh2oraclemysqlpostgresql等)。默认情况下,SQL 数据库初始化仅在使用嵌入式内存数据库时执行。要始终初始化 SQL 数据库,无论其类型如何,设置spring.sql.init.modealways. 同样,要禁用初始化,请设置spring.sql.init.modenever。默认情况下,Spring Boot 启用其基于脚本的数据库初始化程序的快速失败特性。这意味着,如果脚本导致异常,应用程序将无法启动。您可以通过设置来调整该行为spring.sql.init.continue-on-error

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

如果您使用的是高级数据库迁移工具,例如 Flyway 或 Liquibase,则应单独使用它们来创建和初始化架构。不建议将基本schema.sqldata.sql脚本与 Flyway 或 Liquibase 一起使用,并且将在未来的版本中删除支持。

9.4。初始化 Spring Batch 数据库

如果您使用 Spring Batch,它预装了适用于大多数流行数据库平台的 SQL 初始化脚本。Spring Boot 可以检测您的数据库类型并在启动时执行这些脚本。如果您使用嵌入式数据库,则默认情况下会发生这种情况。您还可以为任何数据库类型启用它,如以下示例所示:

特性
spring.batch.jdbc.initialize-schema=always
yaml
spring:
  batch:
    jdbc:
      initialize-schema: "always"

spring.batch.jdbc.initialize-schema您还可以通过设置显式关闭初始化never

9.5。使用更高级别的数据库迁移工具

Spring Boot 支持两个更高级别的迁移工具:FlywayLiquibase

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

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

通常,迁移是表单中的脚本V<VERSION>__<NAME>.sql(带有<VERSION>下划线分隔的版本,例如“1”或“2_1”)。默认情况下,它们位于名为 的目录中classpath:db/migration,但您可以通过设置来修改该位置spring.flyway.locations。这是一个或多个位置的逗号分隔classpath:列表filesystem:。例如,以下配置将在默认类路径位置和/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}"

上面的配置不是 using db/migration,而是根据数据库的类型(例如db/migration/mysqlMySQL)设置要使用的目录。支持的数据库列表在DatabaseDriver.

迁移也可以用 Java 编写。Flyway 将使用任何实现JavaMigration.

FlywayProperties提供 Flyway 的大部分设置和一小组附加属性,可用于禁用迁移或关闭位置检查。如果您需要对配置进行更多控制,请考虑注册一个FlywayConfigurationCustomizerbean。

Spring Boot 调用Flyway.migrate()以执行数据库迁移。如果您想要更多控制权,请提供一个@Beanthat implements FlywayMigrationStrategy

Flyway 支持 SQL 和 Java回调。要使用基于 SQL 的回调,请将回调脚本放在classpath:db/migration目录中。要使用基于 Java 的回调,请创建一个或多个实现Callback. 任何此类 bean 都会自动注册到Flyway. 它们可以通过使用@Order或实现来订购Ordered。也可以检测到实现已弃用FlywayCallback接口的 Bean,但它们不能与CallbackBean 一起使用。

默认情况下,Flyway 在您的上下文中自动装配 ( @Primary)DataSource并将其用于迁移。如果您喜欢使用不同的DataSource,您可以创建一个并将其标记@Bean@FlywayDataSource。如果您这样做并想要两个数据源,请记住创建另一个数据源并将其标记为@Primary. DataSource或者,您可以通过设置spring.flyway.[url,user,password]外部属性来使用 Flyway 的原生。设置spring.flyway.urlspring.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/db/migration仅在dev配置文件处于活动状态时运行。

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

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

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

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

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

有关LiquibaseProperties可用设置(例如上下文、默认架构等)的详细信息,请参阅。

9.6。依赖于一个初始化的数据库

作为应用程序上下文刷新的一部分,在应用程序启动时执行数据库初始化。为了允许在启动期间访问已初始化的数据库,会自动检测充当数据库初始化程序的 bean 和需要已初始化该数据库的 bean。初始化依赖于已初始化数据库的 Bean 被配置为依赖于那些初始化它的 Bean。如果在启动期间,您的应用程序尝试访问数据库但尚未初始化,您可以配置对初始化数据库并要求数据库已初始化的 bean 的额外检测。

9.6.1。检测数据库初始化器

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

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

如果您为数据库初始化库使用第三方启动器,它可能会提供一个检测器,以便也自动检测其他类型的 bean。要检测其他 bean,请注册DatabaseInitializerDetectorin的实现META-INF/spring.factories

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

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

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

  • DSLContext(jOOQ)

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

  • JdbcOperations

  • NamedParameterJdbcOperations

如果您使用的是第三方启动器数据访问库,它可能会提供一个检测器,以便也自动检测其他类型的 bean。要检测其他 bean,请注册DependsOnDatabaseInitializationDetectorin的实现META-INF/spring.factories。或者,使用 注释 bean 的类或其@Bean方法@DependsOnDatabaseInitialization

10. 消息传递

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

10.1。禁用事务处理的 JMS 会话

如果您的 JMS 代理不支持事务会话,则必须完全禁用对事务的支持。如果您创建自己的JmsListenerContainerFactory,则无需执行任何操作,因为默认情况下无法进行交易。如果要使用DefaultJmsListenerContainerFactoryConfigurer来重用 Spring Boot 的默认值,可以禁用事务处理会话,如下所示:

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

}
科特林
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
import javax.jms.ConnectionFactory

@Configuration(proxyBeanMethods = false)
class MyJmsConfiguration {

    @Bean
    fun jmsListenerContainerFactory(connectionFactory: ConnectionFactory?,
            configurer: DefaultJmsListenerContainerFactoryConfigurer): DefaultJmsListenerContainerFactory {
        val listenerFactory = DefaultJmsListenerContainerFactory()
        configurer.configure(listenerFactory, connectionFactory)
        listenerFactory.setTransactionManager(null)
        listenerFactory.setSessionTransacted(false)
        return listenerFactory
    }

}

前面的示例覆盖了默认工厂,它应该应用于您的应用程序定义的任何其他工厂(如果有)。

11. 批量申请

当人们在 Spring Boot 应用程序中使用 Spring Batch 时,经常会出现许多问题。本节将解决这些问题。

11.1。指定批处理数据源

默认情况下,批处理应用程序需要DataSource存储作业详细信息。Spring Batch 默认需要一个DataSource。要让它使用DataSource应用程序的 main 以外的bean,请DataSource声明一个DataSourcebean,@Bean并用@BatchDataSource. 如果您这样做并且想要两个数据源,请记住标记另一个@Primary。要获得更大的控制权,请实施BatchConfigurer. 有关更多详细信息,请参阅Javadoc@EnableBatchProcessing

有关 Spring Batch 的更多信息,请参阅Spring Batch 项目页面

11.2. 在启动时运行 Spring Batch 作业

@EnableBatchProcessing通过添加到您的@Configuration类之一来启用 Spring Batch 自动配置。

默认情况下,它会在启动时在应用程序上下文中执行所有 操作(详情请参阅)。您可以通过指定(采用逗号分隔的作业名称模式列表)来缩小到一个或多个特定作业。JobsJobLauncherApplicationRunnerspring.batch.job.names

有关更多详细信息,请参阅BatchAutoConfiguration@EnableBatchProcessing

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 Batch 需要存储库的数据存储Job。如果使用 Spring Boot,则必须使用实际的数据库。请注意,它可以是内存数据库,请参阅配置作业存储库

12. 执行器

Spring Boot 包括 Spring Boot 执行器。本节回答了使用它时经常出现的问题。

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

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

有关更多详细信息,请参阅“生产就绪功能”部分中的ManagementServerProperties源代码和“ actuator.html ”。

12.2. 自定义“whitelabel”错误页面

如果您遇到服务器错误(使用 JSON 和其他媒体类型的机器客户端应该看到带有正确错误代码的合理响应),Spring Boot 会安装一个“whitelabel”错误页面,您会在浏览器客户端中看到该页面。

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

用您自己的错误页面覆盖错误页面取决于您使用的模板技术。例如,如果您使用 Thymeleaf,则可以添加error.html模板。如果你使用 FreeMarker,你可以添加一个error.ftlh模板。通常,您需要使用名称或处理路径View的 a 来解析。除非您替换了一些默认配置,否则您应该在您的 中找到 a ,因此命名将是这样做的一种方法。查看更多选项。error@Controller/errorBeanNameViewResolverApplicationContext@BeanerrorErrorMvcAutoConfiguration

有关如何在 servlet 容器中注册处理程序的详细信息,另请参阅“错误处理”部分。

12.3. 清理敏感值

env和端点返回的信息configprops可能有些敏感,因此默认情况下会清理匹配某些模式的键(即它们的值被替换为******)。Spring Boot 对此类键使用合理的默认值:任何以单词“password”、“secret”、“key”、“token”、“vcap_services”、“sun.java.command”结尾的键都会被完全清除。此外,任何将单词credentials(配置为正则表达式,即.*credentials.*)作为键的一部分的键也被完全清理。

此外,Spring Boot 对具有以下结尾之一的键的类似 URI 的值的敏感部分进行清理:

  • address

  • addresses

  • uri

  • uris

  • url

  • urls

URI 的敏感部分使用格式标识<scheme>://<username>:<password>@<host>:<port>/。例如,对于属性,经过清理的结果值为 。myclient.uri=http://user1:[email protected]:8081http://user1:******@localhost:8081

12.3.1. 自定义清理

消毒可以通过两种不同的方式进行定制。

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

要对清理进行更多控制,请定义一个SanitizingFunctionbean。调用函数的SanitizableData提供了对键和值以及PropertySource它们的来源的访问。例如,这允许您清理来自特定属性源的每个值。每个SanitizingFunction都按顺序调用,直到函数更改可清理数据的值。如果没有函数更改其值,则执行内置的基于键的清理。

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

Spring Boot 健康指标返回一个Status类型以指示整体系统健康状况。如果您想监控或提醒特定应用程序的健康水平,您可以使用 Micrometer 将这些状态导出为指标。默认情况下,Spring Boot 使用状态码“UP”、“DOWN”、“OUT_OF_SERVICE”和“UNKNOWN”。要导出这些,您需要将这些状态转换为一组数字,以便它们可以与 Micrometer 一起使用Gauge

以下示例显示了编写此类导出器的一种方法:

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

}
科特林
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)
class MyHealthMetricsExportConfiguration(registry: MeterRegistry, healthEndpoint: HealthEndpoint) {

    init {
        // This example presumes common tags (such as the app) are applied elsewhere
        Gauge.builder("health", healthEndpoint) { health ->
            getStatusCode(health).toDouble()
        }.strongReference(true).register(registry)
    }

    private fun getStatusCode(health: HealthEndpoint): Int {
        val status = health.health().status
        if (Status.UP == status) {
            return 3
        }
        if (Status.OUT_OF_SERVICE == status) {
            return 2
        }
        if (Status.DOWN == status) {
            return 1
        }
        return 0
    }

}

13. 安全

本节解决使用 Spring Boot 时有关安全性的问题,包括将 Spring Security 与 Spring Boot 一起使用时出现的问题。

有关 Spring Security 的更多信息,请参阅Spring Security 项目页面

13.1. 关闭 Spring Boot 安全配置

如果您在应用程序中@Configuration使用 aWebSecurityConfigurerAdapterSecurityFilterChainbean 定义 a,它会关闭 Spring Boot 中的默认 webapp 安全设置。

13.2. 更改 UserDetailsS​​ervice 并添加用户帐户

如果您提供@Bean类型为AuthenticationManagerAuthenticationProvider或的 a,则不会创建UserDetailsService默认值@Beanfor 。InMemoryUserDetailsManager这意味着您可以使用 Spring Security 的完整功能集(例如各种身份验证选项)。

添加用户帐户的最简单方法是提供您自己的UserDetailsServicebean。

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

确保所有主要端点只能通过 HTTPS 使用,对于任何应用程序来说都是一项重要的工作。如果您使用 Tomcat 作为 servlet 容器,那么 Spring BootRemoteIpValve如果检测到某些环境设置会自动添加 Tomcat 自己的,并且您应该能够依靠HttpServletRequest报告它是否安全(即使是处理真正的 SSL 终止)。x-forwarded-for标准行为取决于某些请求标头(和)的存在与否x-forwarded-proto,其名称是常规的,因此它应该适用于大多数前端代理。您可以通过向 中添加一些条目来打开阀门,application.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"

(其中任何一个属性的存在都会打开阀门。或者,您可以通过使用beanRemoteIpValve自定义来添加。)TomcatServletWebServerFactoryWebServerFactoryCustomizer

要将 Spring Security 配置为要求所有(或部分)请求的安全通道,请考虑添加您自己的SecurityFilterChainbean,该 bean 添加以下HttpSecurity配置:

java
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((channel) -> channel.anyRequest().requiresSecure());
        return http.build();
    }

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

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        // Customize the application security ...
        http.requiresChannel().anyRequest().requiresSecure()
        return http.build()
    }

}

14. 热插拔

Spring Boot 支持热插拔。本节回答有关其工作原理的问题。

14.1. 重新加载静态内容

有几种热重载选项。推荐的方法是使用spring-boot-devtools,因为它提供了额外的开发时特性,例如支持快速应用程序重启和 LiveReload 以及合理的开发时配置(例如模板缓存)。Devtools 通过监视类路径的变化来工作。这意味着必须“构建”静态资源更改才能使更改生效。默认情况下,当您保存更改时,这会在 Eclipse 中自动发生。在 IntelliJ IDEA 中,Make Project 命令会触发必要的构建。由于默认的重启排除,对静态资源的更改不会触发应用程序的重启。但是,它们确实会触发实时重新加载。

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

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

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

Spring Boot 支持的大多数模板技术都包含一个禁用缓存的配置选项(本文档稍后将介绍)。如果您使用该spring-boot-devtools模块,这些属性会在开发时自动为您配置。

14.2.1. 百里香模板

如果您使用 Thymeleaf,请设置spring.thymeleaf.cachefalse. 有关ThymeleafAutoConfiguration其他 Thymeleaf 自定义选项,请参阅。

14.2.2. FreeMarker 模板

如果您使用 FreeMarker,请设置spring.freemarker.cachefalse. 有关FreeMarkerAutoConfiguration其他 FreeMarker 自定义选项,请参阅。

14.2.3. Groovy 模板

如果您使用 Groovy 模板,请设置spring.groovy.template.cachefalse. 有关GroovyTemplateAutoConfiguration其他 Groovy 自定义选项,请参阅。

14.3. 快速应用程序重启

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

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

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

许多现代 IDE(Eclipse、IDEA 等)支持字节码的热交换。因此,如果您进行不影响类或方法签名的更改,它应该干净地重新加载而没有副作用。

15. 测试

Spring Boot 包括许多测试实用程序和支持类,以及提供常见测试依赖项的专用启动器。本节回答有关测试的常见问题。

15.1. 使用 Spring Security 进行测试

Spring Security 支持以特定用户身份运行测试。例如,下面代码片段中的测试将使用具有该ADMIN角色的经过身份验证的用户运行。

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.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("/"));
    }

}
科特林
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 org.springframework.test.web.servlet.request.MockMvcRequestBuilders

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

    @Test
    @WithMockUser(roles = ["ADMIN"])
    fun requestProtectedUrlWithUser() {
        mvc.perform(MockMvcRequestBuilders.get("/"))
    }

}

Spring Security 提供了与 Spring MVC Test 的全面集成,这也可以在使用@WebMvcTestslice 和MockMvc.

有关 Spring Security 的测试支持的更多详细信息,请参阅 Spring Security 的参考文档

15.2. 使用测试容器进行集成测试

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

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

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

@SpringBootTest
@Testcontainers
class MyIntegrationTests {

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

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

}
科特林
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@SpringBootTest
@Testcontainers
internal class MyIntegrationTests {

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

    companion object {
        @Container
        var neo4j: Neo4jContainer<*> = Neo4jContainer<Nothing>("neo4j:4.2")
    }

}

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

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

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

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

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

}
科特林
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@SpringBootTest
@Testcontainers
internal class MyIntegrationTests {

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

    companion object {

        @Container
        var neo4j: Neo4jContainer<*> = Neo4jContainer<Nothing>("neo4j:4.2")

        @DynamicPropertySource
        fun neo4jProperties(registry: DynamicPropertyRegistry) {
            registry.add("spring.neo4j.uri") { neo4j.boltUrl }
        }

    }

}

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

15.3. 包含在切片测试中的结构@Configuration

切片测试通过将 Spring Framework 的组件扫描限制为基于其类型的一组有限组件来工作。对于不是通过组件扫描创建的任何 bean,例如,使用@Bean注释创建的 bean,切片测试将无法在应用程序上下文中包含/排除它们。考虑这个例子:

import org.apache.commons.dbcp2.BasicDataSource;

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.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
        return http.build();
    }

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

}

对于@WebMvcTest具有上述@Configuration类的应用程序,您可能希望将SecurityFilterChainbean 放在应用程序上下文中,以便您可以测试您的控制器端点是否得到正确保护。但是,MyConfiguration@WebMvcTest 的组件扫描过滤器不会选择它,因为它与过滤器指定的任何类型都不匹配。您可以通过使用@Import(MyConfiguration.class). 这将加载所有 bean,MyConfiguration包括BasicDataSource测试 Web 层时不需要的 bean。将配置类拆分为两个将启用仅导入安全配置。

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(proxyBeanMethods = false)
public class MySecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
        return http.build();
    }

}
import org.apache.commons.dbcp2.BasicDataSource;

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.second")
    public BasicDataSource secondDataSource() {
        return DataSourceBuilder.create().type(BasicDataSource.class).build();
    }

}

当某个域的 bean 需要包含在切片测试中时,拥有单个配置类可能效率低下。相反,将应用程序的配置构造为具有特定域的 bean 的多个粒度类可以启用仅针对特定切片测试导入它们。

16. 建造

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

16.1. 生成构建信息

Maven 插件和 Gradle 插件都允许生成包含项目坐标、名称和版本的构建信息。插件还可以配置为通过配置添加其他属性。当存在这样的文件时,Spring Boot 会自动配置一个BuildPropertiesbean。

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

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.7.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
有关更多详细信息,请参阅Spring Boot Maven 插件文档

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

springBoot {
    buildInfo()
}
有关更多详细信息,请参阅Spring Boot Gradle 插件文档

16.2. 生成 Git 信息

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

对于 Maven 用户, POM 包含一个用于生成文件spring-boot-starter-parent的预配置插件。git.properties要使用它,请将以下声明添加Git Commit Id Plugin到您的 POM 中:

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

Gradle 用户使用该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. 这是上面列出的两个插件的默认格式。使用这种格式可以将时间解析为 aDate及其格式,当序列化为 JSON 时,由 Jackson 的日期序列化配置设置控制。

16.3. 自定义依赖版本

POM 管理公共依赖项的spring-boot-dependencies版本。Maven 和 Gradle 的 Spring Boot 插件允许使用构建属性自定义这些托管依赖项版本。

每个 Spring Boot 版本都针对这组特定的第三方依赖项进行设计和测试。覆盖版本可能会导致兼容性问题。

要使用 Maven 覆盖依赖版本,请参阅Maven 插件文档的这一部分

要覆盖 Gradle 中的依赖版本,请参阅Gradle 插件文档的这一部分

16.4. 使用 Maven 创建可执行 JAR

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

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

有关完整的使用详情,请参阅插件文档

16.5。使用 Spring Boot 应用程序作为依赖项

与 war 文件一样,Spring Boot 应用程序不打算用作依赖项。如果您的应用程序包含您想与其他项目共享的类,则推荐的方法是将该代码移动到单独的模块中。然后,您的应用程序和其他项目可以依赖单独的模块。

如果您无法按照上面的建议重新排列代码,则必须将 Spring Boot 的 Maven 和 Gradle 插件配置为生成适合用作依赖项的单独工件。可执行存档不能用作依赖项,因为可执行 jar 格式将应用程序类打包在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 Boot 应用程序

要将远程调试器附加到使用 Maven 启动的 Spring Boot 应用程序,您可以使用maven pluginjvmArguments的属性。

有关更多详细信息,请参阅此示例

16.9。在不使用 spring-boot-antlib 的情况下从 Ant 构建可执行存档

要使用 Ant 构建,您需要获取依赖项、编译,然后创建 jar 或 war 存档。要使其可执行,您可以使用该spring-boot-antlib模块,也可以按照以下说明进行操作:

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

  2. 在 jar 或war的嵌套BOOT-INF/lib目录中添加运行时依赖项。WEB-INF/lib切记不要压缩存档中的条目。

  3. 在 jar 或war的嵌套目录中添加provided(嵌入式容器)依赖项。切记不要压缩存档中的条目。BOOT-INF/libWEB-INF/lib-provided

  4. 在存档的根目录添加spring-boot-loader类(以便Main-Class可用)。

  5. 使用适当的启动器(例如JarLauncherjar 文件)作为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 Boot 支持传统部署以及更现代的部署形式。本节回答有关传统部署的常见问题。

17.1. 创建可部署的战争文件

由于 Spring WebFlux 不严格依赖 servlet API,并且应用程序默认部署在嵌入式 Reactor Netty 服务器上,因此 WebFlux 应用程序不支持 War 部署。

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

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

}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {

    override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
        return application.sources(MyApplication::class.java)
    }

}

fun main(args: Array<String>) {
    runApplication<MyApplication>(*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依赖项不在测试类路径上,因此任何基于 Web 的集成测试都会失败。

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

17.2. 将现有应用程序转换为 Spring Boot

要将现有的非 Web Spring 应用程序转换为 Spring Boot 应用程序,请替换创建您的代码ApplicationContext并将其替换为对SpringApplication或的调用SpringApplicationBuilder。Spring MVC Web 应用程序通常可以先创建一个可部署的 war 应用程序,然后再将其迁移到一个可执行的 war 或 jar 中。请参阅关于将 jar 转换为战争的入门指南

SpringBootServletInitializer要通过扩展(例如,在名为 的类中Application)并添加 Spring Boot注释来创建可部署的战争@SpringBootApplication,请使用类似于以下示例中所示的代码:

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


}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {

    override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
        // 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 为它们提供自己的默认值,但在您需要这样做之前应该可以让某些东西工作。

静态资源可以移动到/public(或/static/resources/META-INF/resources)类路径根目录中。这同样适用于messages.properties(Spring Boot 在类路径的根目录中自动检测到)。

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

  • A@Bean类型ServletorServletRegistrationBean将该 bean 安装在容器中,就好像它是 a<servlet/><servlet-mapping/>in一样web.xml

  • @Bean类型Filteror的 AFilterRegistrationBean行为类似(作为 a<filter/><filter-mapping/>)。

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

一旦 war 文件开始工作,您可以通过向mainApplication的 .

java
public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
}
科特林
fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

如果您打算将应用程序作为战争或可执行应用程序启动,则需要在SpringBootServletInitializer回调可用的方法和main类似于以下的类中的方法中共享构建器的自定义:

java
import org.springframework.boot.Banner;
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 builder) {
        return customizerBuilder(builder);
    }

    public static void main(String[] args) {
        customizerBuilder(new SpringApplicationBuilder()).run(args);
    }

    private static SpringApplicationBuilder customizerBuilder(SpringApplicationBuilder builder) {
        return builder.sources(MyApplication.class).bannerMode(Banner.Mode.OFF);
    }

}
科特林
import org.springframework.boot.Banner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {

    override fun configure(builder: SpringApplicationBuilder): SpringApplicationBuilder {
        return customizerBuilder(builder)
    }

    companion object {

        @JvmStatic
        fun main(args: Array<String>) {
            customizerBuilder(SpringApplicationBuilder()).run(*args)
        }

        private fun customizerBuilder(builder: SpringApplicationBuilder): SpringApplicationBuilder {
            return builder.sources(MyApplication::class.java).bannerMode(Banner.Mode.OFF)
        }

    }

}

应用程序可以分为不止一类:

  • Servlet 3.0+ 应用程序没有web.xml.

  • 带有web.xml.

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

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

所有这些都应该可以翻译,但每个都可能需要稍微不同的技术。

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

还不是 Spring 应用程序的应用程序可以转换为 Spring Boot 应用程序,前面提到的指南可能会有所帮助。但是,您可能还会遇到问题。在这种情况下,我们建议在 Stack Overflow 上使用标签提问spring-boot

17.3. 将 WAR 部署到 WebLogic

要将 Spring Boot 应用程序部署到 WebLogic,您必须确保您的 servlet 初始化程序直接实现WebApplicationInitializer(即使您从已经实现它的基类扩展)。

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

java
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 {

}
科特林
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
import org.springframework.web.WebApplicationInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer(), WebApplicationInitializer

如果使用 Logback,还需要告诉 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>

1. see XML Configuration