Spring Boot AOP 优雅实现异常重试机制
零侵入、可配置、易扩展
通过 自定义注解 + AOP 环绕通知 在任意方法上实现 异常自动重试 ,支持
重试次数、间隔、异常类型、快速失败 等高级特性。
一、实现效果
java
@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)
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 定义注解
java
@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)
java
@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
java
@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 (开箱即用,但缺少自定义策略) |
四、测试用例
java
@SpringBootTest
class RetryableTest {
@Autowired RemoteService service;
@Test
void shouldRetryAndFinallySuccess() {
// 第 1 次失败,第 2 次成功
String result = service.call();
assertThat(result).isEqualTo("success");
}
}
五、一句话总结
一个注解 + 一个切面 = 零侵入、可配置、易维护的异常重试机制 ,
让 网络抖动、偶发异常 不再影响业务稳定性,真正做到 优雅重试。