本文为官方文档直译版本。原文链接
篇幅较长,遂分两篇
本节解答了在使用 Spring Boot 时经常出现的 "我该怎么做… "的问题。虽然没有面面俱到,但也涵盖了不少内容。
如果您遇到的具体问题我们在此未涉及,您可能需要查看 stackoverflow.com,看看是否有人已经提供了答案。这里也是提出新问题的好地方(请使用 spring-boot
标签)。
我们也非常乐意扩展本部分。如果你想添加 “如何做”,请向我们发送拉取请求。
本节包括与 Spring Boot 应用程序直接相关的主题。
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
。
Spring Boot 自动配置会尽力 “做正确的事”,但有时也会失败,而且很难说清原因。
在任何 Spring Boot ApplicationContext
中都有一个非常有用的 ConditionEvaluationReport
。如果启用 DEBUG
日志输出,就能看到它。如果使用 spring-boot-actuator
(请参阅 Actuator 章节),还有一个以 JSON 格式呈现报告的条件端点。使用该端点可以调试程序,并查看 Spring Boot 在运行时添加了哪些功能(哪些功能未添加)。
通过查看源代码和 Javadoc,还可以回答更多问题。阅读代码时,请记住以下经验法则:
*AutoConfiguration
的类并阅读它们的源代码。特别注意 @Conditional*
注解,了解它们启用了哪些功能以及何时启用。在命令行中添加 --debug
或系统属性 -Ddebug
,以便在控制台上获取应用程序中所有自动配置决定的日志。在启用了Actuator的运行中应用程序中,查看条件端点(/actuator/conditions
或 JMX 对应端点)以获取相同信息。@ConfigurationProperties
的类(如 ServerProperties),从中读取可用的外部配置选项。@ConfigurationProperties
注解有一个 name
属性,作为外部属性的前缀。因此,ServerProperties
的前缀为 “server
”,其配置属性包括 server.port
、server.address
等。在启用了Actuator的运行应用程序中,查看 configprops
端点。Binder
上的 bind
方法,以轻松的方式从Environment
中显式提取配置值。该方法通常带有前缀。Environment
的 @Value
注解。@ConditionalOnExpression
注解,该注解可根据 SpEL 表达式开关功能,通常使用从Environment
中解析的占位符进行评估。SpringApplication
具有 ApplicationListeners
和 ApplicationContextInitializers
,用于将自定义应用到上下文或环境中。Spring Boot 会从 META-INF/spring.factories
中加载大量此类自定义,供内部使用。注册其他自定义的方法不止一种:
SpringApplication
前调用其 addListeners
和 addInitializers
方法。META-INF/spring.factories
,并打包一个 jar 文件,让所有应用程序都将其作为一个库来使用。SpringApplication
会向监听器发送一些特殊的 ApplicationEvents
(有些甚至在上下文创建之前),然后也会为 ApplicationContext
发布的事件注册监听器。有关完整列表,请参阅 “Spring Boot 功能” 部分中的 “应用程序事件和监听器”。
还可以使用 EnvironmentPostProcessor
在应用上下文刷新之前自定义环境。每个实现都应在 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 默认加载的所有常用属性源。因此,可以从环境中获取文件的位置。前面的示例在列表末尾添加了自定义资源属性源,因此在任何其他位置定义的键都会优先。自定义实现可以定义其他顺序。
在
@SpringBootApplication
中使用@PropertySource
似乎是在环境中加载自定义资源的便捷方法,但我们并不推荐这样做。在应用程序上下文被刷新之前,此类属性源不会被添加到环境中。这对于配置某些属性(如在刷新开始前读取的logging.*
和spring.main.*
)来说为时已晚。
您可以使用 ApplicationBuilder
类创建parent/child ApplicationContext
层次结构。有关详细信息,请参阅 “Spring Boot 功能” 部分中的 “features.html”。
并非所有 Spring 应用程序都必须是网络应用程序(或网络服务)。如果您既想在mian
方法中执行一些代码,又想引导一个 Spring 应用程序来设置要使用的基础架构,您可以使用 Spring Boot 的 SpringApplication
功能。SpringApplication
会根据是否需要网络应用程序来更改其 ApplicationContext
类。要帮助它,首先要做的就是将与服务器相关的依赖项(如 servlet API)从类路径中删除。如果做不到这一点(例如,你从同一个代码库中运行两个应用程序),那么你可以在 SpringApplication
实例上显式调用 setWebApplicationType(WebApplicationType.NONE)
或设置 applicationContextClass
属性(通过 Java API 或使用外部属性)。您希望作为业务逻辑运行的应用代码可作为 CommandLineRunner
实现,并作为 @Bean
定义放入上下文中。
本节包括有关设置和读取属性以及配置设置及其与 Spring Boot 应用程序交互的主题。
与其硬编码项目构建配置中指定的某些属性,不如使用现有的构建配置自动扩展这些属性。这在 Maven 和 Gradle 中都可以实现。
通过使用资源过滤,你可以从 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
添加到类路径中(用于热重载)。这样做可以规避资源过滤和此功能。相反,你可以使用exec:java
目标或自定义插件配置。更多详情,请参阅插件使用页面。
如果不使用spring-boot-starter-parent
,则需要在 pom.xml
的
元素中包含以下元素:
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
resource>
resources>
您还需要在
中包含以下元素:
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-resources-pluginartifactId>
<version>2.7version>
<configuration>
<delimiters>
<delimiter>@delimiter>
delimiters>
<useDefaultDelimiters>falseuseDefaultDelimiters>
configuration>
plugin>
如果在配置中使用标准 Spring 占位符(如
${placeholder}
),则useDefaultDelimiters
属性非常重要。如果未将该属性设置为false
,则构建时可能会扩展这些占位符。
通过配置 Java 插件的 processResources
任务,可以自动展开 Gradle 项目中的属性,如下例所示:
tasks.named('processResources') {
expand(project.properties)
}
然后,你就可以使用占位符来引用 Gradle 项目的属性了,如下例所示:
app:
name: "${name}"
description: "${description}"
Gradle 的展开方法使用 Groovy 的 SimpleTemplateEngine
,它可以转换 ${..}
标记。${..}
风格与 Spring 自己的属性占位符机制相冲突。要同时使用 Spring 属性占位符和自动扩展,请按如下方式转义 Spring 属性占位符: \${..}
.
SpringApplication
具有 bean 属性设置器,因此您可以在创建应用程序时使用其 Java API 来修改其行为。或者,你也可以通过在 spring.main.*
中设置属性来将配置外部化。例如,在 application.properties
中可能有以下设置:
spring:
main:
web-application-type: "none"
banner-mode: "off"
这样,启动时就不会打印 Spring Boot 横幅,应用程序也不会启动嵌入式 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);
}
}
或 SpringApplicationBuilder
的 sources(...)
方法:
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"
实际应用程序将显示横幅(由配置重载),并使用三个 ApplicationContext
源。应用程序源是:
MyApplication
(来自代码)MyDatabaseConfig
(来自外部配置)MyJmsConfig
(来自外部配置)默认情况下,不同来源的属性会按照定义的顺序添加到 Spring Environment
中(具体顺序请参见 "Spring Boot 功能 "部分中的 “features.html”)。
您还可以提供以下系统属性(或环境变量)来改变行为:
spring.config.name
(SPRING_CONFIG_NAME
): 默认以application
作为文件名的根。spring.config.location
(SPRING_CONFIG_LOCATION
): 要加载的文件(如类路径资源或 URL)。为该文件设置了一个单独的环境属性源,它可以被系统属性、环境变量或命令行覆盖。无论您在环境中设置了什么,Spring Boot 都会如上所述加载 application.properties
。默认情况下,如果使用 YAML,那么扩展名为".yml “和”.yml "的文件也会被添加到列表中。
如果您想了解加载文件的详细信息,可以将
org.springframework.boot.context.config
的日志级别设置为trace
。
有些人喜欢使用(例如)--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
属性的大写同义词。
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
(类似于 JSON 对象),Spring Boot 会对 Map 进行扁平化处理,使其深入一级并具有以句号分隔的键,就像很多人习惯使用 Java 中的属性文件一样。
前面的 YAML 示例与下面的 application.properties
文件相对应:
spring.application.name=cruncher
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000
有关 YAML 的更多信息,请参见 “Spring Boot 功能” 部分中的 “features.html”。
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"
以这种方式设置的值会被 System 属性或环境变量设置取代,但不会被 SpringApplicationBuilder.profiles()
方法取代。因此,可以使用后一种 Java API 来增强配置文件,而无需更改默认值。
有关详细信息,请参阅 “Spring Boot 功能” 部分中的 “features.html”。
默认配置文件是在没有激活任何配置文件的情况下启用的配置文件。默认情况下,默认配置文件的名称是 default
,但可以使用系统属性 (spring.profiles.default
) 或操作系统环境变量 (SPRING_PROFILES_DEFAULT
) 进行更改。
在 Spring Boot 中,还可以在 application.properties
中设置默认配置文件名称,如下例所示:
spring:
profiles:
default: "dev"
有关详细信息,请参阅 “Spring Boot 功能” 部分中的 “features.html”。
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
在上例中,默认端口为 9000。但是,如果名为 “development” 的 Spring 配置文件处于活动状态,则端口为 9001。如果 “production” 处于活动状态,则端口为 0。
文件合并的顺序是文件出现的先后顺序。后面的值优先于前面的值。
Spring Boot 会在运行时将 application.properties
(或 YAML 文件和其他地方)中的外部属性绑定到应用程序中。单个位置不可能(技术上也不可能)列出所有支持的属性,因为这些属性可能来自类路径上的其他 jar 文件。
具有 Actuator 功能的运行中应用程序有一个 configprops
端点,可显示通过 @ConfigurationProperties
可用的所有绑定属性。
附录中包含一个 application.properties 示例,其中列出了 Spring Boot 支持的最常见属性。这份明确的列表来自于对 @ConfigurationProperties
和 @Value
注解源代码的搜索,以及对 Binder 的偶尔使用。有关加载属性的确切顺序,请参阅 “features.html”。
每个 Spring Boot Web 应用程序都包含一个嵌入式 Web 服务器。这一特性会导致许多操作问题,包括如何更改嵌入式服务器和如何配置嵌入式服务器。本节将回答这些问题。
许多 Spring Boot 启动程序都包含默认的嵌入式容器。
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
包含了 Reactor Netty,但你也可以使用 spring-boot-starter-tomcat
、spring-boot-starter-jetty
或 spring-boot-starter-undertow
代替。当切换到不同的 HTTP 服务器时,您需要将默认依赖项替换为您需要的依赖项。为了帮助完成这一过程,Spring Boot 为每个受支持的 HTTP 服务器提供了单独的启动器。
下面的 Maven 示例展示了如何为 Spring MVC 排除 Tomcat 并包含 Jetty:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
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", "Use Undertow instead of Reactor Netty")
}
}
}
使用
WebClient
类时需要使用spring-boot-starter-reactor-netty
,因此即使需要包含不同的 HTTP 服务器,也可能需要保持对 Netty 的依赖。
如果您的类路径包含启动 Web 服务器所需的位,Spring Boot 会自动启动它。要禁用此行为,请在 application.properties
中配置 WebApplicationType
,如下例所示:
spring:
main:
web-application-type: "none"
在独立应用程序中,主 HTTP 端口默认为 8080
,但可以使用 server.port
进行设置(例如,在 application.properties
中或作为系统属性)。由于放宽了环境值的绑定,还可以使用 SERVER_PORT
(例如,作为操作系统环境变量)。
如果要完全关闭 HTTP 端点,但仍要创建 WebApplicationContext
,可使用 server.port=-1
(这样做有时对测试很有用)。
有关详细信息,请参阅 “Spring Boot 功能” 部分中的 “web.html” 或 ServerProperties 源代码。
要扫描空闲端口(使用操作系统本地端口以防止冲突),请使用 server.port=0
。
您可以通过日志输出或 WebServerApplicationContext
访问服务器运行的端口。获取端口并确保其已被初始化的最佳方法是添加一个 ApplicationListener
类型的 @Bean
,并在事件发布时将容器从事件中提取出来。
使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_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)
class MyWebIntegrationTests {
@LocalServerPort
int port;
// ...
}
@LocalServerPort
是@Value("${local.server.port}")
的元注解。不要尝试在常规应用程序中注入端口。正如我们刚才看到的,只有在容器初始化后才会设置该值。与测试相反,应用程序代码回调会提前处理(在值实际可用之前)。
Jetty、Tomcat、Reactor Netty 和 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
属性来配置此行为。
SSL 可通过设置各种 server.ssl.*
属性进行声明式配置,通常在 application.properties
或 application.yaml
中设置。下面的示例展示了使用 Java KeyStore 文件设置 SSL 属性:
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 连接器更容易进行编程配置。
您可以使用 PEM 编码文件代替 Java KeyStore 文件。应尽可能使用 PKCS#8 密钥文件。PEM 编码的 PKCS#8 密钥文件以 -----BEGIN PRIVATE KEY-----
或 -----BEGIN ENCRYPTED PRIVATE KEY-----
头文件开始。
如果您有其他格式的文件,例如 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>
server:
port: 8443
ssl:
certificate: "classpath:my-cert.crt"
certificate-private-key: "classpath:my-cert.key"
trust-certificate: "classpath:ca-cert.crt"
另外,也可以在 SSL bundle 中配置 SSL 信任材料,并应用于网络服务器,如本示例所示:
server:
port: 8443
ssl:
bundle: "example"
有关所有支持属性的详细信息,请参阅 Ssl。
您可以使用 server.http2.enabled
配置属性在 Spring Boot 应用程序中启用 HTTP/2 支持。h2
(HTTP/2 over TLS)和 h2c
(HTTP/2 over TCP)均受支持。要使用 h2
,还必须启用 SSL。未启用 SSL 时,将使用 h2c
。例如,当应用程序在执行 TLS 终止的代理服务器后运行时,可能需要使用 h2c。
Spring Boot 默认搭载 Tomcat 10.1.x,开箱即支持 h2c
和 h2
。另外,如果主机操作系统安装了 libtcnative
库及其依赖项,也可以使用 libtcnative
来支持 h2
。
如果 JVM 库路径中还没有库,则必须提供库目录。可以使用 JVM 参数(如 -Djava.library.path=/usr/local/opt/tomcat-native/lib
)这样做。更多信息请参阅 Tomcat 官方文档。
要支持 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-server
和 Conscrypt 库spring-boot-webflux-starter
默认使用 Reactor Netty 作为服务器。Reactor Netty 支持开箱即用的 h2c
和 h2
。为优化运行时性能,该服务器还支持使用本地库的 h2
。要实现这一点,您的应用程序需要额外的依赖关系。
Spring Boot 会管理 io.netty:netty-tcnative-boringssl-static
“uber jar” 的版本,其中包含适用于所有平台的本地库。开发人员可以选择使用分类器只导入所需的依赖项(参见 Netty 官方文档)。
Undertow 开箱即支持 h2c
和 h2
。
一般来说,你应该首先考虑使用许多可用配置密钥之一,并通过在 application.properties
或 application.yaml
文件中添加新条目来定制你的网络服务器。请参阅 “发现外部属性的内置选项”)。server.*
命名空间在此非常有用,它包括 server.tomcat.*
、server.jetty.*
等命名空间,用于实现特定于服务器的功能。请参阅应用程序属性列表。
前面的章节已经介绍了许多常见用例,如压缩、SSL 或 HTTP/2。但是,如果您的用例不存在配置密钥,那么您就应该看看 WebServerFactoryCustomizer。你可以声明这样一个组件,并访问与你的选择相关的服务器工厂:你应该为所选的服务器(Tomcat、Jetty、Reactor Netty、Undertow)和所选的网络堆栈(Servlet 或 Reactive)选择变体。
下面的示例是使用 spring-boot-starter-web
(servlet 栈)的 Tomcat:
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
// customize the factory here
}
}
Spring Boot 在内部使用该基础架构来自动配置服务器。自动配置的
WebServerFactoryCustomizer
Bean 的顺序为0
,将在任何用户定义的自定义器之前处理,除非它有明确的顺序说明。
一旦使用自定义器访问了 WebServerFactory
,就可以使用它来配置特定部分,如连接器、服务器资源或服务器本身–所有这些都使用服务器特定的 API。
此外,Spring Boot 还提供:
Server | Servlet stack | Reactive stack |
---|---|---|
Tomcat |
TomcatServletWebServerFactory |
TomcatReactiveWebServerFactory |
Jetty |
JettyServletWebServerFactory |
JettyReactiveWebServerFactory |
Undertow |
UndertowServletWebServerFactory |
UndertowReactiveWebServerFactory |
Reactor |
N/A | NettyReactiveWebServerFactory |
最后,您还可以声明自己的 WebServerFactory
Bean,它将覆盖 Spring Boot 提供的 Bean。这样做时,自动配置的自定义器仍会应用于自定义工厂,因此请谨慎使用该选项。
在使用 spring-boot-starter-web
的 Servlet 栈应用程序中,有两种方法可以在应用程序中添加 Servlet
、Filter
、ServletContextListener
和 Servlet API 支持的其他监听器:
要使用 Spring Bean 添加 Servlet
、Filter
或 servlet *Listener
,必须为其提供 @Bean
定义。当你想注入配置或依赖关系时,这样做非常有用。不过,您必须非常小心,以免它们导致过多其他 Bean 的急迫初始化,因为它们必须在应用程序生命周期的早期安装到容器中。(例如,让它们依赖于数据源或 JPA 配置就不是一个好主意)。您可以绕过这些限制,在首次使用时而不是在初始化时懒散地初始化 Bean。
就 Filter 和 Servlet 而言,您还可以通过添加FilterRegistrationBean
或ServletRegistrationBean
来添加映射和初始化参数,以代替或补充底层组件。
如果在过滤器注册时没有指定
DispatcherType
,则使用REQUEST
。这与 servlet 规范的默认派发器类型一致。
与其他 Spring Bean 一样,您也可以定义 servlet filter Bean 的顺序;请务必查看 “web.html” 部分。
如前所述,任何 Servlet 或 Filter Bean 都会自动向 servlet 容器注册。要禁用特定 Filter 或 Servlet Bean 的注册,请为其创建一个注册 Bean 并将其标记为禁用,如下例所示:
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;
}
}
通过用 @ServletComponentScan
对 @Configuration
类进行注解,并指定包含要注册的组件的包,@WebServlet
、@WebFilter
和 @WebListener
注解类可自动与嵌入式 servlet 容器注册。默认情况下,@ServletComponentScan
会从注解类的包中扫描。
访问日志可通过 Tomcat、Undertow 和 Jetty 各自的命名空间进行配置。
例如,以下设置使用自定义模式记录 Tomcat 的访问日志。
server:
tomcat:
basedir: "my-tomcat"
accesslog:
enabled: true
pattern: "%t %a %r %s (%D microseconds)"
日志的默认位置是相对于 Tomcat 基本目录的日志目录。默认情况下,日志目录是一个临时目录,因此您可能需要固定 Tomcat 的基本目录或使用日志的绝对路径。在上例中,日志位于
my-tomcat/logs
中,相对于应用程序的工作目录。
Undertow 的访问日志可以类似方式配置,如下例所示:
server:
undertow:
accesslog:
enabled: true
pattern: "%t %a %r %s (%D milliseconds)"
options:
server:
record-request-start-time: true
请注意,除了启用访问日志和配置其模式外,还启用了记录请求开始时间。在访问日志模式中包含响应时间 (%D
) 时需要这样做。日志存储在相对于应用程序工作目录的日志目录中。您可以通过设置 server.undertow.accesslog.dir
属性自定义该位置。
最后,Jetty 的访问日志也可以按以下方式配置:
server:
jetty:
accesslog:
enabled: true
filename: "/var/log/jetty-access.log"
默认情况下,日志会重定向到 System.err
。有关详细信息,请参阅 Jetty 文档。
如果您的应用程序在代理、负载平衡器或云中运行,请求信息(如主机、端口、方案…)可能会发生变化。您的应用程序可能在 10.10.10.10:8080
上运行,但 HTTP 客户端只能看到 example.org
。
RFC7239 “转发头” 定义了转发 HTTP 头;代理可以使用该头提供原始请求的相关信息。您可以配置应用程序读取这些标头,并在创建链接时自动使用这些信息,然后通过 HTTP 302 响应、JSON 文档或 HTML 页面发送给客户端。还有一些非标准头信息,如 X-Forwarded-Host
、X-Forwarded-Port
、X-Forwarded-Proto
、X-Forwarded-Ssl
和 X-Forwarded-Prefix
。
如果代理添加了常用的 X-Forwarded-For
和 X-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
”。
如果使用 Tomcat,还可以额外配置用于携带 “forwarded” 信息的标头名称,如下例所示:
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}"
通过将
internal-proxies
设置为空,可以信任所有代理(但在生产中不要这样做)。
您可以完全控制 Tomcat RemoteIpValve
的配置,方法是关闭自动配置(为此请设置 server.forward-headers-strategy=NONE
),并使用 WebServerFactoryCustomizer
Bean 添加一个新的阀门实例。
您可以在 TomcatServletWebServerFactory
中添加 org.apache.catalina.connector.Connector
,它可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如下例所示:
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;
}
}
嵌入式 Tomcat 的 MBean 注册表默认是禁用的。这最大限度地减少了 Tomcat 的内存占用。如果您想使用 Tomcat 的 MBeans,例如让 Micrometer 使用它们来公开度量指标,就必须使用 server.tomcat.mbeanregistry.enabled
属性来实现,如下例所示:
server:
tomcat:
mbeanregistry:
enabled: true
向 UndertowServletWebServerFactory
添加一个 UndertowBuilderCustomizer
,并向 Builder
添加一个监听器,如下例所示:
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");
}
}
如果要在使用嵌入式容器的 Spring Boot 应用程序中使用 @ServerEndpoint
,则必须声明一个 ServerEndpointExporter
@Bean
,如下例所示:
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();
}
}
上例中显示的 Bean 会向底层 WebSocket 容器注册任何带有 @ServerEndpoint
注释的 Bean。当部署到独立的 servlet 容器时,这一角色由 servlet 容器初始化器执行,而不需要 ServerEndpointExporter
Bean。
Spring Boot 有许多包含 Spring MVC 的启动器。请注意,有些启动程序包含对 Spring MVC 的依赖,而不是直接包含 Spring MVC。本节将回答有关 Spring MVC 和 Spring Boot 的常见问题。
只要 Jackson2 位于类路径上,Spring Boot 应用程序中的任何 Spring @RestController
默认都应呈现 JSON 响应,如下例所示:
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();
}
}
只要 MyThing
可以被 Jackson2 序列化(对于普通 POJO 或 Groovy 对象而言为 true),那么 localhost:8080/thing
默认会提供一个 JSON 表示。请注意,在浏览器中,有时可能会看到 XML 响应,因为浏览器倾向于发送偏好 XML 的接受头。
如果在类路径上有 Jackson XML 扩展(jackson-dataformat-xml
),就可以使用它来呈现 XML 响应。之前用于 JSON 的示例也可以使用。要使用 Jackson XML 渲染器,请在项目中添加以下依赖关系:
<dependency>
<groupId>com.fasterxml.jackson.dataformatgroupId>
<artifactId>jackson-dataformat-xmlartifactId>
dependency>
如果 Jackson 的 XML 扩展不可用,而 JAXB 可用,则可通过将 MyThing
注释为 @XmlRootElement
来呈现 XML,如下例所示:
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class MyThing {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
您需要确保 JAXB 库是项目的一部分,例如添加
<dependency>
<groupId>org.glassfish.jaxbgroupId>
<artifactId>jaxb-runtimeartifactId>
dependency>
要让服务器渲染 XML 而不是 JSON,可能需要发送
Accept: text/xml
标头(或使用浏览器)。
Spring MVC(客户端和服务器端)使用 HttpMessageConverters
来协商 HTTP 交换中的内容转换。如果类路径上有Jackson,你就已经获得了Jackson2ObjectMapperBuilder
提供的默认转换器,它的实例会为你自动配置。
默认创建的 ObjectMapper
(或 Jackson XML 转换器的 XmlMapper
)实例具有以下自定义属性:
MapperFeature.DEFAULT_VIEW_INCLUSION
已禁用FAIL_ON_UNKNOWN_PROPERTIES
已禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
已禁用SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS
已禁用Spring Boot 还提供了一些功能,可以让您更轻松地自定义这一行为。
您可以使用环境配置 ObjectMapper
和 XmlMapper
实例。Jackson 提供了一整套开/关功能,可用于配置其处理过程的各个方面。这些功能在几个枚举(Jackson 中)中进行了描述,这些枚举映射到环境中的属性:
Enum | Property | Values |
---|---|---|
com.fasterxml.jackson.databind.cfg.EnumFeature |
spring.jackson.datatype.enum. |
true , false |
com.fasterxml.jackson.databind.cfg.JsonNodeFeature |
spring.jackson.datatype.json-node. |
true , false |
com.fasterxml.jackson.databind.DeserializationFeature |
spring.jackson.deserialization. |
true , false |
com.fasterxml.jackson.core.JsonGenerator.Feature |
spring.jackson.generator. |
true , false |
com.fasterxml.jackson.databind.MapperFeature |
spring.jackson.mapper. |
true , false |
com.fasterxml.jackson.core.JsonParser.Feature |
spring.jackson.parser. |
true , false |
com.fasterxml.jackson.databind.SerializationFeature |
spring.jackson.serialization. |
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。请注意,无论哪种情况,这样做都会禁用对象映射器的所有自动配置。
如果您提供任何 MappingJackson2HttpMessageConverter
类型的 @Beans
,它们将取代 MVC 配置中的默认值。此外,我们还提供了一个 HttpMessageConverters
类型的便利 Bean(如果使用默认 MVC 配置,该 Bean 始终可用)。它有一些有用的方法来访问默认和用户增强的消息转换器。
有关详细信息,请参阅 “自定义 @ResponseBody 渲染” 部分和 WebMvcAutoConfiguration 源代码。
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 源代码。
Spring Boot 采用 servlet 5 jakarta.servlet.http.Part
API 来支持上传文件。默认情况下,Spring Boot 为 Spring MVC 配置的每个文件最大大小为 1MB,单个请求中的文件数据最大为 10MB。您可以使用 MultipartProperties
类中公开的属性来覆盖这些值、中间数据的存储位置(例如,存储到 /tmp
目录)以及数据刷新到磁盘的阈值。例如,如果要指定文件不受限制,可将 spring.servlet.multipart.max-file-size
属性设置为-1
。
当你想在 Spring MVC 控制器处理程序方法中接收多文件编码文件数据作为 MultipartFile
类型的 @RequestParam
注解参数时,多文件支持会很有帮助。
有关详细信息,请参阅 MultipartAutoConfiguration 源。
建议使用容器对多文件上传的内置支持,而不是引入额外的依赖,如 Apache Commons File Upload。
默认情况下,所有内容都从应用程序的根目录 (/
) 提供。如果您希望映射到不同的路径,可以按如下方式配置:
spring:
mvc:
servlet:
path: "/mypath"
如果您有额外的 servlet,可以为每个 servlet 声明一个 Servlet
或 ServletRegistrationBean
类型的 @Bean
,Spring Boot 会以透明的方式向容器注册它们。由于 Servlet 是以这种方式注册的,因此可以将它们映射到 DispatcherServlet
的子上下文中,而无需调用它。
自行配置 DispatcherServlet
并不常见,但如果确实需要,则必须提供 DispatcherServletPath
类型的 @Bean
以提供自定义 DispatcherServlet
的路径。
要完全控制 MVC 配置,最简单的方法就是使用 @EnableWebMvc
注解提供自己的 @Configuration
。这样一来,所有的 MVC 配置就都掌握在你的手中了。
ViewResolver
是 Spring MVC 的核心组件,它将 @Controller
中的视图名称转换为实际的视图实现。请注意,ViewResolver
主要用于 UI 应用程序,而不是 REST 类型的服务(View 并不用于渲染 @ResponseBody
)。ViewResolver
有许多实现可供选择,Spring 本身并不主张使用哪种实现。另一方面,Spring Boot 会根据它在类路径和应用上下文中的发现,为你安装一个或两个。DispatcherServlet
会使用它在应用上下文中找到的所有解析器,依次尝试每个解析器,直到得到结果。如果要添加自己的解析器,必须注意添加的顺序和位置。
WebMvcAutoConfiguration
会在上下文中添加以下 ViewResolvers
:
defaultViewResolver
” 的 InternalResourceViewResolver
。它用于定位可通过使用 DefaultServlet
呈现的物理资源(包括静态资源和 JSP 页面,如果使用的话)。它会为视图名称添加前缀和后缀,然后在 servlet 上下文中查找具有该路径的物理资源(默认值均为空,但可通过 spring.mvc.view.prefix
和 spring.mvc.view.suffix
进行外部配置)。您可以通过提供相同类型的 Bean 来覆盖它。beanNameViewResolver
” 的 BeanNameViewResolver
。它是视图解析器链中一个有用的成员,可拾取与正在解析的视图同名的任何 Bean。无需覆盖或替换它。viewResolver
” 的 ContentNegotiatingViewResolver
。这是一个复合解析器,委托给所有其他解析器,并试图找到与客户端发送的 “Accept” HTTP 头信息相匹配的解析器。有一篇关于 ContentNegotiatingViewResolver 的博客很有用,你可以参考一下以了解更多信息,也可以查看源代码以了解细节。你可以通过定义一个名为 “viewResolver
” 的 Bean 来关闭自动配置的 ContentNegotiatingViewResolver
。thymeleafViewResolver
” 的 ThymeleafViewResolver
。它通过在视图名称周围加上前缀和后缀来查找资源。前缀是 spring.thymeleaf.prefix
,后缀是 spring.thymeleaf.suffix
。前缀和后缀的默认值分别为 “classpath:/templates/
“和”.html
”。你可以通过提供一个同名的 Bean 来覆盖 ThymeleafViewResolver
。freeMarkerViewResolver
” 的 FreeMarkerViewResolver
。它通过在视图名称周围加上前缀和后缀,在加载器路径(外部化为 spring.freemarker.templateLoaderPath
,默认值为 “classpath:/templates/
”)中查找资源。前缀外部化为 spring.freemarker.prefix
,后缀外部化为 spring.freemarker.suffix
。前缀和后缀的默认值分别为空和".ftlh
"。您可以通过提供同名的 Bean 来覆盖 FreeMarkerViewResolver
。groovy-templates
),你还会有一个名为 “groovyMarkupViewResolver
” 的 GroovyMarkupViewResolver
。它通过在视图名称周围加上前缀和后缀(外部化为 spring.groovy.template.prefix
和 spring.groovy.template.suffix
)来查找加载器路径中的资源。前缀和后缀的默认值分别为 “classpath:/templates/
“和”.tpl
”。您可以通过提供同名的 Bean 来覆盖 GroovyMarkupViewResolver
。mustacheViewResolver
” 的 MustacheViewResolver
。它通过在视图名称周围添加前缀和后缀来查找资源。前缀是 spring.mustache.prefix
,后缀是 spring.mustache.suffix
。前缀和后缀的默认值分别为 “classpath:/templates/
“和”.mustache
”。您可以通过提供同名的 Bean 来覆盖 MustacheViewResolver
。更多详情,请参阅以下章节: