WebFlux 配置

WebFlux Java 配置声明了处理带有注解控制器或函数式端点请求所需的组件,并提供了一个 API 来自定义配置。这意味着您不需要理解 Java 配置创建的底层 bean。但是,如果您想了解它们,可以在 WebFluxConfigurationSupport 中查看它们,或在 特殊 Bean 类型中阅读更多关于它们的信息。对于配置 API 中不提供的更高级的自定义,您可以通过 高级配置模式完全控制配置。

启用 WebFlux 配置

您可以在 Java 配置中使用 @EnableWebFlux 注解,如下例所示:

  • Java

  • Kotlin

@Configuration
@EnableWebFlux
public class WebConfig {
}
@Configuration
@EnableWebFlux
class WebConfig

当使用 Spring Boot 时,您可能希望使用 WebFluxConfigurer 类型的 @Configuration 类,但不要使用 @EnableWebFlux,以保留 Spring Boot WebFlux 的自定义。更多详细信息请参见 webflux-config-customizeSpring Boot 专用文档

前面的例子注册了许多 Spring WebFlux 基础设施 bean,并适应了类路径上可用的依赖项 — 例如 JSON、XML 等。

WebFlux 配置 API

在您的 Java 配置中,您可以实现 WebFluxConfigurer 接口,如下例所示:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	// Implement configuration methods...
}
@Configuration
class WebConfig : WebFluxConfigurer {

	// Implement configuration methods...
}

转换、格式化

默认情况下,会安装各种数字和日期类型的格式化器,并支持通过字段和参数上的 @NumberFormat@DurationFormat@DateTimeFormat 进行自定义。

要在 Java 配置中注册自定义格式化器和转换器,请使用以下内容:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void addFormatters(FormatterRegistry registry) {
		// ...
	}

}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun addFormatters(registry: FormatterRegistry) {
		// ...
	}
}

默认情况下,Spring WebFlux 在解析和格式化日期值时会考虑请求的 Locale。这适用于日期以字符串形式表示的“input”表单字段。然而,对于“date”和“time”表单字段,浏览器会使用 HTML 规范中定义的固定格式。对于这种情况,日期和时间格式可以按如下方式自定义:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void addFormatters(FormatterRegistry registry) {
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
		registrar.setUseIsoFormat(true);
		registrar.registerFormatters(registry);
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun addFormatters(registry: FormatterRegistry) {
		val registrar = DateTimeFormatterRegistrar()
		registrar.setUseIsoFormat(true)
		registrar.registerFormatters(registry)
	}
}

有关何时使用 FormatterRegistrar 实现的更多信息,请参见 FormatterRegistrar SPIFormattingConversionServiceFactoryBean

验证

默认情况下,如果类路径上存在 Bean Validation(例如 Hibernate Validator),则 LocalValidatorFactoryBean 会注册为一个全局 验证器,用于 @Controller 方法参数上的 @Valid@Validated

在您的 Java 配置中,您可以自定义全局 Validator 实例,如下例所示:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public Validator getValidator() {
		// ...
	}

}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun getValidator(): Validator {
		// ...
	}

}

请注意,您也可以局部注册 Validator 实现,如下例所示:

  • Java

  • Kotlin

@Controller
public class MyController {

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.addValidators(new FooValidator());
	}

}
@Controller
class MyController {

	@InitBinder
	protected fun initBinder(binder: WebDataBinder) {
		binder.addValidators(FooValidator())
	}
}

如果您需要注入 LocalValidatorFactoryBean,请创建一个 bean 并用 @Primary 标记它,以避免与 MVC 配置中声明的 bean 发生冲突。

内容类型解析器

您可以配置 Spring WebFlux 如何从请求中确定 @Controller 实例的请求媒体类型。默认情况下,只检查 Accept 头,但您也可以启用基于查询参数的策略。

以下示例显示了如何自定义请求内容类型解析:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
		// ...
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun configureContentTypeResolver(builder: RequestedContentTypeResolverBuilder) {
		// ...
	}
}

HTTP 消息编解码器

以下示例展示了如何自定义请求和响应体的读取和写入方式:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
		configurer.defaultCodecs().maxInMemorySize(512 * 1024);
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
		configurer.defaultCodecs().maxInMemorySize(512 * 1024)
	}
}

ServerCodecConfigurer 提供了一组默认的读取器和写入器。您可以使用它来添加更多读取器和写入器,自定义默认的,或完全替换默认的。

对于 Jackson JSON 和 XML,可以考虑使用 Jackson2ObjectMapperBuilder,它使用以下属性自定义 Jackson 的默认属性:

如果检测到类路径上有以下众所周知的模块,它还会自动注册它们:

视图解析器

以下示例展示了如何配置视图解析:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		// ...
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		// ...
	}
}

ViewResolverRegistry 提供了与 Spring Framework 集成的视图技术的快捷方式。以下示例使用 FreeMarker(这也需要配置底层的 FreeMarker 视图技术):

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {


	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.freeMarker();
	}

	// Configure Freemarker...

	@Bean
	public FreeMarkerConfigurer freeMarkerConfigurer() {
		FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
		configurer.setTemplateLoaderPath("classpath:/templates");
		return configurer;
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		registry.freeMarker()
	}

	// Configure Freemarker...

	@Bean
	fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
		setTemplateLoaderPath("classpath:/templates")
	}
}

您还可以插入任何 ViewResolver 实现,如下例所示:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {


	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		ViewResolver resolver = ... ;
		registry.viewResolver(resolver);
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		val resolver: ViewResolver = ...
		registry.viewResolver(resolver
	}
}

为了支持 内容协商 和通过视图解析(除了 HTML 之外)渲染其他格式,您可以配置一个或多个基于 HttpMessageWriterView 实现的默认视图,该实现接受 spring-web 中任何可用的 编解码器。以下示例展示了如何实现:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {


	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.freeMarker();

		Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
		registry.defaultViews(new HttpMessageWriterView(encoder));
	}

	// ...
}
@Configuration
class WebConfig : WebFluxConfigurer {


	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		registry.freeMarker()

		val encoder = Jackson2JsonEncoder()
		registry.defaultViews(HttpMessageWriterView(encoder))
	}

	// ...
}

有关与 Spring WebFlux 集成的视图技术的更多信息,请参阅 视图技术

静态资源

此选项提供了一种便捷的方式,可以从基于 Resource-的位置列表提供静态资源。

在下一个示例中,给定一个以 /resources 开头的请求,相对路径用于查找和提供类路径上相对于 /static 的静态资源。资源以一年后的过期时间提供,以确保最大限度地利用浏览器缓存并减少浏览器发出的 HTTP 请求。Last-Modified 头也被评估,如果存在,则返回 304 状态码。以下列表显示了该示例:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**")
				.addResourceLocations("/public", "classpath:/static/")
				.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
	}

}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
		registry.addResourceHandler("/resources/**")
				.addResourceLocations("/public", "classpath:/static/")
				.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
	}
}

资源处理器还支持 ResourceResolver 实现和 ResourceTransformer 实现的链,可用于创建用于处理优化资源的工具链。

您可以使用 VersionResourceResolver 基于从内容计算的 MD5 哈希、固定的应用程序版本或其他信息来处理版本化的资源 URL。ContentVersionStrategy(MD5 哈希)是一个不错的选择,但有一些明显的例外(例如与模块加载器一起使用的 JavaScript 资源)。

以下示例展示了如何在 Java 配置中使用 VersionResourceResolver

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**")
				.addResourceLocations("/public/")
				.resourceChain(true)
				.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
	}

}
@Configuration
class WebConfig : WebFluxConfigurer {

	override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
		registry.addResourceHandler("/resources/**")
				.addResourceLocations("/public/")
				.resourceChain(true)
				.addResolver(VersionResourceResolver().addContentVersionStrategy("/**"))
	}

}

您可以使用 ResourceUrlProvider 重写 URL 并应用解析器和转换器的完整链(例如,插入版本)。WebFlux 配置提供了一个 ResourceUrlProvider,以便它可以注入到其他地方。

与 Spring MVC 不同,目前在 WebFlux 中,没有办法透明地重写静态资源 URL,因为没有视图技术可以利用非阻塞的解析器和转换器链。当只提供本地资源时,解决方法是直接使用 ResourceUrlProvider(例如,通过自定义元素)并阻塞。

请注意,当同时使用 EncodedResourceResolver(例如,Gzip、Brotli 编码)和 VersionedResourceResolver 时,它们必须按该顺序注册,以确保始终根据未编码的文件可靠地计算基于内容的版本。

对于 WebJars,推荐且最有效的使用方式是版本化的 URL,例如 /webjars/jquery/1.2.0/jquery.min.js。相关的资源位置在 Spring Boot 中已开箱即用(或可以通过 ResourceHandlerRegistry 手动配置),并且不需要添加 org.webjars:webjars-locator-lite 依赖。

无版本 URL,例如 /webjars/jquery/jquery.min.js,通过 WebJarsResourceResolver 支持,当 org.webjars:webjars-locator-lite 库存在于类路径上时,它会自动注册。该解析器可以重写 URL 以包含 jar 的版本,并且还可以匹配没有版本的传入 URL — 例如,从 /webjars/jquery/jquery.min.js/webjars/jquery/1.2.0/jquery.min.js

基于 ResourceHandlerRegistry 的 Java 配置提供了进一步的选项,用于精细控制,例如上次修改行为和优化的资源解析。

路径匹配

您可以自定义与路径匹配相关的选项。有关各个选项的详细信息,请参阅 PathMatchConfigurer javadoc。以下示例展示了如何使用 PathMatchConfigurer

Spring WebFlux 依赖于请求路径的解析表示,称为 RequestPath,用于访问解码后的路径段值,其中分号内容已删除(即路径或矩阵变量)。这意味着,与 Spring MVC 不同,您无需指明是否解码请求路径,也无需指明是否删除分号内容以进行路径匹配。 Spring WebFlux 也不支持后缀模式匹配,与 Spring MVC 不同,在 Spring MVC 中,我们也 建议 放弃对其的依赖。

API 版本

要启用 API 版本控制,请使用 WebFluxConfigurerApiVersionConfigurer 回调:

  • Java

  • Kotlin

@Configuration
public class WebConfiguration implements WebFluxConfigurer {

	@Override
	public void configureApiVersioning(ApiVersionConfigurer configurer) {
		configurer.useRequestHeader("X-API-Version");
	}
}
@Configuration
class WebConfiguration : WebMvcConfigurer {

	override fun configureApiVersioning(configurer: ApiVersionConfigurer) {
		configurer.useRequestHeader("X-API-Version")
	}
}

您可以通过下面列出的内置选项之一解析版本,或者使用自定义 ApiVersionResolver

  • 请求头

  • 请求参数

  • 路径段

  • 媒体类型参数

要从路径段解析,您需要指定预期包含版本的路径段的索引。路径段必须声明为 URI 变量,例如 "/{version}"、"/api/{version}" 等,其中实际名称并不重要。 由于版本通常位于路径的开头,请考虑通过 路径匹配 选项将其作为所有处理程序的通用路径前缀进行外部配置。

默认情况下,版本使用 SemanticVersionParser 解析,但您也可以配置自定义 ApiVersionParser

支持的版本会从请求映射中声明的版本中透明地检测出来,以方便使用,但您可以通过 WebFlux 配置中的一个标志将其关闭,并只考虑配置中明确配置的版本为支持的版本。 版本不受支持的请求将被 InvalidApiVersionException 拒绝,导致 400 响应。

您可以设置 ApiVersionDeprecationHandler 以向客户端发送有关已弃用版本的信息。内置的标准处理程序可以根据 RFC 9745RFC 8594 设置“Deprecation”、“Sunset”和“Link”头。

一旦配置了 API 版本控制,您就可以开始根据请求版本将请求映射到 控制器方法

阻塞执行

WebFlux Java 配置允许您自定义 WebFlux 中的阻塞执行。

您可以通过提供一个 AsyncTaskExecutor(例如 VirtualThreadTaskExecutor)来在单独的线程上调用阻塞的控制器方法,如下所示:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configureBlockingExecution(BlockingExecutionConfigurer configurer) {
		AsyncTaskExecutor executor = ...
		configurer.setExecutor(executor);
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	@Override
	fun configureBlockingExecution(configurer: BlockingExecutionConfigurer) {
		val executor = ...
		configurer.setExecutor(executor)
	}
}

默认情况下,返回类型未被配置的 ReactiveAdapterRegistry 识别的控制器方法被认为是阻塞的,但您可以通过 BlockingExecutionConfigurer 设置自定义控制器方法谓词。

WebSocketService

WebFlux Java 配置声明了一个 WebSocketHandlerAdapter bean,它提供了对 WebSocket 处理器调用的支持。这意味着为了处理 WebSocket 握手请求,所有需要做的就是通过 SimpleUrlHandlerMappingWebSocketHandler 映射到 URL。

在某些情况下,可能需要使用提供的 WebSocketService 服务创建 WebSocketHandlerAdapter bean,该服务允许配置 WebSocket 服务器属性。例如:

  • Java

  • Kotlin

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public WebSocketService getWebSocketService() {
		TomcatRequestUpgradeStrategy strategy = new TomcatRequestUpgradeStrategy();
		strategy.setMaxSessionIdleTimeout(0L);
		return new HandshakeWebSocketService(strategy);
	}
}
@Configuration
class WebConfig : WebFluxConfigurer {

	@Override
	fun webSocketService(): WebSocketService {
		val strategy = TomcatRequestUpgradeStrategy().apply {
			setMaxSessionIdleTimeout(0L)
		}
		return HandshakeWebSocketService(strategy)
	}
}

高级配置模式

@EnableWebFlux 导入 DelegatingWebFluxConfiguration,它:

  • 为 WebFlux 应用程序提供默认的 Spring 配置

  • 检测并委托给 WebFluxConfigurer 实现以自定义该配置。

对于高级模式,您可以删除 @EnableWebFlux 并直接从 DelegatingWebFluxConfiguration 扩展,而不是实现 WebFluxConfigurer,如下例所示:

  • Java

  • Kotlin

@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {

	// ...
}
@Configuration
class WebConfig : DelegatingWebFluxConfiguration {

	// ...
}

您可以保留 WebConfig 中现有方法,但现在您也可以覆盖基类中的 bean 声明,并且类路径上仍有任意数量的其他 WebMvcConfigurer 实现。