03-Mono & Flux操作

上一节主要介绍了如何基于Mono和Flux构建一个数据流,Mono是Flux的特例,但大部分API 都是一致的所以重点讲的是Flux。本章将讲Flux和Mono的操作符,如 转换、过滤、异常处理、日志、Debug等一些API。

1. buffer

buffer方法相当于将序列中的一部分数据收集到一个集合中,并作为一个新的流元素发送到下游。
在这里插入图片描述
这里看buffer的重载方法特别多啊,基本功能肯定是一样的只是多了一些附加条件。我们加单示例几种。
示例1: 指定收集的元素个数

public static void test_01(){
        Flux.just(1,2,3,4,5,6).buffer(2).subscribe(System.out::println);
    }

结果:

 [1, 2]
 [3, 4]
 [5, 6]

示例2:当断言为true时 buffer中断收集元素, 但包含最后一个元素。

public static void test_39(){
    Flux.range(1, 10)
            .bufferUntil(i->i>5)
            .subscribe(System.out::println);
}

结果:

 [1, 2, 3, 4, 5, 6]
 [7]
 [8]
 [9]
 [10]

示例3: 收集返回true的元素,当返回false时中断流,并不包含返回false的元素。

public static void test_40(){
        Flux.range(1, 10)
                .bufferWhile(i->i>5)
                .subscribe(System.out::println);
    }

结果:

[6, 7, 8, 9, 10]

2. window

字面意思窗口,功能跟buffer类似。区别是window将元素分成一个个Flux对象而不是一个Flux中的集合元素。
示例:

public static void test_04(){
   Flux.just(1,2,3,4,5,6).window(2).toIterable().forEach(f->{
       f.subscribe(System.out::println);
       System.out.println("--------------------");
   });
}

结果:

 1
 2
 --------------------
 3
 4
 --------------------
 5
 6
 --------------------

这里将每个Flux对象转成了集合进行了遍历打印。

3. map

转换方法,将每一个元素按map中定义的Function函数进行转换。
示例:

public static void test_41(){
    Flux.range(1,5)
            .map(i -> i*i)
            .subscribe(System.out::println);
}

结果:

1
4
9
16
25

4. flatmap

也是一个转换方法,跟Java 中lambda中的flatmap类似,它会将元素转成一个流或者说Flux对象,然后对流进行合并。对于lambda中flatmap常见的案例就是将[“hello”, “world”] 转成单个字母。这里同样适用。

public static void test_42(){
     Flux.just("hello", "world")
             .flatMap(w->Flux.just(w.split("")))
             .subscribe(System.out::print);
 }

结果:

helloworld

5. filter

过滤方法,对元素序列中的所有元素进行过滤。重载方法也非常多, 这里只简单介绍。
案例:

 public static void test_08(){
        Flux.just(1,2,3,4,5,6).filter(i->i%2==0).subscribe(System.out::println);
    }

结果:

2
4
6

7. block

阻塞等待获取元素
示例:

// 取第一个元素
System.out.println(Flux.just(1, 2, 3, 4, 5, 6).blockFirst());
// 取最后一个元素
System.out.println(Flux.just(1, 2, 3, 4, 5, 6).blockLast());

8. skip

在这里插入图片描述
跳过指定个数或时间或者断言的元素。

  • 跳过两个元素后取第一个元素。
System.out.println(Flux.just(1, 2, 3, 4, 5, 6).skip(2).blockFirst());
  • 当skipUntil返回True时,停止跳过元素。
 public static void test_26(){
	Flux.range(1, 100)
	          .skipUntil(i->i > 10)
	          .subscribe(System.out::println);
	}

结果:

11
12
13
14
15
16
...

9. then

then()返回一个Mono 也就是说它会忽略上游的流数据,但其它的重载方法可以重新生成新的数据流。
在这里插入图片描述

public static void test_11(){
	Flux.just(1, 2, 3, 4, 5, 6)
        // 返回 Flux<Void>
        .then()
        .subscribe(System.out::println);
}

实际上并不会打印任何东西。

10. when

在这里插入图片描述
这个一个聚合操作,将等待一个或者多个Publisher执行完成之后在进行加下来的操作。

    public static void test_12(){
        Flux.just(1, 2, 3, 4, 5, 6)
                .flatMap(i->{
                    System.out.println(i);
                    return Mono.when(s->{
                        System.out.println("all done");
                        s.onComplete();
                    },
                    s->{
                        System.out.println("all done-2");
                        s.onComplete();
                    });
                }).subscribe();
    }
1
all done
all done-2
2
all done
all done-2
3
all done
all done-2

11. merge 合并多个流

在这里插入图片描述
每个Publisher将返回一个Flux流,merge将多个流合并成一个流。合并的方式是交错的方式。
示例:

public static void test_13(){
    Flux.merge(Flux.interval(Duration.ofMillis(0), Duration.ofMillis(100)).take(2),
            Flux.interval(Duration.ofMillis(50), Duration.ofMillis(100)).take(2))
            .subscribe(System.out::println);
}

第一个Flux延时0毫秒每100毫秒生成一个元素,返回一个只有两个元素的流。第二个Flux延时50毫秒每100毫秒生成一个元素也返回由两个元素的流。这样算一下其实是每50秒生成一个元素。
结果如下:

0
0
1
1

也就说明合并两个流时是按照它们生成的顺序合并的。

12 mergeSequential

    public static void test_14(){
        Flux.mergeSequential(Flux.interval(Duration.ofMillis(0), Duration.ofMillis(100)).take(3),
                Flux.interval(Duration.ofMillis(50), Duration.ofMillis(100)).take(2))
                .subscribe(System.out::println);
    }

结果:

0
1
2
0
1

说明mergeSequential 是按Pulisher的顺序合并流的。

13 zip

这也是一个合并方法,不过它是按一对一匹配的,如果序列中有对应不上的元素会被丢弃。

public static void test_15(){
        Flux.zip(Flux.just(1, 2, 5), Flux.just(3, 4)).subscribe(System.out::println);
    }

结果:

[1,3]
[2,4]

14 异常处理

14.1 onErrorReturn

    public static void test_18(){

        Flux.range(1, 6)
                .map(i -> 10/(i-3))
                .onErrorReturn(5)   // 出错后给一个默认值 然后序列就终端了 元素序列只剩出错前的一段
                .map(i -> i*i)
                .subscribe(System.out::println, System.err::println);
    }
25
100
25

onErrorReturn异常发生时返回一个给定的默认值,然后后面的元素就丢掉了。

14.2 subscribe

public static void test_19(){
    Flux.range(1, 6)
            .map(i -> 10/(i-3))
            .map(i -> i*i)
            .subscribe(System.out::println, System.err::println);
}

这里并没有显示的处理异常,只是通过subscribe的第二个参数将异常打印出来。

14.3 onErrorResume

public static void test_20(){

        Flux.range(1, 6)
                .map(i -> 10/(i-3))
                .onErrorResume(e->{
                    e.printStackTrace();
                    return Mono.just(10);
                })
                .map(i -> i*i)
                .subscribe(System.out::println, System.err::println);
    }

结果:

25
100
java.lang.ArithmeticException: / by zero
	at com.spring.reactive.OperateApiTest.lambda$test_20$16(OperateApiTest.java:231)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113)
	at reactor.core.publisher.FluxRange$RangeSubscription.fastPath(FluxRange.java:130)
	at reactor.core.publisher.FluxRange$RangeSubscription.request(FluxRange.java:108)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:169)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2154)
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onSubscribe(FluxOnErrorResume.java:74)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96)
	at reactor.core.publisher.FluxRange.subscribe(FluxRange.java:68)
	at reactor.core.publisher.Flux.subscribe(Flux.java:8095)
	at reactor.core.publisher.Flux.subscribeWith(Flux.java:8268)
	at reactor.core.publisher.Flux.subscribe(Flux.java:8065)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7989)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7959)
	at com.spring.reactive.OperateApiTest.test_20(OperateApiTest.java:237)
	at com.spring.reactive.OperateApiTest.main(OperateApiTest.java:541)
100

这里和onErrorReturn类似,只不过这里可以获取到异常信息。

14.4 onErrorMap

public static void test_21(){
   Flux.range(1, 6)
           .map(i -> 10/(i-3))
           .onErrorMap(e-> {throw new RuntimeException("111");})
           .map(i -> i*i)
           .subscribe(System.out::println, System.err::println);

}
25
100
java.lang.RuntimeException: 111

它可以获取到异常信息,但返回值也必须是一个异常。

14.5 doOnError

public static void test_22(){

        Flux.range(1, 6)
                .map(i -> 10/(i-3))
                .doOnError(e->{
                    System.out.println(e.getLocalizedMessage());
                })
                .onErrorMap(e-> {throw new RuntimeException("111");})
                .map(i -> i*i)
                .subscribe(System.out::println, System.err::println);

    }

相当于异常发生时的一个回调,不会返回任何东西,可以打印一下异常信息或者异常通知等等。

15. defaultIfEmpty 默认返回值

public static void test_23(){

        Flux.just().defaultIfEmpty(0).subscribe(System.out::println);

    }

在流为空的时候,相当于给一个默认值。

16. any 流中是否有符合条件的任意一个元素

    public static void test_28(){
        Flux.range(1, 20)
                .any(i->i %2 == 0)
                .subscribe(System.out::println);
    }

结果是true,有任意一个结果满足即可。

17. all 流中所有元素是否都满足条件

    public static void test_29(){

        Flux.just("abc", "ela", "ade", "pqa", "kang")
        .all(a -> a.contains("a"))
        .subscribe(System.out::println);

    }

18. subscribe

订阅流,也可以说消费流。最简单的方式就是给一个Consumer实现来消费消费流。同时它还有许多重载方法。
在这里插入图片描述
如果选择空参方法就是丢弃流。除了使用Consumer参数还可以实现一个Subscribe来消费流。

 public static void test_33(){

        Flux.just(1,2,3,4)
                .subscribe(new Subscriber<Integer>() {
                    Subscription sub;
                    @Override
                    public void onSubscribe(Subscription s) {
                        System.out.println("onSubscribe");;
                        sub = s;
                        // 每次消费一个元素
                        sub.request(1);
                    }
                    @Override
                    public void onNext(Integer integer) {
                        System.out.println("onNext");
                        System.out.println(integer);
                        sub.request(1);
                    }
                    @Override
                    public void onError(Throwable t) {
                        System.out.println("onError");;
                        t.printStackTrace();
                    }
                    @Override
                    public void onComplete() {
                        System.out.println("onComplete");;
                    }
                });
    }

当如果有比较复杂的订阅需求时可以使用这种方式来实现自己的消费逻辑。同时还可以使用有默认实现方法的BaseSubscribe来实现。

public static void test_34(){

        Flux.just(1,2,3,4)
                .subscribe(new BaseSubscriber<Integer>() {
                    Subscription sub;

                    @Override
                    protected void hookOnSubscribe(Subscription s) {
                        System.out.println("onSubscribe");;
                        sub = s;
                        // 每次消费一个元素
                        sub.request(1);
                    }

                    @Override
                    protected void hookOnNext(Integer value) {
                        System.out.println("onNext");
                        System.out.println(value);
                        sub.request(1);
                    }

                    @Override
                    protected void hookOnComplete() {
                        System.out.println("onComplete");;
                    }
                });

    }

总结

本章介绍了大部分比较常用的Reactor方法,满足基本使用情况。熟练的掌握这些方法可以更快速的进行响应式编码。当然还有一些如Debug、log等等都是辅助开发的手段有需要大家可以自行了解。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
WebFlux是Spring框架的一部分,它提供了一种响应式编程的方式来构建基于非阻塞I/O的应用程序。在WebFlux中,我们可以使用两种主要的数据类型:Mono和Flux。 1. Mono: Mono是一种包含零个或一个元素的响应式流。它类似于Java 8中的Optional,但具有更多的操作符和功能。下面是一些使用Mono的示例: ```java Mono&lt;String&gt; mono = Mono.just(&quot;Hello&quot;); // 创建一个包含单个元素的Mono mono.subscribe(System.out::println); // 订阅并打印元素 Mono&lt;Integer&gt; emptyMono = Mono.empty(); // 创建一个空的Mono emptyMono.subscribe(System.out::println); // 不会输出任何内容 Mono&lt;String&gt; errorMono = Mono.error(new RuntimeException(&quot;Error&quot;)); // 创建一个包含错误的Mono errorMono.subscribe(System.out::println, Throwable::printStackTrace); // 打印错误信息 ``` 2. FluxFlux是一种包含零个或多个元素的响应式流。它类似于Java 8中的Stream,但具有更多的操作符和功能。下面是一些使用Flux的示例: ```java Flux&lt;String&gt; flux = Flux.just(&quot;Hello&quot;, &quot;World&quot;); // 创建一个包含多个元素的Flux flux.subscribe(System.out::println); // 订阅并打印元素 Flux&lt;Integer&gt; emptyFlux = Flux.empty(); // 创建一个空的Flux emptyFlux.subscribe(System.out::println); // 不会输出任何内容 Flux&lt;String&gt; errorFlux = Flux.error(new RuntimeException(&quot;Error&quot;)); // 创建一个包含错误的Flux errorFlux.subscribe(System.out::println, Throwable::printStackTrace); // 打印错误信息 ``` 通过使用Mono和Flux,我们可以在WebFlux中处理异步和非阻塞的操作,例如处理HTTP请求和响应。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值