上篇文章<你的响应阻塞了没有?--Spring-WebFlux源码分析>介绍了spring5.0 新出来的异步非阻塞服务,很多读者说太理论了,太单调了,这次我们就通过一个从0开始的实例实战一下。
1.准备工作
spring 提供的IDE工STS,配置好maven即可
2.创建spring boot start项目spring5-webflux,并添加依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>spring5-webflux</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring5-webflux</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.增加处理器HelloWorldHandler
package com.example.demo; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; @Component public class HelloWorldHandler { public Mono<ServerResponse> helloWorld(ServerRequest request) { return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN) .body(BodyInserters.fromObject("Hello World!")); } }
4.增加路由器,对应HandlerFunction
package com.example.demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.server.RequestPredicates; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; @Configuration public class HelloWorldRouter { @Bean public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) { return RouterFunctions .route(RequestPredicates.GET("/helloWorld").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloWorldHandler::helloWorld); } }
默认启动时,提供了DefaultRouterFunction的实例
/** * Route to the given handler function if the given request predicate applies. * <p>For instance, the following example routes GET requests for "/user" to the * {@code listUsers} method in {@code userController}: * <pre class="code"> * RouterFunction<ServerResponse> route = * RouterFunctions.route(RequestPredicates.GET("/user"), userController::listUsers); * </pre> * @param predicate the predicate to test * @param handlerFunction the handler function to route to if the predicate applies * @param <T> the type of response returned by the handler function * @return a router function that routes to {@code handlerFunction} if * {@code predicate} evaluates to {@code true} * @see RequestPredicates */ public static <T extends ServerResponse> RouterFunction<T> route( RequestPredicate predicate, HandlerFunction<T> handlerFunction) { return new DefaultRouterFunction<>(predicate, handlerFunction); }
5.启动spring boot项目,调试进入DispatchHandler
@Override public Mono<Void> handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return createNotFoundError(); } return Flux.fromIterable(this.handlerMappings) .concatMap(mapping -> mapping.getHandler(exchange)) .next() .switchIfEmpty(createNotFoundError()) .flatMap(handler -> invokeHandler(exchange, handler)) .flatMap(result -> handleResult(exchange, result)); }
此时HandlerMapping已经初始化完成
5.1 获取Handler
AbstractHandlerMapping.java
@Override public Mono<Object> getHandler(ServerWebExchange exchange) { return getHandlerInternal(exchange).map(handler -> { if (logger.isDebugEnabled()) { logger.debug(exchange.getLogPrefix() + "Mapped to " + handler); } if (CorsUtils.isCorsRequest(exchange.getRequest())) { CorsConfiguration configA = this.corsConfigurationSource.getCorsConfiguration(exchange); CorsConfiguration configB = getCorsConfiguration(handler, exchange); CorsConfiguration config = (configA != null ? configA.combine(configB) : configB); if (!getCorsProcessor().process(config, exchange) || CorsUtils.isPreFlightRequest(exchange.getRequest())) { return REQUEST_HANDLED_HANDLER; } } return handler; }); }
通过RouterFunctions获取HandlerAdapter
5.2 执行HandlerAdapter
HelloWorldHandler的类型是HandlerFunctionAdapter类型,触发HandlerFunctionAdapter执行handle方法
@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)); }
最后调用HelloWorldHandler的helloWorld方法
@Component public class HelloWorldHandler { public Mono<ServerResponse> helloWorld(ServerRequest request) { return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN) .body(BodyInserters.fromObject("Hello World!")); } }
5.3 执行HandleResult
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { return getResultHandler(result).handleResult(exchange, result) .onErrorResume(ex -> result.applyExceptionHandler(ex).flatMap(exceptionResult -> getResultHandler(exceptionResult).handleResult(exchange, exceptionResult))); }
因HelloWorldRouter返回结果类型是ServerResponse,故调用ServerResponseResultHandler来处理结果
@Override public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { ServerResponse response = (ServerResponse) result.getReturnValue(); Assert.state(response != null, "No ServerResponse"); return response.writeTo(exchange, new ServerResponse.Context() { @Override public List<HttpMessageWriter<?>> messageWriters() { return messageWriters; } @Override public List<ViewResolver> viewResolvers() { return viewResolvers; } }); }
6.总结
spring mvc和spring webflux是一对兄弟,他们的处理流程类似,mvc是同步阻塞服务,webflux是异步非阻塞服务,它们直接的关系如下:
spring webflux 增加了functional endpoint,RouterFunction(RouterFunctions构建)等同于HanderMapping, HandlerFunction(HandlerFunctionAdapter继承自HandlerAdapter来代理)等同于HandlerAdapter
spring webflux 引入了spring boot2,Reactor,lambda表达式,语法更简洁,但可读性变弱。
参考文献:
【1】https://www.journaldev.com/20763/spring-webflux-reactive-programming
微信公众号: 架构师日常笔记 欢迎关注!