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
如果您需要访问BeanFactory
或Environment
,则FailureAnalyzer
可以分别实现BeanFactoryAware
或EnvironmentAware
。
源码学习:
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.port
,server.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
具有ApplicationListeners
与ApplicationContextInitializers
,可以被用于应用自定义的context或environment。Spring Boot从META-INF/spring.factories
加载了许多此类自定义项,以供内部使用。注册其他自定义项的方法有多种:
- 在运行之前,通过对每个应用程序进行编程,方法是调用SpringApplication的
addListeners
和addInitializers
方法。 - 通过设置
context.initializer.classes
或context.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-jetty
或spring-boot-starter-undertow
代替。
对于反应栈的应用,spring-boot-starter-webflux
通过包括spring-boot-starter-reactor-netty
包括反应堆的Netty,但你可以使用spring-boot-starter-tomcat
,spring-boot-starter-jetty
或spring-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端口
您可以从日志输出或通过ServletWebServerApplicationContext
的WebServer
访问服务器正在运行的端口。最好的方法是确保它已初始化,并添加一个@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-server
和org.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栈的应用,有两种方法可以添加Servlet
,Filter
,ServletContextListener
,和由Servlet API支持的其他监听器:
- 使用Spring Bean添加Servlet,Filter或Listener
- 使用类路径扫描添加Servlet,Filter和Listener
使用Spring Bean添加Servlet,Filter或Listener
要使用Spring bean添加Servlet
,Filter
或Servlet *Listener
,你必须提供一个它的@Bean定义。当您要注入配置或依赖项时,这样做非常有用。但是,您必须非常小心,以免引起过多其他bean的急切(Eager)初始化,因为必须在应用程序生命周期的早期就将它们安装在容器中。(例如,让它们依赖于您DataSource或JPA配置不是一个好主意。)您可以通过在首次使用bean时而不是在初始化时对bean进行初始化(即懒加载)来解决这些限制。
在Filters和Servlets的情况下,还可以通过添加FilterRegistrationBean
或ServletRegistrationBean
来添加映射和初始化参数。
如果在过滤器注册上未指定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-For
和X-Forwarded-Proto
标头(大多数代理服务器都这样做),则绝对链接应正确呈现,只要在application.properties中将server.forward-headers-strategy
设置为NATIVE
或FRAMEWORK
。
如果您的应用程序在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
,请使用添加了TomcatContextCustomizer
的WebServerFactoryCustomizer
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。