六、Spring5之Webflux

Spring5 框架新功能(Webflux)

1、SpringWebflux 介绍

(1)是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 使用
当前一种比较流行的响应式编程出现的框架。
在这里插入图片描述
(2)使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,Webflux 是一种异步非阻
塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor (响应式编程)的相关 API 实现
的。
(3)解释什么是异步非阻塞

  • 异步和同步
  • 非阻塞和阻塞

上面都是针对对象不同

  • 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同
    步,如果发送请求之后不等着对方回应就去做其他事情就是异步

  • 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻
    塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞

比如A给B发送请求,B收到请求之后 执行完业务逻辑才回复就是阻塞
而受到请求后立刻返回 再去执行业务逻辑就是非阻塞

(4)Webflux 特点:

  • 非阻塞式:在有限资源下,提高系统吞吐量伸缩性(通俗点说就是能在有限的资源下处理更多的请求),以 Reactor 为基础实现响应式编程
  • 函数式编程:Spring5 框架基于 java8,Webflux 使用 Java8 函数式编程方式实现路由请求

(5)比较 SpringMVC
在这里插入图片描述

  • 两个框架都可以使用注解方式,都运行在 Tomcat 等容器中
  • SpringMVC 采用命令式(一行一行代码执行)编程,Webflux 采用异步响应式编程
  • 如何选取:一般项目使用mvc,如果有远程服务调用的可以用Webflux,能在有限的资源中处理更多的请求量,比如微服务的网关可以用Webflux(非阻塞异步方式)实现
2、响应式编程

(1)什么是响应式编程

  • 响应式编程是一种面向数据流变化传播的编程范式。这意味着可以在编程语言中很方便
    地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

  • 电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公
    式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。

(2)Java8 及其之前版本
提供的观察者模式(观察关心数据的变化,当关心数据发生变化发生响应)两个类 Observer 和 Observable(JDK9之后flow)
新建springboot项目
在这里插入图片描述

package com.liang.demoreactor8.reactor8;

import java.util.Observable;

public class ObserverDemo extends Observable {
    public static void main(String[] args) {
        ObserverDemo observerDemo= new ObserverDemo();
        //添加观察者
        observerDemo.addObserver((o,arg)->{
            System.out.println("发生了变化");
        });

        observerDemo.addObserver((o,arg)->{
            System.out.println("收到被观察者通知,准备发生改变");
        });
        //监控当前数据的变化
        observerDemo.setChanged();
        //数据变化后通知
        observerDemo.notifyObservers();

    }
}

在这里插入图片描述
执行之后没有效果,因为现在是观察者,需要进行通知才有响应

//监控当前数据的变化
        observerDemo.setChanged();
        //数据变化后通知
        observerDemo.notifyObservers();

在这里插入图片描述

3、响应式编程(Reactor 实现)

(1)响应式编程操作中,Reactor 是满足 Reactive 规范框架

(2)Reactor 有两个核心类,Mono 和 Flux,这两个类实现接口 Publisher(发布者),提供丰富操作符。
Flux 对象实现发布者,返回 N 个元素;
Mono 实现发布者,返回 0 或者 1 个元素

(3)Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:
元素值错误信号完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉
订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者

在这里插入图片描述
箭头表示时间轴,连续发送几个信号,竖线代表完成信号

(4)代码演示 Flux 和 Mono

  • 引入依赖
<dependency>
 <groupId>io.projectreactor</groupId>
 <artifactId>reactor-core</artifactId>
 <version>3.1.5.RELEASE</version>
</dependency>
  • 编程代码
package com.liang.demoreactor8.reactor8;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class TestReactor {
    public static void main(String[] args) {
        //just方法直接去声明相关的元素
        Flux.just(1,2,3,4);
        Mono.just(1);

        //其他的方法 数组形式数据流
        Integer[] array = {1,2,3,4};
        Flux.fromArray(array);

        //list集合形式数据流
        List<Integer> list = Arrays.asList(array);
        Flux.fromIterable(list);

        //stream形式数据流
        Stream<Integer> stream = list.stream();
        Flux.fromStream(stream);
    }
}

(5)三种信号特点

  • 错误信号和完成信号都是终止信号,不能共存的
  • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
  • 如果没有错误信号,没有完成信号,表示是无限数据流

(6)调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触
发数据流,不订阅什么都不会发生的

//just方法直接去声明相关的元素
        Flux.just(1,2,3,4).subscribe(System.out::println);
        Mono.just(1).subscribe(System.out::print);

在这里插入图片描述
(7)操作符

对数据流进行一道道操作,成为操作符,比如工厂流水线

  • map 元素映射为新元素
    在这里插入图片描述
    上方元素通过map操作后变换成下方元素

  • flatMap 元素映射为流
    把每个元素转换流,把转换之后多个流合并大的流
    在这里插入图片描述
    上方元素变成流 把流做成输出

4、SpringWebflux 执行流程和核心 API

SpringWebflux 基于 Reactor,默认使用容器是 Netty,Netty 是高性能的 NIO 框架,异步非阻
塞的框架

(1)Netty

  • BIO(阻塞)
    在这里插入图片描述
    建立socket之后只有一直往下执行才可以进行其他操作

  • NIO(非阻塞)
    在这里插入图片描述
    channerl通道 每个操作都是一个通道,每个操作调用一个通道,通道注册到selector(选择器,多路复用器),选择器关注里面的状态

(2)SpringWebflux 执行过程和 SpringMVC(DispatchServlet) 相似的

  • SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler
  • 接口 WebHandler 有一个方法
    在这里插入图片描述
    在这里插入图片描述
    (3)SpringWebflux 里面 DispatcherHandler,负责请求的处理
  • HandlerMapping:请求查询到处理的方法
  • HandlerAdapter:真正负责请求处理
  • HandlerResultHandler:响应结果处理

(4)SpringWebflux 实现函数式编程,两个接口:RouterFunction(路由处理)和 HandlerFunction(处理函数)

5、SpringWebflux(基于注解编程模型)

SpringWebflux 实现方式有两种:注解编程模型和函数式编程模型

使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,

SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器

  • 创建 SpringBoot 工程,引入 Webflux 依赖
    在这里插入图片描述
  • 配置启动端口号
server.port=8080
  • 创建包和相关类

实体类:

package com.liang.demoreactor8.webflux.model;

public class User {
    private String id;
    private String name;
    private String age;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

接口:

package com.liang.demoreactor8.webflux.service;

import com.liang.demoreactor8.webflux.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

//用户操作接口
public interface UserService {
    //根据 id 查询用户
    Mono<User> getUserById(int id);
    //查询所有用户
    Flux<User> getAllUser();
    //添加用户
    Mono<Void> saveUserInfo(Mono<User> user);
}

实现类:

package com.liang.demoreactor8.webflux.service.impl;

import com.liang.demoreactor8.webflux.model.User;
import com.liang.demoreactor8.webflux.service.UserService;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

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","30"));
        this.users.put(3,new User("jack","nv","50"));
    }
    //根据 id 查询
    @Override
    public Mono<User> getUserById(int id) {
        return Mono.justOrEmpty(this.users.get(id));
    }
    //查询多个用户
    @Override
    public Flux<User> getAllUser() {
        return Flux.fromIterable(this.users.values());
    }
    //添加用户
    @Override
    public Mono<Void> saveUserInfo(Mono<User> userMono) {
        return userMono.doOnNext(person -> {
            //向 map 集合里面放值
            int id = users.size()+1;
            users.put(id,person);
        }).thenEmpty(Mono.empty());
    }
}

controller:

package com.liang.demoreactor8.webflux.controller;


import com.liang.demoreactor8.webflux.model.User;
import com.liang.demoreactor8.webflux.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class UserController {
    //注入 service
    @Autowired
    private UserService userService;
    //id 查询
    @GetMapping("/user/{id}")
    public Mono<User> geetUserId(@PathVariable int id) {
        return userService.getUserById(id);
    }
    //查询所有
    @GetMapping("/user")
    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);
    }
}

  • 说明
    SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC+Servlet+Tomcat

SpringWebflux 方式实现,异步非阻塞 方式,基于 SpringWebflux+Reactor+Netty

6、SpringWebflux(基于函数式编程模型)

(1)在使用函数式编程模型操作时候,需要自己初始化服务器

(2)基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发
给对应的 handler)和 HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数
式接口的实现并且启动需要的服务器。

( 3 ) SpringWebflux 请 求 和 响 应 不 再 是 ServletRequest 和 ServletResponse ,而是
ServerRequest 和 ServerResponse

  • 把注解编程模型工程复制一份 ,保留 entity 和 service 内容
  • 第二步 创建 Handler(具体实现方法)
package com.liang.demoreactor8.webflux;

import com.liang.demoreactor8.webflux.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

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 操作符 flatMap
        return
                userMono
                        .flatMap(person ->
                                ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                                        .body(fromObject(person)))
                        .switchIfEmpty(notFound);
    }
    //查询所有
    public Mono<ServerResponse> getAllUsers() {
        //调用 service 得到结果
        Flux<User> users = this.userService.getAllUser();
        return
                ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.cl
                        ass);
    }
    //添加
    public Mono<ServerResponse> saveUser(ServerRequest request) {
        //得到 user 对象
        Mono<User> userMono = request.bodyToMono(User.class);
        return
                ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
    } }
  • 初始化服务器,编写 Router
    创建路由的方法
//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("/users").and(accept(APPLICATION_JSON)),handler::get
AllUsers);
}

创建服务器完成适配

//2 创建服务器完成适配
public void createReactorServer() {
 //路由和 handler 适配
 RouterFunction<ServerResponse> route = routingFunction();
 HttpHandler httpHandler = toHttpHandler(route);
 ReactorHttpHandlerAdapter adapter = new 
ReactorHttpHandlerAdapter(httpHandler);
 //创建服务器
 HttpServer httpServer = HttpServer.create();
 httpServer.handle(adapter).bindNow();
}

最终调用:

public static void main(String[] args) throws Exception{
 Server server = new Server();
 server.createReactorServer();
 System.out.println("enter to exit");
 System.in.read();
}

使用webclient调用

public class Client {
 public static void main(String[] args) {
 //调用服务器地址
 WebClient webClient = WebClient.create("http://127.0.0.1:5794");
 //根据 id 查询
 String id = "1";
 User userresult = webClient.get().uri("/users/{id}", id)
 .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User
.class)
 .block();
 System.out.println(userresult.getName());
 //查询所有
 Flux<User> results = webClient.get().uri("/users")
 .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User
.class);
 results.map(stu -> stu.getName())
 .buffer().doOnNext(System.out::println).blockFirst();
 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值