Spring-retry详解( 三 )


  • ExponentialBackOffPolicy
指数退避策略 , 当设置 multiplier 时使用 , 每次重试时间间隔为 当前延迟时间 * multiplier 。
例如:默认初始 0.1 秒 , 系数是 2 , 那么下次延迟 0.2 秒 , 再下次就是延迟 0.4 秒 , 如此类推 , 最大 30 秒 。
  • ExponentialRandomBackOffPolicy
指数随机退避策略 。在指数退避策略的基础上增加了随机性 。
  • UniformRandomBackOffPolicy
均匀随机策略 , 设置 maxDely 但没有设置 multiplier 时使用 , 重试间隔会在 maxDelay 和 delay 间随机
原理切入点@EnableRetry@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@EnableAspectJAutoProxy(proxyTargetClass = false)@Import(RetryConfiguration.class)@Documentedpublic @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*/boolean proxyTargetClass() default false;}@EnablRetry 中使用了两个特殊的注解
  • @EnableAspectJAutoProxy
这个注解的作用是开启 aop 的功能 , 默认使用 jdk 的动态代理 。如果 proxyTargetClass 参数为 true , 则使用 cglib 的动态代理 。
  • @Import
Import 引入了 RetryConfiguration 的 bean。我们重点看下这个 bean 。
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)@Componentpublic class RetryConfiguration extends AbstractPointcutAdvisorimplements IntroductionAdvisor, BeanFactoryAware, InitializingBean {private Advice advice;private Pointcut pointcut;我们可以看到 RetryConfiguration 继承了 AbstractPointcutAdvisor , 所以 RetryConfiguration 需要实现 getAdvice() 和 getPointcut() 接口 , 所以这个 bean 的作用就是为 @Retryable 注解注册 pointcut 切点和 advice 增强 。我们再来看他的 初始化方法
@Overridepublic void afterPropertiesSet() throws Exception {this.retryContextCache = findBean(RetryContextCache.class);this.methodArgumentsKeyGenerator = findBean(MethodArgumentsKeyGenerator.class);this.newMethodArgumentsIdentifier = findBean(NewMethodArgumentsIdentifier.class);this.retryListeners = findBeans(RetryListener.class);this.sleeper = findBean(Sleeper.class);Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(1);retryableAnnotationTypes.add(Retryable.class);this.pointcut = buildPointcut(retryableAnnotationTypes); //创建 pointcutthis.advice = buildAdvice(); //创建 adviceif (this.advice instanceof BeanFactoryAware) {((BeanFactoryAware) this.advice).setBeanFactory(this.beanFactory);}} protected Advice buildAdvice() {AnnotationAwareRetryOperationsInterceptor interceptor = new AnnotationAwareRetryOperationsInterceptor();if (this.retryContextCache != null) {interceptor.setRetryContextCache(this.retryContextCache);}if (this.retryListeners != null) {interceptor.setListeners(this.retryListeners);}if (this.methodArgumentsKeyGenerator != null) {interceptor.setKeyGenerator(this.methodArgumentsKeyGenerator);}if (this.newMethodArgumentsIdentifier != null) {interceptor.setNewItemIdentifier(this.newMethodArgumentsIdentifier);}if (this.sleeper != null) {interceptor.setSleeper(this.sleeper);}return interceptor; }上面代码用到了 AnnotationClassOrMethodPointcut , 其实它最终还是用到了 AnnotationMethodMatcher 来根据注解进行切入点的过滤 。这里就是 @Retryable 注解了
下面来看 AnnotationAwareRetryOperationsInterceptor 的 invoke() 方法
@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {//获取真正的代理类MethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());if (delegate != null) {//代理类存在 , 则执行代理类的 invoke()方法return delegate.invoke(invocation);}else {//否则 , 直接执行目标方法return invocation.proceed();}}这里 getDelegate() 会处理 @Retryable 的相关参数以及决定使用哪种重试策略和退避策略 。
private MethodInterceptor getDelegate(Object target, Method method) {ConcurrentMap<Method, MethodInterceptor> cachedMethods = this.delegates.get(target);if (cachedMethods == null) {cachedMethods = new ConcurrentHashMap<Method, MethodInterceptor>();}MethodInterceptor delegate = cachedMethods.get(method);if (delegate == null) {//获取方法上的 Retryable 注解MethodInterceptor interceptor = NULL_INTERCEPTOR;Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);if (retryable == null) {//获取类上的 Retryable 注解retryable = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Retryable.class);}if (retryable == null) {//获取目标类或者方法上的 Retryable 注解retryable = findAnnotationOnTarget(target, method, Retryable.class);}if (retryable != null) {if (StringUtils.hasText(retryable.interceptor())) {//是否实现了自定义拦截 , 优先级最高interceptor = this.beanFactory.getBean(retryable.interceptor(), MethodInterceptor.class);}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;}


推荐阅读