美文网首页
spring-retry(4.interceptor包、anno

spring-retry(4.interceptor包、anno

作者: 沉寂之舟 | 来源:发表于2018-01-24 22:21 被阅读298次

    这部分是retry包中最难部分了,主要是为了利用AOP机制,把任意声明为@Retryable的方法都变成可以可重试的。

    interceptor包和annotation紧密相关。

    annotation.png
    1. MethodArgumentsKeyGenerator接口:
    Object getKey(Object[] item);
    

    传入item通常为方法的参数,返回的Object为这些参数的唯一标识

    1. FixedKeyGenerator类:
      其简单实现,无论何种参数数组传入,都返回给定的Label值。
    2. MethodInvocationRecoverer接口:
      定义回复接口方法,具体声明如下:
    T recover(Object[] args, Throwable cause);
    
    1. NewMethodArgumentsIdentifier接口:
      区别判断一组参数,之前是否执行过
    2. RetryOperationsInterceptor类
      由于Retry是利用AOP机制实现的,因而需要定义MethodInterceptor把我们声明的方法绑定到RetryTemplate的调用中去。
    public Object invoke(final MethodInvocation invocation) throws Throwable {
            //先取到该方法调用的名字、
            String name;
            if (StringUtils.hasText(label)) {
                name = label;
            } else {
                name = invocation.getMethod().toGenericString();
            }
            final String label = name;
            //构造回调函数
            RetryCallback<Object, Throwable> retryCallback = new RetryCallback<Object, Throwable>() {
    
                public Object doWithRetry(RetryContext context) throws Exception {
                    // 在上下文中登记Label
                    context.setAttribute(RetryContext.NAME, label);
                    // 多一重保险,判断是代理方法调用
                    if (invocation instanceof ProxyMethodInvocation) {
                        try {
                            //实际利用动态方法调用,执行方法本身
                            return ((ProxyMethodInvocation) invocation).invocableClone().proceed();
                        }
                        catch (Exception e) {
                            // 捕捉后重新抛出
                            throw e;
                        }
                        catch (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");
                    }
                }
    
            };
            // 判断有无恢复方法,如果有,就构造一个恢复回调
            if (recoverer != null) {
                ItemRecovererCallback recoveryCallback = new ItemRecovererCallback(
                        invocation.getArguments(), recoverer);
                // 实际还是传入RetryTemplate执行方法调用
                return this.retryOperations.execute(retryCallback, recoveryCallback);
            }
            // 实际还是传入RetryTemplate执行方法调用
            return this.retryOperations.execute(retryCallback);
    
        }
    
    1. StatefulRetryOperationsInterceptor类:
      RetryOperationsInterceptor类是公用一个RetryTemplate的。而又状态的RetryOperationsInterceptor就必须每个实例都有自己的RetryTemplate,再配合RetryState决定是否需要抛出RollbackException了。其核心invoke方法如下:
    public Object invoke(final MethodInvocation invocation) throws Throwable {
    
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Executing proxied method in stateful retry: "
                        + invocation.getStaticPart() + "("
                        + ObjectUtils.getIdentityHexString(invocation) + ")");
            }
    
            Object[] args = invocation.getArguments();
            Object defaultKey = Arrays.asList(args);
            if (args.length == 1) {
                defaultKey = args[0];
            }
    
            Object key = createKey(invocation, defaultKey);
            // 构造重试状态
            RetryState retryState = new DefaultRetryState(key,
                    this.newMethodArgumentsIdentifier != null
                            && this.newMethodArgumentsIdentifier.isNew(args),
                    this.rollbackClassifier);
            // 实际还是传入RetryTemplate执行方法调用
            Object result = this.retryOperations
                    .execute(new MethodInvocationRetryCallback(invocation, label),
                            this.recoverer != null
                                    ? new ItemRecovererCallback(args, this.recoverer) : null,
                            retryState);
    
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Exiting proxied method in stateful retry with result: ("
                        + result + ")");
            }
    
            return result;
    
        }
    
    1. RetryInterceptorBuilder类
      流式构造的工厂RetryInterceptor类。具体使用的例子如下:
     StatefulRetryOperationsInterceptor interceptor = RetryInterceptorBuilder.stateful() //构造有状态的Interceptor
            .maxAttempts(5).backOffOptions(1, 2, 10) // initialInterval, multiplier,
            .build();
    

    这个工厂能生产3种不同的Interceptor,StatefulRetryInterceptor(有状态的),StatelessRetryInterceptor(无状态),CircuitBreakerInterceptor(有状态加熔断)。

    1. RecoverAnnotationRecoveryHandler<T>类:
      MethodInvocationRecoverer的实现,根据一个方法,查找对应@Recover注解方法,封装到Recovery处理之中,也就是@Retryable和@Recover的自动匹配过程。从构造器可以看出,保存了目标类和目标方法,然后进行解析。
    // target为目标类,method为需要Revover的目标方法
    public RecoverAnnotationRecoveryHandler(Object target, Method method) {
            this.target = target;
            init(target, method);
        }
    

    其核心的init方法代码如下:

    private void init(Object target, Method method) {
            final Map<Class<? extends Throwable>, Method> types = new HashMap<Class<? extends Throwable>, Method>();
            //保存传入的方法作为备份方法
            final Method failingMethod = method;
            //调用ReflectionUtils反射工具,查找符合条件目标方法
            ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(),
                    new MethodCallback() {
                        @Override
                        // 声明回调函数,每个符合条件的目标方法,都会登记到types和methods中
                        public void doWith(Method method) throws IllegalArgumentException,
                                IllegalAccessException {
                            //查找@Recover接口
                            Recover recover = AnnotationUtils.findAnnotation(method,
                                    Recover.class);
                            if (recover != null
                                    && failingMethod.getReturnType().isAssignableFrom(
                                            method.getReturnType())) {
                                Class<?>[] parameterTypes = method.getParameterTypes();
                                //判断找到的方法和目标方法参数,异常是否一致
                                if (parameterTypes.length > 0
                                        && Throwable.class
                                                .isAssignableFrom(parameterTypes[0])) {
                                    @SuppressWarnings("unchecked")
                                    Class<? extends Throwable> type = (Class<? extends Throwable>) parameterTypes[0];
                                    //登记下这个revover方法的参数
                                    types.put(type, method);
                                    methods.put(method, new SimpleMetadata(
                                            parameterTypes.length, type));
                                } else {
                                    //找不到,就给配置个默认值
                                    classifier.setDefaultValue(method);
                                    methods.put(method, new SimpleMetadata(
                                            parameterTypes.length, null));
                                }
                            }
                        }
                    });
            classifier.setTypeMap(types);
        }
    
    1. AnnotationAwareRetryOperationsInterceptor类:
      注解解析器,查找工程中@Retryable方法,并生成RetryOperationsInterceptor的类。

    2. RetryConfiguration类:
      @EnableRetry引入的配置类,内部封装AnnotationClassOrMethodPointcut,AnnotationClassOrMethodFilter,AnnotationMethodsResolver三个Aop工具类。通过反射查找到目标方法,并应用aop给方法加料(生成proxy对象),从而实现把普通方法变成可重试方法。


    最后总结一下

    虽然看过去东西很多,实际上除了概念理解外,真正需要掌握的核心只有:

    • 三个注解:@EnableRetry,@Retryable,@Recover;
    • 两个策略:补偿策略,重试策略。(具体策略较多,但也比较容易理解);
    • 一个模板:RetryTemplate。

    相关文章

      网友评论

          本文标题:spring-retry(4.interceptor包、anno

          本文链接:https://www.haomeiwen.com/subject/zlupaxtx.html