SpringBoot源码解读与原理分析(三十八)SpringBoot整合WebFlux(一)WebFlux的自动
- 电脑硬件
- 2025-08-03 06:12:02

文章目录 前言第13章 SpringBoot整合WebFlux13.1 响应式编程与Reactor13.1.1 命令式与响应式13.1.2 异步非阻塞13.1.3 观察者模式13.1.4 响应性13.1.5 响应式流13.1.6 背压13.1.7 Reactor13.1.7.1 Publisher13.1.7.2 Subscriber13.1.7.3 Subscription13.1.7.4 Processor13.1.7.5 Flux13.1.7.6 Mono13.1.7.7 Scheduler 13.2 SpringBoot整合WebFlux示例项目13.2.1 WebMvc的开发风格13.2.2 过渡到WebFlux13.2.3 WebFlux的函数式开发12.2.3.1 Controller转Handler12.2.3.2 RequestMapping转Router 13.2.4 WebMvc和WebFlux的对比 13.3 WebFlux的自动装配13.3.1 ReactiveWebServerFactoryAutoConfiguration13.3.2 WebFluxAutoConfiguration13.3.3 WebFluxConfig13.3.3.1 静态资源映射13.3.3.2 视图解析器 13.3.4 EnableWebFluxConfiguration13.3.5 WebFluxConfigurationSupport13.3.5.1 DispatcherHandler13.3.5.2 WebExceptionHandler13.3.5.3 RequestMappingHandlerMapping、RequestMappingHandlerAdapter13.3.5.4 RouterFunctionMapping13.3.5.5 HandlerFunctionAdapter13.3.5.6 ResultHandler 前言
SpringFramework 5.x中对于Web场景的开发提供了两套实现方案:WebMvc与WebFlux。
SpringBoot整合WebMvc基于Servlet,本质上是阻塞的,每个连接都会占用一个线程,因此基于Servlet的阻塞式Web框架在面对海量请求时,性能上没有优势。
为了解决该问题,SpringFramework 5.0版本后引入了WebMvc的孪生兄弟WebFlux,它是一个异步非阻塞式Web框架。
第13章 SpringBoot整合WebFlux 13.1 响应式编程与Reactor 13.1.1 命令式与响应式 命令式编程在基于WebMvc的项目开发中,通过编写Controller前端控制器,注入Service业务逻辑类进行处理,Service中包含与数据库的交互、与中间件的通信等,这种编码风格就是命令式编程。
使用命令式编程的代码,就像是一组前后紧密联系的任务,有明确的先后执行顺序,后面的任务通常需要依赖前面的任务生成的结果才能正确执行。
命令式编程的特点是串行、阻塞。
响应式编程响应式编程不再将这些紧密联系的任务看作一个整体,而是将其拆分为一个个可以并行执行的工作任务,这些任务之间互不干扰。
每个工作任务都可以接收特定的数据,并在处理完成后传递给下一个任务,同时继续处理下一组数据。
在响应式编程中,每个任务不会主动获取数据,而是被动地等待数据提供方给它提供数据,即主张数据以订阅的方式推送(被动接收),而不是以请求的方式拉取(主动获取)。
13.1.2 异步非阻塞使用一个非常经典的故事来解释异步非阻塞。
假设有一个老张烧水的场景,老张有两把烧水壶,分别是没有哨的普通水壶以及壶盖上带哨的响水壶。烧水的场景包含以下4种:
同步阻塞式:使用普通水壶烧水,由于不清楚水烧开的时间,因此需要老张在水壶旁观察,等到水壶冒热气,壶里的水沸腾,老张将水壶离火,烧水结束。在该场景中,由于老张在烧水期间无法完成其他工作,只能等待水烧开,烧水占据了老张的注意力和时间,构成同步阻塞。同步非阻塞:经过上一次烧水后,老张发现烧水太浪费自己的时间,于是下一次烧水时老张选择同时打游戏,每隔一小段时间就去看一下水壶里的水是否烧开,如果水还没有烧开就继续打游戏,水烧开则将水壶离火,烧水结束。在该场景中,老张没有一直盯着水壶,但还是会间歇性消耗精力,只不过在整个烧水的过程中,老张没有一直被水壶占用全部精力和时间,构成同步非阻塞。异步阻塞式:间歇性观察水壶仍然不是最佳选择,老张选择使用响水壶烧水,但由于第一次使用响水壶烧水,老张不确定水壶上的哨是否好用,于是他像第一次烧水那样在水壶旁观察,等到水壶冒热气,同时哨声响起,老张将水壶离火,烧水结束。在该场景中老张不再主动关心水壶的状态,但精力和时间仍然被水壶占用,构成异步阻塞。异步非阻塞:烧完水后老张发现自己很傻,因为哨声响起就意味着水已烧开,无须自己消耗精力和时间,于是后续烧水时,老张都是准备好后直接去打游戏,等到水壶哨声响起,再将水壶离火,烧水结束。在最终的场景中,老张不再主动关心水壶状态,也不需要间歇性检查水壶内水的状态,而只需要在水壶的哨声响起时处理水壶离火的任务,此场景就是异步非阻塞。 13.1.3 观察者模式观察者模式也被称为“发布——订阅者模式”或“监听器模式”。当一个对象被修改/做出某些反应/发布一个信息时,会自动通知依赖它的对象(订阅者)。
观察者模式的三大核心是观察者、被观察的主题和订阅者。观察者(Observer)需要绑定订阅者(Subscriber),并且要观察指定的主题(Subject)。
13.1.4 响应性如上图,在Excel表格中,A2单元格的值是通过公式=A0+A1来定义的,因此最终得到的值是2。如果试着更改A0或A1单元格的值,A2单元格的值也会自动更新。这就是响应性的体现。
A2单元格像是在“观察”着A0和A1单元格中输入的值,当A0或A1单元格中输入的值发生变化时,A2单元格的值也会随之变化,这本身就是观察者模式的体现。由此引出响应式编程的第一个关键概念:变化传递。即A0或A1单元格中输入的值发生变化时,这些变化的值会传递到A2单元格中。在实际使用中,每修改一次A0或A1单元格的值,A2单元格的值就会随之变化,如果将这组变化的内容全部列举,可以形成一组单元格内容的变化事件记录。由此可以引出响应式编程的第二个关键概念:数据流。事件源的每一次变化连起来就是一个事件流。在上面的Excel示例中,仅仅通过一个公式就将绑定了A2单元格和A0、A1单元格的关系。由此可以引出响应式编程的第三个关键概念:声明式。不需要编写命令式代码,仅靠声明两者之间的关系就可以形成双向绑定。简单总结,响应式编程的三个关键点是变化传递、数据流和声明式。
13.1.5 响应式流响应式流有别于Java8中的Stream。普通的Stream是同步阻塞的,在高并发场景下不能有效缓解压力大的问题,而响应式流是异步非阻塞的。
普通的Stream的一个关键特性是,一旦有了消费型方法,它就会将这个流中的所有数据处理完毕,如果这期间的数据量很大,Stream就无法对海量数据进行妥善处理;而响应式流可以通过背压对海量数据进行流量控制,以确保数据的接收速度和处理在合理范围内。
简单总结,响应式流的关键点是异步非阻塞和数据流速控制。
13.1.6 背压**背压是控制数据流速的关键手段。**下面以一个模拟场景来解释:
假设你在一个知名手机生产大厂工作,你的职位是生产流水线上的一名普通工人,你的工作是负责流水线上的一个关键环节。该环节需要的加工时间比较长,而恰好近期与你共同负责相同工作的同事都请假了,剩下你单枪匹马仍然战斗在生产一线。
与此同时,负责你上游工作的同事似乎并不清楚你负责环节的生产现状,而且由于上司的激励政策,上游同事的生产效率非常高,导致你的待加工区积压了非常多半成品,但由于你负责的工序耗时长,积压的半成品过多无法及时处理,于是你不得不向上游同事反馈:你们做慢点,我的工作吞吐量有限。上游同事了解你的现状后改变了半成品处理策略,他们将处理好的半成品不直接传递给你,而暂时由上游同事保管,等你向他们反馈积压的半成品处理完毕后,再继续传递新的半成品。
由此可以体现背压的第一个策略:数据提供方将数据暂存,不传递给下游消费者。
一段时间之后,领导发现你的业绩非常好,于是你升职加薪,以经销商的身份销售该款手机。手机一上市就得到广大消费者的关注,你的店铺生意非常好。正当你的生意做得风生水起时,这批手机在售卖后的一段时间后传出硬件问题,市面销量急剧下降,作为经销商,你自然也不想再销售该款手机,于是你向厂商反映:请不要再提供该款手机。厂商也非常无奈,手机还在正常生产,但经销商都不再提货,于是只好将这部分成品废弃。
由此可以体现出背压的第二个策略:数据提供方将数据丢弃。
简单总结,背压是下游消费者“倒逼”上游数据生产者的数据提供速率,以避免被海量数据压垮,达到两者之间的动态平衡。
13.1.7 Reactor市面上流行的响应式编程框架包括Reactor与ReactiveX(RxJava)。WebFlux底层使用Reactor提供响应式支撑。
Reactor的核心组件如下:
13.1.7.1 Publisher 源码1:Publisher.java public interface Publisher<T> { public void subscribe(Subscriber<? super T> s); }由 源码1 可知,数据生产者Publisher只有一个方法subscribe,该方法会接收一个订阅者Subscriber,构成“订阅”关系。
注意,subscribe方法是一个类似于“工厂”的方法,它可以被多次调用,但是每次调用都会创建一个新的订阅关系,且一个订阅关系只能关联一个订阅者。
13.1.7.2 Subscriber 源码2:Subscriber.java public interface Subscriber<T> { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); }由 源码2 可知,数据订阅者Subscriber接口有4个方法,都是以on作为前缀,代表这些方法属于事件形式。
onSubscribe:当触发订阅时触发;onNext:当接收到下一个数据时触发;onError:当出现异常时触发;onComplete:当生产者的数据都处理完时触发。 13.1.7.3 Subscription 源码3:Subscription.java public interface Subscription { public void request(long n); public void cancel(); }Subscription可以看作是生产者和订阅者之间的订阅关系,完成了两者之间的交互。由 源码2 可知,订阅关系Subscription接口有2个方法:
request:用于主动请求数据/拉取数据;cancel:用于放弃/停止拉取数据。 13.1.7.4 Processor 源码4:Processor.java public interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }Processor可以理解为处理器,一般用于数据的中间环节处理(如数据转换、数据过滤等)。由 源码4 可知,Processor接口继承了Subscriber接口和Publisher接口,是生产者和订阅者的合体。
13.1.7.5 Flux 源码5:Flux.java public abstract class Flux<T> implements CorePublisher<T> {...}**Flux可以理解为“非阻塞的Stream”。**由 源码5 和上图可知,它实现了Publisher接口,内部定义了很多subscribe方法。重载这么多个subscribe方法的目的在于简化操作。
13.1.7.6 Mono 源码6:Mono.java public abstract class Mono<T> implements CorePublisher<T> {...}**Flux可以理解为“非阻塞的Optional”。**它也实现了Publisher接口,具备生产数据的能力,内部API和Flux相似。
13.1.7.7 SchedulerScheduler可以理解为“线程池”,由Schedulers工具类产生。
响应式线程池有以下几种类型:
immediate:与主线程一致。single:只有一个线程的线程池。elastic:弹性线程池,线程池中的线程数量原则上无上限。parallel:并性线程池,线程池中的线程数量等于CPU的数量(JDK中的Runtime类可以调用avaliableProcessors方法来获取CPU梳理)。 13.2 SpringBoot整合WebFlux示例项目WebMvc和WebFlux是地位同等的框架,因此SpringBoot为了避免开发者因WebFlux的使用门槛过高而放弃,在WebFlux的使用过程中允许采用WebMvc的开发风格,即使用@Controller+@RequestMapping注解组合实现基于WebFlux的前端控制和响应。
13.2.1 WebMvc的开发风格 导入WebFlux依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> 编写主启动类 @SpringBootApplication public class WebFluxApp { public static void main(String[] args) { SpringApplication.run(WebFluxApp.class, args); } }这时就可以启动项目了,启动后控制台输出以下信息:
Starting WebFluxApp on DESKTOP-VTHK7VU with PID 14732 (D:\learnspace\workspace\java_src\springboot-demo\springboot-08-webflux\target\classes started by win10 in D:\learnspace\workspace\java_src\springboot-demo) No active profile set, falling back to default profiles: default Netty started on port(s): 8080 Started WebFluxApp in 3.657 seconds (JVM running for 5.44)可以发现,项目启动的嵌入式Web容器不再是Tomcat,而是Netty。
编写Controller类 @RestController public class UserController { @GetMapping("/hello") public String hello() { return "Hello WebFlux!"; } @GetMapping("/list") public List<Integer> list() { return Arrays.asList(1, 2, 3); } }编写完成后启动项目,使用工具访问 http://127.0.0.1:8080/hello和 /list,客户端可以正常接收到服务端的 “Hello WebFlux!” 字符串响应,说明WebFlux可以完美兼容WebMvc的编码方式。
13.2.2 过渡到WebFluxReactor中核心数据的封装模型是Mono和Flux,下面使用这两个模型对UserController进行改造。当返回单个对象时,使用Mono封装;当返回一组数据时,使用Flux封装。
@RestController public class UserController { @GetMapping("/hello2") public Mono<String> hello2() { return Mono.just("Hello WebFlux!"); } @GetMapping("/list2") public Flux<Integer> list2() { return Flux.just(1, 2, 3); } }重新启动项目,并访问 http://127.0.0.1:8080/hello2和 /list2,客户端仍然可以正常接收到服务端响应的正常数据,说明Reactor中的数据模型作为响应主体完全可行。
13.2.3 WebFlux的函数式开发如果要完全丢弃WebMvc的编码风格,则需要使用WebFlux提供的一套全新的函数式API。
在WebMvc中,一个Controller类中标注了@RequestMapping注解的方法在底层会封装为一个个Handler,每个Handler都封装有URL+执行方法以及具体要反射执行的Method对象。这两个核心要素在WebFlux的编码风格中会转换为两个核心组件:HandlerFunction和RouterFunction。
12.2.3.1 Controller转HandlerWebFlux的编码风格不再使用@Controller注解,而是使用原始的@Component注解,且内部的方法不再需要多余的注解,只需要按照WebFlux的规则编写方法。
@Component public class UserHandler { public Mono<ServerResponse> hello3(ServerRequest request) { return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN) .body(Mono.just("Hello Handler"), String.class); } public Mono<ServerResponse> list3(ServerRequest request) { return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON) .body(Flux.just(1, 2, 3), Integer.class); } } 12.2.3.2 RequestMapping转Router由于UserHandler中不再有@Controller注解,因此方法上也不再使用@RequestMapping注解封装URL信息,因此Spring无法感知IOC容器中哪些bean对象具备WebFlux前端控制器的能力,这就需要一个新的组件来定义Bean与具体路由的关系,这个组件就是RouterFunction。
在编写具体的路由规则时,需要一个配置类来编程式创建RouterFunction对象:
@Configuration(proxyBeanMethods = false) public class UserRouterConfig { @Autowired private UserHandler userHandler; @Bean public RouterFunction<ServerResponse> helloRouter() { return RouterFunctions.route(GET("/hello3").and(accept(MediaType.TEXT_PLAIN)), userHandler::hello3) .andRoute(GET("/list3").and(accept(MediaType.APPLICATION_JSON)), userHandler::list3); } }至此,一个基于WebFlux编码风格的示例项目搭建完毕。启动项目,并访问 http://127.0.0.1:8080/hello3和 /list3,客户端仍然可以正常接收到服务端响应的正常数据,说明基于WebFlux编码风格的示例项目搭建成功。
13.2.4 WebMvc和WebFlux的对比 WebMvc基于原生Servlet,它是命令式编程+声明式映射,编码简单、便于调试;Servlet是阻塞的,更适合与传统关系型数据库等阻塞I/O的组件进行交互。WebFlux基于Reactor,它是异步非阻塞的,使用函数式编程,相较于命令式编程更加灵活,可以运行在Netty等纯异步非阻塞的Web容器,以及同时支持同步阻塞和异步非阻塞的基于Servlet 3.1及以上规范的Servlet容器中(如高版本的Tomcat等)。WebMvc和WebFlux都可以使用声明式映射注解编程,配置控制器和映射路径。在实际的项目技术选型中,需要综合考虑项目中使用的技术栈、用户群规模、开发团队能力等多方面因素,决定是采用WebMvc还是WebFlux。
13.3 WebFlux的自动装配WebFlux的自动装配类似于WebMvc,对应的自动配置类是ReactiveWebServerFactoryAutoConfiguration和WebFluxAutoConfiguration。
13.3.1 ReactiveWebServerFactoryAutoConfiguration 源码7:ReactiveWebServerFactoryAutoConfiguration.java @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) @ConditionalOnClass(ReactiveHttpInputMessage.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @EnableConfigurationProperties(ServerProperties.class) @Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class, ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class, ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class, ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class }) public class ReactiveWebServerFactoryAutoConfiguration由 源码7 可知,该自动配置类使用@Import注解导入的核心配置类是BeanPostProcessorsRegistrar和几个嵌入式Web容器类。
与WebMvc的自动配置类ServletWebServerFactoryAutoConfiguration相比,导入的嵌入式Web容器多了一个Netty。
源码8:ReactiveWebServerFactoryConfiguration.java @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(ReactiveWebServerFactory.class) @ConditionalOnClass({HttpServer.class}) static class EmbeddedNetty { @Bean @ConditionalOnMissingBean ReactorResourceFactory reactorServerResourceFactory() { return new ReactorResourceFactory(); } @Bean NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory, ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers) { NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory(); serverFactory.setResourceFactory(resourceFactory); routes.orderedStream().forEach(serverFactory::addRouteProviders); serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList())); return serverFactory; } }由 源码8 可知,EmbeddedNetty中注册的Bean包括NettyReactiveWebServerFactory和ReactorResourceFactory。
NettyReactiveWebServerFactory会在IOC容器的初始化阶段创建嵌入式Netty容器。
ReactorResourceFactory是一个可以管理Reactor Netty资源的工厂,这个设计类似于线程池。
源码9:ReactorResourceFactory.java public class ReactorResourceFactory implements InitializingBean, DisposableBean { // ...... private Supplier<ConnectionProvider> connectionProviderSupplier = () -> ConnectionProvider.fixed("webflux", 500); //...... } 源码10:ConnectionProvider.java static ConnectionProvider fixed(String name, int maxConnections) { return fixed(name, maxConnections, DEFAULT_POOL_ACQUIRE_TIMEOUT); } static ConnectionProvider fixed(String name, int maxConnections, long acquireTimeout) { return fixed(name, maxConnections, acquireTimeout, null, null); } static ConnectionProvider fixed(String name, int maxConnections, long acquireTimeout, @Nullable Duration maxIdleTime, @Nullable Duration maxLifeTime) { // ...... return builder(name).maxConnections(maxConnections) .pendingAcquireMaxCount(-1) // keep the backwards compatibility .pendingAcquireTimeout(Duration.ofMillis(acquireTimeout)) .maxIdleTime(maxIdleTime) .maxLifeTime(maxLifeTime) .build(); } public ConnectionProvider build() { return new PooledConnectionProvider(this); }由 源码9-10 可知,ReactorResourceFactory内部组合了一个ConnectionProvider,它会初始化一个最大连接数为500的连接池,其落地实现类为PooledConnectionProvider。由此可以理解ReactorResourceFactory就是一个Reactor Netty的连接池。
13.3.2 WebFluxAutoConfiguration 源码11:WebFluxAutoConfiguration.java @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @ConditionalOnClass(WebFluxConfigurer.class) @ConditionalOnMissingBean({ WebFluxConfigurationSupport.class }) @AutoConfigureAfter({ ReactiveWebServerFactoryAutoConfiguration.class, CodecsAutoConfiguration.class, ValidationAutoConfiguration.class }) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) public class WebFluxAutoConfiguration由 源码11 可知,WebFluxAutoConfiguration生效的前提是当前项目的Web类型为REACTIVE(@ConditionalOnWebApplication注解),以及需要当前项目类路径下存在WebFluxConfigurer类(@ConditionalOnClass注解)。
在WebFluxAutoConfiguration的内部,有几个静态内部类根据不同功能和场景分别配置对应的组件。
13.3.3 WebFluxConfig 源码12:WebFluxAutoConfiguration.java @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties({ ResourceProperties.class, WebFluxProperties.class }) @Import({ EnableWebFluxConfiguration.class }) public static class WebFluxConfig implements WebFluxConfigurer {...}由 源码12 可知,WebFluxConfig类使用@Import注解导入了EnableWebFluxConfiguration。
WebFluxConfig类本身实现了WebFluxConfigurer接口,因此具备配置WebFlux的能力。
13.3.3.1 静态资源映射 源码13:WebFluxAutoConfiguration.java public static class WebFluxConfig implements WebFluxConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 前置检查 ...... if (!registry.hasMappingForPattern("/webjars/**")) { ResourceHandlerRegistration registration = registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); // ...... } String staticPathPattern = this.webFluxProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { ResourceHandlerRegistration registration = registry.addResourceHandler(staticPathPattern) .addResourceLocations(this.resourceProperties.getStaticLocations()); // ...... } } } 源码14:ResourceProperties.java private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; // ......由 源码13-14 可知,```addResourceHandlers``方法会默认配置几个常用的约定好的静态文件的存放位置:/resources、/static、/public、/webjars等等。这些路径下的静态文件是可以被直接引用的。
13.3.3.2 视图解析器 源码15:WebFluxAutoConfiguration.java public static class WebFluxConfig implements WebFluxConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { this.viewResolvers.orderedStream().forEach(registry::viewResolver); } }由 源码15 可知,WebFlux也支持视图跳转,底层也有视图解析器的配置。
13.3.4 EnableWebFluxConfigurationEnableWebFluxConfiguration与WebMvc中的EnableWebMvcConfiguration相似。
源码16:WebFluxAutoConfiguration.java @Configuration(proxyBeanMethods = false) public static class EnableWebFluxConfiguration extends DelegatingWebFluxConfiguration {...}由 源码16 可知,EnableWebFluxConfiguration继承了父类DelegatingWebFluxConfiguration。
EnableWebFluxConfiguration配置类种注册的组件包括:
FormattingConversionService:参数类型转换器。用于数据的类型转换,如日期与字符串之间的互相转换。Validator:JSR-303参数校验器。RequestMappingHandlerAdapter:标注了@RequestMapping注解的Handler的执行器。RequestMappingHandlerMapping:标注了@RequestMapping注解的Handler的处理器。 13.3.5 WebFluxConfigurationSupportEnableWebFluxConfiguration继承父类DelegatingWebFluxConfiguration,而DelegatingWebFluxConfiguration又集成父类WebFluxConfigurationSupport。
WebFluxConfigurationSupport类中也有一些核心组件的注册。
13.3.5.1 DispatcherHandler 源码18:WebFluxConfigurationSupport.java @Bean public DispatcherHandler webHandler() { return new DispatcherHandler(); }WebFlux中的核心前端控制器是DispatcherHandler,对应WebMvc中的DispatcherServlet。由 源码18 可知,DispatcherHandler组件的注册仅仅是创建一个新对象。
13.3.5.2 WebExceptionHandler 源码19:WebFluxConfigurationSupport.java @Bean @Order(0) public WebExceptionHandler responseStatusExceptionHandler() { return new WebFluxResponseStatusExceptionHandler(); }WebFlux中的异常状态响应器用于处理异常情况下的HTTP状态码响应,如 源码19 所示,其实现类是WebFluxResponseStatusExceptionHandler。
13.3.5.3 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 源码20:WebFluxConfigurationSupport.java @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); // ...... return mapping; } @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter( @Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry, ServerCodecConfigurer serverCodecConfigurer, @Qualifier("webFluxConversionService") FormattingConversionService conversionService, @Qualifier("webFluxValidator") Validator validator) { RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); // ...... return adapter; }因为WebFlux可以完美支持WebMvc中使用@RequestMapping注解的方式定义HAndler,支持这种方式的底层组件就是RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
13.3.5.4 RouterFunctionMapping 源码21:WebFluxConfigurationSupport.java @Bean public RouterFunctionMapping routerFunctionMapping(ServerCodecConfigurer serverCodecConfigurer) { RouterFunctionMapping mapping = createRouterFunctionMapping(); mapping.setOrder(-1); // 此处设置优先级高于RequestMappingHandlerMapping mapping.setMessageReaders(serverCodecConfigurer.getReaders()); mapping.setCorsConfigurations(getCorsConfigurations()); return mapping; }RouterFunctionMapping是基于函数式端点路由编程的Mapping处理器。由 源码21 可知,它的优先级高于RequestMappingHandlerMapping,这意味着WebFlux倾向于开发中使用函数式端点的Web开发,而不是传统的@RequestMapping注解式开发。
13.3.5.5 HandlerFunctionAdapter 源码22:WebFluxConfigurationSupport.java @Bean public HandlerFunctionAdapter handlerFunctionAdapter() { return new HandlerFunctionAdapter(); }HandlerFunctionAdapter是Handler方法的执行器。由 源码22 可知,对应的支撑组件是HandlerFunctionAdapter,它可以直接提取出HandlerFunction中的Handler方法进行调用。
13.3.5.6 ResultHandlerResultHandler是WebFlux中对返回值进行处理的组件,对应到WebMvc中则是HandlerMethodReturnValueHadnler。
默认情况下,WebFlux会注册4种不同的ResultHandler实现类。
ResponseEntityResultHandler:处理HttpEntity和ResponseEntity。 源码23:WebFluxConfigurationSupport.java @Bean public ResponseEntityResultHandler responseEntityResultHandler( @Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry, ServerCodecConfigurer serverCodecConfigurer, @Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) { return new ResponseEntityResultHandler(serverCodecConfigurer.getWriters(), contentTypeResolver, reactiveAdapterRegistry); } ResponseBodyResultHandler:处理@RequestMapping的标注了@ResponseBody注解的Handler。 源码24:WebFluxConfigurationSupport.java @Bean public ResponseBodyResultHandler responseBodyResultHandler( @Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry, ServerCodecConfigurer serverCodecConfigurer, @Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) { return new ResponseBodyResultHandler(serverCodecConfigurer.getWriters(), contentTypeResolver, reactiveAdapterRegistry); } ViewResolutionResultHandler:处理逻辑视图返回值。 源码25:WebFluxConfigurationSupport.java @Bean public ViewResolutionResultHandler viewResolutionResultHandler( @Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry, @Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) { ViewResolverRegistry registry = getViewResolverRegistry(); List<ViewResolver> resolvers = registry.getViewResolvers(); ViewResolutionResultHandler handler = new ViewResolutionResultHandler( resolvers, contentTypeResolver, reactiveAdapterRegistry); handler.setDefaultViews(registry.getDefaultViews()); handler.setOrder(registry.getOrder()); return handler; } ServerResponseResultHandler:处理返回值类型为ServerResponse的。 源码26:WebFluxConfigurationSupport.java @Bean public ServerResponseResultHandler serverResponseResultHandler(ServerCodecConfigurer serverCodecConfigurer) { List<ViewResolver> resolvers = getViewResolverRegistry().getViewResolvers(); ServerResponseResultHandler handler = new ServerResponseResultHandler(); handler.setMessageWriters(serverCodecConfigurer.getWriters()); handler.setViewResolvers(resolvers); return handler; }······
本节完,更多内容请查阅分类专栏:SpringBoot源码解读与原理分析
SpringBoot源码解读与原理分析(三十八)SpringBoot整合WebFlux(一)WebFlux的自动由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“SpringBoot源码解读与原理分析(三十八)SpringBoot整合WebFlux(一)WebFlux的自动”
下一篇
数据库的介绍、分类、作用和特点