guava-retrying 加入自定义注解,用aop方式使用
由于网络环境不太稳定,一些接口方法需要加入重试。正好看到Google's Guava library 项目https://github.com/rholder/guava-retrying,里面功能比较完善。但是官方没有提供注解方式使用,频繁使用会有点麻烦。所以查阅相关资料后编写了支持注解使用的方法
定义注解类
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * des: guava retrying 注解使用 * doc: https://github.com/rholder/guava-retrying */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Retry { //指定异常时重试 由于pjp.proceed();需要try catch 异常,会有问题暂时没找到解决方法,先注释 // Class[] exceptionClass() default {}; //出现Exception时重试 boolean retryIfException() default false; //程序出现RuntimeException异常时重试 boolean retryIfRuntimeException() default false; //重试次数 int attemptNumber() default 3; //重试间隔 ms long waitStrategySleepTime() default 1000; //持续时间; 期间 long duration() default 0; //返回值为指定字符串时重试 String returnResult() default "willRertyString"; //返回值为false时重试(默认关闭) 不支持同时设置指定返回字符串重试 boolean closeReturnFalseRetry() default true; }
AOP类
package net.capitalonline.retry.retryaspect;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Predicates;
import net.capitalonline.retry.Retry;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* des: guava retrying 注解使用
* doc: https://github.com/rholder/guava-retrying
*/
@Aspect
@Component
public class RetryAspect {
@Pointcut("@annotation(net.capitalonline.retry.Retry)")
public void MethodCallConstraintPointcut(){
}
@Around(value = "@annotation(net.capitalonline.retry.Retry)")
public Object monitorAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("enter retry aspect");
Method method;
if (pjp.getSignature() instanceof MethodSignature) { //判断注解是否method 上
MethodSignature signature = (MethodSignature) pjp.getSignature();
method = signature.getMethod();
} else {
return null;
}
Retry annotation = method.getDeclaredAnnotation(Retry.class);
//重试时间,重试次数
if (annotation.duration() <= 0 && annotation.attemptNumber() <= 1) {
return pjp.proceed();
}
//不能设置开启returnFalse重试 和指定返回值重试
if(!annotation.closeReturnFalseRetry() && !annotation.returnResult().equals("willRertyString")){
return pjp.proceed();
}
RetryerBuilder builder = RetryerBuilder.newBuilder();
//重试次数
if (annotation.attemptNumber() > 0) {
builder.withStopStrategy(StopStrategies.stopAfterAttempt(annotation.attemptNumber()));
}
//退出策略
if (annotation.duration() > 0) {
builder.withStopStrategy(StopStrategies.stopAfterDelay(annotation.duration(), TimeUnit.MILLISECONDS));
}
//重试间间隔等待策略
if (annotation.waitStrategySleepTime() > 0) {
builder.withWaitStrategy(WaitStrategies.fixedWait(annotation.waitStrategySleepTime(), TimeUnit.MILLISECONDS));
}
// //停止重试的策略
// if (annotation.exceptionClass().length > 0) {
// for (Class retryThrowable : annotation.exceptionClass()) {
// if (retryThrowable != null && Throwable.class.isAssignableFrom(retryThrowable)) {
// builder.retryIfExceptionOfType(retryThrowable);
// }
// }
// }
//RuntimeException时重试
if (annotation.retryIfRuntimeException()){
builder.retryIfRuntimeException();
}
if (annotation.retryIfException()){
builder.retryIfException();
}
if (!annotation.returnResult().equals("willRertyString")){
builder.retryIfResult(Predicates.equalTo(annotation.returnResult()));
}
if (!annotation.closeReturnFalseRetry()){
builder.retryIfResult(Predicates.equalTo(annotation.closeReturnFalseRetry()));
}
return builder.build().call(() -> {
try {
System.out.println("aspect exec method "+ method.getName());
return pjp.proceed();
} catch (Throwable throwable) {
if (throwable instanceof Exception) {
throw (Exception) throwable;
} else {
throw new Exception(throwable);
}
}
});
// Callable<Object> callable=new Callable<Object>() {
// @Override
// public Object call() throws Exception {
// try {
// return pjp.proceed();
// }catch (Throwable e){
// e.printStackTrace();
// System.out.println("测试");
// return null;
// }
// }
// };
// try {
// return builder.build().call(callable);
// }catch (Exception e){
// System.out.println("重试");
// return "error";
// }
}
}
application.yaml 配置中加入
aop:
proxy-target-class: true
使用实例
import org.springframework.stereotype.Component;
import java.lang.reflect.UndeclaredThrowableException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class TestA {
int x=0;
@Retry(retryIfException = true, waitStrategySleepTime = 1200)
public void test() throws RemoteException{
try {
SimpleDateFormat sdf = new SimpleDateFormat();// 格式化时间
sdf.applyPattern("yyyy-MM-dd HH:mm:ss a");// a为am/pm的标记
Date date = new Date();// 获取当前时间
System.out.println("现在时间:" + sdf.format(date)); // 输出已经格式化的现在时间(24小时制)
int a=1/x;
System.out.println("1231");
} catch (Exception e) {
System.out.println("nonono");
throw new RemoteException("12321");
}
}
}
import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest @WebAppConfiguration @ContextConfiguration(classes = Application.class) public class Tmpmytest { @Autowired TestA testA; @Test public void tmptest() { try { testA.test(); } catch (Exception e) { System.out.println("ada"); } } }