跳到主要内容

WebFlux 配置

ChatGPT-4o 中英对照 WebFlux Config

WebFlux Java 配置声明了使用注解控制器或功能性端点处理请求所需的组件,并提供了一个 API 来自定义配置。这意味着你不需要了解由 Java 配置创建的底层 bean。不过,如果你想了解它们,可以在 WebFluxConfigurationSupport 中查看,或者在 特殊 Bean 类型 中阅读更多相关内容。

对于配置 API 中不可用的更高级自定义,您可以通过高级配置模式完全控制配置。

启用 WebFlux 配置

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

@Configuration
@EnableWebFlux
public class WebConfig {
}
java
备注

在使用 Spring Boot 时,您可能希望使用 WebFluxConfigurer 类型的 @Configuration 类,但不使用 @EnableWebFlux 以保留 Spring Boot WebFlux 的自定义设置。有关更多详细信息,请参见WebFlux 配置 API 部分专门的 Spring Boot 文档

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

WebFlux 配置 API

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

// Implement configuration methods...
}
java

转换,格式化

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

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

}
java

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
java
备注

请参阅 FormatterRegistrar SPIFormattingConversionServiceFactoryBean 以获取有关何时使用 FormatterRegistrar 实现的更多信息。

验证

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

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

}
java

注意,您也可以在本地注册 Validator 实现,如下例所示:

@Controller
public class MyController {

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

}
java
提示

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

内容类型解析器

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

下面的示例展示了如何自定义请求的内容类型解析:

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
// ...
}
}
java

HTTP 消息编解码器

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().maxInMemorySize(512 * 1024);
}
}
java

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

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

如果在类路径上检测到以下知名模块,它还会自动注册:

视图解析器

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// ...
}
}
java

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

@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;
}
}
java

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
ViewResolver resolver = ... ;
registry.viewResolver(resolver);
}
}
java

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

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

// ...
}
java

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

静态资源

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

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

@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));
}

}
java

资源处理器还支持一系列 ResourceResolver 实现和 ResourceTransformer 实现,这些可以用于创建一个处理优化资源的工具链。

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

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

}
java

您可以使用 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-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

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));
}
}
java
提示

Spring WebFlux 依赖于一个解析后的请求路径表示,称为 RequestPath,用于访问解码后的路径段值,并移除分号内容(即路径或矩阵变量)。这意味着,与 Spring MVC 不同,你无需指明是否解码请求路径,也无需指明是否移除分号内容以用于路径匹配。

Spring WebFlux 也不支持后缀模式匹配,这与 Spring MVC 不同,在 Spring MVC 中,我们也建议不再依赖它。

阻塞执行

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

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public void configureBlockingExecution(BlockingExecutionConfigurer configurer) {
AsyncTaskExecutor executor = ...
configurer.setExecutor(executor);
}
}
java

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

WebSocketService

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

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

@Override
public WebSocketService getWebSocketService() {
TomcatRequestUpgradeStrategy strategy = new TomcatRequestUpgradeStrategy();
strategy.setMaxSessionIdleTimeout(0L);
return new HandshakeWebSocketService(strategy);
}
}
java

高级配置模式

@EnableWebFlux 导入了 DelegatingWebFluxConfiguration,它:

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

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

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

@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {

// ...
}
java

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