SpringBoot实战(二十七)集成WebFlux

目录

    • 一、WebFlux
      • 1.1 定义
      • 1.2 WebFlux 与 Spring MVC 区别
    • 二、代码实现
      • 2.1 Maven 配置
      • 2.2 暴露 RESTful API 接口的方式
        • 方式一:基于注解的控制器
        • 方式二:函数式路由器(Functional Endpoints)
      • 2.3 测试Service
      • 2.4 测试ServiceImpl
      • 2.5 测试实体类
      • 2.6 启动类
    • 三、测试结果
      • 3.1 基于注解的控制器-测试
      • 3.2 函数式路由器-测试
        • 1)添加用户接口
        • 2)查询所有用户接口
        • 3)根据ID查询用户接口

一、WebFlux

1.1 定义

WebFlux 是 Spring Framework 5 引入的一个模块,它是一个 非阻塞的、异步的、响应式的 Web 开发框架。WebFlux 设计的核心是为了 使用现代 Web 应用对于高并发、低延迟和高吞吐量的需求,它采用 Reactive 编程模型,通过 Reactor 库实现了异步数据流处理。

  • 在 WebFlux 中,HTTP 请求和响应被建模为 Mono(代表 0~1 个元素的异步序列)和 Flux(代表 0~N个元素的异步序列)类型,这些都是 Reactive Streams 规范的一部分。这意味着 开发者可以通过声明式和函数式编程风格来处理请求和响应的数据流。

WebFlux 提供了两种编程模型:

  1. 注解式控制器: 使用 @Controller 等注解,类似 Spring MVC 的开发体验。
  2. 函数式编程控制器: 使用 Java 8 函数式接口定义路由和处理逻辑。

1.2 WebFlux 与 Spring MVC 区别

WebFlux:

  1. 异步非阻塞: WebFlux 基于反应式编程模型,支持非阻塞 I/O,能够充分利用多核 CPU 资源,并且在高并发场景下具有更好的性能表现,因为 它不会为每个请求分配独立的线程,从而避免了线程上下文切换带来的开销
  2. 响应式编程: WebFlux 使用 Project Reactor(或者 RxJava 作为备选)提供的 Mono 和 Flux 类型来表示可能零个、一个或多个事件的异步序列,使得开发者可以编写异步数据处理逻辑。
  3. 无需 Servlet API: 尽管可以在 Servlet 容器上运行,但它不直接依赖 Servlet API,能在非阻塞服务器(如 NettyUndertow 等)上运行。
  4. 函数式编程风格: 除了提供类似于 Spring MVC 的注解式编程模型外,WebFlux 还支持函数式编程模型,允许通过 RouterFunction 等方式进行更灵活的路由配置。

Spring MVC:

  1. 同步阻塞: Spring MVC 基于传统的 Servlet API,每个 HTTP 请求通常都会绑定到一个单独的线程直到请求处理完成并发送响应为止
  2. 线程模型: 在默认情况下,Spring MVC 应用中,每个请求会创建或从线程池获取一个线程,处理完成后释放回线程池。这种模式在请求处理复杂度较高或线程池大小受限时,可能会影响系统的并发能力。
  3. 依赖 Servlet 容器: Spring MVC 必须部署在支持 Servlet API 的容器中运行(如:Tomcat、Jetty、Undertow、Weblogic等)。
  4. API 和编程模型: Spring MVC 主要采用注解驱动的方式组织控制器和处理请求响应,例如通过 @Controller@RequestMapping 等注解。

二、代码实现

2.1 Maven 配置

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

2.2 暴露 RESTful API 接口的方式

在 Spring WebFlux 框架中,暴露 RESTful API 接口主要有以下两种方式:

方式一:基于注解的控制器
  • 使用 @RestController 注解定义一个控制器类,通过 @RequestMapping@GetMapping@PostMapping 等注解来指定请求路径和 HTTP 方法,处理客户端的请求和响应。

DemoController.java

import com.demo.service.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.annotation.Resource;/*** <p> @Title DemoController* <p> @Description 测试Controller** @author ACGkaka* @date 2023/4/24 18:02*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {@Resourceprivate DemoService demoService;/*** webflux接口测试(返回 0个 或 1个结果)*/@GetMapping("/monoTest")public Mono<Object> monoTest() {/// 写法一:命令式写法
//        String data = getOneResult("monoTest()");
//        return Mono.just(data);// 写法二:响应式写法(语句需要在流中执行)return Mono.create(cityMonoSink -> {String data = demoService.getOneResult("monoTest()");cityMonoSink.success(data);});}/*** webflux接口测试(返回 0个 或 多个结果)*/@GetMapping("/fluxTest")public Flux<Object> fluxTest() {// 写法一:命令式写法
//        List<String> list = getMultiResult("fluxTest()");
//        return Flux.fromIterable(list);// 写法二:响应式写法(语句需要在流中执行)return Flux.fromIterable(demoService.getMultiResult("fluxTest()"));}}
方式二:函数式路由器(Functional Endpoints)
  • 使用 RouterFunctions.route() 或者 RouterFunction<ServerResponse> 来创建路由函数,这种方式更加函数式和声明式。

RouteConfig.java

import com.demo.config.handler.UserReactiveHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 javax.annotation.Resource;import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;/*** <p> @Title RouteConfig* <p> @Description 路由配置** @author ACGkaka* @date 2024/3/21 13:39*/
@Configuration
public class RouteConfig {@Resourceprivate UserReactiveHandler handler;@Beanpublic RouterFunction<ServerResponse> routes() {// 下面的操作相当于 @RequestMappingreturn RouterFunctions.route(POST("/addUser"), handler::addUser).andRoute(GET("/userList"), handler::userList).andRoute(GET("/findUserById/{id}"), handler::findUserById);}
}

2.3 测试Service

DemoService.java

import java.util.List;/*** <p> @Title DemoService* <p> @Description 测试Service** @author ACGkaka* @date 2024/3/20 11:46*/
public interface DemoService {/*** 模拟业务处理,返回单个结果*/String getOneResult(String methodName);/*** 模拟业务处理,返回多个结果*/List<String> getMultiResult(String methodName);/*** 添加用户*/User addUser(User user);/*** 查询所有用户*/List<User> findAllUser();/*** 根据 id 查询用户*/User findUserById(Long id);}

2.4 测试ServiceImpl

DemoServiceImpl.java

import com.demo.entity.User;
import com.demo.service.DemoService;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;/*** <p> @Title DemoServiceImpl* <p> @Description 测试ServiceImpl** @author ACGkaka* @date 2024/3/20 11:46*/
@Service
public class DemoServiceImpl implements DemoService {@Overridepublic String getOneResult(String methodName) {// 模拟业务处理,返回单个结果return String.format("%s方法调用成功", methodName);}@Overridepublic List<String> getMultiResult(String methodName) {// 模拟业务处理,返回多个结果List<String> list = new ArrayList<>(3);for (int i = 0; i < 3; i++) {list.add(String.format("%s方法调用成功,第 %d 条", methodName, i + 1));}return list;}@Overridepublic User addUser(User user) {// 添加用户user.setId(1L);return user;}@Overridepublic List<User> findAllUser() {// 查询所有用户List<User> list = new ArrayList<>();for (int i = 0; i < 3; i++) {int no = i + 1;list.add(new User((long) no, "USER_" + no, "PWD_" + no, 18 + no));}return list;}@Overridepublic User findUserById(Long id) {// 根据 id 查询用户return new User(id, "USER_" + id, "PWD_" + id, 18);}
}

2.5 测试实体类

User.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** <p> @Title User* <p> @Description 用户信息** @author ACGkaka* @date 2024/3/21 11:12*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {/*** 主键*/private Long id;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 年龄*/private Integer age;
}

2.6 启动类

SpringbootDemoApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;@SpringBootApplication
public class SpringbootDemoApplication {/*** 可以使用以下两种方式创建 ApplicationContext*/public static void main(String[] args) {// 方式一SpringApplication.run(SpringbootDemoApplication.class, args);// 方式二:使用 SpringApplicationBuilder 来创建 SpringApplication。// builder 提供了链式调用 API,更加方便,可读性更强。
//        SpringApplicationBuilder builder = new SpringApplicationBuilder()
//                .web(WebApplicationType.REACTIVE).sources(SpringbootDemoApplication.class);
//        builder.run(args);}}

三、测试结果

3.1 基于注解的控制器-测试

Mono<T> 返回类型的接口测试:

  • 请求地址: http://localhost:8080/demo/monoTest

  • 请求结果:

在这里插入图片描述

Flux<T> 返回类型的接口测试:

  • 请求地址: http://localhost:8080/demo/fluxTest
  • 请求结果:

在这里插入图片描述

3.2 函数式路由器-测试

1)添加用户接口
  • 请求地址: http://localhost:8080/addUser
  • 请求结果:(失败测试)

在这里插入图片描述

  • 请求结果:(成功测试)

在这里插入图片描述

2)查询所有用户接口
  • 请求地址: http://localhost:8080/userList
  • 请求结果:

在这里插入图片描述

3)根据ID查询用户接口
  • 请求地址: http://localhost:8080/findUserById/123
  • 请求结果:

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.webflux + springboot 整合(史上最全),https://blog.csdn.net/crazymakercircle/article/details/112977951

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

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

相关文章

Streamlit实战手册:从数据应用到机器学习模型部署

Streamlit实战手册&#xff1a;从数据应用到机器学习模型部署 简介Streamlit核心功能介绍Streamlit的安装创建第一个Streamlit应用界面布局与导航数据处理与展示 Streamlit的进阶应用交互式组件按钮复选框单选按钮滑块 图表与可视化使用Matplotlib绘图使用Plotly创建交互式图表…

【题目】【网络系统管理】2019年全国职业技能大赛高职组计算机网络应用赛项H卷

极安云科专注职业教育技能竞赛培训4年&#xff0c;包含信息安全管理与评估、网络系统管理、网络搭建等多个赛项及各大CTF模块培训学习服务。本团队基于赛项知识点&#xff0c;提供完整全面的系统性理论教学与技能培训&#xff0c;成立至今持续优化教学资源与讲师结构&#xff0…

Springboot 整合 Knife4j (API文档生成工具)

目录 一、Knife4j 介绍 二、Springboot 整合 Knife4j 1、pom.xml中引入依赖包 2、在application.yml 中添加 Knife4j 相关配置 3、打开 Knife4j UI界面 三、关于Knife4j框架中常用的注解 1、Api 2、ApiOperation ​3、ApiOperationSupport(order X) ​4、ApiImplici…

Leetcode 994. 腐烂的橘子

心路历程&#xff1a; 一开始以为和刚做过的岛屿问题很像&#xff0c;只不过是把岛屿问题换成BFS去做&#xff0c;然后再加上一些计数的规则。结果做完后发现只能通过一半左右的测试用例&#xff0c;发现有一个逻辑错误在于&#xff0c;当腐烂的橘子位于两端时&#xff0c;可以…

课时71:流程控制_for循环_综合案例

2.4.5 综合案例 学习目标 这一节&#xff0c;我们从 信息收集、其他实践、小结 三个方面来学习。 信息收集 案例需求 根据提示信息&#xff0c;选择输出 cpu 或者 内存信息。脚本实践-采集系统负载信息 查看脚本内容 [rootlocalhost ~]# cat systemctl_load.sh #!/bin/bas…

C#探索之路基础篇(2):接口Interface的概念、实现、应用范围

文章目录 1 概念2 示例代码&#xff1a;2.1 简单接口的实现2.2 简单的使用接口2.3 使用接口呈现多态性2.4 通过接口实现一个数组迭代器2.5 通过接口来实现松耦合的关系2.6 使用接口实现可扩展、便利性 3 使用范围与时机4 注意事项 不知道大家在学习的过程中&#xff0c;有没有反…

基于Springboot的个人博客系统的设计与实现

目录 1. 第5章 数据库设计 1.1. 数据库概念设计 1.1.1. 用户信息实体 1.1.2. 文章信息实体 1.1.3. 评论信息实体 1.1.4. 附件信息实体 1.1.5. 类别信息实体 1.1.6. 日志信息实体 1.2. 数据库表结构设计 基于Springboot的个人博客系统的设计与实现 第5章 数据库设计 …

笔试总结01

1、spring原理 1、spring原理 spring的最大作用ioc/di,将类与类的依赖关系写在配置文件中&#xff0c;程序在运行时根据配置文件动态加载依赖的类&#xff0c;降低的类与类之间的藕合度。它的原理是在applicationContext.xml加入bean标记,在bean标记中通过class属性说明具体类…

148 Linux 网络编程4 ,高并发服务器 --多路I/O转接服务器 - poll 这个非重点,

Poll 的实现和 select 很像。 实际上poll 的核心就是我们select的优化版本&#xff0c;加入了一个数组&#xff0c; 还将传入传出参数分离开了 #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);fds &#xff1a;需要监听的--文件描述符数组…

数据挖掘|数据集成|基于Python的数据集成关键问题处理

数据挖掘|数据集成|基于Python的数据集成关键问题处理 1. 实体识别2. 数据冗余与相关性分析3. 去除重复记录4. 数据值冲突的检测与处理5. 基于Python的数据集成5.1 merge()方法5.2 Concat()方法 数据集成是把来自多个数据库或文件等不同数据源的数据整合成一致的数据存储。其中…

基于docker创建深度学习开发环境

基于docker创建深度学习开发环境 记录几个链接 第一步&#xff1a;配置docker环境&#xff0c;此处大把教程&#xff0c;不再赘述第二步&#xff1a;拉取nvidia做好的cuda和cudnn镜像&#xff1a; docker pull nvcr.io/nvidia/cuda:12.2.0-devel-ubuntu20.04如果有其他需求&a…

vue3+threejs新手从零开发卡牌游戏(二):初始化场景

在删掉初始化中一些没用的代码后&#xff0c;在views目录下新建game文件夹&#xff0c;在里面新建一个index.vue&#xff0c;这里就当成游戏的主入口。 目录结构如下&#xff1a; 下面开始尝试创建场景&#xff1a; 一、添加一个div作为threejs的画布对象&#xff0c;之后整个…

ubuntu - 编译 linphone-sdk

业务需求需要定制sdk&#xff0c;首先声明我们需要的是在Android4.4上跑的sdk&#xff0c;因此本次编译的sdk最低支持为19&#xff08;不同版本需要的环境不一致&#xff09;&#xff0c;编译过程较容易&#xff0c;难点在于环境配置 环境准备 Ubuntu 18.04.6 android-sdk_r24.…

mysql分页查询多用GitCode平台

目录 一、在GitCode平台AI搜索结果&#xff08;这个更优&#xff09; 二、在百度搜索输入“mysql Java分页查询”的输出结果&#xff1a; 三、推荐的文章 四、GitCode的使用 1&#xff09;如搜索jdk11可以直接下载jdk11的包 2&#xff09;搜索开源项目 3&#xff09;如搜…

3.21 ARM day5

串口控制LED灯亮灭 main.c #include "uart4.h"int main() {//串口初始化uart4_init();//led初始化led_init();char i;int count 0;int count2 0;int count3 0;while (1){i getchar();putchar(i);//键盘输入a&#xff0c;串口工具显示aif (i 1){count;if (coun…

步进电机驱动器的接线与使用(接线详细)

今天小编就来继续学习与使用步行电机的学习&#xff0c;如果位置对你有帮助&#xff0c;评论收藏&#xff0c;点赞一下 步进电机驱动器 步进电机驱动器是一种专用于控制步进电机的电子设备&#xff0c;用于控制步进电机的转动和位置。步进电机是一种将电信号转换为机械运动的电…

Compose UI 之 Segmented buttons 分段按钮

Segmented buttons SegmentedButton 是一种分段式按钮组件,它允许用户在一组相关的选项中选择一个或几个。 上图中:① 单选的分段式按钮。② 多选的分段式按钮。 分段式按钮的几个特点: 分段式按钮是带有状态的按钮,又有单选和多选之分。 从设计上将,不论是单选或是多选…

第二十九章 配置 Web Gateway 的默认参数 - 与 IRIS 的连接(二)

文章目录 第二十九章 配置 Web Gateway 的默认参数 - 与IRIS 的连接(二)Event Log Rotation SizeMaximum Logged Request SizeSSL/TLS Library PathPreserve Mode Exclude File Types 第二十九章 配置 Web Gateway 的默认参数 - 与IRIS 的连接(二) Event Log Rotation Size 这…

fastjson2 反序列化包含多层泛型结构的实体类

前言 由于使用到httpUtill来调用接口 工具类的返回是字符串 其中接口的实现返回值是个多层泛型结构的实体类 例如Result<Page<UserDto>> 当使用 JSON.parseObject("res",new TypeReference<Result<Page<UserDto>>>{})发现在page中存在…

【GPT概念-03】:人工智能中的注意力机制

说明 注意力机制生成分数&#xff08;通常使用输入函数&#xff09;&#xff0c;确定对每个数据部分的关注程度。这些分数用于创建输入的加权总和&#xff0c;该总和馈送到下一个网络层。这允许模型捕获数据中的上下文和关系&#xff0c;而传统的固定序列处理方法可能会遗漏这…