Spring 提供声明式重试支持(@Retryable),需引入 spring-retry 和 AOP 依赖并启用 @EnableRetry;通过配置异常类型、最大尝试次数和指数退避策略实现可靠重试,且仅适用于幂等操作。

Spring 提供了声明式重试支持(@Retryable),配合 spring-retry 模块,能轻松应对网络抖动导致的临时性失败,比如 HTTP 调用超时、远程服务短暂不可用等场景。关键在于合理配置重试策略,避免无效重试或雪崩。
启用 Spring Retry 支持
确保项目中引入了 spring-retry 依赖(Spring Boot 2.4+ 需显式添加):
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在主启动类或配置类上添加 @EnableRetry 注解启用 AOP 代理重试能力。
使用 @Retryable 标注可重试方法
将可能因网络抖动失败的方法(如调用第三方 HTTP 接口)标记为可重试:
立即学习“Java免费学习笔记(深入)”;
@Service
public class PaymentService {
@Retryable(
value = { SocketTimeoutException.class, ConnectException.class, HttpServerErrorException.class },
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public String callPaymentApi(String orderId) {
// 使用 RestTemplate 或 WebClient 发起 HTTP 请求
return restTemplate.getForObject("https://api.pay.com/v1/pay?order=" + orderId, String.class);
}
@Recover
public String recover(SocketTimeoutException e, String orderId) {
log.warn("支付接口重试失败,订单:{},异常:{}", orderId, e.getMessage());
throw new BusinessException("支付请求频繁失败,请稍后重试");
}
}
- value:指定哪些异常触发重试,建议只包含「临时性错误」,如连接超时、5xx 服务端错误;避免对 4xx 客户端错误(如 400、401)重试
- maxAttempts:总尝试次数(含首次),设为 3 表示最多执行 1 次原调用 + 2 次重试
- backoff.delay:首次重试延迟(毫秒),multiplier 表示退避倍数,实现指数退避(1s → 2s → 4s)
- @Recover:定义兜底恢复方法,参数需匹配异常类型和原方法入参,用于统一处理最终失败逻辑
结合 Resilience4j 实现更细粒度控制(推荐进阶场景)
Spring Retry 是声明式、基于代理的,适合简单重试;若需熔断、限流、异步重试或与 WebFlux 兼容,建议迁移到 Resilience4j:
- 通过
RetryConfig精确控制重试条件(如仅对 HTTP 503 响应码重试) - 支持异步重试(
retry.executeSupplierAsync()),不阻塞主线程 - 可与
CircuitBreaker组合,自动熔断持续失败的服务 - 天然支持响应式编程(WebClient + Mono/Flux)
示例(同步调用):
RetryConfig config = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofSeconds(1))
.intervalFunction(IntervalFunction.ofExponentialBackoff())
.retryExceptions(IOException.class, TimeoutException.class)
.build();
Retry retry = Retry.of("payment-api", config);
String result = Try.ofSupplier(Retry.decorateSupplier(retry, () ->
restTemplate.getForObject(url, String.class)))
.recover(throwable -> "fallback_result")
.get();
注意事项与最佳实践
- 重试只适用于「幂等操作」,非幂等接口(如扣款、发券)必须服务端支持幂等键,否则重试会导致重复执行
- 避免在事务方法内直接使用
@Retryable,因默认代理是 JDK 动态代理,事务上下文可能丢失;可改用 CGLIB 代理或提取重试逻辑到无事务 Service - 生产环境建议记录重试日志(如每次重试的耗时、异常类型),便于定位网络抖动频次和模式
- 不要盲目增加重试次数和延迟,应结合下游 SLA 设定(例如对方承诺 99.9% 请求在 2s 内返回,则重试 2 次 + 指数退避较合理)
不复杂但容易忽略。


















