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在解析和格式化日期值时会考虑请求的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 SPI和FormattingConversionServiceFactoryBean。
验证
默认情况下,如果类路径中存在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,可以考虑使用特定于Jackson格式的构建器(如JsonMapper.Builder)来配置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
}
}
- Java
- Kotlin
@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集成的视图技术的更多信息,请参阅View Technologies。
静态资源
此选项提供了一种便捷的方式,可以从基于资源的列表位置提供静态资源。
在下一个示例中,给定一个以 /resources 开头的请求,会使用相对路径来查找并提供相对于类路径(classpath)上 /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))
}
}
另请参阅静态资源的HTTP缓存支持。
资源处理器还支持一系列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来说,像 /webjars/jquery/1.2.0/jquery.min.js 这样的带版本号的URL是推荐且最有效的使用方式。Spring Boot默认已经配置好了相关的资源定位(也可以通过 ResourceHandlerRegistry 手动配置),因此无需额外添加 org.webjars:webjars-locator-lite 依赖。
像 /webjars/jquery/jquery.min.js 这样的无版本号 URL 可以通过 WebJarsResourceResolver 来支持,当类路径(classpath)中存在 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:
- 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中我们推荐逐渐不再依赖于这种模式匹配。
API版本
要启用API版本控制,请使用WebFluxConfigurer的ApiVersionConfigurer回调:
- Java
- Kotlin
@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:
- 请求头
- 请求参数
- 路径段
- 媒体类型参数
要从路径段中解析出版本号,你需要指定该路径段中预期包含版本号的索引。该路径段必须被声明为一个URI变量,例如“/{version}”、“/api/{version}”等,其中具体的名称并不重要。由于版本号通常位于路径的起始位置,可以考虑通过路径匹配选项将其外部配置为所有处理器的通用路径前缀。
默认情况下,版本号是使用SemanticVersionParser来解析的,但你也可以配置一个自定义的ApiVersionParser。
为方便起见,支持的版本会通过请求映射中声明的版本来自动检测,但你可以通过WebFlux配置中的一个标志来关闭这一功能,从而只将配置文件中明确指定的版本视为受支持的版本。对于不支持的版本请求,将会抛出InvalidApiVersionException异常,导致返回400响应。
一旦配置了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识别的控制器方法被视为阻塞(blocking)方法,但您可以通过BlockingExecutionConfigurer设置自定义的控制器方法判断条件。
WebSocketService
WebFlux Java 配置声明了一个 WebSocketHandlerAdapter bean,该 bean 提供了对 WebSocket 处理器调用的支持。这意味着,为了处理 WebSocket 握手请求,所要做的就是通过 SimpleUrlHandlerMapping 将一个 WebSocketHandler 映射到一个 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 实现。