
Spring Retry 的 @Retryable 注解中,exclude 并非“仅对指定异常不重试”,而是“在默认重试所有异常的前提下排除这些”,若未显式设置 include/retryFor,则 exclude 无效;应改用 retryFor + noRetryFor 明确声明重试范围。
spring retry 的 `@retryable` 注解中,`exclude` 并非“仅对指定异常不重试”,而是“在默认重试所有异常的前提下排除这些”,若未显式设置 `include`/`retryfor`,则 `exclude` 无效;应改用 `retryfor` + `noretryfor` 明确声明重试范围。
在 Spring Retry(尤其是 2.0+ 版本)中,@Retryable 的异常控制逻辑常被误解:exclude 不是“白名单式排除”,而是依赖于隐式默认策略的“黑名单式过滤”。当 include(或 retryFor)为空时,Spring Retry 的默认行为是 不重试任何异常 —— 这与许多开发者的直觉相反。因此,仅配置 @Retryable(exclude = MessageTimeoutException.class) 并不能实现“对其他所有异常重试”,反而可能导致完全不重试。
✅ 正确做法是:显式声明哪些异常需要重试(retryFor),再声明哪些例外不重试(noRetryFor)。这既符合语义直觉,也兼容新旧版本,并规避了默认策略的歧义。
✅ 推荐配置(Spring Retry 2.0+)
@Override
@Retryable(
retryFor = Exception.class, // 显式声明:所有 Exception 及其子类都重试
noRetryFor = MessageTimeoutException.class // 明确排除:MessageTimeoutException 不重试
)
public byte[] sendMessage(byte[] message, String guid) {
responseBytes = tcpClientGateway.send(message);
return responseBytes;
}
? 注意:
retryFor = Exception.class是关键。它覆盖了“默认不重试”的行为,建立重试白名单基础;noRetryFor则在此基础上做精准剔除。
⚠️ 为什么原写法失效?
- 你当前使用
@Retryable(exclude = MessageTimeoutException.class),但未设置include或retryFor; - Spring Retry 在
retryFor为空时,不会自动 fallback 到 “重试所有异常”,而是采用保守策略:仅重试显式声明的类型; - 因此实际效果等价于
@Retryable(retryFor = {})→ 无任何异常被重试,exclude形同虚设。
? 验证异常全限定名
从堆栈可知,抛出的是:
org.springframework.integration.MessageTimeoutException
务必确保 noRetryFor 中引用的类与运行时异常类型完全一致(包括包路径):
// ✅ 正确(使用全限定类)
@Retryable(
retryFor = Exception.class,
noRetryFor = org.springframework.integration.MessageTimeoutException.class
)
避免使用自定义别名或错误导入(如 com.yourpackage.MessageTimeoutException)。
? 补充建议:增强可维护性
-
组合多个异常排除:
noRetryFor = { org.springframework.integration.MessageTimeoutException.class, java.net.SocketTimeoutException.class } -
结合
maxAttempts和backoff更清晰:@Retryable( retryFor = Exception.class, noRetryFor = MessageTimeoutException.class, maxAttempts = 3, backoff = @Backoff(delay = 50) ) 全局统一策略(可选):
若多处需相同逻辑,可通过@EnableRetry+ 自定义RetryOperationsInterceptor统一配置,避免重复注解。
✅ 总结
| 配置方式 | 是否推荐 | 说明 |
|---|---|---|
exclude = X.class(无 include) |
❌ 不推荐 | 语义模糊,易失效,已标记 @Deprecated
|
retryFor = Exception.class, noRetryFor = X.class |
✅ 强烈推荐 | 明确、健壮、向前兼容,符合 Spring Boot 2.4+ 最佳实践 |
include = Exception.class, exclude = X.class |
⚠️ 兼容旧版但不推荐 |
include/exclude 已弃用,应迁移到 retryFor/noRetryFor
|
只要确保 retryFor 显式覆盖目标异常基类,并用 noRetryFor 精准排除例外,Spring Retry 就能按预期工作:除 MessageTimeoutException 外,其余异常均重试最多 3 次,间隔 50ms。










