文章目录
- 前言
- 1. 注解模式请求处理
- 2. 函数式请求处理
- 2.1 使用示例
- 2.2 请求处理分析
- 2.2.1 请求处理分发类 DispatcherHandler
- 2.2.2 RouterFunction 路由匹配 HandlerFunction
- 2.2.3 HandlerFunction 处理请求
前言
在 Spring WebFlux源码分析(1)-服务启动流程中已经分析了从服务器启动到服务器将网络请求解析为 HttpRequest 和 HttpResponse 并将其封装为ServerWebExchange向上层传递的过程,接下来就是分析上层的处理逻辑。其实从整体来看,上层最重要的封装对象是根据 ServerWebExchange 生成的 ServerRequest 和 ServerResponse,上层的逻辑都是围绕这两个对象进行的。另外需注意,本文所有分析基于 spring-webflux-5.1.3 版本,笔者在 spring-webflux-5.2.0
版本中已经发现了少许的改变,不过并不影响主要流程,主要影响的是自定义的相关配置
1. 注解模式请求处理
该模式与 SpringMVC 在使用上毫无区别,主要流程可参考SpringMVC 源码分析(2)- 从注解@RequestBody开始,不作具体分析。以下为该模式下具体的请求处理流程图,需要注意其请求参数解析类改变为HttpMessageReader
,并且InvocableHandlerMethod
相关流程也有些许改变
2. 函数式请求处理
2.1 使用示例
- 编写类似 Controller 的处理器
CoolHandler
,需注意其方法定义要符合函数式接口HandlerFunction
的要求,入参为 ServerRequest,返回值为 ServerResponse 及其子类,这样才能将编写的处理方法转化为函数式接口的实现
@Component
public class CoolHandler {
public Mono<ServerResponse> sayHi(ServerRequest request) {
String reqParam = request.queryParams().getFirst("acct");
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue("Hi , this is SpringWebFlux. Got:" + reqParam));
}
}
- 编写类似
@RequestMapping
作用的配置类RouterConfig
配置路由。此处调用了RouterFunctions#route()
方法,将CoolHandler#sayHi()
方法绑定到了请求路径为“/hi” ,请求方法为 GET 的路由上。进入RouterFunctions#route()
方法,可看到其将这些信息封装在DefaultRouterFunction
对象之中,这个对象内部RequestPredicates 代表了路由信息,HandlerFunction< T>
自然就是对应的处理方法。之后当请求到来的时候,同样需要RouterFunctions
去匹配到与路由绑定的处理方法
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> routeCoolHandler(CoolHandler coolHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/hi")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), coolHandler::sayHi)
}
}
2.2 请求处理分析
Spring WebFlux 函数式的请求处理与注解式的主要流程相似,要注意的是函数式处理中有两个函数接口非常重要,分别是 RouterFunction
和 HandlerFunction
。RouterFunction 是路由映射的处理接口,负责根据服务器底层传递上来的 ServerRequest 来匹配能够处理请求的 HandlerFunction,而这个 HandlerFunction 就是开发人员编写的处理方法
2.2.1 请求处理分发类 DispatcherHandler
服务器的请求处理可参考 Spring WebFlux源码分析(1)-服务启动流程,WebFlux 中当请求到来时会进入分发的核心类 DispatcherHandler
,由DispatcherHandler#handle()
方法处理
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
// 1. 遍历 HandlerMapping 集合,匹配能够处理请求的 HandlerFunction
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
// 2. 执行目标处理方法
.flatMap(handler -> invokeHandler(exchange, handler))
// 3. 处理返回值
.flatMap(result -> handleResult(exchange, result));
}
2.2.2 RouterFunction 路由匹配 HandlerFunction
mapping.getHandler(exchange)
会根据传入的ServerWebExchange
对象去匹配我们编写的处理方法,需注意这个ServerWebExchange对象其实就是底层服务器收到网络请求后生成的 request 和 response 的封装。在函数式模式下,mapping.getHandler()
经由抽象类AbstractHandlerMapping#getHandler()
最终调用到了其子类实现RouterFunctionMapping#getHandlerInternal()
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
if (this.routerFunction != null) {
// 关键步骤,根据 ServerWebExchange 生成 ServerRequest ,以便之后调用 HandlerFounction 接口的实现时入参
ServerRequest request = ServerRequest.create(exchange, this.messageReaders);
// RouterFunction 根据 ServerRequest 路由匹配实现 HandlerFounction 的处理方法返回
return this.routerFunction.route(request)
.doOnNext(handler -> setAttributes(exchange.getAttributes(), request, handler));
}
else {
return Mono.empty();
}
}
ServerRequest.create(exchange, this.messageReaders)
最终创建的是DefaultServerRequest
对象,注意创建该对象时将 RouterFunctionMapping 中保存的 HttpMessageReader 列表作为参数传入,这样 DefaultServerRequest 对象就有了解析参数的能力 。与注解模式不同,函数式处理方法中不会自动将请求携带的参数解析为方法声明所要求的参数,而是将整个 ServerRequest 入参,需要手动调用 ServerRequest 相关方法才能将参数解析出来,这与上古时代基于原始的Servlet 开发直接将 HttpRequest 入参如出一辙
static ServerRequest create(ServerWebExchange exchange, List<HttpMessageReader<?>> messageReaders) {
return new DefaultServerRequest(exchange, messageReaders);
}
this.routerFunction.route(request)
会调用RouterFunction
函数式接口的route()
方法,根据 ServerRequest 对象匹配到合适的HandlerFunction
实现。这部分流程最终会进入到抽象类RouterFunctions
中,而这个类里面有许多RouterFunction
接口的实现类,可以将其理解为一个路由处理的聚合类。在以上使用示例步骤2中,我们提到路由信息封装在DefaultRouterFunction
对象中,进入其route()
方法,会发现其首先调用了this.predicate.test(request)
来判断传入的ServerRequest
是否符合路由要求,如返回true
说明匹配到了处理方法,将保存的HandlerFunction
实现返回即可
@Override
public Mono<HandlerFunction<T>> route(ServerRequest request) {
// 此处就是路由匹配的关键点,校验请求的路径等信息是否能够匹配配置的路由信息
if (this.predicate.test(request)) {
if (logger.isTraceEnabled()) {
String logPrefix = request.exchange().getLogPrefix();
logger.trace(logPrefix + String.format("Matched %s", this.predicate));
}
return Mono.just(this.handlerFunction);
}
else {
return Mono.empty();
}
}
this.predicate.test(request)
这段代码会进入到RequestPredicates
类,与RouterFunctions的聚合作用类似,RequestPredicates 也是路由信息 RequestPredicate 的聚合。如我们所知,通常一个 Http 请求至少包括了请求路径
请求方法GET/POST
请求头
等信息,而在函数式模式中,WebFlux 用PathPatternPredicate
封装请求路径
信息,HttpMethodPredicate
封装请求方法GET/POST
信息,HeadersPredicate
封装请求头
信息,通过这些对象的组合来描述一个完整的路由。在使用示例步骤2中,我们以RouterFunctions.route(RequestPredicates.GET("/hi"))
来绑定路由,进入RequestPredicates.GET("/hi")
会发现该方法的作用其实是完成HttpMethodPredicate 与 PathPatternPredicate 的组合。了解这些以后,很容易就能明白RequestPredicate#test()
方法的作用就是校验路由各部分信息了
public static RequestPredicate GET(String pattern) {
return method(HttpMethod.GET).and(path(pattern));
}
//1. 生成 Http 请求方法信息封装体 HttpMethodPredicate
public static RequestPredicate method(HttpMethod httpMethod) {
return new HttpMethodPredicate(httpMethod);
}
// 2. 生成请求路径信息封装体 PathPatternPredicate
public static RequestPredicate path(String pattern) {
Assert.notNull(pattern, "'pattern' must not be null");
if (!pattern.isEmpty() && !pattern.startsWith("/")) {
pattern = "/" + pattern;
}
return pathPredicates(DEFAULT_PATTERN_PARSER).apply(pattern);
}
public static Function<String, RequestPredicate> pathPredicates(PathPatternParser patternParser) {
Assert.notNull(patternParser, "PathPatternParser must not be null");
return pattern -> new PathPatternPredicate(patternParser.parse(pattern));
}
// 3. 连接 HttpMethodPredicate 与 PathPatternPredicate
default RequestPredicate and(RequestPredicate other) {
return new RequestPredicates.AndRequestPredicate(this, other);
}
2.2.3 HandlerFunction 处理请求
- 通过以上步骤就路由到了能够处理 Http 请求的 HandlerFunction 方法,接下来就是执行处理方法,也就是
DispatchHandler
中的代码handler -> invokeHandler(exchange, handler)
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
if (this.handlerAdapters != null) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(exchange, handler);
}
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
handlerAdapter.handle(exchange, handler)
最终调用到HandlerFunctionAdapter#handle()
方法,在这里handlerFunction.handle(request)
就是执行我们编写的处理方法,之后则是进入返回值处理的流程,不作继续分析
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
ServerRequest request = exchange.getRequiredAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
return handlerFunction.handle(request)
.map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));
}