我是刚接触Reactor和反应式编程的新手。
我目前正在编写类似于以下代码:
Mono.just(userId)
.map(repo::findById)
.map(user-> {
if(user == null){
throw new UserNotFoundException();
}
return user;
})
// ... other mappings
这个例子可能很愚蠢,确实有更好的方法来实现这种情况,但是重点是:
throw new
在map
块中使用异常是错误的还是应该将其替换为return Mono.error(new UserNotFoundException())
?
这两种方式有什么实际区别?
有几种方法可以被视为方便的异常抛出方法:
可以简化可能导致错误或空流的元素处理的一种方法是operator handle
。
以下代码显示了如何使用它来解决问题:
Mono.just(userId)
.map(repo::findById)
.handle((user, sink) -> {
if(!isValid(user)){
sink.error(new InvalidUserException());
} else if (isSendable(user))
sink.next(user);
}
else {
//just ignore element
}
})
如我们所见,.handle
操作员需要通过BiConsumer<T, SynchronousSink<>
才能处理元素。在这里,我们的BiConsumer中有两个参数。第一个元素是上游元素,第二个元素是上游元素,SynchronousSink
它帮助我们向下游同步提供元素。这样的技术扩展了提供元素处理的不同结果的能力。例如,如果元素无效,我们可以向该元素提供错误,SycnchronousSync
这将取消上游onError
并向下游产生信号。反过来,我们可以使用相同的handle
运算符来“过滤”
。一旦手柄BiConsumer
被执行并且没有提供任何元素,Reactor会将其视为一种过滤,并将为我们请求其他元素。最后,在元素有效的情况下,我们可以简单地在SynchronousSink#next
下游调用和传播我们的元素,或者在其上应用一些映射,因此这里将handle
作为map
操作符。此外,我们可以安全地使用该运算符,而不会影响性能,并提供复杂的元素验证,例如元素验证或向下游发送错误。
#concatMap
+ 抛出Mono.error
在映射期间引发异常的选项之一是替换map
为concatMap
。从本质上讲,concatMap
它的作用几乎相同flatMap
。唯一的区别是concatMap
一次仅允许一个子流。这种行为大大简化了内部实现,并且不影响性能。因此,我们可以使用以下代码来以更实用的方式引发异常:
Mono.just(userId)
.map(repo::findById)
.concatMap(user-> {
if(!isValid(user)){
return Mono.error(new InvalidUserException());
}
return Mono.just(user);
})
在上述示例中,如果用户无效,我们将使用返回异常Mono.error
。我们可以使用Flux.error
以下方法对通量执行相同的操作:
Flux.just(userId1, userId2, userId3)
.map(repo::findById)
.concatMap(user-> {
if(!isValid(user)){
return Flux.error(new InvalidUserException());
}
return Mono.just(user);
})
注意 ,在两种情况下,我们都返回仅包含一个元素的 冷流 。在Reactor中,在返回的流是冷 标量
流的情况下,有一些优化可以提高性能。因此,建议使用流量/单声道concatMap
+
.just
,empty
,error
其结果是,当我们需要更复杂的映射,这可能与最终return null
或throw new ...
。
注意! 永远不要检查传入元素的可空性。Reactor项目永远不会
null
为您发送值,因为这违反了Reactive
Streams规范(请参见规则2.13)。因此,如果repo.findById
返回null,Reactor将为您抛出NullPointerException。
concatMap
比这更好flatMap
?从本质上讲,flatMap
它旨在合并一次执行的多个子流中的元素。这意味着flatMap在其下应具有异步流,因此它们可能在多个线程上处理数据,也可能是多个网络调用。随后,这种期望对实现产生了很大的影响,因此flatMap
应该能够处理来自多个流的数据Thread
(意味着并发数据结构的使用),如果另一个流耗尽了,则使元素排队(意味着Queue
为每个s
分配额外的内存子流),并且不违反反应式流规范规则(意味着确实非常复杂的实现)。计算所有这些事实以及我们取代平原的事实map
使用Flux/Mono.error
(不改变执行的同步性)引发异常的更方便的方式(同步操作)导致我们不需要这样一个复杂的运算符,并且可以使用更简单concatMap
的异步处理对象一次只能处理一个流,并进行了一些优化以处理标量冷流。
switchOnEmpty
因此,在结果为空时引发异常的另一种方法是switchOnEmpty
operator。以下代码演示了如何使用该方法:
Mono.just(userId)
.flatMap(repo::findById)
.switchIfEmpty(Mono.error(new UserNotFoundExeception()))
如我们所见,在这种情况下,repo::findById
应该将Mono
of
User
作为返回类型。因此,在User
找不到实例的情况下,结果流将为空。因此,Reactor将调用Mono
指定为switchIfEmpty
parameter
的Alternative 。
可以将其视为可读性差的代码或不良做法( 我个人认为 ),但是您可以像使用Project
Reactor一样引发异常。即使这样做在某种程度上可能会违反Reactive Streams规范( 在这种情况下 从语义的角度来看是 违反
的,因为在幕后的运算符是s Subscriber
链中Subscriber
的a,因此-
从语义上讲,在lambda中引发异常可以映射为引发onNext
违反规范规则2.13的方法中的异常)。但是,由于Reactor会为您捕获引发的异常,然后将其作为onError
信号传播到下游,因此不禁止这样做。
.handle
运算符以提供复杂的元素处理concatMap
+ Mono.error
,但是这种技术最适合异步元素处理的情况。flatMap
+Mono.error``flatMap
Null
因为返回类型是禁止的,所以您将null
在下游而不是下游使用map``onError``NullPointerException
switchIfEmpty
在所有情况下,当你需要发送一个错误信号,如果在调用一些特定的函数的结果与完成 空 流我是一个新的项目Reactor和反应编程一般。 我目前正在编写一段类似于以下内容的代码: 这两种做法有什么实际区别吗?
我使用PowerMock(Mockito)模拟同一类中另一个方法的子调用。更具体地说,我有这样的东西: 现在在我的单元测试中,我能够使用间谍模拟MyMethod2的响应,并做一些类似。但是,当我做这样的事情时,会发生一些奇怪的事情:。当我在测试期间调用myClass.myMethod1()时,它会抛出一个NullPointerException,但奇怪的是,如果我使用调试器并检查,e是MyExce
抛出异常的行为是否可能抛出不同的异常? 为了抛出异常,必须(可选地)分配新对象,并调用其构造函数(隐式调用fillinstacktrace)。在某些情况下,听起来像addSupressed也被称为。那么如果没有足够的内存会发生什么呢?JVM是否需要预分配内置异常?例如,(1/0)会抛出OutOfMemoryError而不是ArithmeticException吗? 此外,构造函数是一个方法调用,因
上一节展示了如何为ListOfNumbers类中的writeList方法编写异常处理程序。 有时,它适合代码捕获可能发生在其中的异常。 但在其他情况下,最好让一个方法进一步推给上层来调用堆栈处理异常。 例如,如果您将ListOfNumbers类提供为类包的一部分,则可能无法预期包的所有用户的需求。 在这种情况下,最好不要捕获异常,并允许一个方法进一步推给上层来调用堆栈来处理它。 如果writeLi
我正试图为一个方法编写一个测试用例,该方法基于特定的逻辑抛出异常。然而,测试用例失败了,因为预期的异常和获得的异常是不同的。 我如何解决这个问题?
请问在Spring webflux中抛出选中的自定义异常的正确方法是什么?我想坚持,这是关于检查自定义异常,如MyException。java,而不是RuntimeException,它是关于抛出异常,而不是处理异常。 我尝试了以下方法: 这是不可能的,因为get秒dStepFromFirstStepAfterCheck方法中存在未处理的异常。 如果我抛出并传播,私有的SecondStep get