Spring retry(二)- 源码解析-启动装配篇 @EnableRetry

上一篇文章,我们快速介绍了下spring-retry的使用技巧,本篇我们将会剖析源码去学习

一、 EnableRetry注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
@Documented
public @interface EnableRetry {/*** Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to* standard Java interface-based proxies. The default is {@code false}.* @return whether to proxy or not to proxy the class*/@AliasFor(annotation = EnableAspectJAutoProxy.class)boolean proxyTargetClass() default false;/*** Indicate the order in which the {@link RetryConfiguration} AOP <b>advice</b> should* be applied.* <p>* The default is {@code Ordered.LOWEST_PRECEDENCE - 1} in order to make sure the* advice is applied before other advices with {@link Ordered#LOWEST_PRECEDENCE} order* (e.g. an advice responsible for {@code @Transactional} behavior).*/int order() default Ordered.LOWEST_PRECEDENCE - 1;}

英文翻译我就不再解释了,上面说的很清楚;这里重点提一下@Import(RetryConfiguration.class)这个注解,表明了@EnableRetry注解的启动配置类是RetryConfiguration, 通过@Import注解来注入对应的配置类,这样的做法同样可见于@EnableAsync/@EnableScheduling等注解上; 看到这里,如何定义一个Enable配置注解是不是会了呢~

二、 RetryConfiguration如何构建 ponintCut和advisor

@SuppressWarnings("serial")
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Component
public class RetryConfiguration extends AbstractPointcutAdvisorimplements IntroductionAdvisor, BeanFactoryAware, InitializingBean, SmartInitializingSingleton, ImportAware {//在这里构建了ponintCut和advice@Overridepublic void afterPropertiesSet() throws Exception {this.retryContextCache = findBean(RetryContextCache.class);this.methodArgumentsKeyGenerator = findBean(MethodArgumentsKeyGenerator.class);this.newMethodArgumentsIdentifier = findBean(NewMethodArgumentsIdentifier.class);this.sleeper = findBean(Sleeper.class);Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<>(1);retryableAnnotationTypes.add(Retryable.class);//这里构建基于@Retryable注解的切点this.pointcut = buildPointcut(retryableAnnotationTypes);//这里构建了aop的通知this.advice = buildAdvice();this.advice.setBeanFactory(this.beanFactory);if (this.enableRetry != null) {setOrder(enableRetry.getNumber("order"));}}}

RetryConfiguration继承AbstractPointcutAdvisor实现了一个环绕切面,通知的逻辑见AnnotationAwareRetryOperationsInterceptor的实现; 这里需要补一下spring AOP的基础知识,详情见文档 https://cloud.tencent.com/developer/article/1808649 ,我觉的这篇文章已经写的非常好了,就不再细述;

接下来我们重点关注 AnnotationAwareRetryOperationsInterceptor 的实现逻辑,这里才是retgry启动业务的实现逻辑;

三、AnotationAwareRetryOperationsInterceptor 重试retry逻辑的装配

我们看下它的核心实现

public class AnnotationAwareRetryOperationsInterceptor implements IntroductionInterceptor, BeanFactoryAware {//...省略其它代码@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {//这里获取代理MethodInterceptorMethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());if (delegate != null) {return delegate.invoke(invocation);}else {return invocation.proceed();}}/*** 拿到代理类的实现**private MethodInterceptor getDelegate(Object target, Method method) {//缓存解析结果,主要用于class级别的retry配置ConcurrentMap<Method, MethodInterceptor> cachedMethods = this.delegates.get(target);if (cachedMethods == null) {cachedMethods = new ConcurrentHashMap<>();}MethodInterceptor delegate = cachedMethods.get(method);if (delegate == null) {MethodInterceptor interceptor = NULL_INTERCEPTOR;Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);if (retryable == null) {retryable = classLevelAnnotation(method, Retryable.class);}if (retryable == null) {retryable = findAnnotationOnTarget(target, method, Retryable.class);}if (retryable != null) {//假如你需要实现自定义的拦截器,那就走的是这里的逻辑,例如你要实现retry的上下文放置到数据库里记录,不放在内存里,那你就要考虑这里作文章了if (StringUtils.hasText(retryable.interceptor())) {interceptor = this.beanFactory.getBean(retryable.interceptor(), MethodInterceptor.class);}//有状态的retry, 走的是这里,我没有深入研究stateful的应用场景,总感觉即便是有状态的重试,必然跟业务逻辑本身也强关联的,那状态保护的逻辑,肯定也会抽出来,所以我不考虑放到框架本身去做,毕竟不同业务,状态保护方法不同;else if (retryable.stateful()) {interceptor = getStatefulInterceptor(target, method, retryable);}//这里应该是通用逻辑链路了else {interceptor = getStatelessInterceptor(target, method, retryable);}}cachedMethods.putIfAbsent(method, interceptor);delegate = cachedMethods.get(method);}this.delegates.putIfAbsent(target, cachedMethods);return delegate == NULL_INTERCEPTOR ? null : delegate;}//无状态的重试拦截器private MethodInterceptor getStatelessInterceptor(Object target, Method method, Retryable retryable) {//这里基于注解生产重试策略 和 延迟策略RetryTemplate template = createTemplate(retryable.listeners());template.setRetryPolicy(getRetryPolicy(retryable, true));template.setBackOffPolicy(getBackoffPolicy(retryable.backoff(), true));return RetryInterceptorBuilder.stateless().retryOperations(template).label(retryable.label())//这里关注下怎么构建recover的.recoverer(getRecoverer(target, method)).build();}//...省略其它代码
}

最终,无状态的重试拦截器见,所以RetryOperationsInterceptor是常用链路的实现逻辑

public static class StatelessRetryInterceptorBuilder extends RetryInterceptorBuilder<RetryOperationsInterceptor> {private final RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();@Overridepublic RetryOperationsInterceptor build() {if (this.recoverer != null) {this.interceptor.setRecoverer(this.recoverer);}if (this.retryOperations != null) {this.interceptor.setRetryOperations(this.retryOperations);}else {this.interceptor.setRetryOperations(this.retryTemplate);}if (this.label != null) {this.interceptor.setLabel(this.label);}return this.interceptor;}private StatelessRetryInterceptorBuilder() {}}

四、RetryOperationsInterceptor 常用retry operation的实现逻辑

RetryOperationsInterceptor是直接重试拦截器的实现逻辑,它的逻辑比较简单,封装了retryTemplate的执行逻辑;核心代码见RetryOperationsInterceptor#invoke()

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {//这个名字主要用于构建上下文的时候用,正常有么有意义不大String name;if (StringUtils.hasText(this.label)) {name = this.label;}else {name = invocation.getMethod().toGenericString();}final String label = name;//重试回调,这里不作赘述RetryCallback<Object, Throwable> retryCallback = new MethodInvocationRetryCallback<Object, Throwable>(invocation, label) {@Overridepublic Object doWithRetry(RetryContext context) throws Exception {context.setAttribute(RetryContext.NAME, this.label);context.setAttribute("ARGS", new Args(invocation.getArguments()));/** If we don't copy the invocation carefully it won't keep a reference to* the other interceptors in the chain. We don't have a choice here but to* specialise to ReflectiveMethodInvocation (but how often would another* implementation come along?).*/if (this.invocation instanceof ProxyMethodInvocation) {context.setAttribute("___proxy___", ((ProxyMethodInvocation) this.invocation).getProxy());try {return ((ProxyMethodInvocation) this.invocation).invocableClone().proceed();}catch (Exception | Error e) {throw e;}catch (Throwable e) {throw new IllegalStateException(e);}}else {throw new IllegalStateException("MethodInvocation of the wrong type detected - this should not happen with Spring AOP, "+ "so please raise an issue if you see this exception");}}};//有recover的时候,走这里就好if (this.recoverer != null) {ItemRecovererCallback recoveryCallback = new ItemRecovererCallback(invocation.getArguments(),this.recoverer);try {Object recovered = this.retryOperations.execute(retryCallback, recoveryCallback);return recovered;}finally {RetryContext context = RetrySynchronizationManager.getContext();if (context != null) {context.removeAttribute("__proxy__");}}}//没有recover走这里,都是调用RetryTemplate#execute的逻辑return this.retryOperations.execute(retryCallback);}

至此,全部的装配逻辑就讲结束,流程看下来并不复杂,对于未来自己想要定义一个类似的注解具有很大的参考意义!下一篇,我们讲解一下retry的真正的执行逻辑,并对整体的设计做一个介绍和思考总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/60869.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

现货黄金走势图中的止盈点

对平仓时机的把握能力&#xff0c;是衡量现货黄金投资者水平的重要标志&#xff0c;止盈点设置得是否合理&#xff0c;在行情兑现的时候能否及时地离场&#xff0c;是事关投资者账户浮盈最终能否落袋为安的“头等大事”&#xff0c;要在现货黄金走势图中把握止盈点&#xff0c;…

Linux-安装redis6.2.1及主备复制模式(replication)

Linux-安装redis6.2.1 下载redis6.2.1资源上传至安装目录解压及编译解压修改名称编译 修改配置文件主节点从节点 启动及测试启动主节点从节点 测试 下载redis6.2.1资源 地址》https://redis.io/download/ 上传至安装目录 例&#xff1a;/data/replication/ 解压及编译 解…

网络编辑的选择能力

以往的网络编辑给人的印象就是个信息搬运工&#xff0c;日常的工作就是从合作媒 体的网站上拷贝新闻&#xff0c;然后粘贴在发布系统中&#xff0c;唯一做的有点儿创造性的工作就是 根据字数要求做一个吸引人的标题就行了。这样的工作还谈不上具备媒介素养&#xff0c;因 为…

【kubernetes系列】Calico原理及配置

概述 Calico是针对容器&#xff0c;虚拟机和基于主机的本机工作负载的开源网络和网络安全解决方案。 Calico支持广泛的平台&#xff0c;包括Kubernetes&#xff0c;OpenShift&#xff0c;Docker EE&#xff0c;OpenStack和裸机服务。 Calico在每个计算节点都利用Linux Kernel实…

DP4863 国产双声道音频功率放大器芯片

产品概述&#xff1a; DP4863 电路是一种双声道桥接音频功率放大器。在 5 V 电源电压下&#xff0c;它能向 4 Ω 负载提供 2.2 W 的输出功率&#xff0c;或向 3 Ω 负载提供 2.5 W的输出功率&#xff0c;THD N 小于 1 %。此外&#xff0c;它还具有耳机输入端&#xff0c;可驱…

《Go 语言第一课》课程学习笔记(十四)

接口 认识接口类型 接口类型是由 type 和 interface 关键字定义的一组方法集合&#xff0c;其中&#xff0c;方法集合唯一确定了这个接口类型所表示的接口。type MyInterface interface {M1(int) errorM2(io.Writer, ...string) }我们在接口类型的方法集合中声明的方法&#…

博士后申请有哪些技巧?

在博士后申请过程中&#xff0c;有一些关键的技巧可以帮助申请者提高成功的机会。以下是知识人网小编的一些建议&#xff1a; 1.精选合适的导师和研究课题&#xff1a;选择与自己研究方向相关且感兴趣的导师和课题非常重要。导师的声誉、研究成果和合作风格都会影响你的博士后经…

Java写作的规范篇(一)

为什么要写下此篇&#xff1f; 首先在我日常的开发中&#xff0c;总是感觉自己在使用一种面向对象的语言然后在面向过程编程。代码中十分不规范&#xff0c;虽然意识到了这个问题。但是想要解决还是需要花费很多的思路去构思如何像写一篇优雅的作文一样写一篇优雅的程序代码。话…

智慧工地源码带开发手册文档 app 数据大屏、硬件对接、萤石云

智慧工地解决方案依托计算机技术、物联网、云计算、大数据、人工智能、VR、AR等技术相结合&#xff0c;为工程项目管理提供先进技术手段&#xff0c;构建工地现场智能监控和控制体系&#xff0c;弥补传统方法在监管中的缺陷&#xff0c;最终实现项目对人、机、料、法、环的全方…

一、设计模式概述-设计模式概述

一、什么是设计模式 软件设计模式&#xff08;Software Design Pattern&#xff09;&#xff0c;又称设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题&#xff0c;以及该问题的解…

黑马程序员上课笔记 P2 JAVA 前置课CMD的使用

引入&#xff1a; 一、人机交互 人机交互的故事&#xff1a; 计算机刚出现的时候的三个特点&#xff0c;占地广 造价高 耗电多 故事&#xff1a; 在计算机的历史中&#xff0c;人机交互&#xff08;Human-Computer Interaction&#xff0c;简称HCI&#xff09;一直在不断地…

Python:使用Resend发送邮件

官网&#xff1a;https://resend.com/ 很简单&#xff0c;只需调用api接口&#xff0c;即可发送邮件 需要提前准备好参数 api_key 从Resend申请的keyto_email 接收邮件的邮箱地址 import requestsheaders {Authorization: Bearer <api_key>,Content-Type: applicati…

自然语言处理(NLP)技术的例子

以下是几个自然语言处理&#xff08;NLP&#xff09;技术的例子&#xff1a; 机器翻译&#xff1a;机器翻译是将一种自然语言的文本转换成另一种语言的文本的过程。这种技术应用于在线翻译器、多语言聊天机器人、多语言搜索引擎等地方。 文本分类&#xff1a;文本分类将文本分…

ExpressLRS开源之RC链路性能测试

ExpressLRS开源之RC链路性能测试 1. 源由2. 分析3. 测试方案4. 测试设计4.1 校准测试4.2 实验室测试4.3 拉距测试4.4 遮挡测试 5. 总结6. 参考资料 1. 源由 基于ExpressLRS开源基本调试验证方法&#xff0c;对RC链路性能进行简单的性能测试。 修改设计总能够满足合理的需求&a…

CA证书颁发机构服务器

目录 一、CA证书颁发机构是什么&#xff1f; 二、数字证书可以干什么&#xff1f; 三、PKI&#xff1a;即公钥加密体系&#xff08;public key cryptography&#xff09; 四、CA在网络中的工作流程及原理&#xff08;以网站为例&#xff09; 五、HTTPS 的工作原理 六、CA私有证…

xml

1.xml 1.1概述【理解】 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为…

哪里可以找到优质的文章?

我认为中外科技内容过去主要是“信息差”&#xff0c;即人们可以直接从国外文章中摄取信息并直接实践&#xff0c;谁快谁赢。 而现在主要是“观点差”&#xff0c;国内科技相关的理论和评论文章的数量和质量都还比较弱。 所以&#xff0c;优质文章建议多找外文。 参考风险投资人…

IBM Spectrum LSF Explorer 为要求苛刻的分布式和任务关键型高性能技术计算环境提供强大的工作负载管理

IBM Spectrum LSF Explorer 适用于 IBM Spectrum LSF 集群的强大、轻量级报告解决方案 亮点 ● 允许不同的业务和技术用户使用单一解决方案快速创建和查看报表和仪表板 ● 利用可扩展的库提供预构建的报告 ● 自定义并生成性能、工作负载和资源使用情况的报…

自动驾驶和辅助驾驶系统的概念性架构(一)

摘要&#xff1a; 本文主要介绍包括功能模块图&#xff0c;涵盖了底层计算单元、示例工作负载和行业标准。 前言 本文档参考自动驾驶计算联盟(Autonomous Vehicle Computing Consortium)关于自动驾驶和辅助驾驶计算系统的概念系统架构。 该架构旨在与SAE L1-L5级别的自动驾驶保…

MySQL之从单机到集群

写在前面 本文一起看下MySQL是单机存在的问题&#xff0c;以及为了解决这些问题所提出的各种解决方案。 1&#xff1a;从单机到集群 并非业务发展初期我们就直接使用集群来支撑业务&#xff0c;而是简单的使用单机版本&#xff0c;但是随着业务的发展&#xff0c;单机的各种…