zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Vue 获取dom元素中的自定义属性值
    php文件包含
    php数组运算符
    管理一组事物的状态
    php数据类型转换
    php函数
    PHP循环while do while循环
    php的流程控制 if elseif swich case for循环
    php的工作原理
    python算数、逻辑运算,位运算
  • 原文地址:https://www.cnblogs.com/davidwang456/p/10407557.html
Copyright © 2011-2022 走看看