Spring Boot AOP 优雅实现异常重试机制
Spring Boot AOP 优雅实现异常重试机制
零侵入、可配置、易扩展
通过 自定义注解 + AOP 环绕通知 在任意方法上实现 异常自动重试,支持
重试次数、间隔、异常类型、快速失败 等高级特性。
一、实现效果
@Service
public class RemoteService {@Retryable(retryTimes = 3, interval = 2_000,retryOn = ConnectException.class,fastFailOn = IllegalArgumentException.class)public String call() {// 调用第三方接口return restTemplate.getForObject(url, String.class);}
}
- 发生异常后自动重试 3 次,每次间隔 2 秒
- 出现
IllegalArgumentException
立即结束重试 - 成功后直接返回结果,对业务代码 零侵入
二、核心步骤
1. 引入依赖(Spring Boot 3.x)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retryable {int retryTimes() default 3; // 最大重试次数long interval() default 1_000; // 间隔毫秒Class<? extends Throwable>[] retryOn() default {}; // 需要重试的异常Class<? extends Throwable>[] fastFailOn() default {}; // 立即失败的异常
}
3. AOP 切面(RetryableAspect)
@Slf4j
@Aspect
@Component
public class RetryableAspect {@Around("@annotation(retryable)")public Object around(ProceedingJoinPoint pjp, Retryable retryable) throws Throwable {int maxAttempts = retryable.retryTimes();long interval = retryable.interval();Set<Class<?>> retrySet = Set.of(retryable.retryOn());Set<Class<?>> failSet = Set.of(retryable.fastFailOn());Throwable lastEx = null;for (int attempt = 1; attempt <= maxAttempts; attempt++) {try {return pjp.proceed();} catch (Throwable ex) {lastEx = ex;// 快速失败if (failSet.stream().anyMatch(c -> c.isAssignableFrom(ex.getClass()))) {log.error("Fast fail on {}", ex.getClass().getSimpleName());throw ex;}// 最后一轮不再重试if (attempt == maxAttempts) {log.error("Exhausted {} attempts", maxAttempts);throw ex;}// 匹配重试异常if (retrySet.isEmpty() || retrySet.stream().anyMatch(c -> c.isAssignableFrom(ex.getClass()))) {log.warn("Retry {}/{} after {}ms caused by {}", attempt, maxAttempts, interval, ex.toString());Thread.sleep(interval);} else {throw ex;}}}throw new IllegalStateException("Should never reach here", lastEx);}
}
4. 在启动类开启 AOP
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
三、高级扩展(可选)
特性 | 实现方式 |
---|---|
指数退避 | 将 interval 改为 (long) (interval * Math.pow(2, attempt-1)) |
重试日志追踪 | 利用 MDC 注入 traceId ,方便链路追踪 |
异步重试 | 把 Thread.sleep 换成 CompletableFuture.delayedExecutor |
Spring Retry 集成 | 使用 @EnableRetry + @Retryable (开箱即用,但缺少自定义策略) |
四、测试用例
@SpringBootTest
class RetryableTest {@Autowired RemoteService service;@Testvoid shouldRetryAndFinallySuccess() {// 第 1 次失败,第 2 次成功String result = service.call();assertThat(result).isEqualTo("success");}
}
五、一句话总结
一个注解 + 一个切面 = 零侵入、可配置、易维护的异常重试机制,
让 网络抖动、偶发异常 不再影响业务稳定性,真正做到 优雅重试。