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 AbstractPointcutAdvisor
      implements IntroductionAdvisor, BeanFactoryAware, InitializingBean, SmartInitializingSingleton, ImportAware {

    //在这里构建了ponintCut和advice
    @Override
    public 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 {
    //...省略其它代码
   
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
       //这里获取代理MethodInterceptor
       MethodInterceptor 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();

   @Override
   public 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) {

      @Override
      public 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的真正的执行逻辑,并对整体的设计做一个介绍和思考总结

posted @ 2023-08-29 10:50  修烛  阅读(430)  评论(0编辑  收藏  举报