此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.4.0! |
反应式 Web 应用程序
Spring Boot 通过为 Spring Webflux 提供自动配置来简化反应式 Web 应用程序的开发。
“Spring WebFlux 框架”
Spring WebFlux 是 Spring Framework 5.0 中引入的新反应式 Web 框架。 与 Spring MVC 不同,它不需要 servlet API,是完全异步和非阻塞的,并通过 Reactor 项目实现反应流规范。
Spring WebFlux 有两种风格:函数式和基于 Comments 的。 基于 Comments 的模型与 Spring MVC 模型非常接近,如以下示例所示:
-
Java
-
Kotlin
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class MyRestController {
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping("/{userId}")
public Mono<User> getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId);
}
@GetMapping("/{userId}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long userId) {
return this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser);
}
@DeleteMapping("/{userId}")
public Mono<Void> deleteUser(@PathVariable Long userId) {
return this.userRepository.deleteById(userId);
}
}
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {
@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long): Mono<User?> {
return userRepository.findById(userId)
}
@GetMapping("/{userId}/customers")
fun getUserCustomers(@PathVariable userId: Long): Flux<Customer> {
return userRepository.findById(userId).flatMapMany { user: User? ->
customerRepository.findByUser(user)
}
}
@DeleteMapping("/{userId}")
fun deleteUser(@PathVariable userId: Long): Mono<Void> {
return userRepository.deleteById(userId)
}
}
WebFlux 是 Spring Framework 的一部分,其参考文档中提供了详细信息。
“WebFlux.fn”是功能变体,它将路由配置与请求的实际处理分开,如以下示例所示:
-
Java
-
Kotlin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {
private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);
@Bean
public RouterFunction<ServerResponse> monoRouterFunction(MyUserHandler userHandler) {
return route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.server.RequestPredicates.DELETE
import org.springframework.web.reactive.function.server.RequestPredicates.GET
import org.springframework.web.reactive.function.server.RequestPredicates.accept
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerResponse
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
@Bean
fun monoRouterFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
return RouterFunctions.route(
GET("/{user}").and(ACCEPT_JSON), userHandler::getUser).andRoute(
GET("/{user}/customers").and(ACCEPT_JSON), userHandler::getUserCustomers).andRoute(
DELETE("/{user}").and(ACCEPT_JSON), userHandler::deleteUser)
}
companion object {
private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
}
}
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
@Component
public class MyUserHandler {
public Mono<ServerResponse> getUser(ServerRequest request) {
...
}
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
...
}
public Mono<ServerResponse> deleteUser(ServerRequest request) {
...
}
}
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono
@Component
class MyUserHandler {
fun getUser(request: ServerRequest?): Mono<ServerResponse> {
...
}
fun getUserCustomers(request: ServerRequest?): Mono<ServerResponse> {
...
}
fun deleteUser(request: ServerRequest?): Mono<ServerResponse> {
...
}
}
“WebFlux.fn” 是 Spring Framework 的一部分,详细信息可在其参考文档中找到。
您可以定义任意数量的RouterFunction bean 来模块化路由器的定义。
如果需要应用优先级,则可以对 bean 进行排序。 |
要开始使用,请添加spring-boot-starter-webflux
模块添加到您的应用程序。
将两者相加spring-boot-starter-web 和spring-boot-starter-webflux modules 会导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。
之所以选择这种行为,是因为许多 Spring 开发人员在spring-boot-starter-webflux 到他们的 Spring MVC 应用程序以使用响应式WebClient .
您仍然可以通过将所选应用程序类型设置为SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) . |
Spring WebFlux 自动配置
Spring Boot 为 Spring WebFlux 提供了自动配置,适用于大多数应用程序。
自动配置在 Spring 的默认值之上添加了以下功能:
-
配置编解码器
HttpMessageReader
和HttpMessageWriter
实例(本文档稍后将介绍)。 -
支持提供静态资源,包括对 WebJars 的支持(本文档稍后将介绍)。
如果要保留 Spring Boot WebFlux 功能,并且想要添加其他 WebFlux 配置,则可以添加自己的@Configuration
type 类WebFluxConfigurer
但没有 @EnableWebFlux
.
如果要将其他自定义添加到自动配置的HttpHandler
中,您可以定义WebHttpHandlerBuilderCustomizer
并使用它们来修改WebHttpHandlerBuilder
.
如果您想完全控制 Spring WebFlux,您可以添加自己的@Configuration
注解@EnableWebFlux
.
Spring WebFlux 转换服务
如果要自定义ConversionService
由 Spring WebFlux 使用,您可以提供WebFluxConfigurer
带有addFormatters
方法。
转换也可以使用spring.webflux.format.*
configuration 属性。
如果未配置,则使用以下默认值:
财产 | DateTimeFormatter |
格式 |
---|---|---|
|
|
|
|
|
java.time 的 |
|
|
java.time 的 |
使用 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器
Spring WebFlux 使用HttpMessageReader
和HttpMessageWriter
接口来转换 HTTP 请求和响应。
它们配置了CodecConfigurer
通过查看 Classpath 中可用的库来获得合理的默认值。
Spring Boot 为编解码器提供了专用的配置属性,spring.codec.*
.
它还通过使用CodecCustomizer
实例。
例如spring.jackson.*
配置键应用于 Jackson 编解码器。
如果需要添加或自定义编解码器,可以创建自定义CodecCustomizer
组件,如以下示例所示:
-
Java
-
Kotlin
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
@Configuration(proxyBeanMethods = false)
public class MyCodecsConfiguration {
@Bean
public CodecCustomizer myCodecCustomizer() {
return (configurer) -> {
configurer.registerDefaults(false);
configurer.customCodecs().register(new ServerSentEventHttpMessageReader());
// ...
};
}
}
import org.springframework.boot.web.codec.CodecCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.http.codec.CodecConfigurer
import org.springframework.http.codec.ServerSentEventHttpMessageReader
class MyCodecsConfiguration {
@Bean
fun myCodecCustomizer(): CodecCustomizer {
return CodecCustomizer { configurer: CodecConfigurer ->
configurer.registerDefaults(false)
configurer.customCodecs().register(ServerSentEventHttpMessageReader())
}
}
}
您还可以利用 Boot 的自定义 JSON 序列化器和反序列化器。
静态内容
默认情况下, Spring Boot 从名为/static
(或/public
或/resources
或/META-INF/resources
) 中。
它使用ResourceWebHandler
从 Spring WebFlux,以便您可以通过添加自己的WebFluxConfigurer
并覆盖addResourceHandlers
方法。
默认情况下,资源映射在 上,但您可以通过设置/**
spring.webflux.static-path-pattern
财产。
例如,将所有资源重新定位到/resources/**
可以按如下方式实现:
-
Properties
-
YAML
spring.webflux.static-path-pattern=/resources/**
spring:
webflux:
static-path-pattern: "/resources/**"
您还可以使用spring.web.resources.static-locations
.
这样做会将默认值替换为目录位置列表。
如果这样做,默认的欢迎页面检测将切换到您的自定义位置。
因此,如果存在index.html
在启动时的任何位置,它都是应用程序的主页。
除了前面列出的“标准”静态资源位置之外,Webjars 内容还有一个特殊情况。
默认情况下,路径为/webjars/**
如果 jar 文件以 Webjars 格式打包,则从 jar 文件中提供。
可以使用spring.webflux.webjars-path-pattern
财产。
Spring WebFlux 应用程序并不严格依赖于 servlet API,因此它们不能部署为 war 文件,也不使用src/main/webapp 目录。 |
欢迎页面
Spring Boot 支持静态和模板化欢迎页面。
它首先查找index.html
文件。
如果未找到,则查找index
模板。
如果找到任何一个,它将自动用作应用程序的欢迎页面。
这仅充当应用程序定义的实际索引路由的回退。
排序由HandlerMapping
beans 的 bean 中,默认情况下,它的作用如下:
|
|
|
|
|
欢迎页面支持 |
模板引擎
除了 REST Web 服务之外,您还可以使用 Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。
Spring Boot 包括对以下模板引擎的自动配置支持:
当您使用具有默认配置的模板引擎之一时,您的模板会自动从src/main/resources/templates
.
错误处理
Spring Boot 提供了一个WebExceptionHandler
它以合理的方式处理所有错误。
它在处理顺序中的位置紧接在 WebFlux 提供的处理程序之前,这些处理程序被视为最后。
对于计算机客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。
对于浏览器客户端,有一个 “whitelabel” 错误处理程序,它以 HTML 格式呈现相同的数据。
您还可以提供自己的 HTML 模板来显示错误(请参阅下一节)。
在直接在 Spring Boot 中自定义错误处理之前,您可以利用 Spring WebFlux 中的 RFC 9457 问题详细信息支持。
Spring WebFlux 可以使用application/problem+json
media 类型,例如:
{
"type": "https://example.org/problems/unknown-project",
"title": "Unknown project",
"status": 404,
"detail": "No project found for id 'spring-unknown'",
"instance": "/projects/spring-unknown"
}
可以通过设置spring.webflux.problemdetails.enabled
自true
.
自定义此功能的第一步通常涉及使用现有机制,但替换或扩充错误内容。
为此,您可以添加ErrorAttributes
.
要更改错误处理行为,您可以实现ErrorWebExceptionHandler
并注册该类型的 bean 定义。
因为ErrorWebExceptionHandler
相当底层,Spring Boot 还提供了一个方便的AbstractErrorWebExceptionHandler
让你以 WebFlux 功能方式处理错误,如以下示例所示:
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.server.ServerResponse.BodyBuilder;
@Component
public class MyErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties webProperties,
ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) {
super(errorAttributes, webProperties.getResources(), applicationContext);
setMessageReaders(serverCodecConfigurer.getReaders());
setMessageWriters(serverCodecConfigurer.getWriters());
}
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml);
}
private boolean acceptsXml(ServerRequest request) {
return request.headers().accept().contains(MediaType.APPLICATION_XML);
}
public Mono<ServerResponse> handleErrorAsXml(ServerRequest request) {
BodyBuilder builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR);
// ... additional builder calls
return builder.build();
}
}
import org.springframework.boot.autoconfigure.web.WebProperties
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler
import org.springframework.boot.web.reactive.error.ErrorAttributes
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.codec.ServerCodecConfigurer
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono
@Component
class MyErrorWebExceptionHandler(
errorAttributes: ErrorAttributes, webProperties: WebProperties,
applicationContext: ApplicationContext, serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(errorAttributes, webProperties.resources, applicationContext) {
init {
setMessageReaders(serverCodecConfigurer.readers)
setMessageWriters(serverCodecConfigurer.writers)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
}
private fun acceptsXml(request: ServerRequest): Boolean {
return request.headers().accept().contains(MediaType.APPLICATION_XML)
}
fun handleErrorAsXml(request: ServerRequest): Mono<ServerResponse> {
val builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
// ... additional builder calls
return builder.build()
}
}
为了获得更完整的图片,您还可以子类化DefaultErrorWebExceptionHandler
直接并覆盖特定方法。
在某些情况下,Web 观测或指标基础设施不会记录在控制器级别处理的错误。 应用程序可以通过在观察上下文中设置已处理的异常来确保此类异常与观察一起记录。
自定义错误页面
如果要显示给定状态代码的自定义 HTML 错误页面,可以添加从error/*
,例如,通过将文件添加到/error
目录。
错误页面可以是静态 HTML(即,添加到任何静态资源目录下),也可以是用模板构建的。
文件名应为确切的状态代码、状态代码系列掩码,或error
,如果没有其他匹配项,则为 default。
请注意,默认错误视图的路径是error/error
,而使用 Spring MVC 时,默认错误视图为error
.
例如,要将404
转换为静态 HTML 文件,则您的目录结构将如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
要映射所有5xx
错误,您的目录结构将如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.mustache
+- <other templates>
Web 过滤器
Web 过滤器 | 次序 |
---|---|
|
|
|
嵌入式反应式服务器支持
Spring Boot 包括对以下嵌入式反应式 Web 服务器的支持:Reactor Netty、Tomcat、Jetty 和 Undertow。 大多数开发人员使用适当的 starter 来获取完全配置的实例。 默认情况下,嵌入式服务器在端口 8080 上侦听 HTTP 请求。
自定义 Reactive 服务器
常见的反应式 Web 服务器设置可以使用 Spring 进行配置Environment
性能。
通常,您会在application.properties
或application.yaml
文件。
常见的服务器设置包括:
Spring Boot 会尽可能多地公开通用设置,但这并不总是可能的。
在这些情况下,专用命名空间(例如server.netty.*
提供特定于服务器的自定义设置。
请参阅ServerProperties 类以获取完整列表。 |
编程自定义
如果需要以编程方式配置反应式 Web 服务器,可以注册一个实现WebServerFactoryCustomizer
接口。WebServerFactoryCustomizer
提供对ConfigurableReactiveWebServerFactory
,其中包括许多自定义 setter 方法。
以下示例显示了以编程方式设置端口:
-
Java
-
Kotlin
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
@Override
public void customize(ConfigurableReactiveWebServerFactory server) {
server.setPort(9000);
}
}
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory
import org.springframework.stereotype.Component
@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
override fun customize(server: ConfigurableReactiveWebServerFactory) {
server.setPort(9000)
}
}
JettyReactiveWebServerFactory
,NettyReactiveWebServerFactory
,TomcatReactiveWebServerFactory
和UndertowReactiveWebServerFactory
是 的专用变体ConfigurableReactiveWebServerFactory
分别具有 Jetty、Reactor Netty、Tomcat 和 Undertow 的其他自定义 setter 方法。
以下示例显示如何自定义NettyReactiveWebServerFactory
,提供对 Reactor Netty 特定的配置选项的访问:
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class MyNettyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers((server) -> server.idleTimeout(Duration.ofSeconds(20)));
}
}
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component
import java.time.Duration
@Component
class MyNettyWebServerFactoryCustomizer : WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
override fun customize(factory: NettyReactiveWebServerFactory) {
factory.addServerCustomizers({ server -> server.idleTimeout(Duration.ofSeconds(20)) })
}
}
直接自定义 ConfigurableReactiveWebServerFactory
对于需要您从ReactiveWebServerFactory
,您可以自己公开此类 bean。
为许多配置选项提供了 setter。
如果你需要做一些更奇特的事情,还提供了几个受保护的方法 “钩子”。
请参阅ConfigurableReactiveWebServerFactory
API 文档了解详细信息。
自动配置的定制器仍应用于您的自定义工厂,因此请谨慎使用该选项。 |
反应式服务器资源配置
在自动配置 Reactor Netty 或 Jetty 服务器时, Spring Boot 将创建特定的 bean,这些 bean 将向服务器实例提供 HTTP 资源:ReactorResourceFactory
或JettyResourceFactory
.
默认情况下,这些资源也将与 Reactor Netty 和 Jetty 客户端共享,以获得最佳性能,前提是:
-
相同的技术用于 Server 和 Client 端
-
客户端实例是使用
WebClient.Builder
由 Spring Boot 自动配置的 bean
开发人员可以通过提供自定义ReactorResourceFactory
或JettyResourceFactory
bean - 这将应用于客户端和服务器。
您可以在 WebClient 运行时 部分了解有关客户端资源配置的更多信息。