SpringBoot源码解读与原理分析(三十八)SpringBoot整合WebFlux(一)WebFlux的自动装配

文章目录

  • 前言
  • 第13章 SpringBoot整合WebFlux
    • 13.1 响应式编程与Reactor
      • 13.1.1 命令式与响应式
      • 13.1.2 异步非阻塞
      • 13.1.3 观察者模式
      • 13.1.4 响应性
      • 13.1.5 响应式流
      • 13.1.6 背压
      • 13.1.7 Reactor
        • 13.1.7.1 Publisher
        • 13.1.7.2 Subscriber
        • 13.1.7.3 Subscription
        • 13.1.7.4 Processor
        • 13.1.7.5 Flux
        • 13.1.7.6 Mono
        • 13.1.7.7 Scheduler
    • 13.2 SpringBoot整合WebFlux示例项目
      • 13.2.1 WebMvc的开发风格
      • 13.2.2 过渡到WebFlux
      • 13.2.3 WebFlux的函数式开发
        • 12.2.3.1 Controller转Handler
        • 12.2.3.2 RequestMapping转Router
      • 13.2.4 WebMvc和WebFlux的对比
    • 13.3 WebFlux的自动装配
      • 13.3.1 ReactiveWebServerFactoryAutoConfiguration
      • 13.3.2 WebFluxAutoConfiguration
      • 13.3.3 WebFluxConfig
        • 13.3.3.1 静态资源映射
        • 13.3.3.2 视图解析器
      • 13.3.4 EnableWebFluxConfiguration
      • 13.3.5 WebFluxConfigurationSupport
        • 13.3.5.1 DispatcherHandler
        • 13.3.5.2 WebExceptionHandler
        • 13.3.5.3 RequestMappingHandlerMapping、RequestMappingHandlerAdapter
        • 13.3.5.4 RouterFunctionMapping
        • 13.3.5.5 HandlerFunctionAdapter
        • 13.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中的响应性

如上图,在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
源码1Publisher.javapublic interface Publisher<T> {public void subscribe(Subscriber<? super T> s);
}

由 源码1 可知,数据生产者Publisher只有一个方法subscribe,该方法会接收一个订阅者Subscriber,构成“订阅”关系。

注意,subscribe方法是一个类似于“工厂”的方法,它可以被多次调用,但是每次调用都会创建一个新的订阅关系,且一个订阅关系只能关联一个订阅者。

13.1.7.2 Subscriber
源码2Subscriber.javapublic 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
源码3Subscription.javapublic interface Subscription {public void request(long n);public void cancel();
}

Subscription可以看作是生产者和订阅者之间的订阅关系,完成了两者之间的交互。由 源码2 可知,订阅关系Subscription接口有2个方法:

  • request:用于主动请求数据/拉取数据;
  • cancel:用于放弃/停止拉取数据。
13.1.7.4 Processor
源码4Processor.javapublic interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}

Processor可以理解为处理器,一般用于数据的中间环节处理(如数据转换、数据过滤等)。由 源码4 可知,Processor接口继承了Subscriber接口和Publisher接口,是生产者和订阅者的合体。

13.1.7.5 Flux
源码5Flux.javapublic abstract class Flux<T> implements CorePublisher<T> {...}

Flux定义了很多subscribe方法

**Flux可以理解为“非阻塞的Stream”。**由 源码5 和上图可知,它实现了Publisher接口,内部定义了很多subscribe方法。重载这么多个subscribe方法的目的在于简化操作。

13.1.7.6 Mono
源码6Mono.javapublic abstract class Mono<T> implements CorePublisher<T> {...}

**Flux可以理解为“非阻塞的Optional”。**它也实现了Publisher接口,具备生产数据的能力,内部API和Flux相似。

13.1.7.7 Scheduler

Scheduler可以理解为“线程池”,由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的编码方式

WebFlux可以完美兼容WebMvc的编码方式

13.2.2 过渡到WebFlux

Reactor中核心数据的封装模型是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中的数据模型作为响应主体完全可行。

Reactor中的数据模型作为响应主体

13.2.3 WebFlux的函数式开发

如果要完全丢弃WebMvc的编码风格,则需要使用WebFlux提供的一套全新的函数式API。

在WebMvc中,一个Controller类中标注了@RequestMapping注解的方法在底层会封装为一个个Handler,每个Handler都封装有URL+执行方法以及具体要反射执行的Method对象。这两个核心要素在WebFlux的编码风格中会转换为两个核心组件:HandlerFunction和RouterFunction。

12.2.3.1 Controller转Handler

WebFlux的编码风格不再使用@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 {@Autowiredprivate UserHandler userHandler;@Beanpublic 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编码风格的示例项目搭建成功。

基于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

源码7ReactiveWebServerFactoryAutoConfiguration.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。

源码8ReactiveWebServerFactoryConfiguration.java@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({HttpServer.class})
static class EmbeddedNetty {@Bean@ConditionalOnMissingBeanReactorResourceFactory reactorServerResourceFactory() {return new ReactorResourceFactory();}@BeanNettyReactiveWebServerFactory 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资源的工厂,这个设计类似于线程池。

源码9ReactorResourceFactory.javapublic class ReactorResourceFactory implements InitializingBean, DisposableBean {// ......private Supplier<ConnectionProvider> connectionProviderSupplier = () -> ConnectionProvider.fixed("webflux", 500);//......
}
源码10ConnectionProvider.javastatic 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

源码11WebFluxAutoConfiguration.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

源码12WebFluxAutoConfiguration.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 静态资源映射
源码13WebFluxAutoConfiguration.javapublic static class WebFluxConfig implements WebFluxConfigurer {@Overridepublic 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());// ......}}
}
源码14ResourceProperties.javaprivate 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 视图解析器
源码15WebFluxAutoConfiguration.javapublic static class WebFluxConfig implements WebFluxConfigurer {@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {this.viewResolvers.orderedStream().forEach(registry::viewResolver);}
}

由 源码15 可知,WebFlux也支持视图跳转,底层也有视图解析器的配置。

13.3.4 EnableWebFluxConfiguration

EnableWebFluxConfiguration与WebMvc中的EnableWebMvcConfiguration相似。

源码16WebFluxAutoConfiguration.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 WebFluxConfigurationSupport

EnableWebFluxConfiguration继承父类DelegatingWebFluxConfiguration,而DelegatingWebFluxConfiguration又集成父类WebFluxConfigurationSupport。

WebFluxConfigurationSupport类中也有一些核心组件的注册。

13.3.5.1 DispatcherHandler
源码18WebFluxConfigurationSupport.java@Bean
public DispatcherHandler webHandler() {return new DispatcherHandler();
}

WebFlux中的核心前端控制器是DispatcherHandler,对应WebMvc中的DispatcherServlet。由 源码18 可知,DispatcherHandler组件的注册仅仅是创建一个新对象。

13.3.5.2 WebExceptionHandler
源码19WebFluxConfigurationSupport.java@Bean
@Order(0)
public WebExceptionHandler responseStatusExceptionHandler() {return new WebFluxResponseStatusExceptionHandler();
}

WebFlux中的异常状态响应器用于处理异常情况下的HTTP状态码响应,如 源码19 所示,其实现类是WebFluxResponseStatusExceptionHandler。

13.3.5.3 RequestMappingHandlerMapping、RequestMappingHandlerAdapter
源码20WebFluxConfigurationSupport.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
源码21WebFluxConfigurationSupport.java@Bean
public RouterFunctionMapping routerFunctionMapping(ServerCodecConfigurer serverCodecConfigurer) {RouterFunctionMapping mapping = createRouterFunctionMapping();mapping.setOrder(-1);  // 此处设置优先级高于RequestMappingHandlerMappingmapping.setMessageReaders(serverCodecConfigurer.getReaders());mapping.setCorsConfigurations(getCorsConfigurations());return mapping;
}

RouterFunctionMapping是基于函数式端点路由编程的Mapping处理器。由 源码21 可知,它的优先级高于RequestMappingHandlerMapping,这意味着WebFlux倾向于开发中使用函数式端点的Web开发,而不是传统的@RequestMapping注解式开发。

13.3.5.5 HandlerFunctionAdapter
源码22WebFluxConfigurationSupport.java@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {return new HandlerFunctionAdapter();
}

HandlerFunctionAdapter是Handler方法的执行器。由 源码22 可知,对应的支撑组件是HandlerFunctionAdapter,它可以直接提取出HandlerFunction中的Handler方法进行调用。

13.3.5.6 ResultHandler

ResultHandler是WebFlux中对返回值进行处理的组件,对应到WebMvc中则是HandlerMethodReturnValueHadnler。

默认情况下,WebFlux会注册4种不同的ResultHandler实现类。

  • ResponseEntityResultHandler:处理HttpEntity和ResponseEntity。
源码23WebFluxConfigurationSupport.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。
源码24WebFluxConfigurationSupport.java@Bean
public ResponseBodyResultHandler responseBodyResultHandler(@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,ServerCodecConfigurer serverCodecConfigurer,@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {return new ResponseBodyResultHandler(serverCodecConfigurer.getWriters(),contentTypeResolver, reactiveAdapterRegistry);
}
  • ViewResolutionResultHandler:处理逻辑视图返回值。
源码25WebFluxConfigurationSupport.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的。
源码26WebFluxConfigurationSupport.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源码解读与原理分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/713687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcoder Day32| 贪心算法part05

763.划分字母区间 字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。 示例&#xff1a; 输入&#xff1a;S "ababcbacadefegdehijhklij"输出&#xff1a;[9,7…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 3月2日,星期六

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年3月2日 星期六 农历正月廿二 1、 气象局&#xff1a;3月份仍有5次冷空气影响我国&#xff1b;全国多地或提前入春。 2、 央行&#xff1a;将外籍来华人员移动支付单笔交易限额由1000美元提高到5000美元。 3、 神舟十七号航…

【源码】imx6ull实现触摸屏单点实验

一、本实验实验的器材&#xff1a; 1.正点原子imx6ull的阿尔法开发板v2.2 2.屏幕ALIENTEK 4.3 RGBLCD 二、实验已经移植好的文件&#xff1a; 仓库代码&#xff1a;https://gitee.com/wangyoujie11/atkboard_-linux_-driver.git 1.文件说明 23_multitouch &#xff1a;驱动代…

【YOLO v5 v7 v8 小目标改进】ODConv:在卷积核所有维度(数量、空间、输入、输出)上应用注意力机制来优化传统动态卷积

ODConv&#xff1a;在卷积核所有维度&#xff08;数量、空间、输入、输出&#xff09;上应用注意力机制来优化传统的动态卷积 提出背景传统动态卷积全维动态卷积效果 小目标涨点YOLO v5 魔改YOLO v7 魔改YOLO v8 魔改 论文&#xff1a;https://openreview.net/pdf?idDmpCfq6Mg…

leedcode刷题--day7(字符串)

23 文章讲解 力扣地址 C class Solution { public:void reverseString(vector<char>& s) {int left 0;int right s.size() - 1; // right 应该初始化为 s.size() - 1while (left < right) {swap(s[left], s[right]); // 直接交换 s[left] 和 s[right] 的值lef…

(学习日记)2024.02.29:UCOSIII第二节

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

WSL2外部网络设置

1 关闭所有WSL系统 wsl --shutdown 2 打开Hyper-V管理器 3 将“虚拟交换机管理器”-> ”WSL连接类型“设置为“外部网络” 4 启动WSL系统&#xff0c;手动修改WSL网络 将WSL网络IP修改为192.168.1.9 sudo ip addr del $(ip addr show eth0 | grep inet\b | awk {print $2} |…

FinalMLP:用于推荐系统的简单但强大的双流 MLP 模型

原文地址&#xff1a;FinalMLP: A Simple yet Powerful Two-Stream MLP Model for Recommendation Systems 了解 FinalMLP 如何转变在线推荐&#xff1a;通过尖端 AI 研究解锁个性化体验 2024 年 2 月 14 日 介绍 世界正在向数字时代发展&#xff0c;在这个时代&#xff0c;…

持安科技孙维伯:零信任在攻防演练下的最佳实践|DISCConf 2023

近日&#xff0c;在2023数字身份安全技术大会上&#xff0c;持安科技联合创始人孙维伯应主办方的特别邀请&#xff0c;发表了主题为“零信任在攻防演练下的最佳实践”的演讲。 孙维伯在2023数字身份安全技术大会上发表演讲 以下为本次演讲实录&#xff1a; 我是持安科技的联合…

【c++】 STL的组件简介与容器的使用时机

STL六大组件简介 STL提供了六大组件&#xff0c;彼此之间可以组合套用&#xff0c;这六大组件分别是:容器、算法、迭代器、仿函数、适配器&#xff08;配接器&#xff09;、空间配置器。 容器&#xff1a;各种数据结构&#xff0c;如vector、list、deque、set、map等,用来存放…

微信云开发-- Mac安装 wx-server-sdk依赖

第一次上传部署云函数时&#xff0c;会提示安装依赖wx-server-sdk 一. 判断是否安装wx-server-sdk依赖 先创建一个云函数&#xff0c;然后检查云函数目录。 如果云函数目录下只显示如下图所示三个文件&#xff0c;说明未安装依赖。 如果云函数目录下显示如下图所示四个文件&a…

EdgeX Foundry 边缘物联网中间件平台

文章目录 1.EdgeX Foundry2.平台架构3.平台服务3.1.设备服务3.2.核心服务3.3.支持服务3.4.应用服务3.5.安全服务3.6.管理服务 EdgeX Foundry # EdgeX Foundryhttps://iothub.org.cn/docs/edgex/ https://iothub.org.cn/docs/edgex/edgex-foundry/1.EdgeX Foundry EdgeX Found…

嵌入式 Linux 下的 LVGL 移植

目录 准备创建工程修改配置修改 lv_drv_conf.h修改 lv_conf.h修改 main.c修改 Makefile 编译运行更多内容 LVGL&#xff08;Light and Versatile Graphics Library&#xff09;是一个轻量化的、开源的、在嵌入式系统中广泛使用的图形库&#xff0c;它提供了一套丰富的控件和组件…

微软为金融界带来革命性突破——推出Microsoft 365中的下一代AI助手:Microsoft Copilot for Finance

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

雷龙CS SD NAND(贴片式TF卡)测评体验

前段时间有幸免费得到了雷龙出品的贴片式的TF卡的芯片及转接板&#xff0c;两片贴片式nand芯片&#xff0b;一个转接板&#xff0c;一种一个已让官方焊接完好&#xff1b;如下图所示&#xff1a; 正面&#xff1a; 背面&#xff1a; 通过转接板&#xff0c;可以将CS SD NAND(贴…

数电实验之流水灯、序列发生器

最近又用到了数电实验设计的一些操作和设计思想&#xff0c;遂整理之。 广告流水灯 实验内容 用触发器、组合函数器件和门电路设计一个广告流水灯&#xff0c;该流水灯由 8 个 LED 组成&#xff0c;工作时始终为 1 暗 7 亮&#xff0c;且这一个暗灯循环右移。 1) 写出设计过…

关于DisableIEToEdge插件闪退问题的解决方案

关于DisableIEToEdge插件闪退问题.今天终于测试找到最佳解决方案了&#xff01; 1.管理员权限运行Windows powershell. 2.执行一下两条命令修复系统环境 DISM.exe /Online /Cleanup-image /Restorehealth sfc /scannow 3.关闭Windows安全中心的所有安全选项。 4.管理员权限运行…

【计算机考研择校】四川大学vs电子科技大学哪个难度更大?

川大在文科&#xff0c;经管方面比科大强&#xff0c;医学在国内都很强。但工科方面特别是电子信息领域明显是科大强于川大。毕竟一个是综合大学&#xff0c;一个是工科大学不可同日而语。 就业上&#xff0c;电子科大在IT领域的社会声誉口碑不错。就业一向都很好。这个多问问…

张俊将出席用磁悬浮技术改变生活演讲

演讲嘉宾&#xff1a;张俊 空压机销售总监 亿昇(天津)科技有限公司 演讲题目&#xff1a;用磁悬浮技术改变生活 会议简介 “十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息化融合&#xff0c;明确“低碳经济”新的战略目标&#xff0c;热能产业是能源产业和…

Python环境下一种改进的基于梯度下降的自适应短时傅里叶变换

在数字信号处理技术中&#xff0c;傅里叶变换及其逆变换是一种信号时频分析方法。该方法将信号的时域描述及频域描述联系在一起&#xff0c;时域信号可通过正变换转变为频域信号&#xff0c;频域信号可通过逆变换转变为时域信号进行分析。但傅里叶变换及其逆变换是一种信号的整…