14.4、SpringWebFlux-2

news2025/7/21 4:18:51

14.4、SpringWebFlux-2

14.4.3、SpringWebFlux执行流程和核心 API

SpringWebFlux 基于 Reactor,默认容器是 NettyNetty 是高性能的 NIO 框架,异步非阻塞(AIO,是 NIO 的升级)的框架

14.4.3.1、执行流程

Netty

  • BIO

    image-20221118163742764

  • NIO(非阻塞)

    image-20221118163829711

    • 数据读取到 channelchannel 注册到 selectorselector 监听 channel 变化,channel 发生读取或写操作,该线程就执行相应的操作

SpringWebFlux 执行过程和 SpringMVC 基本上是一样的

SpringWebFLux 核心控制器 DispatchHandler,实现接口 WebHandler

  • HandlerMapping:请求查询到处理的方法
  • HandlerAdapter:真正负责qin跪求出来
  • HandlerResultHandler:响应结果处理
  1. 将依赖改为 spring-boot-starter-webflux
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <!--<artifactId>spring-boot-starter</artifactId>-->
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

  1. 接口 WebHandler 有一个方法

    public interface WebHandler {
        Mono<Void> handle(ServerWebExchange var1);
    }
    
  2. 进入实现类 DispatchHandler

    image-20221118170027929

  3. public Mono<Void> handle(ServerWebExchange exchange) {// 放 http 请求响应信息
        return this.handlerMappings == null ? 
            // 若 handlerMappings 为空则直接抛异常
            this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
            //根据请求地址获取 handler对象(处理器对象)
            return mapping.getHandler(exchange);
        }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
            //调用相应的处理业务方法(更 SpringMVC 的调用处理器的方法是一样的,都用到了适配器模式)
            return this.invokeHandler(exchange, handler);
        }).flatMap((result) -> {
            //返回处理的结果
            return this.handleResult(exchange, result);
        });
    }
    

this.invokeHandler(exchange, handler);

  • private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
        if (this.handlerAdapters != null) {
            Iterator var3 = this.handlerAdapters.iterator();
    
            while(var3.hasNext()) {
                HandlerAdapter handlerAdapter = (HandlerAdapter)var3.next();
                //遍历寻找当前处理器对应的所支持的适配器,然后执行相应的方法返回
                if (handlerAdapter.supports(handler)) {
                    return handlerAdapter.handle(exchange, handler);
                }
            }
        }
    
        return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
    }
    

SpringMVC 中的适配器(controller)。与上述的差别是:SpringMVC 只是返回了相应的适配器对象,然后在 doDispatch 方法中进行处理:mv = ha.handle(...),返回的是 modelAndView

  • image-20221008003640839

实际上就是:根据映射规则查找响应的控制器,在执行对应的控制器方法,最后返回渲染后的结果

14.4.3.1、核心API

SpringWebFlux 实现函数式编程,两个接口:RouterFunction(路由处理)、HandlerFunction(处理具体的函数)

HandlerFunction*

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    //请求的处理方法在里面执行
    Mono<T> handle(ServerRequest var1);
}

RouterFunction 一般都用其工具类 RouterFunctions来进行路由

@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
    Mono<HandlerFunction<T>> route(ServerRequest var1);

    default RouterFunction<T> and(RouterFunction<T> other) {
        return new RouterFunctions.SameComposedRouterFunction(this, other);
    }

    default RouterFunction<?> andOther(RouterFunction<?> other) {
        return new RouterFunctions.DifferentComposedRouterFunction(this, other);
    }

    default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
        return this.and(RouterFunctions.route(predicate, handlerFunction));
    }

    default RouterFunction<T> andNest(RequestPredicate predicate, RouterFunction<T> routerFunction) {
        return this.and(RouterFunctions.nest(predicate, routerFunction));
    }

    default <S extends ServerResponse> RouterFunction<S> filter(HandlerFilterFunction<T, S> filterFunction) {
        return new RouterFunctions.FilteredRouterFunction(this, filterFunction);
    }

    default void accept(RouterFunctions.Visitor visitor) {
        visitor.unknown(this);
    }
}

14.4.4、SpringWebFlux(基于注解编程模型)

SpringWebFlux 实现方式有 2 2 2 种,注解编程模型和函数式编程模型

使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器

  1. 创建 SpringBoot 工程,引入相关依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    
  2. 配置启动的端口号 application.properties

    server.port=8081
    
  3. 创建相应的 serviceentitycontroller

    • entityUser

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private String name;
          private String gender;
          private Integer age;
      }
      
    • Service

      Uservice

      public interface UserService {
          /**
           * 根据 id 查询用户
           *
           * @param id 用户id
           * @return 返回发布者
           */
          Mono<User> getUserById(Integer id);
      
          /**
           * 查询所有用户
           *
           * @return 返回所有的发布者
           */
          Flux<User> getAllUser();
      
          /**
           * 添加用户
           *
           * @param user 用户发布者
           * @return 返回空的发布者
           */
          Mono<Void> saveUserInfo(Mono<User> user);
      }
      

      UserServiceImpl

      @Service
      public class UserServiceImpl implements UserService {
      
          /**
           * 创建 map 集合存储数据,模拟从数据库中获取数据
           */
          private final Map<Integer, User> users = new HashMap<>();
      
          public UserServiceImpl() {
              this.users.put(1, new User("lucy", "nan", 20));
              this.users.put(2, new User("mary", "nv", 18));
              this.users.put(3, new User("tom", "nan", 21));
          }
      
          @Override
          public Mono<User> getUserById(Integer id) {
              //放入数据
              return Mono.justOrEmpty(this.users.get(id));
          }
      
          @Override
          public Flux<User> getAllUser() {
              //放入集合数据
              return Flux.fromIterable(users.values());
          }
      
          @Override
          public Mono<Void> saveUserInfo(Mono<User> userMono) {
              return userMono.doOnNext(user -> {
                  //向 map 集合里面放值
                  int id = users.size() + 1;
                  users.put(id, user);
              }).thenEmpty(Mono.empty());//清空,终止信号
          }
      }
      
    • controller

      UserController

      @Controller
      public class UserController {
      
          //注入service
          @Autowired
          private UserService userService;
      
          /**
           * id查询
           */
          @GetMapping("/user/{id}")
          @ResponseBody
          public Mono<User> getUserId(@PathVariable int id) {
              return userService.getUserById(id);
          }
      
          /**
           * 查询所有
           */
          @GetMapping("/users")
          @ResponseBody
          public Flux<User> getUsers() {
              return userService.getAllUser();
          }
      
          /**
           * 添加
           */
          @PostMapping("/saveUser")
          public Mono<Void> saveUser(@RequestBody User user) {
              Mono<User> userMono = Mono.just(user);
              return userService.saveUserInfo(userMono);
          }
      
      }
      
      
  4. 启动项目,postMan 测试

    http://localhost:8081/user/1

    image-20221118195300094

    http://localhost:8081/users

    image-20221118195323778

说明

  • SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC + Servlet + Tomcat
  • SpringWebFlux 方式实现,异步非阻塞方式,基于 SpringWebFlux + Reactor + Netty

14.4.5、SpringWebFlux(基于函数式编程模型)

  • 在使用函数式编程模型操作时候,需要自己初始化服务器
  • 基于函数式编程模型时候,有两个核心接口:
    • RouterFuction(实现路由功能,请求转发给对应的 handler
    • HandlerFunction(处理请求生成响应的函数)
  • 核心任务定义两个函数式接口的实现并且启动需要的服务器
  • SpringWebFlux 请求和响应不再是 ServletRequstServletResonse。而是 ServerRequstServerResonse
  1. 把注解编程模型工程复制,删除 controller

    image-20221118201109494

  2. 创建 handler.UserHandler

    public class UserHandler {
        private final UserService userService;
    
        public UserHandler(UserService userService) {
            this.userService = userService;
        }
    
        //根据 id 查询
        public Mono<ServerResponse> getUserById(ServerRequest request) {
            //获取 id 值
            int userId = Integer.parseInt(request.pathVariable("id"));
            
            //空置处理
            Mono<ServerResponse> notFound = ServerResponse.notFound().build();
            
            //调用 service 方法得到数据
            Mono<User> userMono = this.userService.getUserById(userId);
            
            //把 userMono 进行转换为 ServerResponse 返回
            return userMono.flatMap((user ->
                    ServerResponse.ok()
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyValue(user)))
                	//如果为空抛异常
                	.switchIfEmpty(notFound);
            
            //以下方式,更上面的一样
            /*
            return ServerResponse.ok()
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(userMono, User.class);
    		*/
        }
    
        //查询所有,一个 http 请求只有 1 个响应,不可能发一个 http 请求给 n 个响应
        public Mono<ServerResponse> getAllUsers() {
            //调用 service 得到结果
            Flux<User> users = this.userService.getAllUser();
            
            //Flux<Users> 被封装到 serverResponse 的 body 中
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users, User.class);
        }
    
        //添加
        public Mono<ServerResponse> saveUser(ServerRequest request) {
            //得到 user 对象
            Mono<User> userMono = request.bodyToMono(User.class);
            this.userService.saveUserInfo(userMono);
            
            //buid 表示有订阅,有变化通知我,来执行上面的具体操作
            return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
        }
    }
    
  3. 初始化服务器,编写 Router

    image-20221118204737900

  4. Server (进行路由和服务适配)

    package com.cjf.webfluxdemo1;
    
    import com.cjf.webfluxdemo1.handler.UserHandler;
    import com.cjf.webfluxdemo1.service.UserService;
    import com.cjf.webfluxdemo1.service.impl.UserServiceImpl;
    import org.springframework.http.MediaType;
    import org.springframework.http.server.reactive.HttpHandler;
    import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
    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;
    import reactor.netty.http.server.HttpServer;
    
    import java.util.ArrayDeque;
    
    
    public class Server {
    
        //1.创建 Router 路由
        public RouterFunction<ServerResponse> routingFunction() {
            UserService userService = new UserServiceImpl();
            //创建 handler 对象
            UserHandler userHandler = new UserHandler(userService);
    
            //设置路由,路径设置,接受的格式为 JSON ,具体调用的方法去处理请求
            return RouterFunctions
                    .route(RequestPredicates.GET("/users/{id}").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))
                            , (request -> userHandler.getUserById(request)))
                    .andRoute(RequestPredicates.GET("/users").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))
                            , (request) -> userHandler.getAllUsers());
        }
    
        //2 创建服务器完成适配
        public void createReactorServer() {
            //路由和 handler 适配
            RouterFunction<ServerResponse> route = routingFunction();
            HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
            ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
    
            //创建服务器
            HttpServer httpServer = HttpServer.create();
            //现在进行构建。
            httpServer.handle(adapter).bindNow();
        }
        
       
    }
    
    
    
    • 组织路由 nest

      例如:定义 url 公共前缀

          //1.创建 Router 路由
          public RouterFunction<ServerResponse> routingFunction() {
              UserService userService = new UserServiceImpl();
              //创建 handler 对象
              UserHandler userHandler = new UserHandler(userService);
      
              //设置路由,路径设置,接受的格式为 JSON ,具体调用的方法去处理请求
              RouterFunction<ServerResponse> f = RouterFunctions.route(RequestPredicates.GET("/users/{id}").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))
                              , (request -> userHandler.getUserById(request)))
                      .andRoute(RequestPredicates.GET("/users").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))
                              , (request) -> userHandler.getAllUsers());
              
              //nest,组织定义 url 公共前缀 此时为:/here/users/{id}
              return RouterFunctions.nest(RequestPredicates.path("/here"),f);
      
          }
      

  • RouterFunction.route(...)

    public static <T extends ServerResponse> RouterFunction<T> 
        //RequestPredicate predicate 传入一个请求断言,类似于 gateway 中设置路由地址等信息,
        //HandlerFunction<T> handlerFunction 具体的处理该请求的信息,以上我们使用 lambda创建
        route(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
        return new DefaultRouterFunction(predicate, handlerFunction);
    }
    

  • HandlerFunction

    @FunctionalInterface
    public interface HandlerFunction<T extends ServerResponse> {
        Mono<T> handle(ServerRequest var1);
    }
    
  1. 进行测试

    public class Server {
    
        public static void main(String[] args) throws IOException {
            Server server = new Server();
            server.createReactorServer();
            System.out.println("enter to exit");
            System.in.read();
        }
        //....
    }
    
    • 启动,并访问端口

    image-20221118213104988

    • http://localhost:2762/users

image-20221118213218959

  1. 使用 具体的内容:WebClient 测试调用

    Client

    public class Client {
        public static void main(String[] args) {
            //调用服务器地址
            WebClient webClient = WebClient.create("http://127.0.0.1:31366");
    
            //根据 id 查询
            String id = "1";
            User user = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON)
                    //检索数据获取响应体
                    .retrieve()
                    //从中得到数据
                    .bodyToMono(User.class)
                    //执行,订阅
                    .block();
            System.out.println(user);
    
            //查询所用
            Flux<User> res = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON)
                    .retrieve()
                    .bodyToFlux(User.class);
            res.map((user1->user1.getName())).buffer().doOnNext(System.out::println).blockFirst();
        }
    }
    
    

    启动服务器,然后执行调用

    image-20221118220403608

    image-20221118221147104

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jrtplib开源库系列之三:jrtplib发送接收数据流程

说明 前面2篇文章主要说明了如何安装jrtplib库&#xff0c;以及对example1进行了说明&#xff0c;这篇文章主要说下jrtplib库数据的收发流程。 数据收发流程 从例子1就可以很好的说明jrtplib的使用是非常简单的&#xff0c;主要分为以下几步 1. 设置会话参数(比如时间戳&am…

同花顺_代码解析_技术指标_C

本文通过对同花顺中现成代码进行解析&#xff0c;用以了解同花顺相关策略设计的思想 CBJX 成本均线 成本价均线不同于一般移动平均线系统&#xff0c;成本价均线系统首次将成交量引入均线系统&#xff0c;充分提高均线系统的可靠性。成本均线不容易造成虚假信号或骗线&#xf…

牛客网项目-开发注册功能

前言 本文是对牛客网项目的总结&#xff0c;本文主要讲解页面注册逻辑 当我们点击首页的注册按钮时&#xff0c;会跳转到注册页面&#xff0c;然后再祖册页面提交账号&#xff0c;密码邮箱后会跳转到首页或者直接登录页面进行登录&#xff0c;这个操作可以自己设定 【设计逻辑…

SAP ABAP BAPI_SALESORDER_CREATEFROMDAT2 成本中心 kostl

BAPI_SALESORDER_CREATEFROMDAT2提供参数无成本中心字段&#xff0c;所以需要用扩展字段实现。 BAPE_VBAK&#xff1a; BAPE_VBAKX VBAKKOZ VBAKKOZX 封装扩展结构&#xff1a; DATA: LS_EXTENSION TYPE BAPIPAREX, LT_EXTENSION TYPE TABLE OF BAPIPAREX. DATA: LS_B…

目标检测论文解读复现之十一:基于特征融合与注意力的遥感图像小目标检测

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

【MySQL】5.触发器

文章目录1. 触发器概述2. 触发器的相关语法3. 触发的NEW与OLD4. 总结1. 触发器概述 触发器&#xff0c;就是一种特殊的存储过程。触发器和存储过程一样是一个能够完成特定功能、存储在数据库服务器上的SQL片段&#xff0c;但是触发器无需调用&#xff0c;当对数据库表中的数据…

python批量读取nc气象数据并转为tif

python批量nc数据转tif 各类地理数据中&#xff0c;NC格式是很常见的&#xff0c;然而这种格式ArcGIS是无法打开的。一旦下载的话nc也是多时序的&#xff08;多年、多月等等&#xff09;&#xff0c;让我们看看如何批量操作吧。 直接上代码&#xff1a; import numpy as np …

[附源码]Python计算机毕业设计本科生外出请假管理信息系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

数据结构和算法

1.数据结构 食谱和算法之间最大的区别就在于算法是严密的。食谱上经常会有描述得比较模糊的部分&#xff0c; 而算法的步骤都是用数学方式来描述的&#xff0c;所以十分明确。 算法和程序有些相似&#xff0c;区别在于程序是以计算机能够理解的编程语言编写而成的&#xff0c;…

构造函数详解

构造函数详解1.构造函数的概念与特性2.默认构造函数&#xff08;1&#xff09;概念&#xff08;2&#xff09;分类&#xff08;3&#xff09;工作原理3.初始化列表&#xff08;1&#xff09;定义&#xff08;2&#xff09;为什么使用初始化列表&#xff08;3&#xff09;必须使…

WebRTC系列<五>我与一位大佬的聊天记录

原本打算想用webrtc部署虚幻项目。后来在了解虚幻过程中&#xff0c;得知虚幻有像素流插件&#xff0c;导出项目里带有STUN和TURN服务&#xff0c;但是在webGL项目里比如three.js、babylon.js如果也能部署在服务器端&#xff0c;那就厉害了&#xff0c;也很有想象力空间。 基本…

表白墙网站练习【前端+后端+数据库】

表白墙网站练习【前端后端数据库】 开发该表白墙&#xff08;简单网站&#xff09;的基本步骤&#xff1a; 1.约定前后端交互接口 2.开发服务器代码 编写Servlet能够处理前端发来的请求编写数据库代码&#xff0c;来获取/存储关键数据 3.开发客户端代码 基于ajax能够构造请…

一体化Ethercat通信伺服电机在汇川H5U PLC上的应用案例介绍(上)

内容介绍了一体化低压伺服Ethercat通信的电机在汇川H5UPLC上的使用&#xff0c;本篇主要讲解环境的搭建以及使用AutoShop软件的在线调试功能&#xff0c;简单控制电机位置、速度模式运行&#xff1b; 一、系统构成 本系统主要构成是电脑&#xff0c;H5U-1614MTD-A8&#xff0c;…

家长杂志家长杂志社家长编辑部2022年第30期目录

卷首语 读懂童心,营造乐学趣学好场景 本刊编辑部; 1 本刊视线_关注《家长》投稿&#xff1a;cn7kantougao163.com 留守儿童学习动力不足的成因与激发策略 蔡斌林; 4-6 农村留守儿童加强心理健康教育的策略 张芸; 7-9 本刊视线_学校体育 中学体育线上线下教学融…

【Struts2框架】idea快速搭建struts2框架

文章目录什么是SSH框架&#xff1f;Struts2框架1、struts2的环境搭建1.1 创建web项目&#xff08;maven&#xff09;&#xff0c;导入struts2核心jar包1.2 配置web.xml&#xff08;过滤器&#xff09;&#xff0c;是struts2的入口&#xff0c;先进入1.3 创建核心配置文件struts…

STM32 Bootloader开发记录 3 固件签名校验

STM32 Bootloader开发记录 3 固件签名校验 文章目录STM32 Bootloader开发记录 3 固件签名校验1. 移植mbedtls1.1 编译mbedtls1.2 修复rsa_sign的一个bug1.3 测试RSA1.3.1 **RSA加解密&#xff1a;**1.3.2 **RSA签名验签&#xff1a;**1.3.3 **生成秘钥对**1.4 移植到STM321.4.1…

NFV中:DPDK与SR-IOV应用场景及性能对比

DPDK与SR-IOV两者目前主要用于提高IDC&#xff08;数据中心&#xff09;中的网络数据包的加速。但是在NFV&#xff08;网络功能虚拟化&#xff09;场景下DPDK与SR-IOV各自的使用场景是怎样的&#xff1f;以及各自的优缺点&#xff1f; 本文主要通过从以下几点来阐述这个问题&a…

视觉SLAM十四讲(高翔版本),ch4章节部分笔记

目标&#xff1a;理解slam的框架以及它的理论知识。供以后自己查阅。 这一章主要非常重要&#xff0c;也是理解后续优化的基础&#xff0c;它是将旋转矩阵和平移向量&#xff0c;转化为李代数的形式进行优化&#xff0c;因为它有很多好处。好处如下&#xff1a; 意思就是采用…

Linux硬盘垃圾清理心得

最近有台系统盘才10G的服务器咔咔报警&#xff0c;一共才10G的空间&#xff0c;运维还设置了80%的报警阈值&#xff0c;实在难顶。为了清理硬盘里的垃圾&#xff0c;敲了不少命令&#xff0c;怕以后忘了&#xff0c;记录一下。 首先输入df -h查看一下硬盘空间占用情况&#xf…

呼叫中心中间件(mod_cti基于FreeSWITCH)-通话记录(CDR)接口

支持把FreeSWITCH的通话记录写入mysql,sqlserver,oracle等数据库&#xff0c;也可以写入redis的list&#xff0c;或者PUBLISH到redis的channel,方便业务程序实时获取通话记录。 使用说明 如果一个通话是A呼叫B&#xff0c;那么就有2个通话记录&#xff0c;一个叫aleg,一个叫b…