因为有很多重试场景,每次都是我们硬编,也没有找到现成的重试方案,因此写了一个重试标注。
优点:用法很简单,只需在需要重试的方法加上标注,并指定重试次数。把spring-repeater.xml引入即可。由于它重试的条件是方法发生异常,因此需要被重试的方法必需在发生异常且需重试时将异常抛出来。如果代码够规范这不应该是问题。
例如需要对updateInfoToDb方法进行异常后重试3次(实际最多只重试2次,最多共执行3次)
@Repeater(3)
public String updateInfoToDb(String id) throws Throwable {
if("1".equals(id)){throw new MyException();}else if("2".equals(id)){throw new RuntimeException("id:"+ id);}return "id:" + id;
}
缺点:做了一个性能测试,对于加上重试标注的方法比原生方法的执行效率会慢10倍。因此大家需要慎重考虑使用场景。
定义重试annotation
package com.jd.test.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Repeater {
/**
* 重试次数
* @return
*/
int value() default 3 ;
//Class<? extends Throwable>[] exception();
}
package com.jd.test.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.jd.test.annotation.Repeater;
/**
* 定义重试切面
*
* @author pengxiantie
*/
@Aspect
@Component
public class RepeaterPointcut {
// com.jd.test.service包及子包所有类
// @Pointcut("execution(* com.jd.test.service.*.* (. .))")
// RepeaterBusinessImpl 及子包所有类
// @Pointcut("execution(* com.jd.test.service.impl.RepeaterBusinessImpl.*(..))")
// 实现RepeaterAware接口的目标类
// @Pointcut("this (com.jd.test.service.RepeaterAware)") //
// @Pointcut("execution(* com.jd.test.service.impl.FeeBusinessImpl.*(..))")
/**
* 定义一个切入点。指定所有标注@Repeater的方法。
*/
@SuppressWarnings("unused")
@Pointcut("@annotation(com.jd.test.annotation.Repeater)")
private void repeaterMethod() {
}
/**
* 定义环绕通知。 @Around("anyMethod() && @annotation(repeater)"),表示任意方法且带@annotation的方法
*
* @param invocation
* @param repeater
* @return
* @throws Throwable
*/
@Around("repeaterMethod() && @annotation(repeater)")
public Object doBasicProfiling(ProceedingJoinPoint invocation, Repeater repeater) throws Throwable {
int repeatCount = repeater.value();
if(repeatCount<=0){
repeatCount = 1;
}
Throwable exception = null;
//如果失败,则重试。
for (int i = 0; i <= repeatCount; i++) {
try {
return invocation.proceed();
} catch (Throwable e) {
exception = e;
}
}
//重试后仍没有执行成功,抛出异常
throw exception;
}
}
spring-repeater.xml 打开aop功能
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
"
>
<!-- 打开apo功能 -->
<aop:aspectj-autoproxy/>
</beans>