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