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.rhkb.cn/news/282209.html

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

相关文章

【Charles如何对手机APP进行抓包和弱网测试】

一、Charles对APP抓包 1、前提条件&#xff1a; 1&#xff09;电脑上必须安装Charles工具&#xff0c;如没有安装可参考&#xff1a;【Charles抓包工具下载安装详细操作步骤】-CSDN博客 2&#xff09;手机和电脑必须在同一个局域网内&#xff08;连接同一个WiFi&#xff09;…

ng发布静态资源 发布项目 发布数据

描述&#xff1a;把一个项目或者数据发布出来&#xff0c;通过http的形式访问&#xff0c;比如发布一个js文件&#xff0c;用http://localhost:6060/data/jquery/jquery.min.js访问。 步骤&#xff1a;配置nginx.conf文件&#xff0c;nginx.conf位于conf目录下&#xff0c;在se…

P2822 [NOIP2016 提高组] 组合数问题题解

题目 组合数表示的是从n个物品中选出m个物品的方案数。举个例子&#xff0c;从(1,2,3) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3)这三种选择方法。根据组合数的定义&#xff0c;我们可以给出计算组合数的一般公式&#xff1a; 其中n!12⋯n&#xff1b;特别地&#xff0…

Java 在PDF中插入页眉、页脚

在处理PDF文档时&#xff0c;有时需要为文档中的每一页添加页眉和页脚&#xff0c;以包含一些有用的信息&#xff0c;如文档标题、章节名称、日期、页码等。对于需要自动化处理的场景&#xff0c;或者需要在大量文档中添加一致的页眉和页脚&#xff0c;可以通过编程的方式来实现…

QGraphicsView的使用,view坐标,scene坐标,item坐标

Graphics View绘图构架 QGraphicsScene&#xff08;场景&#xff09;&#xff1a;可以管理多个图形项QGraphicsItem&#xff08;图形项&#xff09;&#xff1a;也就是图元&#xff0c;支持鼠标事件响应。QGraphicsView&#xff08;视图&#xff09;&#xff1a;关联场景可以让…

【数据库系统】数据库完整性和安全性

第六章 数据库完整性和安全性 基本内容 安全性&#xff1b;完整性&#xff1b;数据库恢复技术&#xff1b;SQL Server的数据恢复机制&#xff1b; 完整性 实体完整性、参照完整性、用户自定义完整性 安全性 身份验证权限控制事务日志&#xff0c;审计数据加密 数据库恢复 冗余…

Redis学习二--常见问题及处理

基本概念 Redis基本概念数据结构 机制 持久化机制&#xff1a; RDB(内存快照)&#xff1a;某一时刻的内存快照以二进制的方式写入磁盘&#xff0c;可以手动触发和自动触发。 优点&#xff1a;生成文件小&#xff0c;恢复速度快&#xff0c;适用于灾难恢复。 缺点&#xff1a…

关于Zookeeper分布式锁

背景 之前说到分布式锁的实现有三种 1、基于数据库实现的分布式锁 2、Redis分布式锁 3、Zookeeper分布式锁 前者redis分布式锁博客已具体介绍&#xff0c;此博客最终决定补齐关于Zookeeper分布式锁的实现原理。 简述 Zoopkeeper&#xff0c;它是一个为分布式的协调服务&…

固态继电器(SSR)您需要了解的一切

固态继电器&#xff08;也称SSR&#xff0c;SS继电器或SSR开关&#xff09;是一种集成的非接触式电子开关设备&#xff0c;由集成电路&#xff08;IC&#xff09;和分立组件紧密组装而成。处于现代电气应用的最前沿&#xff0c;与机电同类产品相比&#xff0c;具有许多优势。本…

重学SpringBoot3-Profiles介绍

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-Profiles介绍 Profiles简介如何在Spring Boot中使用Profiles定义Profiles激活ProfilesIDEA设置active profile使用Profile-specific配置文件 条件化Bean…

flex布局

文章目录 1. 概念2. 和浮动的区别3. 伸缩容器和伸缩项目3.1. 伸缩容器3.2. 伸缩项目 4. 主轴与侧轴5. 主轴属性6. 纵轴属性6.1. align-self 示例 7. flex 实现水平垂直居中7.1. 方法一7.2. 方法二 8. 伸缩性8.1. flex-basis8.2. flex-shrink8.3. flex-grow&#xff08;伸&#…

如何做人才运营战略?

招聘人才和人才获取是同义词&#xff0c;但它们并不相同。招聘是大多数雇主的短期解决方案&#xff0c;而人才获取是一个长期解决方案。 企业要想改善企业文化朝着统一的愿景努力&#xff0c;就需要关注长期规划。 人才获取vs人才招聘 招聘是为了填补空缺&#xff0c;人才获取…

在服务器(Ubuntu20.04)安装用户级别的cuda11.8

1、cuda11.8的下载 首先在cuda官网下载我们需要的cuda版本&#xff0c;这里我下载的是cuda11.8&#xff08;我的最高支持cuda12.0&#xff09; 这里我直接使用wget命令下载不了&#xff0c;于是我直接在浏览器输入后面的链接下载到本地&#xff0c;之后再上传至服务器的&am…

SpringBoot2.7集成Swagger3

Swagger2已经在17年停止维护了&#xff0c;取而代之的是 Swagger3&#xff08;基于openApi3&#xff09;&#xff0c;所以新项目要尽量使用Swagger3. Open API OpenApi是业界真正的 api 文档标准&#xff0c;其是由 Swagger 来维护的&#xff0c;并被linux列为api标准&#x…

Stable Diffusion WebUI 生成参数:宽度/高度/生成批次/每批数量/提示词相关性/随机种子

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 大家好,我是水滴~~ 本文将继续了解 Stable Diffusion WebUI 的生成参数,主要内容有:宽度、高度、生成批次、每批数量、提示词相关性、随机种子。希望能对你有所帮助。 文章目录 宽度(Width)和高度(Height)…

前端实例:页面布局1(后端数据实现)

效果图 注&#xff1a;这里用到后端语言php&#xff08;页面是.php文件&#xff09;,提取纯html也可以用 inemployee_index.php <?php include(includes/session.inc); $Title _(内部员工首页); $ViewTopic 内部员工首页; $BookMark 内部员工首页; include(includes/…

异地组网有哪些实现方式?为什么要选择SD-WAN?

建立跨地域的异地组网在当前数字化时代变得越来越重要&#xff0c;主要是因为企业业务的不断扩展和多样化。异地网络连接不仅有助于改善内部通信效率&#xff0c;还能提高数据处理能力和业务连续性。那么&#xff0c;到底有哪些方式可以实现异地组网呢&#xff1f;应该选择哪种…

【Linux】进程

本文主要介绍了进程的相关理解&#xff1a;查看进程、进程状态、进程的优先级、环境变量、进程地址空间、Linux内核进程调度队列。 目录 冯诺依曼体系结构 操作系统 进程 查看进程 几点预备小知识 进程创建的代码方式 为什么要创建子进程 样例代码&#xff1a;依次创建多…

【自然语言处理七-经典论文-attention is all you need】

然语言处理七-经典论文-attention is all you need 摘要原文译文小结 1&#xff1a;引言原文译文小结 2&#xff1a;背景原文译文小结 3&#xff1a;模型架构原文译文小结 3.1 编码器和解码器原文译文小结 3.2 注意力原文译文小结3.2.1 缩放点积注意力原文总结 3.2.2 多头注意力…

用例图画法

介绍 在软件工程中&#xff0c;用例图是一种用于描述系统功能和与之交互的参与者&#xff08;Actors&#xff09;之间关系的图形表示方法。 绘图步骤 确定参与者&#xff08;Actors&#xff09;&#xff1a;识别系统中的各个参与者&#xff0c;这些参与者可以是人、其他系统或外…