在日常开发中,我们经常会遇到第三方接口不稳定、网络抖动导致的调用失败场景。很多人第一反应是在 try-catch 里写 for 循环重试,再搭配 Thread.sleep() 控制间隔——这种写法不仅冗余,还难以维护。
今天给大家推荐 Spring Retry 框架,它基于 AOP 实现,能让你零侵入式地为方法添加重试功能,大幅简化代码!
一、快速上手:三步集成 Spring Retry
1. 添加 Maven 依赖
Spring Retry 核心依赖 + AOP 依赖(因为其底层是 AOP 实现),这里推荐使用 2.0.12 稳定版本:
<!-- Spring Retry 核心依赖 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>2.0.12</version>
</dependency>
<!-- AOP 依赖(Spring Boot 项目推荐此 starter) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>2. 启用 Spring Retry 功能
在 Spring Boot 主启动类上添加 @EnableRetry 注解,一键开启重试功能:
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableRetry // 启用重试功能
@SpringBootApplication
public class SpringRetryDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringRetryDemoApplication.class, args);
}
}3. 核心注解:@Retryable 标记重试方法
在需要重试的方法上添加 @Retryable 注解,即可实现重试逻辑。
基础用法
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class RetryDemoService {
// 标记该方法需要重试
@Retryable
public void basicRetry() {
int random = (int) (Math.random() * 10);
System.out.println("当前随机数:" + random);
// 模拟异常:随机数为偶数时抛出异常
if (random % 2 == 0) {
throw new RuntimeException("随机数为偶数,触发异常");
}
System.out.println("方法执行成功!");
}
}基础用法说明
- 未指定异常类型时,方法抛出任何异常都会触发重试。
- 默认重试次数:
3次(包含首次执行,实际重试 2 次)。 - 默认重试间隔:
1秒。 - 当重试次数耗尽仍失败时,会抛出
ExhaustedRetryException异常。
二、进阶配置:灵活定制重试策略
@Retryable 注解提供了丰富的属性,可根据业务需求精准控制重试逻辑。
1. @Retryable 核心属性说明
| 属性名 | 作用 | 示例 |
|---|---|---|
value/retryFor | 指定触发重试的异常类型 | retryFor = RuntimeException.class |
include | 同 value,优先级更高 | include = {NullPointerException.class} |
exclude | 指定不触发重试的异常类型 | exclude = IllegalArgumentException.class |
maxAttempts | 最大重试次数(包含首次执行) | maxAttempts = 5 |
backoff | 配置重试间隔策略 | @Backoff(delay = 1000, multiplier = 2) |
stateful | 是否有状态重试(异常信息保留) | stateful = true |
2. 实战示例:指数退避重试
需求:调用第三方接口时,仅在抛出 RuntimeException 时重试,最大重试 5 次,重试间隔按 1s → 2s → 4s → 8s 指数增长(避免高频重试压垮接口)。
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RetryDemoController {
@GetMapping("/test/retry")
// 仅对RuntimeException重试,最大5次,指数退避间隔
@Retryable(
retryFor = RuntimeException.class,
maxAttempts = 5,
backoff = @Backoff(delay = 1000, multiplier = 2.0)
)
public String testRetry() {
int random = (int) (Math.random() * 10);
System.out.println("[" + System.currentTimeMillis() + "] 当前随机数:" + random);
if (random % 2 == 0) {
throw new RuntimeException("随机数为偶数,触发重试");
}
return "调用成功!随机数:" + random;
}
}三、兜底处理:@Recover 重试失败后的恢复逻辑
当重试次数耗尽仍失败时,我们需要一个兜底方法来处理最终的失败(比如记录日志、返回默认结果),这时候就需要 @Recover 注解。
1. @Recover 用法规则
- 恢复方法和
@Retryable方法应该在同一个类中。 - 后续参数需和
@Retryable方法的参数列表完全一致。 - 返回值需和
@Retryable方法的返回值完全一致。
2. 实战示例:重试失败后返回默认结果
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class RetryDemoService {
@Retryable(
retryFor = RuntimeException.class,
maxAttempts = 3,
backoff = @Backoff(delay = 1000)
)
public String callThirdPartyApi(String param) {
System.out.println("调用第三方接口,参数:" + param);
// 模拟接口调用失败
throw new RuntimeException("第三方接口超时");
}
// 重试失败后的恢复方法
@Recover
public String recover(RuntimeException e, String param) {
System.out.println("重试次数耗尽,执行兜底逻辑!异常信息:" + e.getMessage());
System.out.println("请求参数:" + param);
// 返回默认结果
return "接口调用失败,已触发兜底策略";
}
}四、注意事项(避坑指南)
- @Retryable 不能修饰 private 方法:因为 Spring AOP 无法代理 private 方法,重试逻辑会失效。
- 避免同类方法调用:如果在同一个类中调用
@Retryable方法(非代理调用),重试逻辑也会失效。 - 重试策略要合理:避免设置过短的间隔和过多的重试次数,增加服务压力。
五、总结
Spring Retry 凭借注解化的方式,让我们摆脱了手写重试逻辑的繁琐,实现了代码的优雅和解耦。核心要点如下:
- 三步集成:加依赖 → 启注解 → 标记方法。
- 灵活配置:通过
@Retryable属性定制重试次数、间隔、触发异常。 - 兜底保障:通过
@Recover处理重试失败的最终逻辑。
掌握 Spring Retry,能让你在应对不稳定接口时更加从容,大幅提升系统的健壮性!
评论 (0)