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-customize专门的 Spring 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-core 依赖。

无版本 URL(如 /webjars/jquery/jquery.min.js)通过 WebJarsResourceResolver 提供支持,当类路径中存在 org.webjars:webjars-locator-core 库时,它会自动注册,但代价是类路径扫描可能会减慢应用程序启动速度。解析器可以重写 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 中,我们也 建议 放弃对它的依赖。

阻塞执行

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 实现。