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

这篇具有很好参考价值的文章主要介绍了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的真正的执行逻辑,并对整体的设计做一个介绍和思考总结文章来源地址https://www.toymoban.com/news/detail-685176.html

到了这里,关于Spring retry(二)- 源码解析-启动装配篇 @EnableRetry的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 拆解Spring boot:Springboot为什么如此丝滑而简单?源码剖析解读自动装配

    🎉🎉欢迎光临,终于等到你啦🎉🎉 🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.csdn

    2024年03月23日
    浏览(44)
  • Spring Boot 启动扩展点深入解析

    Spring Boot以其“约定优于配置”的理念和简洁的自动配置机制,极大地简化了Spring应用的初始化和开发过程。然而,在某些特定场景下,我们可能需要对Spring Boot的启动过程进行定制或扩展。这时,了解Spring Boot的启动扩展点就显得尤为重要。 来自:gwzkb.com 来自:dlanye.com Spring

    2024年04月08日
    浏览(51)
  • Android系统启动流程 源码解析

    本文链接:https://blog.csdn.net/feather_wch/article/details/132518105 有道云脑图:https://note.youdao.com/s/GZ9d8vzO 1、整体流程 Boot Room BootLoader idle kthread init init ServiceManager zygote zygote SystemServer app 1、kernel/common/init/main.c 2、andorid.mk-android.bp编译 3、init是用户空间鼻祖 属于C、C++ Framework 1.1 启动源

    2024年02月11日
    浏览(51)
  • 【SpringBoot3.0源码】启动流程源码解析 • 上

    SpringBoot启动类:

    2024年02月02日
    浏览(38)
  • 重试框架入门:Spring-Retry&Guava-Retry

    在日常工作中,随着业务日渐庞大,不可避免的涉及到调用远程服务,但是远程服务的健壮性和网络稳定性都是不可控因素,因此,我们需要考虑合适的重试机制去处理这些问题,最基础的方式就是手动重试,侵入业务代码去处理,再高端一点的通过切面去处理,较为优雅的

    2024年02月13日
    浏览(46)
  • Spring Retry

    工作中,经常遇到需要重试的场景,最简单的方式可以用try...catch...加while循环来实现。那么,有没有统一的、优雅一点儿的处理方式呢?有的,Spring Retry就可以帮我们搞定重试问题。 关于重试,我们可以关注以下以下几个方面: 什么情况下去触发重试机制 重试多少次,重试

    2024年02月05日
    浏览(49)
  • [RocketMQ] Broker启动流程源码解析 (二)

    1.Brocker介绍 Broker主要负责消息的存储、投递和查询以及服务高可用保证, Broker包含了以下几个重要子模块。 Remoting Module: 整个Broker的实体, 负责处理来自Client端的请求 Client Manager: 负责管理客户端 Producer和Consumer, 维护Consumer的Topic订阅信息 Store Service: 提供方便简单的API接口处理

    2024年02月09日
    浏览(53)
  • spring retry 配置及使用

    依赖 开始使用 代码示例 业务注解 代码示例 注意 两个方法的返回值要一样,否则是不起作用的 NullPointerException 必须要将异常类型当作参数传入 如果不这样的话无法进行回调,当然不配置 @Recever 也可以,那就不会有回调处理了 写一个action 调用一下方法 输出结果 以上最简单

    2024年01月19日
    浏览(38)
  • Spring Boot启动源码分析

    版本:spring-boot-starter-parent版本为2.3.0 Spring Boot项目的启动入口是一个main方法,因此我们从该方法入手即可 跟踪run方法 这里分两步debug: new SpringApplication( primarySources ) 执行run()方法 deduceFromClasspath推断应用程序类型 该方法根据是否存在指定路径的类来推断应用程序类型。有

    2024年02月07日
    浏览(50)
  • springboot启动流程 (3) 自动装配

    在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。 本文将详细分析该注解的工作流程。 启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。 自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-embedded.jar,那么可能需要一个

    2024年02月09日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包