Java注解

目标需求:我们定义一个可以作用于方法上的注解,被注解的方法在产生异常时会自动重新执行

怎样方便的实现这种功能呢,这里分享一种结合Spring AOP的方式

注解分为两种:编译期注解运行时注解。这里我们定义的属于是运行时注解,通过在运行时通过反射来获取注解信息从而完成需求。

定义注解

首先来定义一个注解,注意要使用到两类元注解

Retention元注解指定该注解保留到运行时

Target元注解指定作用范围为方法

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TryAgainAnnotation {
    int tryAgainTimes() default 1;
}

这里我们定义了一个重复次数,默认为1

定义异常

该异常为我们执行业务逻辑时指定抛出的异常,附带一个message

public class TryAgainException extends RuntimeException{
    public TryAgainException(String message) {
        super(message);
    }
}

主逻辑:使用AOP来处理注解

使用到的通知类型为环绕通知,切入点设置为上面我们定义的注解

@Aspect
@Component
public class TryAgainAspect {

    @Pointcut("@annotation(com.example.springboottest.TryAgainAnnotation)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object doRetry(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取AOP拦截的方法签名
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        // 获取目标处理对象
        Object target = proceedingJoinPoint.getTarget();
        // 获取注解中的信息(重复次数)
        // 首先从对象拿到方法, 再从方法拿到注解
        Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
        TryAgainAnnotation tryAgainAnnotation = method.getAnnotation(TryAgainAnnotation.class);
        // 获取重试次数
        int maxRetries = tryAgainAnnotation.tryAgainTimes();
        // 开始执行主逻辑(重新执行代码)
        int attempts = 0;
        while (attempts <= maxRetries) {
            attempts++;
            try {
                // 尝试重复执行
                return proceedingJoinPoint.proceed();
            }
            catch (TryAgainException e) {
                System.out.println("出错了! 错误信息:" + e.getMessage());
                // 如果次数达到了, 则抛出异常并终止执行, 否则重新执行
                if (attempts > maxRetries)
                    throw new RuntimeException("重试结束, 执行失败");
                else
                    System.out.println("方法正在重试~~");
            }
        }
        return null;
    }
}

测试一下

准备一个Controller和一个Service

@RestController
public class TestController {

    @Autowired
    AppService appService;

    @RequestMapping("/test")
    public void test() {
        appService.print();
    }
}

设置重试次数为3次

@Service
public class AppService {
    @TryAgainAnnotation(tryAgainTimes = 3)
    public void print() {
        throw new TryAgainException("假装出错了");
    }
}

当我们访问127.0.0.1:8080/test时,控制台此时会打印

出错了! 错误信息:假装出错了
方法正在重试~~
出错了! 错误信息:假装出错了
方法正在重试~~
出错了! 错误信息:假装出错了
方法正在重试~~
出错了! 错误信息:假装出错了
java.lang.RuntimeException: 重试结束, 执行失败

可以看到完美的符合了我们的重复三次的需求,并且可以复用到各种其他的方法上面~

其中要注意到一点的是,不能将被注解的方法放在Controller下面(如果Controller中包含了主调用逻辑的话)。因为AOP是基于动态代理的,必须要使用其他对象的方法才能生成代理类完成动态代理,同样的道理也适用于同样基于AOP的Spring事务注解@Transactional

Last modification:January 4th, 2022 at 04:22 pm