Spring Boot 3.0.0-M1 Reference Documentation(Spring Boot中文参考文档)-17

2023年1月8日11:57:49

17. “如何做”指南

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

如果你有特别的问题,我们没有涵盖到,你可能想要检查stackoverflow.com来查看,如果某人已经提供了一个答案。这个也是一个非常好的地方回答新的问题(请使用spring-boot标签)。

17.1. Spring Boot应用程序

这部分包含直接相关Spring Boot应用程序的主题。

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

如果你需要访问BeanFactory或者Environment,你的FailureAnalyzer可以依次实现BeanFactoryAware或者EnvironmentAware

17.1.2. 排除自动配置问题

Spring Boot自动配置尽最大的努力“做正确的事情”,但是有时候失败,他告诉为什么是非常困难的。

在任何Spring Boot ApplicationContext中存在一个非常有用的ConditionEvaluationReport。如果你启用BEBUG日志输出,你可以看到它。如果你使用spring-boot-actuator(请查看Actuator章节),也有一个conditions端点以JSON的格式呈现报告。使用这个端点来调试应用程序并查看Spring Boot在运行时已经添加了哪些特性(和哪个没有被添加)。

通过查看源码和Javadoc,更多的问题被解答。当阅读代码时,记住以下经验规则:

  • 查找成为*AutoConfiguration的类和阅读他们源码。特别注意@Conditional*注解找出他们何时启用了哪些特性。添加--debug到命令行或者系统属性-Ddebug来获取在所有应用程序中自动配置决策的控制台的日志。在一个启用actuator正在运行的应用程序中,查看conditions端点(/actuator/conditions或者等价的JMX)了解相同的信息。
  • 查找@ConfigurationProperties类(例如ServerProperties)并读来自可用的外部配置选项。@ConfigurationProperties注解有name属性,它作为外部属性前缀。因此,ServerPropertiesprefix="server"并他的配置属性是server.protserver.address和其他。在启用actuator的运行中的应用程序,可以在configprops端点查看.
  • 查找在Binder上的bind方法的用法,以轻松的方式显式地从Environment中提取配置值。它通常带有前缀。
  • 查找@Value注解,直接和Environment绑定
  • 查找@ConditionalOnExpression注解,它在响应SpEL表达式时,打开和关闭特性,通常使用从Environment解析的占位符进行计算。

17.1.3. 在启动之前定制环境或者ApplicationContext

SpringApplicationApplicationListenersApplicationContextInitializers,他们被用来上下文和环境的定制。Spring Boot从META-INF/spring.factories加载许多这样的定制以内部使用。有超过一种方式来注册额外的定制:

  • 程序化的,每个应用程序,在你调用它之前,在SpringApplication中通过调用addListenersaddInitializers方法。
  • 声明式的,每个应用程序,通过设置context.initializer.class或者context.listener.class属性。
  • 声明式的,对于所有应用程序,通过添加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配置文件:

import java.io.IOException;

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

public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

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

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

}

Environment已经使用所有的默认情况下Spring Boot加载的通常的属性源做好准备。因此可以从环境中获取文件的位置。前面的示例在列表的最后添加custom-resource属性,以便在任何通常的其他位置定义的key有优先级。自定义的实现可以定义另一个顺序。

当在你的@SpringBootApplication上使用@PropertySource可能看起来成为一个方便的方式来加载在Environment中的一个自定义资源,我们不建议这样做。这些属性资源是不会被添加到Environment,直到应用程序上下文已经刷新。对于配置某个属性来说,这样太晚了,例如logging.*spring.main.*,他们在刷新开始之前被读取。

17.1.4. 构建一个ApplicationContext层次(添加一个Parent或者Root上下文)

你可以使用ApplicationBuilder类来创建parent/child ApplicationContext层次。请查看在Spring Boot特性章节“流式Builder API”了解更多信息。

17.1.5. 创建一个非web应用程序

并不是所有的Spring application必须是web应用程序(或者web服务)。如果你想在main方法执行一些代码但是也引导一个Spring应用程序建立基础设施来使用,你可以使用Spring Boot的SpringApplication特性。SpringApplication改变它的ApplicationContext类,取决于它是否认为它需要一个web应用程序。你可以做的第一件事来帮助它是将与服务器相关的依赖项(例如servlet API)从类路径中移除。如果你不能这么做(例如,基于相同的代码,你运行两个应用程序),然后你可以显示地在你的SpringApplication实例上调用setWebApplicationType(WebApplicationType.NONE)或者设置applicationContextClass属性(通过Java API或者使用外部属性)。你想要作为你的业务逻辑执行的应用程序代码可以作为CommandLineRunner实现并作为@Bean定义放到上下文中。

17.2. 属性和配置

这部分包含的主题有:设置和读取属性,配置设置和他们与Spring Boot应用程序的相互作用。

17.2.1. 在构建时自动化扩展属性

你可以通过使用现有的构建配置自动扩展在你的项目构建配置中的一些属性,而不是硬编码他们。可以同时在Maven和Gradle中实现。

使用Maven自动化属性扩展

你可以通过使用资源过滤自动化扩展来自Maven项目的属性。如果你使用spring-boot-starter-parent,你可以使用@...@占位符依赖你的Maven项目属性,如下示例所示:

app:
  encoding: "@project.build.sourceEncoding@"
  java:
    version: "@java.version@"

只有生产配置是这样过滤的(换句话说,没有对src/test/resources应用任何过滤)。

如果你启用addResources标识,spring-boot:run目标可以直接添加src/main/resources到类路径(出于热部署目的)。这样绕过了资源过滤和这个特性,你可以使用eec:java目标或者定制插件的配置。请查看插件用法页面了解更多详情。

如果你没有使用启动器父类,你需要你的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,通过构建可能扩展这些属性。

使用Gradle自动化属性扩展

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

tasks.named('processResources') {
    expand(project.properties)
}

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

app:
  name: "${name}"
  description: "${description}"

Gradle的expand方法使用Groovy的SimpleTemplateEngine,它转换${...} 符号。${...}格式与Spring自己的属性占位符机制冲突。要与自动化扩展一起使用Spring属性占位符,转译Spring 属性占位符,如下\${..}

17.2.2. 外部化SpringApplication配置

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

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

然后Spring Boot banner在启动时不会打印,并且应用程序不会启动内嵌的web应用服务。

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

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

@SpringBootApplication
public class MyApplication {

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

}

或者提供给SpringApplicationBuildersources(...)方法:

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

public class MyApplication {

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

}

上面所提供的示例,如果我们有以下配置:

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

真正的应用程序将展示banner(因为配置重写)并为ApplicationContext使用了三个源。应用程序源是:

  • MyApplication(来自代码)
  • MyDatabaseConfig(来自外部配置)
  • MyJmsConfig(来自外部配置)

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

默认情况下,来自不同源的属性以被定义的顺序被添加到Spring Environment(请查看在‘Spring Boot特性的章节’的“外部化配置”了解确切的顺序)。

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

  • spring.config.name(SPRING_CONFIG_NAME):application作为文件名称的根路径的默认值
  • spring.config.location(SPRING_CONFIG_LOCATION):要加载的文件(例如类路径资源或者URL)。为这个文档设置了单独的Environment属性源,可以通过系统属性,环境变量或者命令行覆盖它。

和你在环境中所设置的无关,Spring Boot一直如上所述地加载application.properties。默认情况下,如果YAML被使用,然后带有’.yml’文件扩展也将被添加到列表。

Spring Boot以DEBUG级别记录被加载的配置文件,以及在TRACE级别为找到的候选文件。

请查ConfigFileApplicationListener看了解更多详情。

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

一些人喜欢在命令行使用(例如)--port=9000代替--server.port=9000来设置配置属性。你可以通过使用在application.properties中的占位符启用这个行为,如下示例所示:

server:
  port: "${port:8080}"

如果你继承spring-boot-starter-parentPOM,maven-resources-plugins的默认的过滤器token已经从${*}更改为@(也就是,@maven.token代替${maven.token})来避免与Spring风格占位符冲突。如果你已经为application.properties直接启用Maven过滤器,你可能也想要改变默认的过滤器token以使用其他的界定符

在特定的情况下,端口绑定工作在PaaS环境中,如Heroku或者Foundry。在这两个平台中,PORT环境变量被自动设置并且Spring可以绑定到Environment属性的大写同义词。

17.2.5. 使用YAML用于外部属性

YAML是JSON的超集,正如此,分层结构的格式存储外部属性是非常方便的语法,如下示例所示:

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

创建一个名为application.yml的文件和将它放入到根类路径。然后添加snakeyaml到你的依赖(Maven 坐标org.yaml:snakeyaml,如果你使用了spring-boot-starter则已经包含)。YAML文件被转换为Java Map<String,Object>(像JSON对象),并且Spring Boot平铺这个map以便他是一级深度并有分隔周期key,因为许多人在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

请查看Spring Boot特性章节中的“使用YAML工作”了解更多关于YAML信息。

17.2.6. 设置活跃的Spring配置文件

Spring Environment有用于这个的API,但是你通常需要设置一个系统属性(spring.profiles.active)或者一个OS环境变量(SPRING_PROFILES_ACTIVE)。而且,你可以使用-D参数启动你的应用程序(记住在主类或者jar归档之前放入它),如下:

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

在Spring Boot中,你也可以在application.properties中设置活跃的配置文件,如下示例所示:

spring:
  profiles:
    active: "production"

这种方式设置的值通过使用系统属性或者环境变量设置替换,但是不会被SpringApplicationBuilder.profiles()方法替换。因此,后者Java API可以在没有更改默认值的情况下用来增加配置文件。

请查看在Spring Boot特性章节中“配置文件”了解更多信息。

17.2.7. 设置默认的配置文件名称

默认的配置文件是一个如果没有配置文件是活跃被启用的配置文件。默认情况下,默认的配置文件的名称是default,但是可以通过系统属性(spring.profiles.default)或者OS环境变量(SPRING_PROFILES_DEFAULT)更改。

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

spring:
  profiles:
    default: "dev"

请查看在Spring Boot特性章节中“配置文件”了解更多信息。

17.2.8. 依据环境修改配置

Spring Boot 支持多文档YAML和属性文件(请查看使用多文档文件工作了解细节),这些文件基于活跃的配置文件有条件的被激活。

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

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.

文档以在文档中遇到的顺序的合并。最后的值覆盖前面的值。

17.2.9. 发现内建的选项用于外部属性

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

一个使用Actuator特性的正在运行的应用程序有一个configprops端点,它通过@ConfigurationProperties展示了所有可用的绑定和可绑定的属性。

附录包括一个application.properties示例,使用了大量的常用的Spring Boot支持的属性列表。最终的列表来自于搜索@ConfigurationProperties@Value注解的源码以及偶尔使用的Binder。要了解更多关于加载属性的完整顺序,请查看“外部化配置”。

17.3. 内嵌web服务器

每一个Spring Boot应用程序包含一个内嵌的web服务器。这个特性导致许多如果做问题,包括如果修改内嵌服务器和如何配置内嵌服务器。这部分将回答这些问题。

17.3.1. 使用另一个web服务器

许多Spring Boot启动器包含默认的内嵌容器:

  • 对于servlet 栈应用程序,spring-boot-starter-web通过包括spring-boot-starter-tomcat包含Tomcat,但是你可以使用spring-boot-starter-jetty或者spring-boot-starter-undertomw代替。
  • 对于响应式栈应用程序,spring-boot-starter-webflux通过包含spring-boot-starter-reactor-netty包含Reactor Netty,但是你可以使用spring-boot-starter-tomcat,spring-boot-starter-jetty,或者spring-boot-starter-undertomw代替。

当转换到一个不同的HTTP服务器,你需要将默认的依赖项转换为你所需要的依赖项。为了帮助这个过程,Spring Boot为每一个支持的HTTP服务器提供一个单独的启动器。

以下Maven示例展示如何排除Tomcat和包含Jetty用于Spring MVC:

<properties>
    <servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- Exclude the Tomcat dependency -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

servlet API的版本已经被重写,因为与Tomcat 9和Undertow2不同,Jetty9.4不支持servlet 4.0。

如果你希望使用Jetty 10,它不支持servlet 4.0,你可以正如下方示例展示的这样做:

<properties>
    <jetty.version>10.0.8</jetty.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- Exclude the Tomcat dependency -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <exclusions>
        <!-- Exclude the Jetty-9 specific dependencies -->
        <exclusion>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>websocket-server</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>javax-websocket-server-impl</artifactId>
        </exclusion>
    </exclusions>
</dependency>

注意除了排除Tomcat启动器外,还需要排除两个特定的Jetty9依赖。

以下Gradle示例配置了必须的依赖和模块替换来使用Undertow代替Reator Netty用于Spring WebFlux:

dependencies {
    implementation "org.springframework.boot:spring-boot-starter-undertow"
    implementation "org.springframework.boot:spring-boot-starter-webflux"
    modules {
        module("org.springframework.boot:spring-boot-starter-reactor-netty") {
            replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty")
        }
    }
}

使用WebClient类需要spring-boot-starter-reactor-netty,所以即使你需要包含一个不同的HTTP服务器,你可能需要保持对Netty的依赖。

17.3.2. 禁用web服务器

如果你的类路径包含必须的片段来启动一个web服务器,Spring Boot将自动启动它。要禁用这种行为,在application.properties中配置WebApplicationType,如下示例所示:

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

17.3.3. 修改HTTP端口

在一个单独的应用程序中,主要的HTTP端口默认是8080,但是可以使用server.port设置(例如,在application.properties或者系统属性)。感谢宽松的Environment值绑定,你也可以使用SERVER_PORT(例如,作为一个系统变量)。

要完全关闭HTTP端点但仍创建一个WebApplicationContext,使用servet.prot=-1(这样做有时对于测试是有用的)。

要了解更多详情,请查看在Spring Boot特性章节中的“定制内嵌servelt容器”,或者ServerProperties源码。

17.3.4.使用一个随机的未赋值的HTTP端口

要对空闲端口(使用OS native防止冲突)扫描,使用servet.port=0

17.3.5. 在运行时发现HTTP端口

你可以从日志输出或者通过它的WebServerWebServerApplicationContext访问正在运行服务器的端口。获取它并确保它被初始化的最好的方法是添加一个类型为ApplicationListener<WebServerInitializedEvent>@Bean并在发布时将容器从事件中拉出。

使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOW_PORT)的测试也可以通过使用@LocalServerPort注解注入真实的端口到一个字段:

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

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

    @LocalServerPort
    int port;

    // ...

}

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

17.3.6. 启用HTTP响应压缩

Jetty,Tomcat,Reactor Netty和Undertow支持HTTP响应压缩。他可以在applicaton.properties被启用,如下:

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属性配置这个行为。

17.3.7. 配置SSL

SSL可以通过设置多个servler.ssl.*属性声明式配置,典型方式在application.properties或者application.yml.以下示例展示使用Java

  • 作者:dzq584462393
  • 原文链接:https://blog.csdn.net/dzq584462393/article/details/125602446
    更新时间:2023年1月8日11:57:49 ,共 14600 字。