WebFlux配置

WebFlux Java配置声明了处理带有注解的控制器或功能端点请求所需的组件,并提供了一个API来定制配置。这意味着你不需要理解Java配置创建的底层bean。但是,如果你想了解它们,你可以在WebFluxConfigurationSupport中看到它们,或者在特殊Bean类型中阅读更多关于它们的信息。spring-doc.cadn.net.cn

对于更高级的自定义设置,如果配置API无法提供,您可以通过 高级配置模式获得对配置的完全控制。spring-doc.cadn.net.cn

启用WebFlux配置

您可以使用@EnableWebFlux注解在您的Java配置中,如下例所示:spring-doc.cadn.net.cn

@Configuration
@EnableWebFlux
public class WebConfig {
}
@Configuration
@EnableWebFlux
class WebConfig
在使用Spring Boot时,您可能希望使用@Configuration个类型为WebFluxConfigurer的类,但不包含 @EnableWebFlux,以保留Spring Boot WebFlux的自定义设置。更多详细信息请参阅 WebFlux配置API部分专门的Spring Boot文档

上一个示例注册了多个Spring WebFlux 基础结构bean,并根据类路径上可用的依赖进行适应 — 对于JSON、XML和其他格式。spring-doc.cadn.net.cn

WebFlux配置API

在你的Java配置中,你可以实现WebFluxConfigurer接口, 如下例所示:spring-doc.cadn.net.cn

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

	// Implement configuration methods...
}

转换与格式化

默认情况下,会安装针对多种数字和日期类型的格式化程序,并支持通过字段和参数上的 @NumberFormat@DurationFormat@DateTimeFormat 进行自定义。spring-doc.cadn.net.cn

要在Java配置中注册自定义格式化程序和转换器,请使用以下方法:spring-doc.cadn.net.cn

@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 规范中定义的固定格式。对于此类情况,可以如下自定义日期和时间格式:spring-doc.cadn.net.cn

@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 SPIFormattingConversionServiceFactoryBean 以获取有关何时使用 FormatterRegistrar 实现的更多信息。

验证

默认情况下,如果Bean Validation存在于类路径上(例如,Hibernate Validator),则LocalValidatorFactoryBean会被注册为全局验证器,用于@Valid@Validated@Controller方法参数上使用。spring-doc.cadn.net.cn

在你的Java配置中,你可以自定义全局Validator实例, 如下例所示:spring-doc.cadn.net.cn

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

}
@Configuration
class WebConfig : WebFluxConfigurer {

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

}

请注意,您还可以在本地注册 Validator 个实现, 如下例所示:spring-doc.cadn.net.cn

@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配置中的那个使用@Fallback进行标记,以避免冲突。

内容类型解析器

您可以配置Spring WebFlux如何从请求中确定@Controller实例的请求媒体类型。默认情况下,仅检查Accept标头,但您还可以启用基于查询参数的策略。spring-doc.cadn.net.cn

以下示例展示了如何自定义请求的内容类型解析:spring-doc.cadn.net.cn

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

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

HTTP消息编解码器

以下示例展示了如何自定义请求和响应体的读取和写入方式:spring-doc.cadn.net.cn

@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 提供了一组默认的读取器和写入器。您可以使用它来添加更多的读取器和写入器,自定义默认的读取器和写入器,或完全替换默认的读取器和写入器。spring-doc.cadn.net.cn

对于Jackson,考虑使用Jackson格式特定的构建器,如ObjectMapper来配置Jackson的默认属性。spring-doc.cadn.net.cn

视图解析器

以下示例展示了如何配置视图解析:spring-doc.cadn.net.cn

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

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

The ViewResolverRegistry 为视图技术提供了快捷方式,这些技术与Spring Framework集成。以下示例使用了FreeMarker(这也需要配置底层的FreeMarker视图技术):spring-doc.cadn.net.cn

@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实现,如下例所示:spring-doc.cadn.net.cn

@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的任何可用编解码器。以下示例展示了如何进行配置:spring-doc.cadn.net.cn

@Configuration
public class WebConfig implements WebFluxConfigurer {


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

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

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


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

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

	// ...
}

请参阅 视图技术 了解更多关于与 Spring WebFlux 集成的视图技术。spring-doc.cadn.net.cn

静态资源

此选项提供了一种方便的方式来从基于Resource的列表位置提供静态资源。spring-doc.cadn.net.cn

在下一个示例中,给定一个以 /resources 开头的请求,相对路径用于相对于类路径上的 /static 查找并提供静态资源。资源以一年后的过期时间提供,以确保浏览器缓存的最大使用,并减少浏览器发出的 HTTP 请求。还对 Last-Modified 头进行评估,如果存在,则返回 304 状态码。下面的列表显示了示例:spring-doc.cadn.net.cn

@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 实现, 这些可以用来创建一个工具链,用于处理优化后的资源。spring-doc.cadn.net.cn

您可以使用 VersionResourceResolver 用于基于 MD5 哈希计算的版本化资源 URL,该哈希从内容、固定的应用程序版本或其他信息中计算得出。一个 ContentVersionStrategy(MD5 哈希)通常是不错的选择,但有一些显著的例外(例如与模块加载器一起使用的 JavaScript 资源)。spring-doc.cadn.net.cn

以下示例展示了如何在您的Java配置中使用VersionResourceResolverspring-doc.cadn.net.cn

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

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

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

对于 WebJars,带版本的 URL 如 /webjars/jquery/1.2.0/jquery.min.js 是推荐且最有效的方法。 相关的资源位置通过 Spring Boot 默认配置(或可以通过 ResourceHandlerRegistry 手动配置),不需要添加 org.webjars:webjars-locator-lite 依赖项。spring-doc.cadn.net.cn

类似/webjars/jquery/jquery.min.js这样的无版本URL是通过 WebJarsResourceResolver支持的,该插件会在 org.webjars:webjars-locator-lite库存在于类路径上时自动注册。解析器可以重写URL以包含jar的版本号,也可以匹配不带版本号的传入URL — 例如,从/webjars/jquery/jquery.min.js/webjars/jquery/1.2.0/jquery.min.jsspring-doc.cadn.net.cn

基于ResourceHandlerRegistry的Java配置提供了更进一步的选项,以便进行细粒度控制,例如,最后修改行为和优化资源解析。

路径匹配

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

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

Spring WebFlux 也不支持后缀模式匹配,与 Spring MVC 不同,在 Spring MVC 中我们还推荐远离对它的依赖。spring-doc.cadn.net.cn

API 版本

要启用API版本控制,请使用ApiVersionConfigurer回调中的WebFluxConfigurer:spring-doc.cadn.net.cn

@Configuration
public class WebConfiguration implements WebFluxConfigurer {

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

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

您可以通过以下列出的内置选项之一解析版本,或者使用自定义的ApiVersionResolver:spring-doc.cadn.net.cn

要从路径段解析版本号,您需要指定预期包含版本号的路径段的索引。路径段必须声明为URI变量,例如"/{version}", "/api/{version}"等,实际名称并不重要。由于版本号通常位于路径起始位置,建议通过 路径匹配选项将其作为所有处理器的公共路径前缀在外部配置。spring-doc.cadn.net.cn

默认情况下,版本号会被解析为SemanticVersionParser,但您也可以配置自定义的ApiVersionParserspring-doc.cadn.net.cn

为了方便起见,支持的版本会从请求映射中声明的版本自动检测,但您可以通过WebFlux配置中的标志关闭此功能,并仅考虑在配置中明确设置为支持的版本。 对于使用不支持版本的请求,将拒绝该请求并返回状态码400响应。spring-doc.cadn.net.cn

您可以设置ApiVersionDeprecationHandler以向客户端发送关于废弃版本的信息。内置的标准处理器可以根据RFC 9745RFC 8594设置“废弃”、“日落”和“链接”头信息。spring-doc.cadn.net.cn

一旦API版本控制配置完成,您就可以开始根据请求版本将请求映射到 控制器方法spring-doc.cadn.net.cn

阻塞执行

WebFlux Java 配置允许您自定义 WebFlux 中的阻塞执行。spring-doc.cadn.net.cn

您可以通过提供一个 AsyncTaskExecutor(例如以下的 VirtualThreadTaskExecutor) 在单独的线程上调用阻塞的控制器方法:spring-doc.cadn.net.cn

@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 设置自定义的控制器方法断言。spring-doc.cadn.net.cn

WebSocketService

WebFlux Java配置声明了一个WebSocketHandlerAdapter bean,该bean支持WebSocket处理程序的调用。这意味着要处理WebSocket握手请求,只需通过SimpleUrlHandlerMappingWebSocketHandler映射到URL即可。spring-doc.cadn.net.cn

在某些情况下,可能需要使用提供的WebSocketService服务来创建WebSocketHandlerAdapter bean,这允许配置WebSocket服务器属性。 例如:spring-doc.cadn.net.cn

@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 如下:spring-doc.cadn.net.cn

对于高级模式,你可以移除 @EnableWebFlux 并直接从 DelegatingWebFluxConfiguration 扩展,而不是实现 WebFluxConfigurer, 如下例所示:spring-doc.cadn.net.cn

@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {

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

	// ...
}

你可以保留现有的方法在WebConfig中,但现在你也可以覆盖基类中的bean声明,并且仍然可以在类路径上有任何数量的其他WebMvcConfigurer实现。spring-doc.cadn.net.cn