|
对于最新的稳定版本,请使用 Spring Boot 3.5.5! |
响应式 Web 应用程序
Spring Boot 通过为 Spring Webflux 提供自动配置,简化了响应式 Web 应用程序的开发。
“Spring WebFlux 框架”
Spring WebFlux 是 Spring Framework 5.0 中引入的新响应式 Web 框架。 与 Spring MVC 不同的是,它不需要 servlet API,是完全异步和非阻塞的,并通过 Reactor 项目实现 Reactive Streams 规范。
Spring WebFlux 有两种类型:函数式和基于注释的。 基于注释的非常接近 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 的一部分,详细信息在其参考文档中可用。
您可以定义任意数量的RouterFunctionbeans 来模块化路由器的定义。
如果需要应用优先级,可以订购 Bean。 |
要开始使用,请添加spring-boot-starter-webflux模块添加到您的应用程序中。
将两者都添加spring-boot-starter-web和spring-boot-starter-webflux模块导致 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类型类WebFluxConfigurer但没有 @EnableWebFlux.
如果要向自动配置的HttpHandler,您可以定义类型为WebHttpHandlerBuilderCustomizer并使用它们来修改WebHttpHandlerBuilder.
如果您想完全控制 Spring WebFlux,您可以添加自己的@Configuration注释为@EnableWebFlux.
Spring WebFlux 转换服务
如果要自定义ConversionServiceSpring WebFlux 使用时,你可以提供一个WebFluxConfigurer带有addFormatters方法。
也可以使用spring.webflux.format.*配置属性。
如果未配置,则使用以下默认值:
| 属性 | DateTimeFormatter |
格式 |
|---|---|---|
|
|
|
|
|
java.time 的 |
|
|
java.time 的 |
具有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器
Spring WebFlux 使用HttpMessageReader和HttpMessageWriter接口来转换 HTTP 请求和响应。
它们配置为CodecConfigurer通过查看类路径中可用的库来获得合理的默认值。
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模板。
如果找到其中任何一个,则会自动将其用作应用程序的欢迎页面。
这仅充当应用程序定义的实际索引路由的回退。
排序由HandlerMappingbean,默认如下:
|
|
|
在 中声明的端点 |
|
欢迎页面支持 |
模板引擎
除了 REST Web 服务,您还可以使用 Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。
Spring Boot 包括对以下模板引擎的自动配置支持:
| WebFlux 并非所有 FreeMarker 功能都支持。 有关更多详细信息,请查看每个属性的描述。 |
当您将这些模板引擎之一与默认配置一起使用时,您的模板会自动从src/main/resources/templates.
错误处理
Spring Boot 提供了一个WebExceptionHandler以合理的方式处理所有错误。
它在处理顺序中的位置紧接在 WebFlux 提供的处理程序之前,这些处理程序被认为是最后的。
对于机器客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。
对于浏览器客户端,有一个“白标”错误处理程序,它以 HTML 格式呈现相同的数据。
您还可以提供自己的 HTML 模板来显示错误(请参阅下一节)。
在直接在 Spring Boot 中自定义错误处理之前,您可以利用 Spring WebFlux 中的 RFC 9457 问题详细信息支持。
Spring WebFlux 可以使用application/problem+json媒体类型,例如:
{
"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.
自定义此功能的第一步通常涉及使用现有机制,但替换或增强错误内容。
为此,您可以添加一个类型的 beanErrorAttributes.
要更改错误处理行为,您可以实现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如果没有其他匹配项,则为默认值。
请注意,默认错误视图的路径是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>
网络过滤器
| 网页过滤器 | 次序 |
|---|---|
|
|
|
嵌入式响应式服务器支持
Spring Boot 包括对以下嵌入式响应式 Web 服务器的支持:Reactor Netty、Tomcat、Jetty 和 Undertow。 大多数开发人员使用适当的Starters来获取完全配置的实例。 默认情况下,嵌入式服务器在端口 8080 上侦听 HTTP 请求。
自定义响应式服务器
可以使用 Spring 配置常见的响应式 Web 服务器设置Environment性能。
通常,你会在application.properties或application.yaml文件。
常见的服务器设置包括:
Spring Boot 尽可能多地公开常见设置,但这并不总是可行的。
对于这些情况,专用命名空间(例如server.netty.*提供特定于服务器的自定义。
请参阅ServerPropertiesclass 获取完整列表。 |
程序化定制
如果您需要以编程方式配置响应式 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 客户端共享,以获得最佳性能,给定:
-
服务器和客户端使用相同的技术
-
客户端实例是使用
WebClient.Builder由 Spring Boot 自动配置的 bean
开发人员可以通过提供自定义ReactorResourceFactory或JettyResourceFactorybean - 这将应用于客户端和服务器。
您可以在 WebClient Runtime 部分了解有关客户端资源配置的更多信息。