spring boot整合spring5-webflux從0開始的實戰及源碼解析


上篇文章<你的響應阻塞了沒有?--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&lt;ServerResponse&gt; 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM