响应式 Web 应用程序
Spring Boot 通过为 Spring WebFlux 提供自动配置,简化了响应式 Web 应用程序的开发。
“Spring WebFlux 框架”
Spring WebFlux 是 Spring Framework 5.0 中引入的新型响应式 Web 框架。 与 Spring MVC 不同,它不依赖 Servlet API,完全支持异步和非阻塞,并通过 响应式流(Reactive Streams) 规范和 Reactor 项目 实现响应式编程。
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 框架的一部分,其详细信息可在该框架的 参考文档 中查阅。
“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 框架的一部分,详细信息请参阅其 参考文档。
您可以定义任意多个 RouterFunction Bean,以模块化方式定义路由器。
如果需要指定优先级顺序,可以对 Bean 进行排序。 |
入门时,请将 spring-boot-starter-webflux 模块添加到您的应用程序中。
在应用程序中同时添加 spring-boot-starter-web 和 spring-boot-starter-webflux 模块,将导致 Spring Boot 自动配置 Spring MVC,而非 WebFlux。
选择此行为的原因是:许多 Spring 开发人员会在其 Spring MVC 应用程序中添加 spring-boot-starter-webflux,以使用响应式 WebClient。
您仍可通过将所选应用类型设为 SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 来强制采用您的选择。 |
Spring WebFlux 自动配置
Spring Boot 为 Spring WebFlux 提供了自动配置,该配置与大多数应用程序兼容良好。
自动配置在 Spring 默认配置的基础上添加了以下功能:
-
为
HttpMessageReader和HttpMessageWriter实例配置编解码器(在本文档后续部分中介绍)。 -
支持提供静态资源,包括对 WebJars 的支持(在本文档后续部分中介绍)。
如果您希望保留 Spring Boot WebFlux 的功能,并且还想添加额外的 WebFlux 配置,则可以添加您自己的类型为 @Configuration 的 WebFluxConfigurer 类,但不能包含 @EnableWebFlux。
如果您希望对自动配置的 HttpHandler 添加额外的自定义配置,可以定义类型为 WebHttpHandlerBuilderCustomizer 的 Bean,并使用它们来修改 WebHttpHandlerBuilder。
如果您希望完全掌控 Spring WebFlux,可以添加一个使用 @EnableWebFlux 注解的自定义 @Configuration。
Spring WebFlux 转换服务
如果您希望自定义 Spring WebFlux 所使用的 ConversionService,可以提供一个带有 addFormatters 方法的 WebFluxConfigurer Bean。
还可以通过 spring.webflux.format.* 配置属性来自定义转换。
若未进行配置,则使用以下默认值:
| 属性 | DateTimeFormatter |
格式 |
|---|---|---|
|
|
|
|
|
java.time 的 |
|
|
java.time 的 |
使用 HttpMessageReader 和 HttpMessageWriter 的 HTTP 编解码器
Spring WebFlux 使用 HttpMessageReader 和 HttpMessageWriter 接口来转换 HTTP 请求和响应。
它们通过 CodecConfigurer 进行配置,从而根据类路径中可用的库提供合理的默认设置。
Spring Boot 为编解码器提供了专用的配置属性:spring.http.codecs.*。
它还通过使用 CodecCustomizer 实例进一步实现自定义。
例如,spring.jackson.* 配置键将应用于 Jackson 编解码器。
如果需要添加或自定义编解码器,您可以创建一个自定义的 CodecCustomizer 组件,如下例所示:
-
Java
-
Kotlin
import org.springframework.boot.http.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.http.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())
}
}
}
您还可以利用 Spring Boot 的自定义 JSON 序列化器和反序列化器。
静态内容
默认情况下,Spring Boot 从类路径中名为 /static(或 /public 或 /resources 或 /META-INF/resources)的目录提供静态内容。
它使用 Spring WebFlux 中的 ResourceWebHandler,因此您可以通过添加自己的 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/** 下的资源,只要以 Webjars 格式打包在 JAR 文件中,即可直接提供服务。
该路径可通过 spring.webflux.webjars-path-pattern 属性进行自定义。
Spring WebFlux 应用程序并不严格依赖 Servlet API,因此无法以 WAR 文件形式部署,也不会使用 src/main/webapp 目录。 |
欢迎页面
Spring Boot 支持静态欢迎页和模板化欢迎页。
它首先在已配置的静态内容位置中查找 index.html 文件。
如果未找到该文件,则接着查找 index 模板。
只要找到其中任意一个,便会自动将其用作应用程序的欢迎页。
此设置仅作为应用程序所定义的实际索引路由的备用方案。
排序顺序由 `HandlerMapping` 类型 Bean 的声明顺序决定,默认顺序如下:
|
使用 |
|
在 |
|
欢迎页支持 |
模板引擎
除了 REST Web 服务外,您还可以使用 Spring WebFlux 提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。
Spring Boot 包含对以下模板引擎的自动配置支持:
| 并非所有 FreeMarker 功能都支持 WebFlux。 有关更多详细信息,请参阅每个属性的说明。 |
当您使用这些模板引擎之一并采用默认配置时,模板会自动从 src/main/resources/templates 中加载。
错误处理
Spring Boot 提供了一个WebExceptionHandler,以合理的方式处理所有错误。
其在处理顺序中的位置紧接在 WebFlux 提供的处理器之前,而 WebFlux 处理器被视为最终的处理器。
对于机器客户端,它会生成一个包含错误详情、HTTP 状态码及异常消息的 JSON 响应。
对于浏览器客户端,则提供了一个“白标”(whitelabel)错误处理器,以 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 来启用此支持。
自定义此功能的第一步通常涉及使用现有机制,但替换或扩充错误内容。
为此,您可以添加一个类型为 ErrorAttributes 的 Bean。
要更改错误处理行为,您可以实现 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.webflux.autoconfigure.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.webflux.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.webflux.error.ErrorAttributes
import org.springframework.boot.webflux.autoconfigure.error.AbstractErrorWebExceptionHandler
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>
要使用 Mustache 模板映射所有 5xx 错误,您的目录结构如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.mustache
+- <other templates>
Web 过滤器
当过滤器的执行顺序至关重要时,它们可以实现 Ordered 接口,或使用 @Order 注解进行标注。
Spring Boot 的自动配置可能会为您配置 Web 过滤器。
当自动配置执行此操作时,将采用下表中所示的执行顺序:
| Web 过滤器 | 顺序 |
|---|---|
|
|
|
API 版本控制
Spring WebFlux 支持 API 版本控制,可用于随时间推移改进 HTTP API。
相同的 @Controller 路径可以多次映射,以支持 API 的不同版本。
有关更多详细信息,请参阅 Spring 框架的参考文档。
一旦添加了映射,您还需要配置 Spring WebFlux,以便它能够使用随请求一起发送的任何版本信息。 通常,版本作为 HTTP 头、查询参数或路径的一部分发送。
要配置Spring WebFlux,您可以使用一个 WebFluxConfigurer bean 并覆盖 configureApiVersioning(…) 方法,或者可以使用属性。
例如,以下内容将使用 X-Version HTTP 头来获取版本信息,并在未发送头时默认使用 1.0.0。
-
Properties
-
YAML
spring.webflux.apiversion.default=1.0.0
spring.webflux.apiversion.use.header=X-Version
spring:
webflux:
apiversion:
default: 1.0.0
use:
header: X-Version
为了获得更全面的控制,您还可以定义 ApiVersionResolver、ApiVersionParser 和 ApiVersionDeprecationHandler beans,这些 beans 将被注入到自动配置的 Spring MVC 配置中。
API版本控制在客户端也得到支持,包括WebClient和RestClient。
有关详细信息,请参阅API版本控制。 |
嵌入式响应式服务器支持
Spring Boot 包含对以下嵌入式响应式 Web 服务器的支持:Reactor Netty、Tomcat 和 Jetty。 大多数开发人员使用适当的 starter 来获得一个完全配置的实例。 默认情况下,嵌入式服务器在端口 8080 上监听 HTTP 请求。
自定义响应式服务器
常见的响应式 Web 服务器设置可通过 Spring Environment 属性进行配置。
通常,您会在 application.properties 或 application.yaml 文件中定义这些属性。
常见的服务器设置包括:
Spring Boot 尽可能地暴露常用配置,但并非所有情况都能做到。
对于这些情况,专用命名空间(例如 server.netty.*)可提供特定于服务器的自定义选项。
请参阅 ServerProperties 类以获取完整列表。 |
编程式自定义
如果需要以编程方式配置您的响应式 Web 服务器,可以注册一个实现 WebServerFactoryCustomizer 接口的 Spring Bean。
WebServerFactoryCustomizer 提供对 ConfigurableReactiveWebServerFactory 的访问,后者包含大量用于自定义的 setter 方法。
以下示例展示了如何以编程方式设置端口号:
-
Java
-
Kotlin
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.reactive.ConfigurableReactiveWebServerFactory;
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.server.reactive.ConfigurableReactiveWebServerFactory
import org.springframework.stereotype.Component
@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
override fun customize(server: ConfigurableReactiveWebServerFactory) {
server.setPort(9000)
}
}
JettyReactiveWebServerFactory, NettyReactiveWebServerFactory, 和 TomcatReactiveWebServerFactory 是 ConfigurableReactiveWebServerFactory 的专用变体,分别提供了针对 Jetty、Reactor Netty 和 Tomcat 的额外自定义设置方法。
下面的示例展示了如何自定义 NettyReactiveWebServerFactory,它提供了对 Reactor Netty 特定配置选项的访问:
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.reactor.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.server.WebServerFactoryCustomizer
import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory
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,以向服务器实例提供 HTTP 资源:ReactorResourceFactory 或 JettyResourceFactory。
默认情况下,出于最佳性能考虑,这些资源也将与 Reactor Netty 和 Jetty 客户端共享,前提是:
-
服务器和客户端使用相同的技术。
-
客户端实例是使用 Spring Boot 自动配置的
WebClient.BuilderBean 构建的
开发人员可以通过提供自定义的 ReactorResourceFactory 或 JettyResourceFactory Bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 - 这将应用于客户端和服务器端。
您可以在WebClient 运行时部分进一步了解客户端的资源配置。