
Spring @Retryable 注解未生效,方法仅执行一次即抛出异常,根本原因是项目中存在多个版本的 spring-retry 依赖(如 1.1.2.RELEASE 和 1.2.4.RELEASE),引发类加载冲突与 AOP 增强失效。
spring `@retryable` 注解未生效,方法仅执行一次即抛出异常,根本原因是项目中存在多个版本的 `spring-retry` 依赖(如 1.1.2.release 和 1.2.4.release),引发类加载冲突与 aop 增强失效。
在 Spring 生态中,@Retryable 的底层实现高度依赖 spring-retry 模块提供的 AnnotationAwareRetryOperationsInterceptor —— 该拦截器由 Spring AOP 在运行时织入目标方法调用链。当项目中同时引入了多个不兼容的 spring-retry 版本(例如通过不同间接依赖传递进来),JVM 类加载器可能加载了旧版的类(如 1.1.2.RELEASE 中的 RetryOperationsInterceptor),而新版注解解析逻辑(如 1.2.4.RELEASE 中增强的 @Retryable 元数据处理)却无法被正确识别或触发,最终导致日志中虽出现 AnnotationAwareRetryOperationsInterceptor.invoke(...) 调用痕迹,但实际未执行任何重试逻辑(如堆栈中仅显示一次 invoke,无后续重试调用)。
✅ 正确的修复方式:统一并锁定 spring-retry 版本
第一步:检查有效依赖树
运行以下命令定位冲突来源(Maven 项目):
mvn dependency:tree | grep "spring-retry"
输出示例:
[INFO] +- org.springframework.retry:spring-retry:jar:1.1.2.RELEASE:compile [INFO] \- com.example:legacy-sdk:jar:2.0.0:compile [INFO] \- org.springframework.retry:spring-retry:jar:1.2.4.RELEASE:compile
第二步:显式排除冗余版本,保留单一权威版本
在 pom.xml 中强制指定唯一版本,并排除传递依赖中的旧版:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.4.RELEASE</version> <!-- 推荐使用较新稳定版(如 1.3.x 或 2.0.x) -->
</dependency>
<!-- 若某第三方依赖(如 legacy-sdk)引入了旧版,需排除 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>legacy-sdk</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</exclusion>
</exclusions>
</dependency>? 提示:Spring Boot 2.2+ 用户建议直接使用 spring-boot-starter-aop + spring-retry(无需手动管理版本),因为 Spring Boot Starter 已对依赖版本做了严格收敛。
⚠️ 补充注意事项
- @EnableRetry 必须启用:确保配置类上标注 @EnableRetry(proxyTargetClass = true)(尤其当目标类无接口时,需 CGLIB 代理)。
-
避免代理失效场景:
- 不要在同一个类内调用 @Retryable 方法(自调用绕过代理);
- 确保目标方法为 public(Spring AOP 仅支持 public 方法增强);
- 若方法位于 @Transactional 内部,需注意事务传播行为——重试失败后事务会回滚,但重试本身不改变事务边界(默认 REQUIRES_NEW 不适用,需手动控制)。
- 异常匹配需精确:@Retryable(value = Exception.class) 可捕获所有 Exception 子类,但若抛出的是 Error(如 OutOfMemoryError)或未声明的 RuntimeException 子类(且未在 value 中显式列出),则不会触发重试。
✅ 验证是否修复成功
添加日志观察重试行为:
@Retryable(
maxAttempts = 3,
value = {ClientTransportException.class},
backoff = @Backoff(delay = 5000, multiplier = 2)
)
public void createQRPayment(Long paymentId) {
LOGGER.info("Executing QR payment creation (attempt #{})...",
RetrySynchronizationManager.getContext().getRetryCount() + 1);
// ... 调用远程服务,故意抛出 ClientTransportException 模拟 404
}成功修复后,日志将显示类似:
INFO Executing QR payment creation (attempt #1)... INFO Executing QR payment creation (attempt #2)... INFO Executing QR payment creation (attempt #3)... ERROR Final retry failed: ClientTransportException: 404 Not Found
总结:Spring Retry 的“静默失效”往往并非配置错误,而是隐蔽的依赖冲突所致。统一 spring-retry 版本是解决此类问题的首要且最有效的手段——它保障了 AOP 拦截器、注解处理器与重试上下文的一致性,让 @Retryable 真正发挥其设计价值。

















