临安建办网站,建造师,传媒公司招聘,为网站网站做宣传上一篇文章#xff0c;我们快速介绍了下spring-retry的使用技巧#xff0c;本篇我们将会剖析源码去学习 一、 EnableRetry注解
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
EnableAspectJAutoProxy(proxyTargetClass false)
Import(RetryConfiguration.c… 上一篇文章我们快速介绍了下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 badvice/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和adviceOverridepublic void afterPropertiesSet() throws Exception {this.retryContextCache findBean(RetryContextCache.class);this.methodArgumentsKeyGenerator findBean(MethodArgumentsKeyGenerator.class);this.newMethodArgumentsIdentifier findBean(NewMethodArgumentsIdentifier.class);this.sleeper findBean(Sleeper.class);SetClass? 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配置ConcurrentMapMethod, 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 RetryInterceptorBuilderRetryOperationsInterceptor {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;//重试回调这里不作赘述RetryCallbackObject, Throwable retryCallback new MethodInvocationRetryCallbackObject, 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 dont copy the invocation carefully it wont keep a reference to* the other interceptors in the chain. We dont 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的真正的执行逻辑并对整体的设计做一个介绍和思考总结