自定义注解-方法重试@RetryProcess

204 阅读2分钟

背景

在项目开发中,有时候会出现接口调用失败,本身调用又是异步的,如果是因为一些网络问题请求超时,总想可以重试几次把任务处理掉。 一些RPC框架,比如dubbo都是有重试机制的,但是并不是每一个项目多会使用dubbo框架,常规的小项目有时候直接使用http进行不同项目之间的交互。

思路

使用spring aop和自定义注解来,建立一套重试机制。 根据切入点和自定义注解,来完成重试工作。

自定义注解

定义注解

package com.github.feifuzeng.study.annotation;

import org.springframework.stereotype.Component;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author feifz
 * @version 1.0.0
 * @Description 重试机制自定义注解
 * @createTime 2019年06月20日 14:05:00
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RetryProcess {
    int value() default 1;

    int sleep() default 1000;
}

注解中value参数为重试次数,默认为1,sleep为重试间隔,默认为1000毫秒。

定义切面

package com.github.feifuzeng.study.annotation;

/**
 * @author feifz
 * @version 1.0.0
 * @Description 重试注解切面
 * @createTime 2019年06月20日 14:07:00
 */

import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicInteger;

@Aspect
@Component
@Log4j2
public class RetryProcessAspect {
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    @AfterThrowing(pointcut=("@annotation(com.github.feifuzeng.study.annotation.RetryProcess)"))
    public void tryAgain(JoinPoint point) {
        try {
            MethodSignature methodSignature = (MethodSignature) point.getSignature();
            RetryProcess retryProcess = methodSignature.getMethod().getAnnotation(RetryProcess.class);

            if (atomicInteger.intValue() < retryProcess.value()) {
                int i = atomicInteger.incrementAndGet();
                Thread.sleep(retryProcess.sleep() * i);
                log.debug("开始重试第" + i + "次");
                MethodInvocationProceedingJoinPoint methodPoint = ((MethodInvocationProceedingJoinPoint) point);
                methodPoint.proceed();
            }
        } catch (Throwable throwable) {
            tryAgain(point);
        }
    }
}

使用示例

@RetryProcess(value = 2,sleep = 2000)
    public List<User> findList() {
        return userMapper.queryUserList();
    }

该自定义注解用于方法上,当改方法在执行过程中存在异常,不管是系统异常还是手动抛出的业务异常,均可实现重试。

知识课堂

JoinPoint 对象

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象. 常用api:

方法名功能
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();获取传入目标方法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中, 添加了

Object proceed() throws Throwable //执行目标方法 Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法

两个方法. MethodInvocationProceedingJoinPoint 对象

查看源码得知MethodInvocationProceedingJoinPoint是ProceedingJoinPoint和StaticPart的实现,只要使用的还是ProceedingJoinPoint对象的两个实现方法。

参考

结语

欢迎关注微信公众号『码仔zonE』,专注于分享Java、云计算相关内容,包括SpringBoot、SpringCloud、微服务、Docker、Kubernetes、Python等领域相关技术干货,期待与您相遇!