Spring WebFlux

一、简介

Spring WebFlux作为一个响应式(reactive-stack)web框架补充,在5.0的版本开始加入到Spring全家桶。这是一个完全非阻塞的,支持Reactive Streams,运行在诸如NettyUndertow, 以及Servlet 3.1+容器上的。Spring WebFlux可以让你使用更少的线程去处理并发请求,同时能够让你使用更少的硬件资源来拓展你的应用。

1.1 Spring WebFlux与Spring MVC的区别

155532924213864.jpg

结合上图,在了解Spring WebFlux之前,我们先来对比说说什么是Spring MVC,这更有益我们去理解WebFlux,图右边对Spring MVC的定义,原文如下:

Spring MVC is built on the Servlet API and uses a synchronous blocking I/O architecture with a one-request-per-thread model.

翻译一下,意思如下:

Spring MVC构建于Servlet API之上,使用的是同步阻塞式I/O模型,什么是同步阻塞式I/O模型呢?就是说,每一个请求对应一个线程去处理。

了解了Spring MVC之后,再来说说Spring WebFlux:

上图左边,官方给出的定义如下:

Spring WebFlux is a non-blocking web framework built from the ground up to take advantage of multi-core, next-generation processors and handle massive numbers of concurrent connections.

翻译一下,内容如下:

Spring WebFlux是一个异步非阻塞式的Web框架,它能够充分利用多核CPU的硬件资源去处理大量的并发请求。

1.2 WebFlux的优势&提升性能?

WebFlux内部使用的是响应式编程(Reactive Programming),以Reactor库为基础,基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。

看到这里,你是不是以为WebFlux能够使程序运行的更快呢?量化一点,比如说我使用WebFlux以后,一个接口的请求响应时间是不是就缩短了呢?

抱歉了,答案是否定的!,以下是官方原话:

Reactive and non-blocking generally do not make applications run faster.

WebFlux并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性

1.3 WebFlux应用场景

上面说到了,Spring WebFlux是一个异步非阻塞式的Web框架,所以,它特别适合应用在IO密集型的服务中,比如微服务网关这样的应用中。

PS:IO密集型包括:磁盘IO密集型,网络IO密集型,微服务网关就属于网络IO密集型,使用异步非阻塞式编程模型,能够显著地提升网关对下游服务转发的吞吐量。

155541524371593.jpg

1.4 选WebFlux还是Spring MVC?

首先你需要明确一点就是:WebFlux不是Spring MVC的替代方案,虽然WebFlux也可以被运行在Servlet容器上(需是Servlet 3.1+以上的容器),但是WebFlux
主要还是应用在异步非阻塞编程模型,而Spring MVC是同步阻塞的,如果你目前在Spring MVC框架中大量使用非同步方案,那么,WebFlux才是你想要的,否则,使用Spring MVC才是你的首选。

在微服务架构中,Spring MVCWebFlux可以混合使用,比如已经提到的,对于那些IO密集型服务(如网关),我们就可以使用WebFlux来实现。

总之一句话,在合适的场景中,选型最合适的技术

1.5 异同点

155533174918637.png

从上图中,可以一眼看出Spring MVCSpring WebFlux的相同点和不同点:

相同点:

  • 都可以使用Spring MVC注解,如@Controller,方便我们在两个Web框架中自由转换;
  • 均可以使用TomcatJettyUndertow Servlet容器(Servlet 3.1+);
  • ...

注意点:

  • Spring MVC因为是使用的同步阻塞式,更方便开发人员编写功能代码,Debug测试等,一般来说,如果Spring MVC能够满足的场景,就尽量不要用WebFlux;
  • WebFlux默认情况下使用Netty作为服务器;
  • WebFlux不支持MySql;

1.6 WebFlux是如何分发请求的

使用过Spring MVC的,应该到知道Spring MVC的前端控制器是DispatcherServlet,而WebFluxDispatcherHandler,它实现了WebHandler接口:

155541700035115.jpg

来看看DispatcherHandler类中处理请求的handle方法:

155541732828610.jpg

  • ①: ServerWebExchange对象中放置每一次HTTP请求响应信息,包括参数等;
  • ②: 判断整个接口映射mappings集合是否为空,空则创建一个Not Found的错误;
  • ③: 根据具体的请求地址获取对应的handlerMapping;
  • ④: 调用具体业务方法,也就是我们定义的接口方法;
  • ⑤: 处理返回的结果;

二、Webflux请求处理流程

2.1 spring mvc处理流程

在了解SpringMvc的请求流程源码之后,理解WebFlux就容易的多,毕竟WebFlux处理流程是模仿Servlet另起炉灶的。

下面是spring mvc的请求处理流程

zEwOTM.png

EwODM.png

具体步骤:

  • 第一步:发起请求到前端控制器(DispatcherServlet)
  • 第二步:前端控制器请求HandlerMapping查找Handler(可以根据xml配置、注解进行查找) 匹配条件包括:请求路径、请求方法、header信息等
  • 第三步:处理器映射器HandlerMapping向前端控制器返回HandlerHandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略HandlerInterceptor是请求路径上的拦截器,需要自己实现这个接口以拦截请求,做一些对handler的前置和后置处理工作。
  • 第四步:前端控制器调用处理器适配器去执行Handler
  • 第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
  • 第六步:Handler执行完成给适配器返回ModelAndView
  • 第七步:处理器适配器向前端控制器返回ModelAndViewModelAndViewspringmvc框架的一个底层对象,包括Modelview
  • 第八步:前端控制器请求视图解析器去进行视图解析(根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
  • 第九步:视图解析器向前端控制器返回View
  • 第十步:前端控制器进行视图渲染(视图渲染将模型数据(在ModelAndView对象中)填充到request域)
  • 第十一步:前端控制器向用户响应结果

2.2 webflux请求处理流程

注解驱动请求的webflux请求处理流程

20200801121205280.png

我们可以对比SpringMVC的请求流程图对比来看

DAvZm9ybWF0L3dlYnA.webp

我们可以看到,处理流程基本一样,有以下主要的点不同

  • 处理核心
    • WebFlux--DispatcherHandler
    • SpringMvc--DispatcherServlet
  • 返回值处理器
    • WebFlux--HandlerResultHandler
    • SpringMvc--HandlerMethodReturnValueHandler
  • 内容协商配置器
    • WebFlux--RequestedContentTypeResolverBuilder
    • SpringMvc--ContentNegotiationConfigurer

还有很多就不一一例举了,想知道核心组件对比结果的同学,可以看下图。注意很多图上的组件名称相同,但是包的位置是不同的,所以大家要注意区分,不要弄混了。

2.2.1 Web MVC VS. WebFlux 核心组件对比

3dlYnA.webp

2.3 核心控制器DispatcherHandler

核心控制器DispatcherHandler等同于阻塞方式的DispatcherServletDispatcherHandler实现ApplicationContextAware
,那么必然会调用setApplicationContext方法

public class DispatcherHandler implements WebHandler, ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        initStrategies(applicationContext);
    }
}

2.3.1 initStrategies初始化

获取HandlerMapping,HandlerAdapter,HandlerResultHandler的所有实例

protected void initStrategies(ApplicationContext context) {
    //获取HandlerMapping及其子类型的bean
    //HandlerMapping根据请求request获取handler执行链
    Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerMapping.class, true, false);
 
    ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
    //排序
    AnnotationAwareOrderComparator.sort(mappings);
    this.handlerMappings = Collections.unmodifiableList(mappings);
 
    //获取HandlerAdapter及其子类型的bean
    Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerAdapter.class, true, false);
 
    this.handlerAdapters = new ArrayList<>(adapterBeans.values());
    //排序
    AnnotationAwareOrderComparator.sort(this.handlerAdapters);
 
    //获取HandlerResultHandler及其子类型的bean
    Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerResultHandler.class, true, false);
 
    this.resultHandlers = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(this.resultHandlers);
}

2.3.1 DispatcherHandler的总体流程:

  • 1、通过HandlerMapping(和DispatcherServlet中的HandlerMapping不同)获取到HandlerAdapter放到ServerWebExchange的属性中
  • 2、获取到HandlerAdapter后触发handle方法,得到HandlerResult
  • 3、通过HandlerResult,触发handleResult,针对不同的返回类找到不同的HandlerResultHandler如视图渲染ViewResolutionResultHandler
    ServerResponseResultHandlerResponseBodyResultHandlerResponseEntityResultHandler不同容器有不同的实现,如ReactorJetty
    Tomcat等。

2.4 HandlerMapping

webflux中引入了一个新的HandlerMapping,即RouterFunctionMapping

RouterFunctionMapping实现了InitializingBean,因此在其实例化的时候,会调用afterPropertiesSet方法

public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {

    @Nullable
    private RouterFunction<?> routerFunction;
    //省略部分代码

    //afterPropertiesSet()方法 是组件初始化后回调 必须实现InitializingBean接口
    //
    @Override
    public void afterPropertiesSet() throws Exception {
        if (CollectionUtils.isEmpty(this.messageReaders)) {
            ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create();
            this.messageReaders = codecConfigurer.getReaders();
        }

        //初始化routerFunction
        if (this.routerFunction == null) {
            initRouterFunctions();
        }
    }

    /**
     * Initialized the router functions by detecting them in the application context.
     * 从应用上下文中查找他们并初始化路由方法
     */
    protected void initRouterFunctions() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for router functions in application context: " +
                    getApplicationContext());
        }

        //查找合并所有路由方法的bean
        List<RouterFunction<?>> routerFunctions = routerFunctions();
        if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
            routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
        }

        //将一个请求中含有多个路由请求方法合并成一个方法
        this.routerFunction = routerFunctions.stream()
                .reduce(RouterFunction::andOther)
                .orElse(null);
    }

    //查找并合并所有路由方法
    private List<RouterFunction<?>> routerFunctions() {
        //声明 SortedRouterFunctionsContainer bean
        SortedRouterFunctionsContainer container = new SortedRouterFunctionsContainer();
        //自动注入到上下文中 
        obtainApplicationContext().getAutowireCapableBeanFactory().autowireBean(container);
        //返回路由
        return CollectionUtils.isEmpty(container.routerFunctions)
                ? Collections.emptyList()
                : container.routerFunctions;
    }

    //省略部分代码
    private static class SortedRouterFunctionsContainer {

        @Nullable
        private List<RouterFunction<?>> routerFunctions;

        //由上面的方法 自动注入bean时实现依赖查找,查找所有的 RouterFunction beans
        //并注入到 List<RouterFunction<?>> 中。这样就会得到所有实现路由方法的集合
        @Autowired(required = false)
        public void setRouterFunctions(List<RouterFunction<?>> routerFunctions) {
            this.routerFunctions = routerFunctions;
        }
    }
}

2.5 HandlerAdapter

webflux中引入了一个新的HandlerAdapter,即HandlerFunctionAdapter

webflux中引入了一个新的HandlerResultHandler,即ServerResponseResultHandler

ServerResponseResultHandler实现了InitializingBean,因此在其实例化的时候,会调用afterPropertiesSet方法

流式处理请求handler()

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    //handlerMappings在initStrategies()方法中已经构造好了
    if (this.handlerMappings == null) {
        return createNotFoundError();
    }
    //构造Flux,数据源为handlerMappings集合
    return Flux.fromIterable(this.handlerMappings)
            //获取Mono<Handler>对象,通过concatMap保证顺序和handlerMappings顺序一致
            //严格保证顺序是因为在一个系统中可能存在一个Url有多个能够处理的HandlerMapping的情况
            .concatMap(mapping -> mapping.getHandler(exchange))
            .next()
            //如果next()娶不到值则抛出错误
            .switchIfEmpty(createNotFoundError())
            //触发HandlerApter的handle方法
            .flatMap(handler -> invokeHandler(exchange, handler))
            //触发HandlerResultHandler 的handleResult方法
            .flatMap(result -> handleResult(exchange, result));
}

触发HandlerApterhandle方法

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)));
}
 
private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
    if (this.resultHandlers != null) {
        for (HandlerResultHandler resultHandler : this.resultHandlers) {
            if (resultHandler.supports(handlerResult)) {
                return resultHandler;
            }
        }
    }
    throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
}

2.6 函数式端点请求处理流程

L3dlYnA.webp

通过上图,我们可以看到,这个处理跟之前的注解驱动请求大有不同,但是请求的流程是万变不离其宗,只是组件有所变化。

接下来我们就跟着流程图一步一步的来解读WebFlux函数端点式请求的源码。

2.6.1 装配阶段

由上图我们可以看到RouterFunctionMapping是由WebFluxConfigurationSupport创建的,接下来看一下RouterFunctions是怎么合并RouterFunction
的并且如何关联到RouterFunctionMapping的。

RouterFunctionMapping的源码,前面已经介绍了。

2.6.2 请求阶段

请求阶段的核心代码就是org.springframework.web.reactive.DispatcherHandler#handle方法,我们来看一下源码。

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    if (logger.isDebugEnabled()) {
        ServerHttpRequest request = exchange.getRequest();
        logger.debug("Processing " + request.getMethodValue() 
              + " request for [" + request.getURI() + "]");
    }
    if (this.handlerMappings == null) {
        return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
    }
    // 1.HTTP请求进来后执行的流程
    return Flux.fromIterable(this.handlerMappings) //2 遍历handlerMappings定位RouterFunctionMapping
            .concatMap(mapping -> mapping.getHandler(exchange))   // 3.获取HandlerFunction
            .next()
            .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))  
            .flatMap(handler -> invokeHandler(exchange, handler))   //4.执行
            .flatMap(result -> handleResult(exchange, result));  //5. 处理结果
}

上面的代码已经把大部分的流程说明清楚了,那么我们来看一下lambda表达式中每个内部方法的具体实现。

首先我们来看一下步骤3的具体实现org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler

@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
    //调用 getHandlerInternal 方法来确定HandlerFunction
    return getHandlerInternal(exchange).map(handler -> {
        if (CorsUtils.isCorsRequest(exchange.getRequest())) {
            CorsConfiguration configA = this.globalCorsConfigSource.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;
    });
}

上面一大段代码其实主要来获取handler的方法是getHandlerInternal(exchange)剩下的部分是跨域处理的逻辑。我们看一下这个方法。

@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
    if (this.routerFunction != null) {
        ServerRequest request = ServerRequest.create(exchange, this.messageReaders);
        exchange.getAttributes().put(RouterFunctions.REQUEST_ATTRIBUTE, request);
        return this.routerFunction.route(request);  //通过路由获取到对应处理的HandlerFunction也就是执行方法
    } else {
        return Mono.empty();
    }
}

获取到对应的HandlerFunction后我们就来执行第四步,调用HandlerFunction

private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
            if (handlerAdapter.supports(handler)) {  //判断HandlerAdapters中是否支持之前获取到的handler
                return handlerAdapter.handle(exchange, handler);  //执行handler 对应下面handle的方法
            }
        }
    }
    return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter#handle方法,这个类中的方法就是处理函数式端点请求的Adapter

具体实现

@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
    HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
    ServerRequest request = exchange.getRequiredAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
    return handlerFunction.handle(request)   //由lambda模式 (返回值-参数)  无需准确的方法签名
          .map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));
}

这里的lambda模式比较难理解,主要是看HandlerFunction这个函数式接口

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {

    /**
     * Handle the given request.
     * @param request the request to handle
     * @return the response
     */
    Mono<T> handle(ServerRequest request);

}

我们只需要满足 入参是ServerRequest类型 返回值是Mono<T> 就可以执行。

调用完具体方法之后,我们就可以进行返回值解析序列化了。这里就是步骤5 处理结果。

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)));
}

我们再来看一下getResultHandler代码

private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
    if (this.resultHandlers != null) {
        for (HandlerResultHandler resultHandler : this.resultHandlers) {
            if (resultHandler.supports(handlerResult)) {
                return resultHandler;
            }
        }
    }
    throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
}

在这里我们看一下resultHandlers中都含有哪些返回值处理器

img

我们通过截图可以看出返回值解析器跟流程图一一对应。

在匹配到对应的返回值解析器之后进行返回值的封装和写会,这里要注意DataBuffer是NIO的写处理,最后写回到浏览器客户端。

三、实战

3.1 添加webflux依赖

新建一个Spring Boot项目,在pom.xml文件中添加webflux依赖:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

3.1.1 定义接口

新建一个controller包,用来放置对外的接口类,再创建一个 HelloWebFluxController.class 类,定义两个接口:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import site.exception.springbootwebfluxhello.entity.User;

@RestController
public class HelloWebFluxController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, WebFlux !";
    }

    @GetMapping("/user")
    public Mono<User> getUser() {
        User user = new User();
        user.setName("name");
        user.setDesc("测试webFlux");
        return Mono.just(user);
    }

}

User.java:


@data
public class User {

    /**
     * 姓名
     */
    private String name;
    /**
     * 描述
     */
    private String desc;
}

以上控制器类中,我们使用的全都是Spring MVC的注解,分别定义了两个接口:

  • 一个GET请求的/hello接口,返回Hello, WebFlux!字符串。
  • 又定义了一个GET请求的/user方法,返回的是JSON格式User对象。

这里注意,User对象是通过Mono对象包装的,你可能会问,为啥不直接返回呢?

WebFlux中,Mono是非阻塞的写法,只有这样,你才能发挥WebFlux非阻塞 + 异步的特性。

补充:在WebFlux中,除了Mono外,还有一个Flux,这哥俩均能充当响应式编程中发布者的角色,不同的是:

  • Mono:返回01个元素,即单个对象。
  • Flux:返回N个元素,即List列表对象。

3.1.2 Mono

Mono是什么?官方描述如下:

A Reactive Streams Publisher with basic rx operators that completes successfully by emitting an element, or with an error.

Mono是响应流Publisher具有基础rx操作符。可以成功发布元素或者错误。如图所示:

617b0d6d94f.png

Mono常用的方法有:

  • Mono.create():使用MonoSink来创建Mono
  • Mono.justOrEmpty():从一个Optional对象或null对象中创建Mono
  • Mono.error():创建一个只包含错误消息的Mono
  • Mono.never():创建一个不包含任何消息通知的Mono
  • Mono.delay():在指定的延迟时间之后,创建一个Mono,产生数字0作为唯一值

3.1.3 Flux

Flux是什么?官方描述如下:

A Reactive Streams Publisher with rx operators that emits 0 to N elements, and then completes (successfully or with an error).

Flux是响应流Publisher具有基础rx操作符。可以成功发布0N个元素或者错误。Flux其实是Mono的一个补充。如图所示:

268e91d1eced0dc36.png

所以要注意:如果知道Publisher01个,则用Mono

Flux最值得一提的是fromIterable方法。fromIterable(Iterable<? extends T> it)可以发布Iterable类型的元素。当然,Flux也包含了基础的操作:map
mergeconcatflatMaptake,这里就不展开介绍了。

3.2 使用配置模式进行WebFlux接口开发

  1. 可以编写一个处理器类Handler代替ControllerServicedao层保持不变。
  2. 配置请求的路由

3.2.1 处理器类Handler

处理器类Handler需要从请求解析参数,并且封装响应,代码如下:

package com.demo.springcloud.reactive.user.info.config.handler;

import com.demo.springcloud.common.exception.BusinessException;
import com.demo.springcloud.reactive.user.info.dto.User;
import com.demo.springcloud.reactive.user.info.service.impl.JpaEntityServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

@Slf4j
@Component
public class UserReactiveHandler {

    @Resource
    private JpaEntityServiceImpl jpaEntityService;

    /**
     * 得到所有用户
     *
     * @param request
     * @return
     */
    public Mono<ServerResponse> getAllUser(ServerRequest request) {
        log.info("方法 getAllUser 被调用了");
        return ok().contentType(APPLICATION_JSON_UTF8)
                .body(Flux.fromIterable(jpaEntityService.selectAllUser()), User.class);
    }

    /**
     * 创建用户
     *
     * @param request
     * @return
     */
    public Mono<ServerResponse> createUser(ServerRequest request) {
        // 2.0.0 是可以工作, 但是2.0.1 下面这个模式是会报异常
        Mono<User> user = request.bodyToMono(User.class);
        /**Mono 使用响应式的,时候都是一个流,是一个发布者,任何时候都不能调用发布者的订阅方法
         也就是不能消费它, 最终的消费还是交给我们的Springboot来对它进行消费,任何时候不能调用它的
         user.subscribe();
         不能调用block
         把异常放在统一的地方来处理
         */
        return user.flatMap(dto -> {
            // 校验代码需要放在这里
            if (StringUtils.isBlank(dto.getName())) {
                throw new BusinessException("用户名不能为空");
            }
            return ok().contentType(APPLICATION_JSON_UTF8).body(
                    Mono.create(cityMonoSink ->
                            cityMonoSink.success(jpaEntityService.addUser(dto))), User.class);
        });
    }

    /**
     * 根据id删除用户
     *
     * @param request
     * @return
     */
    public Mono<ServerResponse> deleteUserById(ServerRequest request) {
        String id = request.pathVariable("id");
        // 校验代码需要放在这里
        if (StringUtils.isBlank(id)) {
            throw new BusinessException("id不能为空");
        }
        User dto = new User();
        dto.setUserId(Long.parseLong(id));
        return ok().contentType(APPLICATION_JSON_UTF8).body(
                Mono.create(cityMonoSink ->
                        cityMonoSink.success(jpaEntityService.delUser(dto))), User.class);
    }

}

3.2.2 路由配置

package com.demo.springcloud.reactive.user.info.config;

import com.demo.springcloud.reactive.user.info.config.handler.UserReactiveHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebFilter;

import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;

@Configuration
public class RoutersConfig {

    @Bean
    RouterFunction<ServerResponse> routes(UserReactiveHandler handler) {

        // 下面的相当于类里面的 @RequestMapping
        // 得到所有用户
        return RouterFunctions.route(GET("/user"), handler::getAllUser)
                // 创建用户
                .andRoute(POST("/user").and(
                        accept(MediaType.APPLICATION_JSON_UTF8)), handler::createUser)
                // 删除用户
                .andRoute(DELETE("/user/{id}"), handler::deleteUserById);
    }

    @Value("${server.servlet.context-path}")
    private String contextPath;

    //处理上下文路径,没有上下文路径,此函数可以忽略
    @Bean
    public WebFilter contextPathWebFilter() {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();

            String requestPath = request.getURI().getPath();
            if (requestPath.startsWith(contextPath)) {
                return chain.filter(
                        exchange.mutate()
                                .request(request.mutate().contextPath(contextPath).build())
                                .build());
            }
            return chain.filter(exchange);
        };
    }
}

3.3 测试

3.3.1 配置模式的WebFlux Rest接口测试

配置模式的WebFlux Rest接口只能使用PostMan测试,例子如下:

在这里插入图片描述

注意,不能带上下文路径:http://192.168.68.1:7705/uaa-react-provider/user

3.3.2 注解模式的WebFlux Rest接口测试

3.3.2.1 swagger增加界面

在这里插入图片描述

CRUD其他的界面,略过

3.4 配置大全

3.4.1 静态资源配置


@Configuration
@EnableWebFlux        //使用注解@EnableWebFlux
public class WebFluxConfig implements WebFluxConfigurer {    //继承WebFluxConfigurer 
    //配置静态资源
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");
        registry.addResourceHandler("/file/**")
                .addResourceLocations("file:" + System.getProperty("user.dir")
                        + File.separator + "file" + File.separator);
        registry.addResourceHandler("/swagger-ui.html**")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    //配置拦截器
    //配置编解码
    //...
}

3.4.2 WebFluxSecurity配置


@Configuration
@EnableWebSecurity
public class WebMvcSecurityConfig extends WebSecurityConfigurerAdapter implements
        AuthenticationEntryPoint,        //未验证回调
        AuthenticationSuccessHandler,        //验证成功回调
        AuthenticationFailureHandler,        //验证失败回调
        LogoutSuccessHandler {        //登出成功回调

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException)
            throws IOException, ServletException {
        sendJson(response, new Response<>(HttpStatus.UNAUTHORIZED.value(), "Unauthorized"));
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception)
            throws IOException, ServletException {
        sendJson(response, new Response<>(1, "Incorrect"));
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication)
            throws IOException, ServletException {
        sendJson(response, new Response<>(0, authentication.getClass().getSimpleName()));
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                Authentication authentication)
            throws IOException, ServletException {
        sendJson(response, new Response<>(0, "Success"));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers("/swagger*/**", "/webjars/**", "/v2/api-docs")
                .permitAll()
                .and()
                .authorizeRequests()
                .antMatchers("/static/**", "/file/**")
                .permitAll()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .logout()
                .logoutUrl("/user/logout")        //虚拟路径,不是控制器定义的路径
                .logoutSuccessHandler(this)
                .permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(this)
                .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/user/login")        //虚拟路径,不是控制器定义的路径
                .successForwardUrl("/user/login")        //是控制器定义的路径
                .failureHandler(this)
                .and()
                .httpBasic()
                .authenticationEntryPoint(this);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }
}    

webflux-验证依赖于用户数据服务,需定义实现ReactiveUserDetailsServiceBean


@Configuration
@EnableWebFluxSecurity        //使用注解@EnableWebFluxSecurity
public class WebFluxSecurityConfig implements
        WebFilter,        //拦截器
        ServerLogoutSuccessHandler,        //登出成功回调
        ServerAuthenticationEntryPoint,        //验证入口
        ServerAuthenticationFailureHandler,        //验证成功回调 
        ServerAuthenticationSuccessHandler {        //验证失败回调

    //实现接口的方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        //配置webflux的context-path
        ServerHttpRequest request = exchange.getRequest();
        if (request.getURI().getPath().startsWith(contextPath)) {
            exchange = exchange.mutate()
                    .request(request.mutate().contextPath(contextPath).build()).build();
        }
        //把查询参数转移到FormData中,不然验证过滤器(ServerFormLoginAuthenticationConverter)接受不到参数
        if (exchange.getRequest().getMethod() == HttpMethod.POST 
                && exchange.getRequest().getQueryParams().size() > 0) {
            ServerWebExchange finalExchange = exchange;
            ServerWebExchange realExchange = new Decorator(exchange) {
                @Override
                public Mono<MultiValueMap<String, String>> getFormData() {
                    return super.getFormData().map(
                            new Function<MultiValueMap<String, String>, 
                                    MultiValueMap<String, String>>() {
                        @Override
                        public MultiValueMap<String, String> apply(
                                MultiValueMap<String, String> stringStringMultiValueMap) {
                            if (stringStringMultiValueMap.size() == 0) {
                                return finalExchange.getRequest().getQueryParams();
                            } else {
                                return stringStringMultiValueMap;
                            }
                        }
                    });
                }
            };
            return chain.filter(realExchange);
        }
        return chain.filter(exchange);
    }

    @Override
    public Mono<Void> onLogoutSuccess(WebFilterExchange webFilterExchange,
                                      Authentication authentication) {
        return sendJson(webFilterExchange.getExchange(), new Response<>("登出成功"));
    }

    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
        return sendJson(exchange, new Response<>(HttpStatus.UNAUTHORIZED.value(), "未验证"));
    }

    @Override
    public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange,
                                              AuthenticationException exception) {
        return sendJson(webFilterExchange.getExchange(), new Response<>(1, "验证失败"));
    }

    @Override
    public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange,
                                              Authentication authentication) {
        return webFilterExchange.getChain().filter(
                webFilterExchange.getExchange().mutate()
                        .request(t -> t.method(HttpMethod.POST).path("/user/login"))  //转发到自定义控制器
                        .build()
        );
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.addFilterAfter(this, SecurityWebFiltersOrder.FIRST)
                .csrf().disable()
                .authorizeExchange()
                .pathMatchers("/swagger*/**", "/webjars/**", "/v2/api-docs")        //swagger
                .permitAll()
                .and()
                .authorizeExchange()
                .pathMatchers("/static/**", "/file/**")        //静态资源
                .permitAll()
                .and()
                .authorizeExchange()
                .anyExchange()
                .authenticated()
                .and()
                .logout()        //登出
                .logoutUrl("/user/logout")
                .logoutSuccessHandler(this)
                .and()
                .exceptionHandling()        //未验证回调
                .authenticationEntryPoint(this)
                .and()
                .formLogin()
                .loginPage("/user/login")
                .authenticationFailureHandler(this)        //验证失败回调
                .authenticationSuccessHandler(this)        //验证成功回调
                .and()
                .httpBasic()
                .authenticationEntryPoint(this);        //basic验证,一般用于移动端
        return http.build();
    }
}

3.4.3 WebSession配置

@Configuration
@EnableRedisWebSession(maxInactiveIntervalInSeconds = 60)
//使用注解@EnableRedisWebSession ,maxInactiveIntervalInSeconds设置数据过期时间,spring.session.timeout不管用
public class RedisWebSessionConfig { //考虑到分布式系统,一般使用redis存储session

    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {
        return new LettuceConnectionFactory();
    }

}
//单点登录使用ReactiveRedisSessionRepository.getSessionRedisOperations().scan方法查询相同用户名的session,
// 删除其他session即可
public Mono<Map<String, String>> findByPrincipalName(String name) {
    return reactiveSessionRepository.getSessionRedisOperations().scan(
              ScanOptions.scanOptions().match(
                      ReactiveRedisSessionRepository.DEFAULT_NAMESPACE + ":sessions:*").build())
            .flatMap(new Function<String, Publisher<Tuple2<String, Map.Entry<Object, Object>>>>() {
                @Override
                public Publisher<Tuple2<String, Map.Entry<Object, Object>>> apply(String s) {
                    return reactiveSessionRepository.getSessionRedisOperations().opsForHash()
                            .entries(s).map(
                                  new Function<Map.Entry<Object, Object>,
                                              Tuple2<String, Map.Entry<Object, Object>>>() {
                                @Override
                                public Tuple2<String, Map.Entry<Object, Object>> apply(
                                        Map.Entry<Object, Object> objectObjectEntry) {
                                    return Tuples.of(s, objectObjectEntry);
                                }
                            });
                }
            })
            .filter(new Predicate<Tuple2<String, Map.Entry<Object, Object>>>() {
                @Override
                public boolean test(Tuple2<String, Map.Entry<Object, Object>> rule) {
                    Map.Entry<Object, Object> t = rule.getT2();
                    String key = "sessionAttr:" + 
                        HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
                    if (key.equals(t.getKey())) {
                        User sci = (User) ((SecurityContextImpl) t.getValue())
                                    .getAuthentication().getPrincipal();
                        return sci.getUsername().equals(name);
                    }
                    return false;
                }
            })
            .collectMap(new Function<Tuple2<String, Map.Entry<Object, Object>>, String>() {
                @Override
                public String apply(Tuple2<String, Map.Entry<Object, Object>> rule) {
                    return name;
                }
            }, new Function<Tuple2<String, Map.Entry<Object, Object>>, String>() {
                @Override
                public String apply(Tuple2<String, Map.Entry<Object, Object>> rule) {
                    return rule.getT1().replace(ReactiveRedisSessionRepository.DEFAULT_NAMESPACE
                            + ":sessions:", "");
                }
            });
		}
    }		
}

对标的SpringWebMVC配置

@Configuration
@EnableRedisHttpSession	//使用注解@EnableRedisHttpSession	
public class RedisHttpSessionConfig { //考虑到分布式系统,一般使用redis存储session

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory();
    }
}
//单点登录使用FindByIndexNameSessionRepository根据用户名查询session,删除其他session即可
Map<String, Session> map = findByIndexNameSessionRepository.findByPrincipalName(name);

3.4.4 文件上传配置

//参数上传
//定义参数bean
@Setter
@Getter
@ToString
@ApiModel
public class QueryBean {

    @ApiModelProperty(value = "普通参数", required = false, example = "")
    private String query;

    @ApiModelProperty(value = "文件参数", required = false, example = "")
    private FilePart image;        //强调,webflux中使用FilePart作为接收文件的类型
}
//定义接口
@ApiOperation("一个接口")
@PostMapping("/path")
//这里需要使用@ApiImplicitParam显示配置【文件参数】才能使swagger界面显示上传文件按钮
@ApiImplicitParams({
	@ApiImplicitParam(
		paramType = "form", //表单参数
		dataType = "__file", //最新版本使用__file表示文件,以前用的是file
		name = "image", //和QueryBean里面的【文件参数image】同名
		value = "文件")	//注释
})
public Mono<Response> bannerAddOrUpdate(QueryBean q) {

}

3.5 WebFlux执行流程

userAdd方法代码如下:

public Mono<User> userAdd(@RequestBody User dto) {
    //命令式写法
    //jpaEntityService.delUser(dto);

    //响应式写法
    return Mono.create(cityMonoSink -> cityMonoSink.success(jpaEntityService.addUser(dto)));
}

由于返回的数据只有一个所以使用的是Mono作为返回数据,使用Mono类静态create方法创建Mono对象,代码如下:

public abstract class Mono<T> implements Publisher<T> {
    static final BiPredicate EQUALS_BIPREDICATE = Object::equals;

    public Mono() {
    }

    public static <T> Mono<T> create(Consumer<MonoSink<T>> callback) {
        return onAssembly(new MonoCreate(callback));
    }
    //...
}

可以到create方法接收一个参数,参数是Consumer对象,通过callback可以看出,这里使用的是callback回调,下面看看Consumer接口的定义:

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> {
            accept(t);
            after.accept(t);
        };
    }
}

通过上面的代码可以看出,有两个方法,一个是默认的方法andThen,还有一个accept方法,Mono.create()方法的参数需要一个实现类,实现Consumer接口;Mono.create方法的参数指向的实例对象, 就是要实现这个accept方法。

例子中,下面的lambda表达式,就是accept方法的实现,实参的类型为Consumer<MonoSink>, accept的实现为 如下:

cityMonoSink -> cityMonoSink.success(jpaEntityService.addUser(dto))

来来来,重复看一下,create方法的实现:

public static <T> Mono<T> create(Consumer<MonoSink<T>> callback) {
    return onAssembly(new MonoCreate(callback));
}

在方法内部调用了onAssembly方法,参数是MonoCreate对象,然后我们看看MonoCreate类,代码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package reactor.core.publisher;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Consumer;
import java.util.function.LongConsumer;

import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Scannable.Attr;
import reactor.core.publisher.FluxCreate.SinkDisposable;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;

final class MonoCreate<T> extends Mono<T> {
    
    final Consumer<MonoSink<T>> callback;

    MonoCreate(Consumer<MonoSink<T>> callback) {
        this.callback = callback;
    }

    public void subscribe(CoreSubscriber<? super T> actual) {
        MonoCreate.DefaultMonoSink<T> emitter = new MonoCreate.DefaultMonoSink(actual);
        actual.onSubscribe(emitter);

        try {
            this.callback.accept(emitter);
        } catch (Throwable var4) {
            emitter.error(Operators.onOperatorError(var4, actual.currentContext()));
        }
    }

    static final class DefaultMonoSink<T> 
            extends AtomicBoolean
            implements MonoSink<T>, InnerProducer<T> {

        final CoreSubscriber<? super T> actual;

        volatile Disposable disposable;

        static final AtomicReferenceFieldUpdater<MonoCreate.DefaultMonoSink, Disposable> DISPOSABLE =
                AtomicReferenceFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class,
                        Disposable.class, "disposable");

        volatile int state;

        static final AtomicIntegerFieldUpdater<MonoCreate.DefaultMonoSink> STATE =
                AtomicIntegerFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class, "state");

        volatile LongConsumer requestConsumer;

        static final AtomicReferenceFieldUpdater<MonoCreate.DefaultMonoSink, LongConsumer>
                REQUEST_CONSUMER =
                AtomicReferenceFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class,
                        LongConsumer.class, "requestConsumer");

        T value;

        static final int NO_REQUEST_HAS_VALUE = 1;
        static final int HAS_REQUEST_NO_VALUE = 2;
        static final int HAS_REQUEST_HAS_VALUE = 3;

        DefaultMonoSink(CoreSubscriber<? super T> actual) {
            this.actual = actual;
        }

        public Context currentContext() {
            return this.actual.currentContext();
        }

        @Nullable
        public Object scanUnsafe(Attr key) {
            if (key != Attr.TERMINATED) {
                return key == Attr.CANCELLED 
                        ? OperatorDisposables.isDisposed(this.disposable) 
                        : super.scanUnsafe(key);
            } else {
                return this.state == 3 || this.state == 1;
            }
        }

        public void success() {
            if (STATE.getAndSet(this, 3) != 3) {
                try {
                    this.actual.onComplete();
                } finally {
                    this.disposeResource(false);
                }
            }
        }

        public void success(@Nullable T value) {
            if (value == null) {
                this.success();
            } else {
                int s;
                do {
                    s = this.state;
                    if (s == 3 || s == 1) {
                        Operators.onNextDropped(value, this.actual.currentContext());
                        return;
                    }

                    if (s == 2) {
                        if (STATE.compareAndSet(this, s, 3)) {
                            try {
                                this.actual.onNext(value);
                                this.actual.onComplete();
                            } finally {
                                this.disposeResource(false);
                            }
                        }
                        return;
                    }
                    this.value = value;
                } while (!STATE.compareAndSet(this, s, 1));
            }
        }

        public void error(Throwable e) {
            if (STATE.getAndSet(this, 3) != 3) {
                try {
                    this.actual.onError(e);
                } finally {
                    this.disposeResource(false);
                }
            } else {
                Operators.onOperatorError(e, this.actual.currentContext());
            }
        }

        public MonoSink<T> onRequest(LongConsumer consumer) {
            Objects.requireNonNull(consumer, "onRequest");
            if (!REQUEST_CONSUMER.compareAndSet(this, (Object) null, consumer)) {
                throw new IllegalStateException("A consumer has already been assigned" +
                        " to consume requests");
            } else {
                return this;
            }
        }

        public CoreSubscriber<? super T> actual() {
            return this.actual;
        }

        public MonoSink<T> onCancel(Disposable d) {
            Objects.requireNonNull(d, "onCancel");
            SinkDisposable sd = new SinkDisposable((Disposable) null, d);
            if (!DISPOSABLE.compareAndSet(this, (Object) null, sd)) {
                Disposable c = this.disposable;
                if (c instanceof SinkDisposable) {
                    SinkDisposable current = (SinkDisposable) c;
                    if (current.onCancel == null) {
                        current.onCancel = d;
                    } else {
                        d.dispose();
                    }
                }
            }
            return this;
        }

        public MonoSink<T> onDispose(Disposable d) {
            Objects.requireNonNull(d, "onDispose");
            SinkDisposable sd = new SinkDisposable(d, (Disposable) null);
            if (!DISPOSABLE.compareAndSet(this, (Object) null, sd)) {
                Disposable c = this.disposable;
                if (c instanceof SinkDisposable) {
                    SinkDisposable current = (SinkDisposable) c;
                    if (current.disposable == null) {
                        current.disposable = d;
                    } else {
                        d.dispose();
                    }
                }
            }
            return this;
        }

        public void request(long n) {
            if (Operators.validate(n)) {
                LongConsumer consumer = this.requestConsumer;
                if (consumer != null) {
                    consumer.accept(n);
                }

                int s;
                do {
                    s = this.state;
                    if (s == 2 || s == 3) {
                        return;
                    }

                    if (s == 1) {
                        if (STATE.compareAndSet(this, s, 3)) {
                            try {
                                this.actual.onNext(this.value);
                                this.actual.onComplete();
                            } finally {
                                this.disposeResource(false);
                            }
                        }
                        return;
                    }
                } while (!STATE.compareAndSet(this, s, 2));

            }
        }

        public void cancel() {
            if (STATE.getAndSet(this, 3) != 3) {
                this.value = null;
                this.disposeResource(true);
            }
        }

        void disposeResource(boolean isCancel) {
            Disposable d = this.disposable;
            if (d != OperatorDisposables.DISPOSED) {
                d = (Disposable) DISPOSABLE.getAndSet(this, OperatorDisposables.DISPOSED);
                if (d != null && d != OperatorDisposables.DISPOSED) {
                    if (isCancel && d instanceof SinkDisposable) {
                        ((SinkDisposable) d).cancel();
                    }
                    d.dispose();
                }
            }
        }
    }
}

上面的代码比较多,我们主要关注下面两个函数:

MonoCreate(Consumer<MonoSink<T>> callback) {
    this.callback = callback;
}

public void subscribe(CoreSubscriber<? super T> actual) {
    MonoCreate.DefaultMonoSink<T> emitter = new MonoCreate.DefaultMonoSink(actual);
    actual.onSubscribe(emitter);

    try {
        this.callback.accept(emitter);
    } catch (Throwable var4) {
        emitter.error(Operators.onOperatorError(var4, actual.currentContext()));
    }
}

通过上面的代码可以看出,一个是构造器,参数是Consumer,里面进行操作保存了Consumer对象,然后在subscribe方法里面有一句代码是this.callback.accept(emitter),就是在这里进行了接口的回调,回调Consumeraccept方法,这个方法是在调用Mono.create()方法的时候实现了。然后在细看subscribe方法,这里面有一个actual.onSubscribe方法,通过方法名可以知道,这里是订阅了消息。webflux是基于reactor模型,基于事件消息和异步,这里也体现了一个异步。

MonoFlux的其他用法可以参照上面的源码流程自己看看,就不细说了。

参考文章

posted @ 2022-04-26 09:46  夏尔_717  阅读(562)  评论(0编辑  收藏  举报