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 配置 API 部分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 在解析和格式化日期值时会考虑请求区域设置。这适用于日期以包含“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 验证(例如,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 框架集成的视图技术提供了快捷方式。以下示例使用 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

  • Java

  • Kotlin

import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerTypePredicate;
import org.springframework.web.reactive.config.PathMatchConfigurer;
import org.springframework.web.reactive.config.WebFluxConfigurer;

@Configuration
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configurePathMatching(PathMatchConfigurer configurer) {
		configurer.addPathPrefix(
				"/api", HandlerTypePredicate.forAnnotation(RestController.class));
	}
}
import org.springframework.context.annotation.Configuration
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.method.HandlerTypePredicate
import org.springframework.web.reactive.config.PathMatchConfigurer
import org.springframework.web.reactive.config.WebFluxConfigurer

@Configuration
class WebConfig : WebFluxConfigurer {

	override fun configurePathMatching(configurer: PathMatchConfigurer) {
		configurer.addPathPrefix(
			"/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
	}
}

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设置自定义控制器方法谓词。

WebSocket服务

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