基于 Guava Retry 在Spring封装一个重试功能
pom依赖
<dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>2.0.0</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
注解类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Retryable {/*** 需要重试的异常类型* this.isAssignableFrom(方法抛出的异常) 也就是说,方法抛出的异常必须是retryOn的子类或者子接口*/Class<? extends Throwable> retryOn() default Throwable.class;// 重试次数int maxAttempts() default 3;// 重试间隔long delayMillis() default 1000;
}
import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
@Aspect
@Order(1)
public class RetryableAspect {@Around("@annotation(retryable)")public Object retry(ProceedingJoinPoint joinPoint, Retryable retryable) throws Throwable {Retryer<Object> retryer = RetryerBuilder.<Object>newBuilder().retryIfExceptionOfType(retryable.retryOn()).withStopStrategy(StopStrategies.stopAfterAttempt(retryable.maxAttempts())).withWaitStrategy(WaitStrategies.fixedWait(retryable.delayMillis(), TimeUnit.MILLISECONDS)).build();try {return retryer.call(() -> {try {return joinPoint.proceed();} catch (Exception exception) {throw exception;} catch (Throwable e) {throw new WrapRetryThrowable(e);}});} catch (RetryException e) {throw e.getLastFailedAttempt().getExceptionCause();}}public static class WrapRetryThrowable extends Exception {public WrapRetryThrowable(Throwable cause) {super(cause);}}}
测试类
在这里插入代码片@RestController
public class RetryController {/*** 顶级异常类测试* @return* @throws Throwable*/@Retryable(retryOn = Exception.class,maxAttempts = 3,delayMillis = 1000)@GetMapping("/throwable")public String performTask() throws Throwable {System.out.println("performTask" + System.currentTimeMillis());// 在这里实现可能抛出异常的业务逻辑throw new Throwable("error");}/*** 异常类测试* @return* @throws CustomException*/@Retryable(retryOn = CustomException.class,maxAttempts = 2,delayMillis = 1000)@GetMapping("/customException")public void customException() {System.out.println("customException" + System.currentTimeMillis());// 在这里实现可能抛出异常的业务逻辑}// 抛出的异常跟枚举异常不一致,不会重试@Retryable(retryOn = CustomException.class,maxAttempts = 3,delayMillis = 1000)@GetMapping("/customException2")public String customException2() throws Exception {System.out.println("customException2" + System.currentTimeMillis());// 在这里实现可能抛出异常的业务逻辑throw new Exception("这是一段自定义异常的抛出");}}