本节回答了在使用Spring Boot时经常出现的一些常见“我该如何做到这一点…”问题。它的覆盖范围并不全面,但涵盖了相当多的内容。

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

我们也非常乐意扩展这一部分。如果您想添加一个“如何做到”,请给我们发送一个pull request

1. Spring Boot 应用程序

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

1.1. 创建自己的FailureAnalyzer

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

AbstractFailureAnalyzerFailureAnalyzer的便捷扩展,它检查异常中指定异常类型的存在以进行处理。您可以从中扩展,以便在实际存在异常时您的实现有机会处理异常。如果由于任何原因您无法处理异常,请返回null,以便让另一个实现有机会处理异常。

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

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
如果您需要访问BeanFactoryEnvironment,请将它们声明为您的FailureAnalyzer实现的构造函数参数。

1.2. 故障排除自动配置

Spring Boot自动配置尽力“做正确的事情”,但有时会失败,很难判断失败的原因。

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

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

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

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

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

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

  • 查找@ConditionalOnExpression注解,根据SpEL表达式开启和关闭功能,通常从Environment中解析占位符进行评估。

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

SpringApplication具有用于对上下文或环境应用自定义的ApplicationListenersApplicationContextInitializers。Spring Boot从META-INF/spring.factories中加载许多此类自定义供内部使用。有多种方法可以注册额外的自定义内容:

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

  • 通过在所有应用程序中添加META-INF/spring.factories并打包一个jar文件,使所有应用程序都将其用作库,从而进行声明性注册。

SpringApplication向监听器发送一些特殊的ApplicationEvents(有些甚至在上下文创建之前)然后为由ApplicationContext发布的事件注册监听器。查看“应用程序事件和监听器”中的“Spring Boot功能”部分,以获取完整列表。

还可以通过使用EnvironmentPostProcessor在刷新应用程序上下文之前自定义Environment。每个实现都应该在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);
        }
    }

}
Kotlin
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属性源添加到列表末尾,以便在任何其他常规位置中定义的键优先。自定义实现可以定义另一顺序。
虽然在@SpringBootApplication上使用@PropertySource可能看起来是加载自定义资源到Environment的便捷方式,但我们不建议这样做。此类属性源直到刷新应用程序上下文时才会添加到Environment中。这对于在刷新开始之前读取的某些属性(如logging.*spring.main.*)进行配置来说太晚了。

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

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

1.5. 创建非Web应用程序

并非所有Spring应用程序都必须是Web应用程序(或Web服务)。如果您想在main方法中执行一些代码,同时还要引导Spring应用程序以设置基础架构以供使用,您可以使用Spring Boot的SpringApplication功能。根据SpringApplication认为是否需要Web应用程序,它会更改其ApplicationContext类。您可以帮助它的第一步是将与服务器相关的依赖项(例如servlet API)从类路径中删除。如果无法这样做(例如,从同一代码库运行两个应用程序),则可以在SpringApplication实例上显式调用setWebApplicationType(WebApplicationType.NONE)或设置applicationContextClass属性(通过Java API或外部属性)。您希望作为业务逻辑运行的应用程序代码可以实现为CommandLineRunner,并作为@Bean定义放入上下文中。

2. 属性和配置

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

2.1. 在构建时自动扩展属性

与在项目的构建配置中指定的一些属性硬编码不同,您可以通过使用现有的构建配置自动扩展它们。这在Maven和Gradle中都是可能的。

2.1.1. 使用Maven自动扩展属性

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

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

如果您不使用starter parent,您需要在pom.xml<build/>元素内包含以下元素:

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

您还需要在<plugins/>内包含以下元素:

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

2.1.2. 使用Gradle自动扩展属性

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

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的配置外部化

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

属性
spring.main.web-application-type=none
spring.main.banner-mode=off
Yaml
spring:
  main:
    web-application-type: "none"
    banner-mode: "off"

然后,Spring 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);
    }

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

}

或者使用SpringApplicationBuildersources(…​)方法:

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

}
Kotlin
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. 更改应用程序外部属性的位置

默认情况下,来自不同来源的属性按照一定顺序添加到Spring的Environment中(请参阅“features.html”中的“Spring Boot特性”部分以获取确切顺序)。

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

  • spring.config.nameSPRING_CONFIG_NAME):默认为文件名根目录application

  • spring.config.locationSPRING_CONFIG_LOCATION):要加载的文件(例如类路径资源或URL)。为此文档设置了单独的Environment属性源,并且可以通过系统属性、环境变量或命令行进行覆盖。

无论您在环境中设置什么,Spring Boot始终会加载application.properties,如上所述。默认情况下,如果使用YAML,则还会将扩展名为‘.yaml’和‘.yml’的文件添加到列表中。

如果您想要关于正在加载的文件的详细信息,可以将org.springframework.boot.context.config的日志级别设置为trace

2.4. 使用‘短’命令行参数

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

属性
server.port=${port:8080}
Yaml
server:
  port: "${port:8080}"
如果您继承自spring-boot-starter-parent POM,则maven-resources-plugins的默认过滤令牌已从${*}更改为@(即,@maven.token@而不是${maven.token})以避免与Spring样式占位符发生冲突。如果您已经为application.properties直接启用了Maven过滤,您可能还想将默认过滤令牌更改为使用其他分隔符
在这种特定情况下,端口绑定适用于PaaS环境,如Heroku或Cloud Foundry。在这两个平台上,PORT环境变量会自动设置,并且Spring可以绑定到Environment属性的大写同义词。

2.5. 使用YAML进行外部属性

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

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

创建一个名为application.yaml的文件并将其放在类路径的根目录中。然后将snakeyaml添加到您的依赖项(Maven坐标org.yaml:snakeyaml,如果使用spring-boot-starter,则已包含)。将YAML文件解析为Java Map<String,Object>(类似于JSON对象),Spring Boot将该映射扁平化,使其一级深度,并具有以句点分隔的键,许多人在Java中的Properties文件中习惯于此。

上述示例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的更多信息,请参见“features.html”中的“Spring Boot特性”部分。

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

Spring的Environment提供了一个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键,则配置文件值(逗号分隔的配置文件列表或配置文件表达式)将被传递给Spring的Environment.acceptsProfiles()方法。如果配置文件表达式匹配,则该文档将包含在最终合并中(否则不包含),如下示例所示:

属性
server.port=9000
#---
spring.config.activate.on-profile=development
server.port=9001
#---
spring.config.activate.on-profile=production
server.port=0
Yaml
server:
  port: 9000
---
spring:
  config:
    activate:
      on-profile: "development"
server:
  port: 9001
---
spring:
  config:
    activate:
      on-profile: "production"
server:
  port: 0

在上面的示例中,默认端口为9000。但是,如果名为‘development’的Spring配置文件处于活动状态,则端口为9001。如果‘production’处于活动状态,则端口为0。

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

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

Spring Boot在运行时将application.properties(或YAML文件和其他位置)中的外部属性绑定到应用程序中。由于贡献可能来自类路径上的其他jar文件,因此没有(并且从技术上讲不能有)单个位置中列出所有支持的属性的详尽列表。

具有Actuator功能的运行中应用程序具有一个显示通过@ConfigurationProperties可用的所有绑定属性的configprops端点。

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

3. 嵌入式Web服务器

每个Spring Boot Web应用程序都包含一个内嵌的Web服务器。这个特性会引发一系列如何问题,包括如何更改内嵌服务器以及如何配置内嵌服务器。本节回答了这些问题。

3.1. 使用另一个Web服务器

许多Spring Boot starters包含默认的内嵌容器。

  • 对于Servlet堆栈应用程序,spring-boot-starter-web包含Tomcat,通过包含spring-boot-starter-tomcat,但您也可以使用spring-boot-starter-jettyspring-boot-starter-undertow

  • 对于响应式堆栈应用程序,spring-boot-starter-webflux包含Reactor Netty,通过包含spring-boot-starter-reactor-netty,但您也可以使用spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow

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

以下Maven示例显示了如何排除Tomcat并包含Jetty用于Spring MVC:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- 排除Tomcat依赖项 -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 使用Jetty替代 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

以下Gradle示例配置了必要的依赖项和模块替换,以在Spring WebFlux中使用Undertow替代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", "使用Undertow代替Reactor Netty")
        }
    }
}
spring-boot-starter-reactor-netty是使用WebClient类所必需的,因此即使需要包含不同的HTTP服务器,您可能仍需要保留对Netty的依赖。

3.2. 禁用Web服务器

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

属性
spring.main.web-application-type=none
Yaml
spring:
  main:
    web-application-type: "none"

3.3. 更改HTTP端口

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

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

有关更多详细信息,请参阅“‘Spring Boot Features’部分中的“web.html”,或ServerProperties源代码。

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

要扫描空闲端口(使用操作系统原生功能以防止冲突),请使用server.port=0

3.5. 在运行时发现HTTP端口

您可以从日志输出或通过其WebServerWebServerApplicationContext中访问服务器正在运行的端口。获取这个并确保它已经初始化的最佳方法是添加一个@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)
class MyWebIntegrationTests {

    @LocalServerPort
    int port;

    // ...

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

可以通过设置各种server.ssl.*属性来声明性地配置SSL,通常在application.propertiesapplication.yaml中。以下示例显示了如何使用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"

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

3.7.1. 使用PEM编码文件

您可以使用PEM编码文件代替Java KeyStore文件。您应该尽可能使用PKCS#8密钥文件。以-----BEGIN PRIVATE KEY----------BEGIN ENCRYPTED PRIVATE KEY-----头开头的PEM编码的PKCS#8密钥文件。

如果您有其他格式的文件,例如PKCS#1(-----BEGIN RSA PRIVATE KEY-----)或SEC 1(-----BEGIN EC PRIVATE KEY-----),您可以使用OpenSSL将它们转换为PKCS#8:

openssl pkcs8 -topk8 -nocrypt -in <input file> -out <output file>

以下示例显示了如何使用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
Yaml
server:
  port: 8443
  ssl:
    certificate: "classpath:my-cert.crt"
    certificate-private-key: "classpath:my-cert.key"
    trust-certificate: "classpath:ca-cert.crt"

另外,SSL信任材料可以在SSL捆绑包中进行配置,并按照此示例应用于Web服务器:

属性
server.port=8443
server.ssl.bundle=example
Yaml
server:
  port: 8443
  ssl:
    bundle: "example"
属性server.ssl.bundle不能与server.ssl下的离散Java KeyStore或PEM属性选项组合。

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

3.8. 配置HTTP/2

您可以通过server.http2.enabled配置属性在Spring Boot应用程序中启用HTTP/2支持。支持h2(TLS上的HTTP/2)和h2c(TCP上的HTTP/2)。要使用h2,还必须启用SSL。当未启用SSL时,将使用h2c。例如,当您的应用程序在代理服务器后运行执行TLS终止时,您可能希望使用h2c

3.8.1. 使用Tomcat配置HTTP/2

Spring Boot默认使用支持h2ch2的Tomcat 10.1.x。或者,您可以使用libtcnative来支持h2,如果主机操作系统上安装了该库及其依赖项。

如果尚未提供库目录,则必须将其提供给JVM库路径。您可以使用JVM参数进行此操作,例如-Djava.library.path=/usr/local/opt/tomcat-native/lib。有关更多信息,请参阅官方Tomcat文档

3.8.2. 使用Jetty配置HTTP/2

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

  • org.eclipse.jetty:jetty-alpn-java-server以使用JDK内置支持

  • org.eclipse.jetty:jetty-alpn-conscrypt-serverConscrypt库

3.8.3. 使用Reactor Netty配置HTTP/2

spring-boot-webflux-starter默认使用Reactor Netty作为服务器。Reactor Netty默认支持h2ch2。为了实现最佳的运行时性能,该服务器还支持使用本机库的h2。要启用此功能,您的应用程序需要有额外的依赖项。

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

3.8.4. 使用Undertow配置HTTP/2

Undertow默认支持h2ch2

3.9. 配置Web服务器

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

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

下面的示例是针对使用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) {
        // 在这里自定义工厂
    }

}
Kotlin
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?) {
        // 在这里自定义工厂
    }

}
Spring Boot在内部使用该基础设施来自动配置服务器。自动配置的WebServerFactoryCustomizer bean的顺序为0,并且将在任何用户定义的自定义器之前进行处理,除非它具有另有规定的显式顺序。

一旦您通过自定义器获得了WebServerFactory的访问权限,您可以使用它来配置特定部分,如连接器、服务器资源或服务器本身 - 都使用特定于服务器的API。

此外,Spring Boot还提供:

服务器 Servlet堆栈 Reactive堆栈

Tomcat

TomcatServletWebServerFactory

TomcatReactiveWebServerFactory

Jetty

JettyServletWebServerFactory

JettyReactiveWebServerFactory

Undertow

UndertowServletWebServerFactory

UndertowReactiveWebServerFactory

Reactor

N/A

NettyReactiveWebServerFactory

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

3.10. 添加Servlet、Filter或Listener到应用程序

在一个servlet堆栈应用程序中,也就是使用了spring-boot-starter-web的应用程序中,有两种方法可以向您的应用程序添加ServletFilterServletContextListener以及Servlet API支持的其他监听器:

3.10.1. 通过使用Spring Bean添加Servlet、Filter或Listener

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

对于过滤器和servlet,您还可以通过添加FilterRegistrationBeanServletRegistrationBean来添加映射和初始化参数,而不是或者除了底层组件。

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

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

禁用Servlet或Filter的注册

如前所述,任何ServletFilter bean都会自动注册到servlet容器中。要禁用特定FilterServlet bean的注册,为其创建一个注册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;
    }

}
Kotlin
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、Filter和Listener

通过使用@WebServlet@WebFilter@WebListener注解的类可以通过在@Configuration类上注解@ServletComponentScan并指定包含您想要注册的组件的包来自动注册到嵌入式servlet容器中。默认情况下,@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 microseconds)
Yaml
server:
  tomcat:
    basedir: "my-tomcat"
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D microseconds)"
日志的默认位置是相对于Tomcat基目录的logs目录。默认情况下,logs目录是一个临时目录,因此您可能希望修复Tomcat的基目录或为日志使用绝对路径。在上面的示例中,日志位于应用程序的工作目录下的my-tomcat/logs中。

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

属性
server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a %r %s (%D milliseconds)
server.undertow.options.server.record-request-start-time=true
Yaml
server:
  undertow:
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D milliseconds)"
    options:
      server:
        record-request-start-time: true

请注意,除了启用访问日志和配置其模式外,还启用了记录请求开始时间。当在访问日志模式中包含响应时间(%D)时,这是必需的。日志存储在相对于应用程序工作目录的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 HTTP头;代理可以使用此头提供有关原始请求的信息。您可以配置您的应用程序来读取这些头,并在创建链接并将其发送给客户端的HTTP 302响应、JSON文档或HTML页面时自动使用该信息。还有一些非标准头,如X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-SslX-Forwarded-Prefix

如果代理添加了常用的X-Forwarded-ForX-Forwarded-Proto头,将server.forward-headers-strategy设置为NATIVE就足以支持它们。通过此选项,Web服务器本身本地支持此功能;您可以查看它们的具体文档以了解特定行为。

如果这还不够,Spring Framework为Servlet堆栈提供了一个ForwardedHeaderFilter,为响应式堆栈提供了一个ForwardedHeaderTransformer。您可以通过将server.forward-headers-strategy设置为FRAMEWORK在应用程序中使用它们。

如果您在Tomcat上运行并在代理处终止SSL,则应将server.tomcat.redirect-context-root设置为false。这允许在执行任何重定向之前尊重X-Forwarded-Proto头。
如果您的应用程序在Cloud Foundry、Heroku或Kubernetes中运行,则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设置为空来信任所有代理(但在生产环境中不要这样做)。

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

3.13. 在Tomcat中启用多个连接器

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

Java
import org.apache.catalina.connector.Connector;

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

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

    private Connector createConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(8081);
        return connector;
    }

}
Kotlin
import org.apache.catalina.connector.Connector
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 MyTomcatConfiguration {

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

    private fun createConnector(): Connector {
        val connector = Connector("org.apache.coyote.http11.Http11NioProtocol")
        connector.port = 8081
        return connector
    }

}

3.14. 启用Tomcat的MBean注册表

默认情况下,嵌入式Tomcat的MBean注册表是禁用的。这可以最小化Tomcat的内存占用。如果您想要使用Tomcat的MBeans,例如使它们可以被Micrometer用于公开指标,您必须使用server.tomcat.mbeanregistry.enabled属性来这样做,如以下示例所示:

属性
server.tomcat.mbeanregistry.enabled=true
Yaml
server:
  tomcat:
    mbeanregistry:
      enabled: true

3.15. 启用Undertow中的多个监听器

UndertowServletWebServerFactory添加一个UndertowBuilderCustomizer,并向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");
    }

}
Kotlin
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.16. 使用@ServerEndpoint创建WebSocket端点

如果要在使用嵌入式容器的Spring Boot应用程序中使用@ServerEndpoint,必须声明一个单一的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();
    }

}
Kotlin
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. Spring MVC

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

4.1. 编写JSON REST服务

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

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

}
Kotlin
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对象为真),则localhost:8080/thing默认提供其JSON表示。请注意,在浏览器中,有时可能会看到XML响应,因为浏览器倾向于发送更喜欢XML的接受标头。

4.2. 编写XML REST服务

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

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

如果Jackson的XML扩展不可用,但JAXB可用,则可以通过将MyThing注释为@XmlRootElement来渲染XML,如下例所示:

Java
import jakarta.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;
    }

}
Kotlin
import jakarta.xml.bind.annotation.XmlRootElement

@XmlRootElement
class MyThing {

    var name: String? = null

}

您需要确保JAXB库是项目的一部分,例如通过添加以下内容:

<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提供的默认转换器,其中的一个实例会自动为您配置。

默认创建的ObjectMapper(或Jackson XML转换器的XmlMapper)实例具有以下自定义属性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION已禁用

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES已禁用

  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS已禁用

  • SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS已禁用

Spring Boot还具有一些功能,使得定制这种行为更加容易。

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

枚举 属性

com.fasterxml.jackson.databind.cfg.EnumFeature

spring.jackson.datatype.enum.<feature_name>

true, false

com.fasterxml.jackson.databind.cfg.JsonNodeFeature

spring.jackson.datatype.json-node.<feature_name>

true, false

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

此基于环境的配置应用于自动配置的Jackson2ObjectMapperBuilder bean,并适用于使用构建器创建的任何映射器,包括自动配置的ObjectMapper bean。

上下文的Jackson2ObjectMapperBuilder可以通过一个或多个Jackson2ObjectMapperBuilderCustomizer bean进行自定义。这样的自定义器bean可以排序(Boot的自定义器具有顺序为0),允许在Boot的自定义化之前和之后应用额外的自定义。

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

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

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

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

4.4. 定制@ResponseBody渲染

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

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

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

4.5. 处理多部分文件上传

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

当您希望将多部分编码的文件数据作为@RequestParam注释的MultipartFile类型参数接收时,多部分支持非常有用,可以在Spring MVC控制器处理程序方法中实现此功能。

查看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是不常见的,但如果您确实需要这样做,还必须提供一个类型为DispatcherServletPath@Bean,以提供自定义DispatcherServlet的路径。

4.7. 关闭默认MVC配置

完全控制MVC配置的最简单方法是提供自己的带有@EnableWebMvc注解的@Configuration。这样做将使所有MVC配置完全由您控制。

4.8. 自定义视图解析器

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

WebMvcAutoConfiguration向您的上下文添加以下ViewResolvers

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

  • 一个名为“beanNameViewResolver”的BeanNameViewResolver。这是视图解析器链中的一个有用成员,并选择与正在解析的View同名的任何bean。不应该有必要覆盖或替换它。

  • 只有在实际存在View类型的bean时,才会添加名为“viewResolver”的ContentNegotiatingViewResolver。这是一个复合解析器,委托给所有其他解析器,并尝试找到与客户端发送的“Accept”HTTP标头匹配的结果。有一个有用的关于ContentNegotiatingViewResolver的博客,您可能希望学习更多,还可以查看详细的源代码。您可以通过定义名为“viewResolver”的bean来关闭自动配置的ContentNegotiatingViewResolver

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

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

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

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

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

5. Jersey

5.1. 使用Spring Security保护Jersey端点

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

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

import java.util.Collections;

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {

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

}

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

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

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

import org.springframework.stereotype.Component;

@Component
public class JerseyConfig extends ResourceConfig {

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

}

6. HTTP Clients

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

6.1. 配置RestTemplate以使用代理

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

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

6.2. 配置Reactor Netty-based WebClient使用的TcpClient

当Reactor Netty在类路径上时,将自动配置基于Reactor Netty的WebClient。要自定义客户端处理网络连接,提供一个ClientHttpConnector bean。以下示例配置了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.ReactorResourceFactory;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;

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

}
Kotlin
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.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没有强制的日志依赖,除了通常由Spring Framework的spring-jcl模块提供的Commons Logging API。要使用Logback,您需要将其和spring-jcl包含在类路径中。推荐的做法是通过starters来实现,所有的starters都依赖于spring-boot-starter-logging。对于Web应用程序,您只需要spring-boot-starter-web,因为它在传递依赖中依赖于日志starter。如果您使用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会从系统的默认位置(例如Logback的classpath:logback.xml)中获取本机配置,但您可以通过使用logging.config属性来设置配置文件的位置。

7.1. 配置Logback进行日志记录

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

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

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

以下文件位于org/springframework/boot/logging/logback/下:

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

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

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

此外,还提供了一个传统的base.xml文件,以兼容早期版本的Spring 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}: 是否在Boot的外部配置中设置了logging.file.name

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

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

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

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

如果Groovy在类路径上,您应该能够使用logback.groovy配置Logback。如果存在,此设置将优先考虑。

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.propertiesapplication.yaml中,如下例所示:

属性
logging.file.name=myapplication.log
Yaml
logging:
  file:
    name: "myapplication.log"

7.2. 配置Log4j进行日志记录

如果在类路径上存在Log4j 2,Spring Boot支持使用它进行日志配置。如果您使用starter来组装依赖关系,您需要排除Logback,然后包含Log4j 2。如果您不使用starters,则需要提供(至少)spring-jcl以及Log4j 2。

推荐的路径是通过starters,尽管需要一些调整。以下示例显示了如何在Maven中设置starters:

<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提供了几种不同的设置starters的方法。一种方法是使用模块替换。为此,声明对Log4j 2 starter的依赖,并告诉Gradle任何默认日志starter的出现都应该被Log4j 2 starter替换,如下例所示:

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", "使用Log4j2替代Logback")
        }
    }
}
Log4j starters汇集了常见日志要求的依赖关系(例如,使Tomcat使用java.util.logging,但使用Log4j 2配置输出)。
为了确保使用java.util.logging执行的调试日志被路由到Log4j 2,请通过将java.util.logging.manager系统属性设置为org.apache.logging.log4j.jul.LogManager来配置其JDK日志适配器

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,包括数据库初始化。如果您需要外部化一些设置,可以将您的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();
    }

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

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

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

以下示例显示了如何使用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();
    }

}
Kotlin
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)
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来解决这个问题。您无法在运行时更改实现,但选项列表将是明确的。

以下示例显示了如何使用DataSourceBuilder创建HikariDataSource

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

}
Kotlin
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,则提供具有合理用户名和密码的默认嵌入式数据库。您可以轻松地从任何 DataSourceProperties对象的状态初始化 DataSourceBuilder,因此您还可以注入Spring Boot自动创建的DataSource。但是,这将使您的配置分为两个命名空间: spring.datasource上的 urlusernamepasswordtypedriver,以及您的自定义命名空间( 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();
    }

}
Kotlin
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()覆盖。

有关更多详细信息,请参阅“data.html”中的“Spring Boot功能”部分以及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();
    }

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

}
Kotlin
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 Data存储库

Spring Data可以为各种类型的@Repository接口创建实现。只要这些@Repository注解包含在一个自动配置包中,通常是您的主应用程序类的包(或子包)上标记有@SpringBootApplication@EnableAutoConfiguration,Spring Boot就会为您处理所有这些。

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

Spring Boot通过扫描自动配置包来确定您的@Repository定义的位置。要获得更多控制,请使用Spring Data的@Enable…Repositories注解。

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

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

Spring Boot通过扫描自动配置包来确定您的@Entity定义的位置。为了更好地控制,可以使用@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 {

    // ...

}
Kotlin
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.*中的属性在创建本地EntityManagerFactory时作为普通JPA属性传递。

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

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

如果需要对Hibernate属性进行高级定制,请考虑注册一个HibernatePropertiesCustomizer bean,在创建EntityManagerFactory之前调用该bean。这比自动配置应用的任何内容都优先。

8.6. 配置Hibernate命名策略

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

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

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

        };
    }

}
Kotlin
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的默认设置,请设置以下属性:

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

}
Kotlin
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.orm:hibernate-jcache在类路径上可用。然后,按照以下示例添加一个HibernatePropertiesCustomizer bean:

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

}
Kotlin
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注册一个使用BeanFactoryBeanContainer实现,以便转换器和实体侦听器可以使用常规依赖注入。

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

8.9. 使用自定义EntityManagerFactory

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

8.10. 使用多个EntityManagerFactories

如果您需要针对多个数据源使用JPA,您可能需要每个数据源一个EntityManagerFactory。Spring ORM中的LocalContainerEntityManagerFactoryBean允许您根据需要配置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) {
        // ... 根据需要映射JPA属性
        return new HibernateJpaVendorAdapter();
    }

}
Kotlin
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)
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 {
        // ... 根据需要映射JPA属性
        return HibernateJpaVendorAdapter()
    }

}

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

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

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

如果使用Spring 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 {

}
Kotlin
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 {

}
Kotlin
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默认不会搜索或使用META-INF/persistence.xml。如果您希望使用传统的persistence.xml,您需要定义自己的类型为LocalEntityManagerFactoryBean@Bean(ID为'entityManagerFactory')并在那里设置持久化单元名称。

请参阅JpaBaseConfiguration获取默认设置。

8.12. 使用Spring Data JPA和Mongo Repositories

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

还有一些标志(spring.data.*.repositories.enabledspring.data.*.repositories.type),您可以在外部配置中使用它们来切换自动配置的repositories。这样做很有用,例如,如果您想关闭Mongo repositories并仍然使用自动配置的MongoTemplate

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

8.13. 自定义Spring Data的Web支持

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

8.14. 将Spring Data Repositories暴露为REST端点

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

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

如果您在自定义的RepositoryRestConfigurer上未指定任何顺序,则它会在Spring Boot内部使用的顺序之后运行。如果需要指定顺序,请确保它大于0。

8.15. 配置被JPA使用的组件

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

如果您正在自行配置组件,可以使用EntityManagerFactoryDependsOnPostProcessor子类作为设置必要依赖关系的便捷方式。例如,如果您使用Hibernate Search与Elasticsearch作为其索引管理器,则任何EntityManagerFactory bean必须配置为依赖于elasticsearchClient bean,如下例所示:

Java
import jakarta.persistence.EntityManagerFactory;

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

/**
 * {@link EntityManagerFactoryDependsOnPostProcessor},确保
 * {@link EntityManagerFactory} bean 依赖于 {@code elasticsearchClient} bean。
 */
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
        extends EntityManagerFactoryDependsOnPostProcessor {

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

}
Kotlin
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor
import org.springframework.stereotype.Component

@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
    EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")

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

如果需要使用多个数据源配置jOOQ,则应为每个数据源创建自己的DSLContext。有关更多详细信息,请参阅JooqAutoConfiguration

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

9. 数据库初始化

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

9.1. 使用JPA初始化数据库

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

  • spring.jpa.generate-ddl(布尔值)开关控制该功能的开启和关闭,与供应商无关。

  • spring.jpa.hibernate.ddl-auto(枚举)是Hibernate的一个功能,以更精细的方式控制行为。稍后在本指南中将更详细地描述此功能。

9.2. 使用Hibernate初始化数据库

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

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

此外,如果Hibernate从头开始创建模式(即,如果ddl-auto属性设置为createcreate-drop),则会在类路径根目录中执行名为import.sql的文件。这对演示和测试可能很有用,但在生产环境中,这可能不是您希望存在于类路径中的内容。这是Hibernate的一个功能(与Spring无关)。

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

Spring Boot可以自动创建您的JDBC DataSource或R2DBC ConnectionFactory的模式(DDL脚本)并初始化其数据(DML脚本)。

默认情况下,它从optional:classpath*:schema.sql加载模式脚本,从optional:classpath*:data.sql加载数据脚本。这些模式和数据脚本的位置可以使用spring.sql.init.schema-locationsspring.sql.init.data-locations进行自定义。前缀optional:表示即使文件不存在,应用程序也会启动。要在文件不存在时使应用程序启动失败,请删除optional:前缀。

此外,Spring Boot处理optional:classpath*:schema-${platform}.sqloptional:classpath*:data-${platform}.sql文件(如果存在),其中${platform}spring.sql.init.platform的值。这允许您根据需要切换到特定于数据库的脚本。例如,您可能选择将其设置为数据库的供应商名称(hsqldbh2oraclemysqlpostgresql等)。

默认情况下,仅在使用嵌入式内存数据库时才执行SQL数据库初始化。要始终初始化SQL数据库,无论其类型如何,请将spring.sql.init.mode设置为always。类似地,要禁用初始化,请将spring.sql.init.mode设置为never。默认情况下,Spring Boot启用其基于脚本的数据库初始化器的快速失败功能。这意味着,如果脚本导致异常,应用程序将无法启动。您可以通过设置spring.sql.init.continue-on-error来调整该行为。

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

初始化脚本支持--用于单行注释和/* */用于块注释。不支持其他注释格式。

如果您使用类似Flyway或Liquibase的高级数据库迁移工具,应仅使用它们来创建和初始化模式。不建议同时使用基本的schema.sqldata.sql脚本以及Flyway或Liquibase,并且在将来的版本中将删除对此的支持。

如果需要使用高级数据库迁移工具初始化测试数据,请参阅关于FlywayLiquibase的部分。

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

与使用db/migration不同,上述配置根据数据库类型设置要使用的目录(例如db/migration/mysql用于MySQL)。支持的数据库列表可在DatabaseDriver中找到。

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

FlywayProperties提供了大部分Flyway的设置和一小部分额外属性,可用于禁用迁移或关闭位置检查。如果需要更多对配置的控制,请考虑注册一个FlywayConfigurationCustomizer bean。

Spring Boot调用Flyway.migrate()执行数据库迁移。如果需要更多控制,提供一个实现FlywayMigrationStrategy@Bean

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

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

有关可用设置的详细信息,请参阅LiquibaseProperties

9.5.3. 仅用于测试的Flyway迁移

如果要创建用于填充测试数据库的Flyway迁移,请将它们放在src/test/resources/db/migration中。例如,名为src/test/resources/db/migration/V9999__test-data.sql的文件将在您的生产迁移之后执行,仅在运行测试时才会执行。您可以使用此文件创建所需的测试数据。此文件不会打包在您的uber jar或容器中。

9.5.4. 使用Liquibase进行仅用于测试的迁移

如果您想创建用于填充测试数据库的Liquibase迁移,您必须创建一个测试变更日志,该日志还包括生产变更日志。

首先,您需要配置Liquibase在运行测试时使用不同的变更日志。一种方法是创建一个Spring Boot test配置文件,并将Liquibase属性放在其中。为此,请创建一个名为src/test/resources/application-test.properties的文件,并在其中放入以下属性:

属性
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-test.yaml
Yaml
spring:
  liquibase:
    change-log: "classpath:/db/changelog/db.changelog-test.yaml"

这将配置Liquibase在test配置文件中运行时使用不同的变更日志。

现在在src/test/resources/db/changelog/db.changelog-test.yaml中创建变更日志文件:

databaseChangeLog:
  - include:
      file: classpath:/db/changelog/db.changelog-master.yaml
  - changeSet:
      runOrder: "last"
      id: "test"
      changes:
        # 在此处插入您的更改

当运行测试时,将使用此变更日志,并且它不会打包在您的uber jar或容器中。它包括生产变更日志,然后声明一个新的changeset,其中runOrder: last设置指定它在运行所有生产changeset之后运行。您现在可以使用例如insert changeset来插入数据,或者使用sql changeset来直接执行SQL。

最后要做的是配置Spring Boot在运行测试时激活test配置文件。为此,您可以将@ActiveProfiles("test")注解添加到您的@SpringBootTest注解的测试类中。

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

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

9.6.1. 检测数据库初始化程序

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

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

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

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

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

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

  • DSLContext(jOOQ)

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

  • JdbcClient

  • JdbcOperations

  • NamedParameterJdbcOperations

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

10. NoSQL

Spring Boot提供了许多支持NoSQL技术的启动器。本节回答了在Spring Boot中使用NoSQL时出现的问题。

10.1. 使用Jedis代替Lettuce

默认情况下,Spring Boot启动器(spring-boot-starter-data-redis)使用Lettuce。您需要排除该依赖项,并改为包含Jedis。Spring Boot管理这两个依赖项,允许您在不指定版本的情况下切换到Jedis。

以下示例展示了如何在Maven中实现这一点:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

以下示例展示了如何在Gradle中实现这一点:

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-data-redis') {
        exclude group: 'io.lettuce', module: 'lettuce-core'
    }
    implementation 'redis.clients:jedis'
    // ...
}

11. 消息

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

11.1. 禁用JMS事务会话

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

Java
import jakarta.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;
    }

}
Kotlin
import jakarta.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)
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
    }

}

上述示例覆盖了默认工厂,如果应用程序定义了其他工厂,则应将其应用于其他工厂。

12. 批处理应用程序

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

12.1. 指定批处理数据源

默认情况下,批处理应用程序需要一个DataSource来存储作业详细信息。Spring Batch默认情况下期望单个DataSource。要使用除应用程序主要DataSource之外的DataSource,请声明一个DataSource bean,并在其@Bean方法上注释@BatchDataSource。如果这样做并且想要两个数据源,请记得将另一个标记为@Primary。要更好地控制,可以在您的一个@Configuration类中添加@EnableBatchProcessing,或者扩展DefaultBatchConfiguration。有关更多详细信息,请参阅@EnableBatchProcessingDefaultBatchConfiguration的Javadoc。

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

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

通过将spring-boot-starter-batch添加到应用程序的类路径中,启用了Spring Batch的自动配置。

如果在应用程序上下文中找到单个Job bean,则会在启动时执行它(有关详细信息,请参阅JobLauncherApplicationRunner)。如果找到多个Job bean,则必须使用spring.batch.job.name指定要执行的作业。

要禁用在应用程序上下文中找到的Job的运行,请将spring.batch.job.enabled设置为false

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

12.3. 从命令行运行

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

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

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

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

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

12.4. 重新启动已停止或失败的作业

要重新启动失败的Job,必须在命令行上重新指定所有参数(标识和非标识)。非标识参数不会从上一次执行中复制。这允许对其进行修改或删除。

当您使用自定义JobParametersIncrementer时,必须收集增量器管理的所有参数以重新启动失败的执行。

12.5. 存储作业存储库

Spring Batch需要一个数据存储库来存储Job。如果使用Spring Boot,则必须使用实际数据库。请注意,它可以是内存数据库,参见配置作业存储库

13. Actuator

Spring Boot包含Spring Boot Actuator。本节回答了在使用过程中经常出现的问题。

13.1. 更改Actuator端点的HTTP端口或地址

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

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

13.2. 自定义‘whitelabel’错误页面

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

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

覆盖错误页面取决于您使用的模板技术。例如,如果您使用Thymeleaf,可以添加一个error.html模板。如果您使用FreeMarker,可以添加一个error.ftlh模板。通常,您需要一个解析名称为errorView或处理/error路径的@Controller。除非替换了一些默认配置,否则您应该在您的ApplicationContext中找到一个BeanNameViewResolver,因此通过命名为error@Bean是一种方法。有关更多选项,请参阅ErrorMvcAutoConfiguration

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

13.3. 自定义清理

要控制清理过程,请定义一个SanitizingFunction bean。调用该函数的SanitizableData提供对键和值以及它们来自的PropertySource的访问权限。这使您可以例如清理来自特定属性源的每个值。每个SanitizingFunction按顺序调用,直到函数更改了可清理数据的值。

13.4. 将健康指标映射到Micrometer指标

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

}
Kotlin
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) = when (health.health().status) {
        Status.UP -> 3
        Status.OUT_OF_SERVICE -> 2
        Status.DOWN -> 1
        else -> 0
    }

}

14. 安全性

本节解答了在使用Spring Boot时涉及的安全性问题,包括在Spring Boot中使用Spring Security时可能出现的问题。

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

14.1. 关闭Spring Boot安全配置

如果在应用程序中定义了一个带有SecurityFilterChain bean的@Configuration,则此操作会关闭Spring Boot中的默认web应用程序安全设置。

14.2. 更改UserDetailsService并添加用户账户

如果提供了一个类型为AuthenticationManagerAuthenticationProviderUserDetailsService@Bean,则不会创建默认的InMemoryUserDetailsManager @Bean。这意味着您可以使用Spring Security提供的完整功能集(例如各种认证选项)。

添加用户账户的最简单方法是提供自己的UserDetailsService bean。

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

确保所有主要端点仅通过HTTPS可用是任何应用程序的重要任务。如果您使用Tomcat作为Servlet容器,那么Spring Boot会自动添加Tomcat自己的RemoteIpValve,如果检测到某些环境设置,则允许您依赖于HttpServletRequest来报告它是否安全(即使在处理真实SSL终止的代理服务器下游)。标准行为取决于某些请求头的存在或缺失(x-forwarded-forx-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"

(这些属性中的任何一个都会启用该阀门。或者,您可以通过自定义TomcatServletWebServerFactory并使用WebServerFactoryCustomizer bean来添加RemoteIpValve。)

要配置Spring Security要求所有(或部分)请求使用安全通道,请考虑添加自己的SecurityFilterChain bean,该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 {
        // 自定义应用程序安全性...
        http.requiresChannel((channel) -> channel.anyRequest().requiresSecure());
        return http.build();
    }

}
Kotlin
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 {
        // 自定义应用程序安全性...
        http.requiresChannel { requests -> requests.anyRequest().requiresSecure() }
        return http.build()
    }

}

15. 热交换

Spring Boot支持热交换。本节回答了关于其工作原理的问题。

15.1. 重新加载静态内容

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

另外,在IDE中运行(尤其是开启调试)是进行开发的好方法(所有现代IDE都允许重新加载静态资源,通常也允许热交换Java类更改)。

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

15.2. 重新加载模板而无需重启容器

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

15.2.1. Thymeleaf模板

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

15.2.2. FreeMarker模板

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

15.2.3. Groovy模板

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

15.3. 快速应用程序重启

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

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

15.4. 重新加载Java类而无需重启容器

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

16. 测试

Spring Boot包含许多测试工具和支持类,以及一个专用的starter,提供常见的测试依赖项。本节回答了关于测试的常见问题。

16.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("/"));
    }

}
Kotlin
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有着全面的集成,这也可以在使用@WebMvcTest切片和MockMvc测试控制器时使用。

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

16.2. 为包含在切片测试中的@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.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
        return http.build();
    }

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

}

对于具有上述@Configuration类的应用程序的@WebMvcTest,您可能希望在应用程序上下文中有SecurityFilterChain bean,以便测试您的控制器端点是否正确保护。但是,MyConfiguration不会被@WebMvcTest的组件扫描过滤器捕获,因为它不匹配过滤器指定的任何类型。您可以通过在测试类上注释@Import(MyConfiguration.class)来显式包含配置。这将加载MyConfiguration中的所有bean,包括在测试Web层时不需要的BasicDataSource 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.authorizeHttpRequests((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,可以使它们仅在特定切片测试中导入。

17. 构建

Spring Boot 包含用于 Maven 和 Gradle 的构建插件。本节回答了关于这些插件的常见问题。

17.1. 生成构建信息

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

要使用 Maven 生成构建信息,请按照以下示例中所示添加 build-info 目标的执行:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.2.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
查看 Spring Boot Maven 插件文档 以获取更多详细信息。
springBoot {
    buildInfo()
}
查看 Spring Boot Gradle 插件文档 以获取更多详细信息。

17.2. 生成 Git 信息

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

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

<build>
    <plugins>
        <plugin>
            <groupId>io.github.git-commit-id</groupId>
            <artifactId>git-commit-id-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
gradle-git-properties 插件来实现相同的结果,示例如下:

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

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

git.properties 中的提交时间应符合以下格式:yyyy-MM-dd’T’HH:mm:ssZ。这是上述两个插件的默认格式。使用此格式可以让时间被解析为一个 Date,并且在序列化为 JSON 时,可以通过 Jackson 的日期序列化配置设置来控制其格式。

17.3. 自定义依赖版本

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

每个 Spring Boot 发布版都是针对特定的第三方依赖项集设计和测试的。覆盖版本可能会导致兼容性问题。
此部分。

此部分。

17.4. 使用 Maven 创建可执行 JAR

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

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
<executions> 部分,如下所示:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.2.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
插件文档 以获取完整的使用详细信息。

17.5. 将 Spring Boot 应用程序用作依赖项

可执行 jar 格式 将应用程序类打包在 BOOT-INF/classes 中。这意味着在将可执行 jar 用作依赖项时,无法找到这些类。

exec,可以使用以下配置:

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

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

17.7. 创建一个非可执行的带排除项的JAR

通常,如果您有一个可执行的Jar和一个非可执行的Jar作为两个单独的构建产品,可执行版本可能有额外的配置文件,在库Jar中是不需要的。例如,application.yaml配置文件可能会从非可执行的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.yaml</exclude>
                        </excludes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

17.8. 使用Maven远程调试启动的Spring Boot应用程序

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

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

17.9. 使用Ant构建可执行存档而不使用spring-boot-antlib

要使用Ant构建,您需要获取依赖项,编译,然后创建一个Jar或War存档。要使其可执行,您可以使用spring-boot-antlib模块,或者您可以按照以下说明操作:

  1. 如果要构建一个Jar,将应用程序的类和资源打包在嵌套的BOOT-INF/classes目录中。如果要构建一个War,通常将应用程序的类打包在嵌套的WEB-INF/classes目录中。

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

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

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

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

以下示例显示了如何使用Ant构建可执行存档:

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

18. 预先处理

当人们使用Spring Boot应用程序的预先处理时,经常会出现一些问题。本节解决了这些问题。

18.1. 条件

预先处理优化应用程序,并根据构建时的环境评估条件。通过条件实现配置文件,因此也会受到影响。

如果您希望在预先处理优化的应用程序中基于条件创建bean,则必须在构建应用程序时设置环境。在构建时进行预先处理创建的bean将始终在运行应用程序时创建,并且无法关闭。为此,您可以设置构建应用程序时应该使用的配置文件。

对于Maven,可以通过设置spring-boot-maven-plugin:process-aot执行的profiles配置来实现:

<profile>
    <id>native</id>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>process-aot</id>
                            <configuration>
                                <profiles>profile-a,profile-b</profiles>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</profile>

对于Gradle,您需要配置ProcessAot任务:

tasks.withType(org.springframework.boot.gradle.tasks.aot.ProcessAot).configureEach {
    args('--spring.profiles.active=profile-a,profile-b')
}

只更改不影响条件的配置属性的配置文件在运行预先处理优化的应用程序时受到无限制的支持。

19. 传统部署

Spring Boot支持传统部署以及更现代化的部署形式。本节回答了关于传统部署的常见问题。

19.1. 创建可部署的War文件

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

生成可部署的war文件的第一步是提供一个SpringBootServletInitializer子类并重写其configure方法。这样做可以利用Spring Framework的servlet 3.0支持,并在应用由servlet容器启动时配置应用程序。通常,您应该更新应用程序的主类以扩展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);
    }

}
Kotlin
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运行应用程序。

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

要将现有的非Web Spring应用程序转换为Spring Boot应用程序,请将创建ApplicationContext的代码替换为调用SpringApplicationSpringApplicationBuilder的代码。Spring MVC Web应用程序通常适合首先创建可部署的war应用程序,然后将其迁移到可执行的war或jar。请参阅转换jar为war的入门指南

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

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) {
        // 自定义应用程序或调用application.sources(...)以添加源
        // 由于我们的示例本身是一个@Configuration类(通过@SpringBootApplication)
        // 我们实际上不需要覆盖此方法。
        return application;
    }


}
Kotlin
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 {
        // 自定义应用程序或调用application.sources(...)以添加源
        // 由于我们的示例本身是一个@Configuration类(通过@SpringBootApplication)
        // 我们实际上不需要覆盖此方法。
        return application
    }

}

请记住,无论您将什么放在sources中,都仅仅是一个Spring ApplicationContext。通常,任何已经工作的内容应该在这里工作。您可能可以稍后删除一些bean,并让Spring Boot为它们提供自己的默认值,但在需要这样做之前,应该能够使某些内容正常工作。

静态资源可以移动到类路径根目录下的/public(或/static/resources/META-INF/resources)。对于messages.properties也是一样(Spring Boot会自动在类路径根目录中检测到)。

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

  • 类型为ServletServletRegistrationBean@Bean会将该bean安装到容器中,就像在web.xml中的<servlet/><servlet-mapping/>

  • 类型为FilterFilterRegistrationBean@Bean会类似地行事(作为<filter/><filter-mapping/>)。

  • 可以通过在您的Application中使用@ImportResource将XML文件中的ApplicationContext添加进来。或者,已经大量使用注解配置的情况下,可以将其重新创建为几行代码的@Bean定义。

一旦war文件正常工作,您可以通过向您的Application添加一个main方法使其可执行,如下例所示:

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

如果您打算将应用程序作为war或可执行应用程序启动,则需要在类似以下类中的方法中共享构建器的自定义内容,该方法既可用于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);
    }

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

    }

}

应用程序可能属于多个类别:

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

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

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

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

所有这些都应该适合转换,但每种可能需要稍微不同的技术。

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

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

19.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 {

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

20. Docker Compose

本节包括与Spring Boot中Docker Compose支持相关的主题。

20.1. 自定义JDBC URL

在使用Docker Compose时,可以通过将org.springframework.boot.jdbc.parameters标签应用于服务来自定义JDBC URL的参数。例如:

services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=myuser'
      - 'POSTGRES_PASSWORD=secret'
      - 'POSTGRES_DB=mydb'
    ports:
      - '5432:5432'
    labels:
      org.springframework.boot.jdbc.parameters: 'ssl=true&sslmode=require'

有了这个Docker Compose文件,使用的JDBC URL是jdbc:postgresql://127.0.0.1:5432/mydb?ssl=true&sslmode=require

20.2. 在多个应用程序之间共享服务

如果要在多个应用程序之间共享服务,请在其中一个应用程序中创建compose.yaml文件,然后在其他应用程序中使用配置属性spring.docker.compose.file引用compose.yaml文件。您还应将spring.docker.compose.lifecycle-management设置为start-only,因为它默认为start-and-stop,停止一个应用程序将关闭其他仍在运行的应用程序的共享服务。将其设置为start-only不会在应用程序停止时停止共享服务,但一个警告是,如果关闭所有应用程序,服务将继续运行。您可以通过在包含compose.yaml文件的目录中的命令行上运行docker compose stop来手动停止服务。