Spring-0221~22

Spring5 Webflux

SpringWebflux介绍

  1. 是Spring5添加的新模块,用于web开发,功能和SpringMVC类似,Webflux使用当前一直比较流行的响应式编程出现的框架

  1. 使用传统web框架,比如SpringMVC,这些基于Servlet容器;而Webflux是一种异步非阻塞的框架异步非阻塞在Servlet3.1之后才支持,核心是基于Reactor的相关API实现的。Reactor就是响应式编程

  1. 异步非阻塞:

  1. 异步和同步:

针对调用者而言,发起请求之后是否等待对方做出回应之后再做其他事情,等待就是同步,不等待就是异步

  1. 阻塞和非阻塞:

被调用者收到一个请求之后,做完了请求任务之后,才给出反馈,就是阻塞;受到请求之后马上给出反馈然后再去做事情就是非阻塞。

总之阻塞就是需要等待回复,

  1. Webflux特点和优势:

  1. 非阻塞式好处:在不扩充系统资源的前提下可以提升系统的吞吐量和伸缩性,以Reactor为基础实现响应式编程

  1. 函数式编程:Spring5就是基于Java8,Webflux使用java8函数式编程方式实现路由请求

  1. 比较SpringMVC

Spring-0221~22_第1张图片

  1. 第一:两个框架都可以使用注解方式操作,都运行在tomcat等容器中

  1. 第二:SpringMVC采用命令式(一行行代码执行)编程方式,Webflux采用异步响应式编程

  1. 例子:网关处理请求;异步非阻塞处理请求

响应式编程

  1. 什么是响应式编程

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

最好的例子就是excel里面的公式,当你修改一个单元格的值,以其为输入计算输出的结果也会随之而变化。

  1. Java8及其之前版本做法:

  1. 使用了一种设计模式:观察者模式

  1. 提供了观察者模式两个类:Observe和Observable

观察指的就是观察值的变化情况,根据变化做出反应

  1. 代码入门

ObserverDemo observer = new ObserverDemo();

        //  添加观察者
        observer.addObserver((o,arg)->{
            System.out.println("发生了变化");
        });

        observer.addObserver((o,arg)->{
            System.out.println("收到被观察者的通知,准备改变");
        });

        //  数据变化
        observer.setChanged();
        //  通知变化,将变化传播
        observer.notifyObservers();
  1. reactor用到的是java9中内容,在java9及以后内容中,Observe和Observable都被Flow取代,Flow才是真正意义上的响应式编程,而刚才那两个属于是伪响应式编程。

  1. reactor底层就是对Flow类进行封装,里面主要有两个接口:Publisher和subscribe

  1. reactor功能比Flow更强大

  1. 总结:响应式编程就是通过监视数据和流的变化做出相应的响应和操作,底层通过订阅和发布的观察者模式进行通信

Reactor相关API如何进行响应式编程

  1. 响应式编程操作中,都需要满足一种规范,Reactive规范。Webflux框架的核心就是基于Reactor相关API实现

  1. Reactor对标SpringMVC中的Servlet,采用响应式编程

  1. Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供了丰富操作符。

  1. Flux对象:实现的是发布者,返回n个元素;

  1. Mono:实现发布者,返回0或者1个元素;

  1. Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:

  1. 元素值:

  1. 错误信号(异常):终止数据流,同时把错误信息传递给订阅者

  1. 完成信号:

  1. 其中错误信号和完成信号都是终止信号,都代表操作终止,用于告诉订阅者数据流结束了。

  1. 代码演示:

  1. 第一步:引入依赖

 
            io.projectreactor
            reactor-core
            3.1.5.RELEASE
  1. 第二步:编程

//  使用了just方法,是一个比较直接的声明数据流的方法
    Flux.just(1,2,3,4);
    Mono.just(1);

//  其他方法声明
//  数组
    Integer [] array = {1,2,3,4};
    Flux.fromArray(array);

//  集合
    List list = Arrays.asList(array);
    Flux.fromIterable(list);

//  Stream流
    Stream stream = list.stream();
    Flux.fromStream(stream);

//  错误信号示例
    Flux.error(new Exception("错误信号测试"));

现在是响应式编程,如果不进行订阅是不会有任何输出的。

//	进行订阅的代码
 Flux.just(1,2,3,4).subscribe(System.out::println);	
  1. 三种信号特点:

  1. 错误信号和完成信号都是终止信号,但是不能共存

  1. 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是一个空数据流

  1. 如果没有错误或完成信号,表示无限数据流

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

  1. 操作符

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

  1. 第一个常见:map,元素映射为新的元素

Spring-0221~22_第2张图片

例如进行平方操作,1,2,3映射完之后称为1,4,9

  1. 第二个常见:flatmap,元素映射为流

Spring-0221~22_第3张图片

三个元素会先变成三个流,三个流进行合并然后输出

SpringWebflux执行流程和核心API

  1. SpringWebflux基于Reactor,默认容器是Netty,是一个高性能,NIO框架,异步非阻塞框架

  1. Netty:异步非阻塞框架

  1. BIO:阻塞方式

Spring-0221~22_第4张图片

  1. NIO:非阻塞方式

Spring-0221~22_第5张图片

多路复用方式

  1. SpringWebflux执行过程和SpringMVC相似的

  1. 核心控制器:DispatchHandler,实现接口WebHandler

  1. DispatchHandler,主要负责请求的处理,实际是请求的映射

  1. HandlerMapping:根据客户端请求处理请求的方法,根据此接口匹配到请求方法

  1. HandlerAdapter:适配器效果,真正负责请求处理,实现具体业务方法

  1. HandlerResultHandler:响应结果处理

  1. SpringWebflux实现函数式编程,两个接口

  1. RouterFunction:路由处理

  1. HandlerFunction:处理函数

SpringWebFlux基于注解编程模型

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

  1. 第一步:创建SpringBoot工程,引入Webflux依赖

  1. 第二步:配置端口号

  1. 第三步:创建包和相关类,创建接口定义操作的方法

/**
 * 用户操作接口
 */
public interface UserService {

    //  根据ID查询用户
    Mono getUserById(int id);

    //  查询所有用户
    Flux getAllUsers();

    //  添加用户
    MonoaddUserInfo(Mono user);
}
/**
 * @author lannisite
 * @program UserServiceImpl
 * @description 接口实现类
 * @date 2023/2/22 17:04
 */
@Service
public class UserServiceImpl implements UserService {

    //  创建map集合存储数据
    private final Map userMap = new HashMap<>();

    public UserServiceImpl(){
        this.userMap.put(1,new User("lucy","female",18));
        this.userMap.put(2,new User("Micheal","female",28));
        this.userMap.put(3,new User("oliver","female",38));
    }

    /**
     * 根据ID查询
     * @param id id
     * @return user
     */
    @Override
    public Mono getUserById(int id) {
        return Mono.justOrEmpty(this.userMap.get(id));
    }

    /**
     * 获取所有用户
     * @return Flux
     */
    @Override
    public Flux getAllUsers() {
        return Flux.fromIterable(this.userMap.values());
    }

    /**
     * 添加用户
     * @param userMono user
     * @return void
     */
    @Override
    public Mono addUserInfo(Mono userMono) {

        return userMono.doOnNext(person->{
            //  向map集合里面存值
            int id = userMap.size()+1;
            userMap.put(id,person);
        }).thenEmpty(Mono.empty()); //  终止信号Mono.empty()
    }
}
@RestController
public class UserController {

    //  注入service
    @Autowired
    private UserService userService;

    //  id查询
    @GetMapping("/user/{id}")
    public Mono getUserById(@PathVariable int id){
        return userService.getUserById(id);
    }

    //  查询所有
    @GetMapping("/users")
    public Flux getAllUsers(@PathVariable int id){
        return userService.getAllUsers();
    }

    //  添加
    @GetMapping("/adduser")
    public MonoaddUser(@RequestBody User user){
       return userService.addUserInfo(Mono.just(user));
    }
}
  1. 总结:

现在看来webflux表面和springMVC似乎没什么不同,但是底层已经完全不一样了。

  1. SpringMVC方式(同步阻塞):基于SpringMVC+servlet+tomcat实现

  1. WebFlux方式(异步非阻塞):基于SpringWebflux+Reactor+Netty进行实现

SpringWebFlux基于函数式编程模型

  1. 在使用函数式编程模型操作时,需要自己初始化服务器,可以使用tomcat但是一般使用Netty

  1. 基于函数式编程模型时候,有两个核心接口:RouterFunction和HandlerFunction

  1. RouterFunction:实现路由功能,将请求转发给对应的Handler

  1. HandlerFunction:处理请求生成响应的函数

核心任务:写两个接口的实现类,并且启动所需要的服务器。

  1. SpringWebflux请求和响应不再是ServletRequest和ServletRseponse,变成ServerRequest和ServerResponse

  1. 代码实现:

  1. 第一步:把注解编程模型工程进行复制,然后修改

  1. 第二步:删除controller,创建handler

/**
 * @author lannisite
 * @program UserHandler
 * @description 描述
 * @date 2023/2/22 21:53
 */

public class UserHandler {


    private final UserService userService;

    public UserHandler(UserService userService){

        this.userService = userService;
    }

    //  根据id查询
    public Mono getUserById(ServerRequest request){
        Integer id = Integer.valueOf(request.pathVariable("id"));

        //  空值处理
        Mono notFound = ServerResponse.notFound().build();
        Mono userMono = this.userService.getUserById(id);

        //  把userMono进行转化返回,
        //  使用Reactor操作符flatMap
        return  userMono.flatMap(person->ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(fromObject(person)))
                .switchIfEmpty(notFound);
    }

    //  查询所有
    public Mono getAllUsers(ServerRequest request){

        //  空值处理
        Mono notFound = ServerResponse.notFound().build();
        Flux userFlux = userService.getAllUsers();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(userFlux,User.class);
    }

    //  添加
    public Mono addUser(ServerRequest request){
        //  得到User对象
        Mono userMono = request.bodyToMono(User.class);
        return ServerResponse.ok().build(this.userService.addUserInfo(userMono));
    }
}
  1. 第三步:初始化服务器,编写Router

Spring-0221~22_第6张图片

创建路由方法

public class Server {

    //  创建路由
    public RouterFunction routingFunction(){

        //  创建handler对象
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHandler(userService);

        //  设置路由部分
        return RouterFunctions.route(GET("users/{id}").and(accept(MediaType.APPLICATION_JSON)),handler::getUserById)
                .andRoute(GET("/users").and(accept(MediaType.APPLICATION_JSON)),handler::getAllUsers);
    }
}
  1. 第四步:创建服务器完成适配

public void createReactorServer(){

    //  路由和handler适配
    RouterFunction route = routingFunction();
    HttpHandler httpHandler = toHttpHandler(route);
    ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

    //  创建服务
    HttpServer httpServer = HttpServer.create();
  	httpServer = httpServer.port(8082);
    httpServer.handle(adapter).bindNow();
}
  1. 第五步:调用

public class Server {

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

    //  创建路由
    public RouterFunction routingFunction(){

        //  创建handler对象
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHandler(userService);

        //  设置路由部分
        return RouterFunctions.route(GET("users/{id}").and(accept(MediaType.APPLICATION_JSON)),handler::getUserById)
                .andRoute(GET("/users").and(accept(MediaType.APPLICATION_JSON)),handler::getAllUsers);
    }


    public void createReactorServer(){

        //  路由和handler适配
        RouterFunction route = routingFunction();
        HttpHandler httpHandler = toHttpHandler(route);
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

        //  创建服务
        HttpServer httpServer = HttpServer.create();
        httpServer = httpServer.port(8082);
        httpServer.handle(adapter).bindNow();
    }
}
  1. 使用WebClient调用

public class Client {

    public static void main(String[] args) {

        //  调用服务器地址
        WebClient webClient = WebClient.create("http://127.0.0.1:8082");

        //  根据id查询
        String id = "1";
        //  block 类似订阅的操作,不订阅就不会执行
        User user = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
        System.out.println(user);

        //  查询所有
        Flux userFlux = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
        userFlux.map(person->person.getName()).buffer().doOnNext(System.out::print).blockFirst();
    }
}

你可能感兴趣的:(JAVA开发,spring)