响应式 Web 应用程序

Spring Boot 通过为 Spring WebFlux 提供自动配置,简化了响应式 Web 应用程序的开发。spring-doc.cadn.net.cn

“Spring WebFlux 框架”

Spring WebFlux 是 Spring Framework 5.0 中引入的新型响应式 Web 框架。 与 Spring MVC 不同,它不依赖 Servlet API,完全支持异步和非阻塞,并通过 响应式流(Reactive Streams) 规范和 Reactor 项目 实现响应式编程。spring-doc.cadn.net.cn

Spring WebFlux 提供两种风格:函数式和基于注解的。 基于注解的风格与 Spring MVC 模型非常相似,如下例所示:spring-doc.cadn.net.cn

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 框架的一部分,其详细信息可在该框架的 参考文档 中查阅。spring-doc.cadn.net.cn

“WebFlux.fn”(函数式变体)将路由配置与请求的实际处理分离开来,如下例所示:spring-doc.cadn.net.cn

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)
	}

}
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 框架的一部分,详细信息请参阅其 参考文档spring-doc.cadn.net.cn

您可以定义任意多个 RouterFunction Bean,以模块化方式定义路由器。 如果需要指定优先级顺序,可以对 Bean 进行排序。

入门时,请将 spring-boot-starter-webflux 模块添加到您的应用程序中。spring-doc.cadn.net.cn

在应用程序中同时添加 spring-boot-starter-webspring-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-doc.cadn.net.cn

自动配置在 Spring 默认配置的基础上添加了以下功能:spring-doc.cadn.net.cn

如果您希望保留 Spring Boot WebFlux 的功能,并且还想添加额外的 WebFlux 配置,则可以添加您自己的类型为 @ConfigurationWebFluxConfigurer 类,但不能包含 @EnableWebFluxspring-doc.cadn.net.cn

如果您希望对自动配置的 HttpHandler 添加额外的自定义配置,可以定义类型为 WebHttpHandlerBuilderCustomizer 的 Bean,并使用它们来修改 WebHttpHandlerBuilderspring-doc.cadn.net.cn

如果您希望完全掌控 Spring WebFlux,可以添加一个使用 @EnableWebFlux 注解的自定义 @Configurationspring-doc.cadn.net.cn

Spring WebFlux 转换服务

如果您希望自定义 Spring WebFlux 所使用的 ConversionService,可以提供一个带有 addFormatters 方法的 WebFluxConfigurer Bean。spring-doc.cadn.net.cn

还可以通过 spring.webflux.format.* 配置属性来自定义转换。 若未进行配置,则使用以下默认值:spring-doc.cadn.net.cn

属性 DateTimeFormatter 格式

spring.webflux.format.datespring-doc.cadn.net.cn

ofLocalizedDate(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.util.DateLocalDatespring-doc.cadn.net.cn

spring.webflux.format.timespring-doc.cadn.net.cn

ofLocalizedTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.time 的 LocalTimeOffsetTimespring-doc.cadn.net.cn

spring.webflux.format.date-timespring-doc.cadn.net.cn

ofLocalizedDateTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.time 的 LocalDateTimeOffsetDateTimeZonedDateTimespring-doc.cadn.net.cn

使用 HttpMessageReader 和 HttpMessageWriter 的 HTTP 编解码器

Spring WebFlux 使用 HttpMessageReaderHttpMessageWriter 接口来转换 HTTP 请求和响应。 它们通过 CodecConfigurer 进行配置,从而根据类路径中可用的库提供合理的默认设置。spring-doc.cadn.net.cn

Spring Boot 为编解码器提供了专用的配置属性:spring.http.codecs.*。 它还通过使用 CodecCustomizer 实例进一步实现自定义。 例如,spring.jackson.* 配置键将应用于 Jackson 编解码器。spring-doc.cadn.net.cn

如果需要添加或自定义编解码器,您可以创建一个自定义的 CodecCustomizer 组件,如下例所示:spring-doc.cadn.net.cn

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 从类路径中名为 /static(或 /public/resources/META-INF/resources)的目录提供静态内容。 它使用 Spring WebFlux 中的 ResourceWebHandler,因此您可以通过添加自己的 WebFluxConfigurer 并重写 addResourceHandlers 方法来自定义该行为。spring-doc.cadn.net.cn

默认情况下,资源映射到 /**,但您可以通过设置 spring.webflux.static-path-pattern 属性来调整该配置。 例如,将所有资源重定位到 /resources/** 可按如下方式实现:spring-doc.cadn.net.cn

spring.webflux.static-path-pattern=/resources/**
spring:
  webflux:
    static-path-pattern: "/resources/**"

您还可以通过使用 spring.web.resources.static-locations 自定义静态资源位置。 这样做会以目录路径列表替换默认值。 如果进行了此项配置,默认欢迎页检测机制将切换至您自定义的位置。 因此,如果在应用启动时,您的任一自定义位置中存在 index.html 文件,则该文件即为应用程序的首页。spring-doc.cadn.net.cn

除了前面列出的“标准”静态资源位置外,Webjars 内容属于一种特殊情况。 默认情况下,任何路径位于 /webjars/** 下的资源,只要以 Webjars 格式打包在 JAR 文件中,即可直接提供服务。 该路径可通过 spring.webflux.webjars-path-pattern 属性进行自定义。spring-doc.cadn.net.cn

Spring WebFlux 应用程序并不严格依赖 Servlet API,因此无法以 WAR 文件形式部署,也不会使用 src/main/webapp 目录。

欢迎页面

Spring Boot 支持静态欢迎页和模板化欢迎页。 它首先在已配置的静态内容位置中查找 index.html 文件。 如果未找到该文件,则接着查找 index 模板。 只要找到其中任意一个,便会自动将其用作应用程序的欢迎页。spring-doc.cadn.net.cn

此设置仅作为应用程序所定义的实际索引路由的备用方案。 排序顺序由 `HandlerMapping` 类型 Bean 的声明顺序决定,默认顺序如下:spring-doc.cadn.net.cn

org.springframework.web.reactive.function.server.support.RouterFunctionMappingspring-doc.cadn.net.cn

使用 RouterFunction Bean 声明的端点spring-doc.cadn.net.cn

org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMappingspring-doc.cadn.net.cn

@Controller 个 Bean 中声明的端点spring-doc.cadn.net.cn

RouterFunctionMapping 表示欢迎页面spring-doc.cadn.net.cn

欢迎页支持spring-doc.cadn.net.cn

模板引擎

除了 REST Web 服务外,您还可以使用 Spring WebFlux 提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。spring-doc.cadn.net.cn

Spring Boot 包含对以下模板引擎的自动配置支持:spring-doc.cadn.net.cn

并非所有 FreeMarker 功能都支持 WebFlux。 有关更多详细信息,请参阅每个属性的说明。

当您使用这些模板引擎之一并采用默认配置时,模板会自动从 src/main/resources/templates 中加载。spring-doc.cadn.net.cn

错误处理

Spring Boot 提供了一个WebExceptionHandler,以合理的方式处理所有错误。 其在处理顺序中的位置紧接在 WebFlux 提供的处理器之前,而 WebFlux 处理器被视为最终的处理器。 对于机器客户端,它会生成一个包含错误详情、HTTP 状态码及异常消息的 JSON 响应。 对于浏览器客户端,则提供了一个“白标”(whitelabel)错误处理器,以 HTML 格式渲染相同的数据。 您也可以提供自定义的 HTML 模板来显示错误(参见下一节)。spring-doc.cadn.net.cn

在直接自定义 Spring Boot 中的错误处理之前,您可以利用 Spring WebFlux 中的 RFC 9457 问题详情 支持。 Spring WebFlux 可以生成采用 application/problem+json 媒体类型的自定义错误消息,例如:spring-doc.cadn.net.cn

{
	"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 来启用此支持。spring-doc.cadn.net.cn

自定义此功能的第一步通常涉及使用现有机制,但替换或扩充错误内容。 为此,您可以添加一个类型为 ErrorAttributes 的 Bean。spring-doc.cadn.net.cn

要更改错误处理行为,您可以实现 ErrorWebExceptionHandler 并注册该类型的 Bean 定义。 由于 ErrorWebExceptionHandler 层级较低,Spring Boot 还提供了一个便捷的 AbstractErrorWebExceptionHandler,让您能够以 WebFlux 函数式方式处理错误,如下例所示:spring-doc.cadn.net.cn

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类,并重写特定方法。spring-doc.cadn.net.cn

在某些情况下,控制器级别处理的错误不会被 Web 观测功能或指标基础设施记录。 应用程序可通过在观测上下文中设置已处理的异常,确保此类异常被观测功能记录。spring-doc.cadn.net.cn

自定义错误页面

如果希望为特定状态码显示自定义的 HTML 错误页面,您可以添加能解析为 error/* 的视图(例如,将文件添加到 /error 目录中)。 错误页面可以是静态 HTML 文件(即,置于任意静态资源目录下),也可以使用模板构建。 文件名应为精确的状态码、状态码系列掩码,或在无其他匹配项时使用的默认值 error。 请注意,默认错误视图的路径为 error/error,而在 Spring MVC 中,默认错误视图为 errorspring-doc.cadn.net.cn

例如,要将 404 映射到一个静态 HTML 文件,您的目录结构应如下所示:spring-doc.cadn.net.cn

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 Mustache 模板映射所有 5xx 错误,您的目录结构如下所示:spring-doc.cadn.net.cn

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.mustache
             +- <other templates>

Web 过滤器

Spring WebFlux 提供了一个 WebFilter 接口,可实现该接口以过滤 HTTP 请求-响应交换。 在应用上下文中找到的 WebFilter Bean 将自动用于过滤每次交换。spring-doc.cadn.net.cn

当过滤器的执行顺序至关重要时,它们可以实现 Ordered 接口,或使用 @Order 注解进行标注。 Spring Boot 的自动配置可能会为您配置 Web 过滤器。 当自动配置执行此操作时,将采用下表中所示的执行顺序:spring-doc.cadn.net.cn

Web 过滤器 顺序

WebFilterChainProxy(Spring Security)spring-doc.cadn.net.cn

-100spring-doc.cadn.net.cn

HttpExchangesWebFilterspring-doc.cadn.net.cn

Ordered.LOWEST_PRECEDENCE - 10spring-doc.cadn.net.cn

API 版本控制

Spring WebFlux 支持 API 版本控制,可用于随时间推移改进 HTTP API。 相同的 @Controller 路径可以多次映射,以支持 API 的不同版本。spring-doc.cadn.net.cn

有关更多详细信息,请参阅 Spring 框架的参考文档spring-doc.cadn.net.cn

一旦添加了映射,您还需要配置 Spring WebFlux,以便它能够使用随请求一起发送的任何版本信息。 通常,版本作为 HTTP 头、查询参数或路径的一部分发送。spring-doc.cadn.net.cn

要配置Spring WebFlux,您可以使用一个 WebFluxConfigurer bean 并覆盖 configureApiVersioning(…​) 方法,或者可以使用属性。spring-doc.cadn.net.cn

例如,以下内容将使用 X-Version HTTP 头来获取版本信息,并在未发送头时默认使用 1.0.0spring-doc.cadn.net.cn

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

为了获得更全面的控制,您还可以定义 ApiVersionResolverApiVersionParserApiVersionDeprecationHandler beans,这些 beans 将被注入到自动配置的 Spring MVC 配置中。spring-doc.cadn.net.cn

API版本控制在客户端也得到支持,包括WebClientRestClient。 有关详细信息,请参阅API版本控制

嵌入式响应式服务器支持

Spring Boot 包含对以下嵌入式响应式 Web 服务器的支持:Reactor Netty、Tomcat 和 Jetty。 大多数开发人员使用适当的 starter 来获得一个完全配置的实例。 默认情况下,嵌入式服务器在端口 8080 上监听 HTTP 请求。spring-doc.cadn.net.cn

自定义响应式服务器

常见的响应式 Web 服务器设置可通过 Spring Environment 属性进行配置。 通常,您会在 application.propertiesapplication.yaml 文件中定义这些属性。spring-doc.cadn.net.cn

常见的服务器设置包括:spring-doc.cadn.net.cn

Spring Boot 尽可能地暴露常用配置,但并非所有情况都能做到。 对于这些情况,专用命名空间(例如 server.netty.*)可提供特定于服务器的自定义选项。spring-doc.cadn.net.cn

请参阅 ServerProperties 类以获取完整列表。

编程式自定义

如果需要以编程方式配置您的响应式 Web 服务器,可以注册一个实现 WebServerFactoryCustomizer 接口的 Spring Bean。 WebServerFactoryCustomizer 提供对 ConfigurableReactiveWebServerFactory 的访问,后者包含大量用于自定义的 setter 方法。 以下示例展示了如何以编程方式设置端口号:spring-doc.cadn.net.cn

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, 和 TomcatReactiveWebServerFactoryConfigurableReactiveWebServerFactory 的专用变体,分别提供了针对 Jetty、Reactor Netty 和 Tomcat 的额外自定义设置方法。 下面的示例展示了如何自定义 NettyReactiveWebServerFactory,它提供了对 Reactor Netty 特定配置选项的访问:spring-doc.cadn.net.cn

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。spring-doc.cadn.net.cn

为许多配置选项提供了 Setter 方法。 此外,还提供了若干受保护的方法“钩子”,以便您在需要执行更复杂操作时使用。 详情请参阅 ConfigurableReactiveWebServerFactory 的 API 文档。spring-doc.cadn.net.cn

自动配置的自定义器仍会应用于您的自定义工厂,因此请谨慎使用该选项。

响应式服务器资源配置

在自动配置 Reactor Netty 或 Jetty 服务器时,Spring Boot 将创建特定的 Bean,以向服务器实例提供 HTTP 资源:ReactorResourceFactoryJettyResourceFactoryspring-doc.cadn.net.cn

默认情况下,出于最佳性能考虑,这些资源也将与 Reactor Netty 和 Jetty 客户端共享,前提是:spring-doc.cadn.net.cn

开发人员可以通过提供自定义的 ReactorResourceFactoryJettyResourceFactory Bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 - 这将应用于客户端和服务器端。spring-doc.cadn.net.cn

您可以在WebClient 运行时部分进一步了解客户端的资源配置。spring-doc.cadn.net.cn