前言
Spring 5发布有两年了,随Spring 5一起发布了一个和Spring WebMvc同级的Spring WebFlux。这是一个支持反应式编程模型的新框架体系。反应式模型区别于传统的MVC最大的不同是异步的、事件驱动的、非阻塞的,这使得应用程序的并发性能会大大提高,单位时间能够处理更多的请求。这里不讲WebFlux是怎么用的,有什么用,这类文章网上有太多了,而且都写的非常不错。下面主要看下WebFlux是怎么从无到有,框架怎么设计的,已期能够更灵活的使用WebFlux。
接口抽象
Spring最牛逼的地方就是,无论啥东西,都可以无缝的集成到Spring。这得益于Spring体系优良的抽象封装能力。WebFlux框架也一样,底层实现其实不是Spring的,它依赖reactor和netty等。Spring做的就是通过抽象和封装,把reactor的能力通过你最熟悉不过的Controller来使用。而且不局限于此,除了支持和Spring Mvc一样的控制器编码模式,还支持路由器模式(RouterFunctions),还支持端点模式(EndPoint)等。WebFlux所有功能其实内部只由几个抽象类构建而成:
- org.springframework.boot.web.reactive.server.ReactiveWebServerFactory
- org.springframework.boot.web.server.WebServer
- org.springframework.http.server.reactive.HttpHandler
- org.springframework.web.reactive.HandlerMapping
- org.springframework.web.server.WebHandler
WebServer
我们从最底层往上层剖析,WebServer见名之意,就是Reacive服务器的抽象类,它定义了服务的基本方法行为,包含启动,停止等接口。结构如下:
1public interface WebServer { 2 void start() throws WebServerException; 3 void stop() throws WebServerException; 4 int getPort(); 5}
Spring默认有五个WebServer的实现,默认的不特别指定情况下,在spring-boot-starter-webflux自带的是Netty的实现,其实现类如下:

ReactiveWebServerFactory
对应WebServer,每个实现都会有一个工厂类对应,主要准备创建WebServer实例的资源,如NettyReactiveWebServerFactory生产WebServer方法:
1 public WebServer getWebServer(HttpHandler httpHandler) { 2 HttpServer httpServer = createHttpServer(); 3 ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter( 4 httpHandler); 5 return new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout); 6 }
可以看到,在创建WebServer实例时,传入了一个入参,HttpHandler。而且进而传入了一个HttpHandlerAdapter实例里,这是因为每个WebServer的接收处理接口的适配器是不一样的,在每个不同的WebServer工厂里通过不过的适配器去适配不同的实现。最后转化成统一设计的HttpHandler里,见下面。
HttpHandler
接下来看下HttpHandler,上面在创建WebServer的时候,传了一个入参,类型就是Httphandler。为了适配不同的WebServer请求响应体,Spring设计了HttpHandler用来转化底层的Http请求响应语义,用来接收处理底层容器的Http请求。其结构如下:
1public interface HttpHandler { 2 Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response); 3}
如在Netty的实现中,Netty接收请求处理的适配器ReactorHttpHandlerAdapter的apply中转化的伪代码如下:
1public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) { 2 NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc()); 3 try { 4 ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory); 5 ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory); 6 7 if (request.getMethod() == HttpMethod.HEAD) { 8 response = new HttpHeadResponseDecorator(response); 9 } 10 11 return this.httpHandler.handle(request, response) 12 .doOnError(ex -> logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage())) 13 .doOnSuccess(aVoid -> logger.trace(request.getLogPrefix() + "Handling completed")); 14 } 15}
WebHandler
其实一般来讲设计到HttpHandler这一层级基本就差不多了,有一致的请求体和响应体了。但是Spring说还不够,对Web开发来讲不够简洁,就又造了一个WebHandler,WebHandler架构更简单,如下:
1public interface WebHandler { 2 Mono<Void> handle(ServerWebExchange exchange); 3}
这回够简洁了,只有一个入参,那请求提和响应体去哪里了呢?被包装到ServerWebExchange中了。我么看下当HttpHandler接收到请求后,是怎么处理然后在调用WebHandler的,最终处理HttpHandler实现是HttpWebHandlerAdapter.java,通过其内部的createExchange方法将请求和响应体封装在ServerWebExchange中了。其handle代码如下:
1 public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { 2 if (this.forwardedHeaderTransformer != null) { 3 request = this.forwardedHeaderTransformer.apply(request); 4 } 5 6 ServerWebExchange exchange = createExchange(request, response); 7 8 LogFormatUtils.traceDebug(logger, traceOn -> 9 exchange.getLogPrefix() + formatRequest(exchange.getRequest()) + 10 (traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : "")); 11 12 return getDelegate().handle(exchange) 13 .doOnSuccess(aVoid -> logResponse(exchange)) 14 .onErrorResume(ex -> handleUnresolvedError(exchange, ex)) 15 .then(Mono.defer(response::setComplete)); 16 }
HandlerMapping
首先看下HandlerMapping的构造,可以看到就是根据web交换器返回了一个Handler对象
1public interface HandlerMapping { 2 String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler"; 3 String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; 4 String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; 5 String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; 6 String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; 7 String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; 8 Mono<Object> getHandler(ServerWebExchange exchange); 9}
上面的“请求“已经到WebHandler了,那么最终是怎么到我们定义的控制器接口的呢?其实,没有HandlerMapping,Spring WebFlux的功能也是完整的,也是可编程的,因为可以基于WebHandler直接编码。我们最弄的一个网关最后就是直接走自定义的WebHandler,根本没有HandlerMapping的什么事情,但是你这么做的话就失去了Spring编码的友好性了。WebFlux的初始化过程中,会去Spring上下文中找name是“webHandler”的的WebHandler实现。默认情况下,Spring会在上下文中初始化一个DispatcherHandler.java的实现,Bean的name就是“webHandler”。这个里面维护了一个HandlerMapping列表,当请求过来时会迭代HandlerMapping列表,返回一个WebHandler处理,代码如下:
1 public Mono<Void> handle(ServerWebExchange exchange) { 2 if (this.handlerMappings == null) { 3 return Mono.error(HANDLER_NOT_FOUND_EXCEPTION); 4 } 5 return Flux.fromIterable(this.handlerMappings) 6 .concatMap(mapping -> mapping.getHandler(exchange)) 7 .next() 8 .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION)) 9 .flatMap(handler -> invokeHandler(exchange, handler)) 10 .flatMap(result -> handleResult(exchange, result)); 11 }
上面mapping的内部结构如下:

上面箭头指向的地方说明了为什么WebFlux支持控制器和路由器模式模式的编码,因为他们分别有实现的HandlerMapping,能够在WebHandler的handler里路由到具体的业务方法里。红框中正是通过@Controller和@ResultMaping定义的接口信息。
启动流程分析
上面介绍了五个主要的抽象接口定义,以及功能。这五个接口在Spring WebFlux里是灵魂一样的存在。不过想要彻底的搞懂Web Flux的设计以及实现原理,仅仅了解上面这些接口定义是远远不够的,看完上面接口的分析肯定有中模糊的似懂非懂的感觉,不着急,接下来分析下,在Spring Boot环境中,Spring WebFlux的启动流程。
ReactiveWebServerApplicationContext
WebFlux的启动都在Reactive的上下文中完成,和WebMvc类似,Mvc也有一个ServletWebServerApplicationContext,他们是同宗同脉的。ReactiveWebServerApplicationContext还有一个父类AnnotationConfigReactiveWebServerApplicationContext,在Spring boot启动中,创建的就是这个父类的实例。在Spring boot的run()方法中创建上下文时有如下代码:
1 protected ConfigurableApplicationContext createApplicationContext() { 2 Class<?> contextClass = this.applicationContextClass; 3 if (contextClass == null) { 4 try { 5 switch (this.webApplicationType) { 6 case SERVLET: 7 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); 8 break; 9 case REACTIVE: 10 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); 11 break; 12 default: 13 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); 14 } 15 } 16 catch (ClassNotFoundException ex) { 17 throw new IllegalStateException( 18 "Unable create a default ApplicationContext, " 19 + "please specify an ApplicationContextClass", 20 ex); 21 } 22 } 23 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); 24 }
可以看到,当webApplicationType是REACTIVE时,加载的就是DEFAULT_REACTIVE_WEB_CONTEXT_CLASS。webApplicationType类型是通过识别你加载了哪个依赖来做的。熟悉Spring启动流程的同学都知道,基础 的Spring上下文是在AbstractApplicationContext的refresh()方法内完成的,针对不同的上下文文实现实例还会有一个onRefresh()方法,完成一些特定的Bean的实例化,如WebFlux的上下文实例就在onRefresh()中完成了WebServer的创建:
1 protected void onRefresh() { 2 super.onRefresh(); 3 try { 4 createWebServer(); 5 } 6 catch (Throwable ex) { 7 throw new ApplicationContextException("Unable to start reactive web server", 8 ex); 9 } 10 } 11 private void createWebServer() { 12 ServerManager serverManager = this.serverManager; 13 if (serverManager == null) { 14 this.serverManager = ServerManager.get(getWebServerFactory()); 15 } 16 initPropertySources(); 17 }
文末
WebFlux里面启动流程太复杂,全盘脱出写的太长严重影响阅读体验。所以上面权当抛砖引玉,开一个好头。不过,WebFlux的启动流程节点博主都已分析并整理成流程图了,结合上面的接口设计分析,搞懂WebFlux的设计及工作原理应该冒点问题。

高清大图链接:https://www.processon.com/view/link/5d0763ede4b039f39f3b5a8a
作者简介:
陈凯玲,2016年5月加入凯京科技。现任凯京科技研发中心架构组经理,救火队队长。独立博客KL博客(http://www.kailing.pub)博主。