扒一扒@Retryable注解,很优雅,有点意思! (2)

简介: 扒一扒@Retryable注解,很优雅,有点意思! (2)

翻源码


源码之下无秘密。

首先我们看一下前面找到的 Debug 入口:

org.springframework.retry.support.RetryTemplate#doExecute

从日志里面可以直观的看出,这个方法里面肯定就包含我要找的 for 循环。

但是...

很遗憾,并不是 for 循环,而是一个 while 循环。问题不大,意思差不多:

image.png

打上断点,然后把项目跑起来,跑到断点的地方我最关心的是下面的调用堆栈:

image.png

被框起来了两部分,一部分是 spring-aop 包里面的内容,一部分是 spring-retry。

然后我们看到 spring-retry 相关的第一个方法:

image.png

恭喜你,如果说前面通过日志找到了第一个打断点的位置,那么通过第一个断点的调用堆栈,我们找到了整个 retry 最开始的入口处,另外一个断点就应该打在下面这个方法的入口处:

org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor#invoke


image.png

说真的,观察日志加调用栈这个最简单的组合拳用好了,调试绝大部分源码的过程中都不会感觉特别的乱。

找到了入口了,我们就从接口处接着看源码。

这个 invoke 方法一进来首先是试着从缓存中获取该方法是否之前被成功解析过,如果缓存中没有则解析当前调用的方法上是否有 @Retryable 注解。

如果是被 @Retryable 修饰的,返回的 delegate 对象则不会是 null。所以会走到 retry 包的代码逻辑中去。

image.png

然后在 invoke 这里有个小细节,如果 recoverer 对象不为空,则执行带回调的。如果为空则执行没有 recoverCallback 对象方法。

我看到这几行代码的时候就大胆猜测: @Recover 注解并不是必须的。

于是我兴奋的把这个方法注解掉并再次运行项目,发现还真是,有点不一样了:

image.png

在我没有看其他文章、没有看官方介绍,仅通过一个简单的示例就发掘到他的一个用法之后,这属于意外收获,也是看源码的一点小乐趣。

其实源码并没有那么可怕的。

image.png

但是看到这里的时候另外一个问题就随之而来了:

这个 recoverer 对象看起来就是我写的 channelNotResp 方法,但是它是在什么时候解析到的呢?

image.png

按下不表,后面再说,当务之急是找到重试的地方。

在当前的这个方法中再往下走几步,很快就能到我前面说的 while 循环中来:

image.png

主要关注这个 canRetry 方法:

org.springframework.retry.RetryPolicy#canRetry

点进去之后,发现是一个接口,拥有多个实现:

image.png

简单的介绍一下其中的几种含义是啥:

  • AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环
  • NeverRetryPolicy:只允许调用RetryCallback一次,不允许重试
  • SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略
  • TimeoutRetryPolicy:超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试
  • ExceptionClassifierRetryPolicy:设置不同异常的重试策略,类似组合重试策略,区别在于这里只区分不同异常的重试
  • CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate
  • CompositeRetryPolicy:组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许即可以重试,悲观组合重试策略是指只要有一个策略不允许即不可以重试,但不管哪种组合方式,组合中的每一个策略都会执行

那么这里问题又来了,我们调试源码的时候这么有多实现,我怎么知道应该进入哪个方法呢?

记住了,接口的方法上也是可以打断点的。你不知道会用哪个实现,但是 idea 知道:

image.png

这里就是用的 SimpleRetryPolicy 策略,即这个策略是 Spring-retry 的默认重试策略。

t == null || retryForException(t)) && context.getRetryCount() < this.maxAttempts

这个策略的逻辑也非常简单:

  • 1.如果有异常,则执行 retryForException 方法,判断该异常是否可以进行重试。
  • 2.判断当前已重试次数是否超过最大次数。

在这里,我们找到了控制重试逻辑的地方。

上面的第二点很好理解,第一点说明这个注解和事务注解 @Transaction 一样,是可以对指定异常进行处理的,可以看一眼它支持的选项:

image.png

注意 include 里面有句话我标注了起来,意思是说,这个值默认为空。且当 exclude 也为空时,默认是所有异常。

所以 Demo 里面虽然什么都没配,但是抛出 TimeoutException 也会触发重试逻辑。

又是一个通过翻源码挖掘到的知识点,这玩意就像是探索彩蛋似的,舒服。

看完判断是否能进行重试调用的逻辑之后,我们接着看一下真正执行业务方法的地方:

org.springframework.retry.RetryCallback#doWithRetry

目录
相关文章
|
4月前
|
Java 编译器
你说啥什么?注解你还不会?
你说啥什么?注解你还不会?
36 0
|
11月前
@PostMapping注解
@PostMapping注解
|
Java 测试技术 Spring
关于@RunWith注解的一点问题
IDEA写springboot测试关于@Runwith的小问题
142 0
关于@RunWith注解的一点问题
|
Java 编译器 Spring
什么是注解
什么是注解
|
Java 编译器
关于@FunctionalInterface注解
FunctionalInterface
326 0
关于@FunctionalInterface注解
|
存储 JSON Java
一文学会注解的正确使用姿势
一文学会注解的正确使用姿势
一文学会注解的正确使用姿势
扒一扒@Retryable注解,很优雅,有点意思! (4)
扒一扒@Retryable注解,很优雅,有点意思! (4)
188 0
扒一扒@Retryable注解,很优雅,有点意思! (4)
|
Java 程序员 开发工具
扒一扒@Retryable注解,很优雅,有点意思! (1)
扒一扒@Retryable注解,很优雅,有点意思! (1)
326 0
扒一扒@Retryable注解,很优雅,有点意思! (1)
|
安全 Java
扒一扒这个注解,我发现还有点意思。 (中)
扒一扒这个注解,我发现还有点意思。 (中)
109 0
扒一扒这个注解,我发现还有点意思。 (中)
|
安全 Oracle Java
扒一扒这个注解,我发现还有点意思。 (上)
扒一扒这个注解,我发现还有点意思。 (上)
107 0
扒一扒这个注解,我发现还有点意思。 (上)

热门文章

最新文章