20191127 Spring Boot官方文档学习(9.1-9.3)

时间:2019-11-27
本文章向大家介绍20191127 Spring Boot官方文档学习(9.1-9.3),主要包括20191127 Spring Boot官方文档学习(9.1-9.3)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

9.“使用方法”指南

9.1。Spring Boot应用程序

9.1.1。创建自己的FailureAnalyzer

FailureAnalyzer被包装在FailureAnalysis中,可以在启动时拦截异常并将其转换为易于阅读的消息。Spring Boot为与应用程序上下文相关的异常,JSR-303验证等提供了此类分析器。您也可以创建自己的。

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

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

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer

如果您需要访问BeanFactoryEnvironment,则FailureAnalyzer可以分别实现BeanFactoryAwareEnvironmentAware

源码学习:

org.springframework.boot.SpringApplication#run(java.lang.String...)
    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
      new Class[] { ConfigurableApplicationContext.class }, context);

spring.factories里读取org.springframework.boot.SpringBootExceptionReporter后,实例化FailureAnalyzers(通过构造函数将spring.factories中所有org.springframework.boot.diagnostics.FailureAnalyzer的值作为FailureAnalyzers构造器的参数)

org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

org.springframework.boot.diagnostics.FailureAnalyzer=\
xxx

org.springframework.boot.diagnostics.FailureAnalyzers#FailureAnalyzers(org.springframework.context.ConfigurableApplicationContext)

org.springframework.boot.SpringApplication中有两个getSpringFactoriesInstances方法,都是用来获取实例的,第一个方法使用的是无参构造函数,第二个使用有参构造函数(可以为多个)。例如,FailureAnalyzers就是使用的第二个方法,在构造器中调用方法获取FailureAnalyzer

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
   return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

9.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端点。
  • 寻找对bind方法的使用,以一种轻松的方式Binder将配置值明确地拉出Environment。它通常与前缀一起使用。
  • 查找@Value注释直接绑定到Environment
  • 寻找@ConditionalOnExpression注释以响应SpEL表达式来打开或关闭功能,这些注释通常使用从Environment中解析的占位符进行评估。

源码学习:

Binder的简单使用:

Binder binder = Binder.get(ctx.getEnvironment()); //绑定简单配置
MyProperties myProperties = binder.bind("my", Bindable.of(MyProperties.class)).get();
System.out.println(myProperties);

@ConfigurationProperties(prefix = "my")使用原理:
通过后置处理器,在实例化Bean之前,调用org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
底层也是使用Binder实现的。

9.1.3。启动之前自定义Environment或ApplicationContext

一个SpringApplication具有ApplicationListenersApplicationContextInitializers,可以被用于应用自定义的context或environment。Spring Boot从META-INF/spring.factories加载了许多此类自定义项,以供内部使用。注册其他自定义项的方法有多种:

  • 在运行之前,通过对每个应用程序进行编程,方法是调用SpringApplication的addListenersaddInitializers方法。
  • 通过设置context.initializer.classescontext.listener.classes属性,以声明方式针对每个应用程序。
  • 声明性地,对于所有应用程序,通过添加META-INF/spring.factories和打包一个jar文件,这些文件都被应用程序用作库。

SpringApplication发送一些特殊ApplicationEvents给监听器(一些甚至在context创建之前),然后注册了监听ApplicationContext发布事件的监听器。参考文档

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

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

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

public class EnvironmentPostProcessorExample 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) {
        if (!path.exists()) {
            throw new IllegalArgumentException("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默认加载的常见的属性来源。因此可以从Environment中获取文件的位置。前面的示例将custom-resource属性源添加到列表的末尾,以便在其他任何常见位置定义的键具有优先权。定制实现可以定义另一个顺序。

@SpringBootApplication使用@PropertySource似乎是从Environment中加载在一个自定义资源的便利和简单的方法,然而我们不建议这样做,因为Spring Boot准备Environment在ApplicationContext被刷新之前。用@PropertySource定义的任何键加载得太晚,对自动配置没有任何影响。

源码学习:

使用事件监听器将PropertySource添加到Environment

org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent

9.1.4。建立ApplicationContext层次结构(添加父上下文或根上下文)

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

9.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定义放到上下文中。

9.2。属性(Properties)和配置(Configuration)

9.2.1。在构建时自动扩展属性

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

使用Maven自动扩展属性

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

app.encoding=@project.build.sourceEncoding@
app.java.version=@java.version@

这样只会过滤生产配置(也就是说,不会对进行过滤src/test/resources)。

如果启用addResources标志,则spring-boot:run goal 可以将src/main/resources直接添加到类路径中(用于热重载)。这样做避免了资源过滤和此功能。相反,您可以使用exec:java goal或自定义插件的配置。

如果您不使用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,则可以通过构建扩展它们。

使用Gradle自动扩展属性

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

processResources {
    expand(project.properties)
}

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

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

Gradle的expand方法使用Groovy 的SimpleTemplateEngine方法来转换${..}令牌。${..}风格与Spring自己的属性占位符机制冲突。要将Spring属性占位符与自动扩展一起使用,请按以下步骤对Spring属性占位符进行转义:\${..}

!!!按照文档不能实现功能

9.2.2。外部化配置SpringApplication

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

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

然后,启动时不会打印Spring Boot标语,并且应用程序也没有启动嵌入式Web服务器。

外部配置中定义的属性会覆盖用Java API指定的值,但用于创建ApplicationContext的sources例外。考虑以下应用程序:

new SpringApplicationBuilder()
    .bannerMode(Banner.Mode.OFF)
    .sources(demo.MyApp.class)
    .run(args);

现在考虑以下配置:

spring.main.sources=com.acme.Config,com.acme.ExtraConfig
spring.main.banner-mode=console

实际应用中,现在显示Banner(配置覆盖),并且ApplicationContext使用了三个sources(按以下顺序): demo.MyApp,com.acme.Config和com.acme.ExtraConfig。

9.2.3。更改应用程序外部属性的位置

默认情况下,来自不同来源的属性会以Environment定义的顺序添加到Spring 中。

扩充和修改此顺序的一种不错的方法是在应用程序源中添加@PropertySource注释。检查传递给SpringApplication静态方法的类以及使用setSources()添加的类,以查看它们是否具有@PropertySources。如果确实如此,那么这些属性会尽早添加到Environment,以便在ApplicationContext生命周期的所有阶段中使用。以这种方式添加的属性的优先级低于使用默认位置(例如application.properties),系统属性,环境变量或命令行添加的属性。

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

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

无论您在Environment中进行什么设置,Spring Boot都将始终如上所述进行加载application.properties。默认情况下,如果使用YAML,则扩展名为.yml的文件也将添加到列表中。

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

请参阅参考资料ConfigFileApplicationListener

9.2.4。使用“简短”命令行参数

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

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属性同义词。

9.2.5。对外部属性使用YAML

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

spring:
    application:
        name: cruncher
    datasource:
        driverClassName: 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,使其深一层,并具有句点分隔的键,这是许多人习惯使用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

9.2.6。设置激活的Spring Profiles

Spring Environment为此提供了一个API,但是您通常会设置一个System属性(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中设置激活的profiles,如以下示例所示:

spring.profiles.active=production

以这种方式设置的值将由系统属性或环境变量设置代替,而不由SpringApplicationBuilder.profiles()方法替代。因此,后一种Java API可用于扩充配置文件,而不是更改默认值。

9.2.7。根据Environment更改配置

YAML文件实际上是由---行分隔的文档序列,每个文档都分别解析为Map。

如果YAML文档包含spring.profiles键,则将profiles文件值(以逗号分隔的profiles列表)输入到Spring Environment.acceptsProfiles()方法中。如果这些配置文件中的任何一个处于活动状态,那么该文档将包含在最终合并中(否则,它不会包含在此文档中),如以下示例所示:

server:
    port: 9000
---

spring:
    profiles: development
server:
    port: 9001

---

spring:
    profiles: production
server:
    port: 0

在前面的示例中,默认端口为9000。但是,如果名为“development”的Spring profile处于活动状态,则端口为9001。如果“production”为活动状态,则该端口为0。

YAML文档按照它们遇到的顺序进行合并。以后的值将覆盖以前的值。

要对属性文件执行相同的操作,可以使用application-${profile}.properties指定特定于profile的值。

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

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

具有Actuator功能的正在运行的应用程序具有一个configprops终结点,该终结点显示了可通过@ConfigurationProperties访问的所有绑定和可绑定属性。

附录中包含一个application.properties示例,其中列出了Spring Boot支持的最常见属性。最终列表来自搜索源代码中的@ConfigurationProperties@Value注释,以及偶尔使用的Binder

9.3。嵌入式Web服务器

9.3.1。使用其他Web服务器

许多Spring Boot启动器都包含默认的嵌入式容器。
对于servlet堆栈应用程序,通过spring-boot-starter-web包括spring-boot-starter-tomcat来包括Tomcat ,但是您可以使用spring-boot-starter-jettyspring-boot-starter-undertow代替。
对于反应栈的应用,spring-boot-starter-webflux通过包括spring-boot-starter-reactor-netty包括反应堆的Netty,但你可以使用spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow代替。

切换到其他HTTP服务器时,除了包括所需的依赖关系之外,还需要排除默认的依赖关系。Spring Boot为HTTP服务器提供了单独的启动器,以帮助简化此过程。

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

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

Servlet API的版本已被覆盖,因为与Tomcat 9和Undertow 2.0不同,Jetty 9.4不支持Servlet 4.0。

以下Gradle示例显示了如何排除Netty并包括Spring WebFlux的Undertow:

configurations {
    // exclude Reactor Netty
    compile.exclude module: 'spring-boot-starter-reactor-netty'
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-webflux'
    // Use Undertow instead
    compile 'org.springframework.boot:spring-boot-starter-undertow'
    // ...
}

spring-boot-starter-reactor-netty使用WebClient类是必需的,因此即使您需要包括其他HTTP服务器,也可能需要保持对Netty的依赖。

9.3.2。禁用Web服务器

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

spring.main.web-application-type=none

9.3.3。更改HTTP端口

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

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

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

9.3.4。使用随机未分配的HTTP端口

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

9.3.5。在运行时发现HTTP端口

您可以从日志输出或通过ServletWebServerApplicationContextWebServer访问服务器正在运行的端口。最好的方法是确保它已初始化,并添加一个@Bean类型为ApplicationListener<ServletWebServerInitializedEvent>,然后在发布事件时将其从事件中拉出。

@Component
public class MyListener implements ApplicationListener<ServletWebServerInitializedEvent> {
    @Override
    public void onApplicationEvent(ServletWebServerInitializedEvent event) {
        System.out.println("MyListener...onApplicationEvent...");
        System.out.println(event.getSource().getPort());
    }
}

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

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

    @Autowired
    ServletWebServerApplicationContext server;

    @LocalServerPort
    int port;

    // ...

}

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

9.3.6。启用HTTP响应压缩

Jetty,Tomcat和Undertow支持HTTP响应压缩。可以在application.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属性来配置此行为。

9.3.7。配置SSL

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

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

有关Ssl所有受支持属性的详细信息,请参见org.springframework.boot.web.server.Ssl

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

9.3.8。配置HTTP/2

您可以使用server.http2.enabled配置属性在Spring Boot应用程序中启用HTTP/2支持。该支持取决于所选的Web服务器和应用程序环境,因为JDK8对协议的支持不是开箱即用的。

Spring Boot不支持h2c(HTTP/2协议的明文版本)。因此,您必须先配置SSL。

Undertow与HTTP/2

从Undertow 1.4.0+开始,在JDK8上没有任何其他要求就支持HTTP/2。

HTTP/2与Jetty

从Jetty 9.4.8开始,Conscrypt库还支持HTTP/2 。要启用该支持,您的应用程序需要具有两个附加依赖项:org.eclipse.jetty:jetty-alpn-conscrypt-serverorg.eclipse.jetty.http2:http2-server

Tomcat与HTTP/2

默认情况下,Spring Boot随Tomcat 9.0.x一起提供,当使用JDK 9或更高版本时,Tomcat 9.0.x支持HTTP/2。另外,如果libtcnative库及其依赖项已安装在主机操作系统上,则可以在JDK 8上使用HTTP/2 。

如果没有,则必须使库文件夹可用于JVM库路径。您可以使用JVM参数(例如-Djava.library.path=/usr/local/opt/tomcat-native/lib)来执行此操作。有关更多信息,请参见Tomcat官方文档

在没有本机支持的情况下,在JDK 8上启动Tomcat 9.0.x会记录以下错误:

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

此错误不是致命错误,并且该应用程序仍以HTTP/1.1 SSL支持启动。

HTTP/2和Reactor Netty

默认情况下,spring-boot-webflux-starter使用Reactor Netty作为服务器使用。使用JDK 9或更高版本的JDK支持,可以将Reactor Netty配置为HTTP/2。对于JDK 8环境或最佳运行时性能,此服务器还支持带有本机库的HTTP/2。为此,您的应用程序需要具有其他依赖关系。

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

9.3.9。配置Web服务器

通常,您首先应该考虑使用许多可用的配置键之一,并通过在您的application.properties(或application.yml,或环境等)中添加新条目来自定义Web服务器。请参阅“发现外部属性的内置选项 ”。server.*命名空间是非常有用的在这里,它包括命名空间像server.tomcat.*server.jetty.*和其他,针对服务器的特定功能。

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

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

@Component
public class MyTomcatWebServerCustomizer
        implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

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

此外,Spring Boot还提供:

Server Servlet stack Reactive stack
Tomcat TomcatServletWebServerFactory TomcatReactiveWebServerFactory
Jetty JettyServletWebServerFactory JettyReactiveWebServerFactory
Undertow UndertowServletWebServerFactory UndertowReactiveWebServerFactory
Reactor N/A NettyReactiveWebServerFactory

一旦访问了WebServerFactory,就可以经常向其添加定制,以配置特定的部分,例如连接器,服务器资源或服务器本身,全部使用服务器特定的API。

作为最后的选择,您也可以声明自己的 WebServerFactory组件,该组件将覆盖Spring Boot提供组件。在这种情况下,您不能再依赖server命名空间中的配置属性。

9.3.10。将Servlet,Filter或Listener添加到应用程序

在一个使用spring-boot-starter-web的servlet栈的应用,有两种方法可以添加ServletFilterServletContextListener,和由Servlet API支持的其他监听器:

  • 使用Spring Bean添加Servlet,Filter或Listener
  • 使用类路径扫描添加Servlet,Filter和Listener

使用Spring Bean添加Servlet,Filter或Listener

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

在Filters和Servlets的情况下,还可以通过添加FilterRegistrationBeanServletRegistrationBean来添加映射和初始化参数。

如果在过滤器注册上未指定dispatcherType ,则使用REQUEST。这符合Servlet规范的默认调度程序类型。

像其他任何Spring bean一样,您可以定义Servlet过滤器bean的顺序。参考文档

禁用Servlet或Filter的注册

正如前面所述,任何Servlet或Filter bean自动注册进servlet容器。要禁用特定Filter或Servlet bean的注册,请为其创建注册bean并将其标记为已禁用,如以下示例所示:

@Bean
public FilterRegistrationBean registration(MyFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
}
使用类路径扫描添加Servlet,Filter和Listener

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

9.3.11。配置访问日志

可以通过它们各自的名称空间为Tomcat,Undertow和Jetty配置访问日志。

例如,以下设置使用自定义模式记录对Tomcat的访问。

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

日志的默认位置是相对于Tomcat基本目录的logs目录。默认情况下,logs目录是一个临时目录,因此您可能需要修复Tomcat的基本目录或对日志使用绝对路径。在前面的示例中,日志相对于应用程序的工作目录my-tomcat/logs可用。

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

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

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

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

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

默认情况下,日志重定向到System.err

9.3.12。在前端代理服务器后面运行

您的应用程序可能需要发送302重定向或使用绝对链接将内容呈现回自身。当在代理后面运行时,调用者需要链接到代理,而不要链接到托管您的应用程序的计算机的物理地址。通常,此类情况是通过与代理之间的协议来处理的,该代理添加了标头,以告诉后端如何构造与自身的链接。

如果代理添加常规X-Forwarded-ForX-Forwarded-Proto标头(大多数代理服务器都这样做),则绝对链接应正确呈现,只要在application.properties中将server.forward-headers-strategy设置为NATIVEFRAMEWORK

如果您的应用程序在Cloud Foundry或Heroku中运行,则server.forward-headers-strategy属性默认为NATIVE。在所有其他情况下,默认为NONE

自定义Tomcat的代理配置

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

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

Tomcat还配置有一个默认正则表达式,该正则表达式与要信任的内部代理匹配。默认情况下,IP地址10/8,192.168/16,169.254/16和127/8是值得信赖的。您可以通过在application.properties上添加一个条目来自定义阀门的配置,如以下示例所示:

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

仅当使用properties文件进行配置时,才需要双反斜杠。如果使用YAML,则单个反斜杠就足够了,并且与上一个示例中所示的192.168.\d{1,3}.\d{1,3}值相等。

您可以通过将internal-proxies设置为空来信任所有代理(但在生产环境中不要这样做)。

您可以通过关闭自动开关(设置 server.forward-headers-strategy=NONE)并在TomcatServletWebServerFactory bean中添加新的Valve实例来完全控制Tomcat的RemoteIpValve配置。

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

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

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addAdditionalTomcatConnectors(createSslConnector());
    return tomcat;
}

private Connector createSslConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    try {
        File keystore = new ClassPathResource("keystore").getFile();
        File truststore = new ClassPathResource("keystore").getFile();
        connector.setScheme("https");
        connector.setSecure(true);
        connector.setPort(8443);
        protocol.setSSLEnabled(true);
        protocol.setKeystoreFile(keystore.getAbsolutePath());
        protocol.setKeystorePass("changeit");
        protocol.setTruststoreFile(truststore.getAbsolutePath());
        protocol.setTruststorePass("changeit");
        protocol.setKeyAlias("apitester");
        return connector;
    }
    catch (IOException ex) {
        throw new IllegalStateException("can't access keystore: [" + "keystore"
                + "] or truststore: [" + "keystore" + "]", ex);
    }
}

9.3.14。使用Tomcat的LegacyCookieProcessor

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

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

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

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

9.3.15。启用Tomcat的MBean Registry

默认情况下,嵌入式Tomcat的MBean注册表是禁用的。这样可以最大程度地减少Tomcat的内存占用。例如,如果要使用Tomcat的MBean,以便可以通过Micrometer公开它们,则必须使用server.tomcat.mbeanregistry.enabled属性,如以下示例所示:

server.tomcat.mbeanregistry.enabled=true

9.3.16。使用Undertow启用多个侦听器

UndertowServletWebServerFactory中添加一个UndertowBuilderCustomizer,并在Builder中添加一个侦听器,如以下示例所示:

@Bean
public UndertowServletWebServerFactory servletWebServerFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {

        @Override
        public void customize(Builder builder) {
            builder.addHttpListener(8080, "0.0.0.0");
        }

    });
    return factory;
}

9.3.17。使用@ServerEndpoint创建WebSocket端点

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

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

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