上一节主要介绍了如何基于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等等都是辅助开发的手段有需要大家可以自行了解。