Spring学习(六):WebFlux
文章目录
- WebFlux概述
- 简介
- 异步非阻塞
- 响应式编程
- 简介
- Reactor实现
- 简介
- 三种信号特点
- 操作符
- 示例代码
- SpringWebflux执行流程和核心API
- 执行流程
- 核心API
- SpringWebflux基于注解编程
- SpringWebflux基于函数式编程
- 具体步骤
- 示例代码
WebFlux概述
简介
- WebFlux是Spring5新添加的模块以用于web开发,功能和SpringMVC类似。
- Webflux使用响应式编程的框架。
- Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于Reactor的相关 API 实现的。
异步非阻塞
异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步。
阻塞和非阻塞针对被调用者,被调用者收到请求之后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后再去做事情就是非阻塞。
响应式编程
简介
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便
地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。比如Excel表格中的公式,公式的值会随着单元格的变化而变化。
Java8提供观察者模式两个类Observer和Observable,Java9提供Flow类实现响应式编程。
Reactor实现
简介
- Reactor是满足Reactive规范的框架
- Reactor有Mono和Flux两个核心类,这两个类都实现了Publisher接口,提供丰富操作符。Flux对象实现发布者,返回 N 个元素;Mono实现发布者,返回 0 或者1个元素
- Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:元素值,错误信号,完成信号
错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了。错误信号终止数据流同时把错误信息传递给订阅者
三种信号特点
- 错误信号和完成信号都是终止信号,不能共存的
- 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
- 如果没有错误信号,没有完成信号,表示是无限数据流
操作符
对数据流进行一道道操作成为操作符,比如工厂流水线。
- map元素映射为新元素
比如上图对1、2和3进行了平方操作然后输出 - flatMap元素映射为流
把每个元素转换流,把转换之后多个流合并大的流
示例代码
# 引入依赖
<dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId><version>3.1.5.RELEASE</version>
</dependency>
public static void main(String[] args) {//just 方法直接声明
Flux.just(1,2,3,4).subscribe(System.out::println);
Mono.just(1).subscribe(System.out::print);//其他的方法(集合和流)Integer[] array = {1,2,3,4};Flux.fromArray(array);List<Integer> list = Arrays.asList(array);Flux.fromIterable(list);Stream<Integer> stream = list.stream();Flux.fromStream(stream);
}
SpringWebflux执行流程和核心API
SpringWebflux基于 Reactor,默认使用容器是 Netty,Netty是高性能的 NIO(同步非阻
塞) 框架。
执行流程
SpringWebflux核心控制器DispatchHandler,实现接口WebHandler,接口中有个handler方法:
public interface WebHandler {Mono<Void> handle(ServerWebExchange var1);
}
public Mono<Void> handle(ServerWebExchange exchange) {// exchange放http请求信息return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {return mapping.getHandler(exchange);//根据请求地址获取对应mapping}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {return this.invokeHandler(exchange, handler);//调用具体的业务方法}).flatMap((result) -> {return this.handleResult(exchange, result);//处理结果返回});
}
核心API
SpringWebflux里面DispatcherHandler类负责请求的处理,具体如下:
- HandlerMapping
请求查询到处理的方法 - HandlerAdapter
真正负责请求处理 - HandlerResultHandler
响应结果处理
SpringWebflux 实现函数式编程,两个接口:
- RouterFunction
路由处理 - HandlerFunction
处理函数
SpringWebflux基于注解编程
SpringWebflux 使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用 Netty服务器。SpringMVC和SpringWebflux对比如下:
- SpringMVC 方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
- SpringWebflux 方式实现,异步非阻塞 方式,基于SpringWebflux+Reactor+Netty
示例代码如下:
这是SpringBoot+Maven项目,需要引入依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
项目结构图:
│
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─webflux
│ │ │ └─demo
│ │ │ │ WebfluxdemoApplication.java
│ │ │ │
│ │ │ ├─controller
│ │ │ │ UserController.java
│ │ │ │
│ │ │ ├─entity
│ │ │ │ User.java
│ │ │ │
│ │ │ └─service
│ │ │ │ UserService.java
│ │ │ │
│ │ │ └─impl
│ │ │ UserServiceImpl.java
│ │ │
│ │ └─resources
│ │ application.properties
User类:
public class User {private String name;private String gender;private Integer age;public User(String name, String gender, Integer age) {this.name = name;this.gender = gender;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}
UserService接口:
public interface UserService {//根据 id 查询用户Mono<User> getUserById(int id);//查询所有用户Flux<User> getAllUser();//添加用户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","female",20));this.users.put(2,new User("mary","female",25));this.users.put(3,new User("jack","male",30));}//根据 id 查询@Overridepublic Mono<User> getUserById(int id) {return Mono.justOrEmpty(this.users.get(id));}//查询多个用户@Overridepublic Flux<User> getAllUser() {return Flux.fromIterable(this.users.values());}//添加用户@Overridepublic Mono<Void> saveUserInfo(Mono<User> userMono) {return userMono.doOnNext(person -> {//向 map 集合里面放值int id = users.size()+1;users.put(id,person);}).thenEmpty(Mono.empty());}
}
UserController类:
@RestController
public class UserController {@Autowiredprivate UserService userService;//id 查询@GetMapping("/user/{id}")public Mono<User> geetUserId(@PathVariable int id) {return userService.getUserById(id);}//查询所有@GetMapping("/getusers")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);}
}
WebfluxdemoApplication类:
@SpringBootApplication
public class WebfluxdemoApplication {public static void main(String[] args) {SpringApplication.run(WebfluxdemoApplication.class,args);}
}
application.properties:
server.port = 8081
运行结果:
SpringWebflux基于函数式编程
在使用函数式编程模型操作时候,需要自己初始化服务器。基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的 handler)和 HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。SpringWebflux 请 求 和 响 应 不 再 是 ServletRequest和ServletResponse ,而是ServerRequest 和 ServerResponse。
具体步骤
- 创建Handler(在这里写具体实现方法)
- 创建Router路由,并初始化服务器做适配
- 创建Router路由
- 创建服务器做适配
- 最终调用方法(main)编写
示例代码
项目结构图:
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─webflux
│ │ │ └─demo
│ │ │ │ Server.java
│ │ │ │
│ │ │ ├─entity
│ │ │ │ User.java
│ │ │ │
│ │ │ ├─handler
│ │ │ │ UserHandler.java
│ │ │ │
│ │ │ └─service
│ │ │ │ UserService.java
│ │ │ │
│ │ │ └─impl
│ │ │ UserServiceImpl.java
entity和service代码不变,与基于注解的代码一样
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.valueOf(request.pathVariable("id"));//空值处理Mono<ServerResponse> notFound = ServerResponse.notFound().build();//调用 service 方法得到数据Mono<User> userMono = this.userService.getUserById(userId);//把 userMono 进行转换返回//使用 Reactor 操作符 flatMapreturnuserMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(person))).switchIfEmpty(notFound);}//查询所有public Mono<ServerResponse> getAllUsers(ServerRequest request) {//调用 service 得到结果Flux<User> users = this.userService.getAllUser();returnServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users, User.class);}//添加public Mono<ServerResponse> saveUser(ServerRequest request) {//得到 user 对象Mono<User> userMono = request.bodyToMono(User.class);returnServerResponse.ok().build(this.userService.saveUserInfo(userMono));}
}
Server类:
public class Server {//1 创建 Router 路由public RouterFunction<ServerResponse> routingFunction() {//创建 hanler 对象UserService userService = new UserServiceImpl();UserHandler handler = new UserHandler(userService);//设置路由return RouterFunctions.route(GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById).andRoute(GET("/getallusers").and(accept(APPLICATION_JSON)),handler::getAllUsers);}//2 创建服务器完成适配public void createReactorServer() {//路由和 handler 适配RouterFunction<ServerResponse> route = routingFunction();HttpHandler httpHandler = toHttpHandler(route);ReactorHttpHandlerAdapter adapter = newReactorHttpHandlerAdapter(httpHandler);//创建服务器HttpServer httpServer = HttpServer.create();httpServer.handle(adapter).bindNow();}//3 最终调用方法(main)编写public static void main(String[] args) throws Exception{Server server = new Server();server.createReactorServer();System.out.println("enter to exit");System.in.read();}
}
运行main方法后效果:
16:05:18.184 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
16:05:18.184 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 0
16:05:18.184 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
16:05:18.204 [reactor-http-nio-1] DEBUG reactor.netty.tcp.TcpServer - [id: 0x16e3bd47, L:/0:0:0:0:0:0:0:0:51646] Bound new server
enter to exit
运行成功会有一个端口,比如我这里是51646,然后在浏览器中用这个端口请求就可以了。如下所示:
如有错误,欢迎指正!
点击获取Spring相关文章的MarkDown源文件(一共6个)
Spring学习(六):WebFlux相关推荐
- Spring学习(六)
AOP和OOP 1.OOP:Object-Oriented Programming,面向对象程序设计,是静态的,一旦类写死了就不能改变了,要更改就得修改代码重新编译,父类类型引用指向对象来实现动态性. ...
- (转)MyBatis框架的学习(六)——MyBatis整合Spring
http://blog.csdn.net/yerenyuan_pku/article/details/71904315 本文将手把手教你如何使用MyBatis整合Spring,这儿,我本人使用的MyB ...
- Spring学习(六)bean装配详解之 【通过注解装配 Bean】【基础配置方式】
本文借鉴:Spring学习(特此感谢!) 通过注解装配 Bean 1.前言 优势 1.可以减少 XML 的配置,当配置项多的时候,XML配置过多会导致项目臃肿难以维护 2.功能更加强大,既能实现 XM ...
- Spring学习笔记之MyBatis
系列文章目录 Spring学习笔记 之 Springhttps://blog.csdn.net/weixin_43985478/article/details/124411746?spm=1001.2 ...
- 一箭双雕 刷完阿里P8架构师spring学习笔记+源码剖析,涨薪8K
关于Spring的叙述: 我之前死磕spring的时候,刷各种资料看的我是一头雾水的,后面从阿里的P8架构师那里拿到这两份资料,从源码到案例详细的讲述了spring的各个细节,是我学Spring的启蒙 ...
- 第四阶段--Spring学习-01
第四阶段--Spring学习 1.概述 2.Spring相关概念 2.1 初识Spring 2.2 Spring系统架构 2.2.1 系统架构图 2.2.3 Spring核心概念-IOC.IOC容器. ...
- Spring 学习总结笔记【七、AOP面向切面编程】
往期文章: Spring 学习总结笔记[一.快速入门] Spring 学习总结笔记[二.IoC-控制反转] Spring 学习总结笔记[三.注解开发] Spring 学习总结笔记[四.整合Junit] ...
- JavaEE——Spring学习笔记03【AOP开发】
JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...
- JavaEE——Spring学习笔记01【Ioc开发的模式】
JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...
最新文章
- 梯度算法求步长的公式_LM(Levenberg-Marquarelt)算法
- 瞄准医疗数据安全四大风险 东软DBA如何绝地阻击
- POJ1904 强联通(最大匹配可能性)
- VC++ COM、ATL开发入门示例
- PHP 设计模式 笔记与总结(8)策略模式
- robotstudio仿真搬运编程_敲黑板 | 机器人是怎么完成任务的?这三种编程方式的区别你造吗...
- CodeForces - 1000D Yet Another Problem On a Subsequence(动态规划+组合数学)
- 你被这些网络迷题难倒过吗?
- php+分针和时针重合,时钟问题—两针重合
- 计算机行业深度分析,广发证券计算机行业深度分析
- composer安装扩展包异常
- docker 搭建nginx php mysql_docker搭建nginx+mysql+php
- Android编程入门-第1天
- nginx网关与gateway网关的区别
- 计算机图像分析系统属于,智能图像分析系统
- 人生历程中的几个观念瓶颈
- 深化“智能二道门”建设,化工厂人员定位系统深度解析!
- 如何在Windows下创建Ramdisk
- Visual Studio 2019安装AutoCAD_2020_dotnet_wizards
- 一篇文章读懂,物联网项目的5大技术要素