容错能力进入 spring-core:Spring Boot 4 原生重试机制全解析
本文是 Spring Boot 4 系列第 09 篇 | 基于 Spring Boot 4.1.0 + Spring Framework 7.0.8 源码 | 预计阅读 20 分钟
文末附「迁移决策树」和「源码位置汇总」,落地时可直接对照。
写在前面
Spring Boot 3.x 时代,想让一个 HTTP 调用失败后自动重试 3 次,得加 spring-retry 依赖、加 spring-aspects、启动类加 @EnableRetry、方法上加 @Retryable------四件事缺一不可。Spring Boot 4 把这件事简化了:重试 API 直接搬进了 spring-core,不需要额外依赖,不需要 AOP,new RetryTemplate(policy).invoke(() -> ...) 一行搞定。
这篇基于 Spring Boot 4.1.0 + Spring Framework 7.0.8 源码,讲清楚四件事:Spring 为什么把重试搬进 spring-core、新 API 怎么用(RetryPolicy + RetryTemplate + Retryable 函数式接口)、Spring Boot 4 如何用 RetryPolicySettings 把配置桥接进去、RabbitMQ / Kafka / Redis 的自动配置里已经怎么用了。
内容速览
spring-retry依赖为什么可以删了spring-core中的重试三件套:RetryPolicy、RetryTemplate、RetryableRetryPolicySettings配置桥接层的设计- RabbitMQ / Kafka / Redis 三个模块的集成方式对比
- 手写一个带重试的 HTTP 调用(含读操作和写操作的差异化策略)
- 从配置到重试执行的完整调用链追踪
- 当前限制:没有
@Retryable注解、没有限流熔断
一、spring-retry 依赖为什么可以删了
先看 Spring Boot 3.x 时代怎么做重试:
xml
<!-- pom.xml:引入独立的重试框架 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId> <!-- 还得加 AOP 支持 -->
</dependency>
java
// 启动类加 @EnableRetry
@SpringBootApplication
@EnableRetry // 别忘了这个!
public class OrderServiceApplication { ... }
// 业务代码加 @Retryable
@Service
public class InventoryClient {
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2.0))
public InventoryResponse queryInventory(Long productId) {
return restClient.get()
.uri("/inventory/{id}", productId)
.retrieve()
.body(InventoryResponse.class);
}
}
三个问题:
- 额外依赖 :
spring-retry是一个独立项目,和 Spring Framework 分开发版、分开维护 - AOP 强耦合 :重试靠
@EnableRetry+ AOP 切面织入,启动变慢还不好调试 - 注解碎片化 :一个方法上可能同时挂着
@Retryable、@Transactional、@Cacheable,多个切面套在一起,执行顺序看运气
Spring Framework 7.0 把重试能力搬进了 spring-core 模块(org.springframework.core.retry 包),spring-retry 不再是必选依赖。任何 Spring Boot 4 应用天然就有重试 API 可用------不需要加依赖、不需要 @EnableRetry、不需要 AOP。
版本快照:Spring Framework 7.0.8 的 spring-core 中包含了完整的 org.springframework.core.retry 包。Spring Boot 4.1.0 在此基础上新增了 RetryPolicySettings 配置桥接类(core/spring-boot/src/main/java/org/springframework/boot/retry/),并在 RabbitMQ / Kafka / Redis 的自动配置中全面采用新的重试 API。整个 Spring Boot 4 代码仓库中没有任何对 spring-retry 的依赖声明。
二、spring-core 中的重试三件套
2.1 包结构一览
从 spring-core-7.0.8.jar 中解出的 org.springframework.core.retry 包:
ruby
org.springframework.core.retry/
├── RetryPolicy # 接口:决定"要不要重试"
├── RetryPolicy.Builder # 构建器:链式创建 RetryPolicy
├── DefaultRetryPolicy # 默认实现
├── RetryTemplate # 核心执行器:真正执行重试逻辑的地方
├── Retryable<R> # 函数式接口:R execute() throws Throwable
├── RetryOperations # 操作接口:RetryTemplate 实现的契约
├── RetryListener # 监听器接口:重试各阶段的回调
├── RetryState # 状态标记接口
├── RetryException # 重试失败异常
├── support/
│ ├── RetryTask # 支持类
│ └── CompositeRetryListener # 组合监听器
2.2 核心 API 逐个拆解
RetryPolicy:决定"该不该重试"
java
public interface RetryPolicy {
boolean shouldRetry(Throwable throwable); // 核心判断:这个异常值得重试吗?(必须实现)
default Duration getTimeout() { ... } // 整个重试过程的最长时间(可选覆盖)
default BackOff getBackOff() { ... } // 退回策略:每次重试间隔多久(可选覆盖)
// 快捷工厂方法
static RetryPolicy withDefaults(); // 默认:最多 3 次尝试,固定 1s 延迟
static RetryPolicy withMaxRetries(long max); // 指定最大尝试次数
static RetryPolicy.Builder builder(); // 完整构建器入口
}
getTimeout() 和 getBackOff() 是 default 方法------只需要实现 shouldRetry(Throwable) 就能得到一个最小化的 RetryPolicy。大多数场景用 builder() 或 withDefaults() 已经够用。
RetryPolicy.Builder:链式构建
java
// Builder 常量定义
Builder.DEFAULT_MAX_RETRIES = 3 // 默认最多重试 3 次
Builder.DEFAULT_DELAY = 1000 // 默认延迟 1000ms
Builder.DEFAULT_MULTIPLIER = 1.0 // 默认倍数(固定延迟)
Builder.DEFAULT_MAX_DELAY = Long.MAX_VALUE // 默认无上限(约 2.92 亿年)
// 链式 API
RetryPolicy policy = RetryPolicy.builder()
.maxRetries(5) // 最多重试 5 次
.delay(Duration.ofSeconds(1)) // 初始延迟 1s
.multiplier(2.0) // 每次翻倍:1s → 2s → 4s → 8s
.maxDelay(Duration.ofSeconds(30)) // 封顶 30s
.jitter(Duration.ofMillis(500)) // ±500ms 随机抖动
.includes(IOException.class, TimeoutException.class) // 只在这些异常时重试
.excludes(IllegalArgumentException.class) // 排除这些异常
.predicate(ex -> ex.getMessage().contains("retryable")) // 自定义条件
.build();
这个 Builder 把原来分散在 spring-retry 里好几个类(SimpleRetryPolicy、ExponentialBackOffPolicy、ExceptionClassifierRetryPolicy)的配置收拢到一处。
RetryTemplate:执行重试
java
public class RetryTemplate implements RetryOperations {
public RetryTemplate() // 空构造
public RetryTemplate(RetryPolicy retryPolicy) // 带策略构造
// 核心方法:执行一个 Retryable 任务
public <R> R execute(Retryable<R> retryable) throws RetryException;
// 便利方法:传 Supplier(无需实现 Retryable 接口)
public <R> R invoke(Supplier<R> supplier);
public void invoke(Runnable runnable);
// 设置监听器
public void setRetryListener(RetryListener listener);
}
RetryTemplate 内部执行流程:
- 调用
retryable.execute()执行业务逻辑 - 如果抛出异常 → 问
RetryPolicy.shouldRetry(exception) - 如果
shouldRetry返回true→ 从RetryPolicy.getBackOff()获取等待时间 →Thread.sleep→ 回到步骤 1 - 如果
shouldRetry返回false或超时或达到最大重试次数 → 抛出RetryException(包装原始异常)
Retryable<R>:函数式接口(不是注解)
这是最容易误解的地方。很多文章提到 "Spring Boot 4 有了 @Retryable 注解",但在 Spring Framework 7.0.8 的 spring-core 中:
Retryable<R>是一个函数式接口,不是注解- 只有一个方法:
R execute() throws Throwable - 用它来包装"会被重试的代码块",以 Lambda 形式传入
RetryTemplate.execute()
java
// 不是 @Retryable(maxAttempts = 3) 注解方式
// 而是函数式接口 + Lambda:
RetryTemplate template = new RetryTemplate(retryPolicy);
String result = template.execute(() -> {
// 这里写可能失败的代码
return restClient.get().uri("/api/data").retrieve().body(String.class);
});
// 或者更简洁的 invoke 方法:
String result = template.invoke(() -> callRemoteService());
为什么是函数式接口而不是注解?这反映了 Spring 团队对"容错"的定位调整:
- 注解方式 (旧
spring-retry):依赖 AOP,重试逻辑在切面里,出错时调用栈很深 - 函数式方式 (新
spring-core):重试逻辑在调用栈上明明白白,debug 时一目了然 - 两者不互斥:
spring-retry项目仍然存在,如果需要声明式重试仍然可以用。但 Spring Framework 7 的spring-core选择"先做好基础设施层",注解方案可以后面再加
RetryListener:重试过程的回调
java
public interface RetryListener {
// 即将开始执行(整个重试流程的入口)
default void onRetryableExecution(RetryPolicy policy, Retryable<?> retryable, RetryState retryState) {}
// 每次重试前(含 RetryState 版本,用于获取当前重试次数)
default void beforeRetry(RetryPolicy policy, Retryable<?> retryable, RetryState retryState) {}
// 每次重试前(简化版)
default void beforeRetry(RetryPolicy policy, Retryable<?> retryable) {}
// 重试成功
default void onRetrySuccess(RetryPolicy policy, Retryable<?> retryable, Object result) {}
// 单次重试失败(但策略判定继续重试)
default void onRetryFailure(RetryPolicy policy, Retryable<?> retryable, Throwable failure) {}
// 策略判定不再重试(exhausted + interruption + timeout 三种结束原因)
default void onRetryPolicyExhaustion(RetryPolicy policy, Retryable<?> retryable, RetryException retryException) {}
default void onRetryPolicyInterruption(RetryPolicy policy, Retryable<?> retryable, RetryException retryException) {}
default void onRetryPolicyTimeout(RetryPolicy policy, Retryable<?> retryable, RetryException retryException) {}
}
RetryState 携带当前重试过程的状态信息:
java
public interface RetryState {
int getRetryCount(); // 当前已重试次数
List<Throwable> getExceptions(); // 历史异常列表
default Throwable getLastException(); // 最近一次异常
default boolean isSuccessful(); // 是否成功
}
配合 CompositeRetryListener 可以串联多个监听器:
java
CompositeRetryListener composite = new CompositeRetryListener(List.of(
new MetricsRetryListener(), // 记录重试次数到 Micrometer
new LoggingRetryListener() // 打印重试日志
));
template.setRetryListener(composite);
三、Spring Boot 的桥接层:RetryPolicySettings
Spring Boot 4.0 在 core/spring-boot 模块中新增了 org.springframework.boot.retry.RetryPolicySettings(@since 4.0.0,作者 Stephane Nicoll),职责是把"配置"翻译成 RetryPolicy。
java
// RetryPolicySettings 核心结构(源码位置:core/spring-boot/.../retry/RetryPolicySettings.java)
public final class RetryPolicySettings {
// 类级别常量,直接引用 RetryPolicy.Builder 的默认值
public static final long DEFAULT_MAX_RETRIES = RetryPolicy.Builder.DEFAULT_MAX_RETRIES; // 3
public static final Duration DEFAULT_DELAY
= Duration.ofMillis(RetryPolicy.Builder.DEFAULT_DELAY); // 1s
public static final double DEFAULT_MULTIPLIER = RetryPolicy.Builder.DEFAULT_MULTIPLIER; // 1.0
public static final Duration DEFAULT_MAX_DELAY
= Duration.ofMillis(RetryPolicy.Builder.DEFAULT_MAX_DELAY); // Long.MAX_VALUE ms
private List<Class<? extends Throwable>> exceptionIncludes = new ArrayList<>();
private List<Class<? extends Throwable>> exceptionExcludes = new ArrayList<>();
private @Nullable Predicate<Throwable> exceptionPredicate;
private Long maxRetries = DEFAULT_MAX_RETRIES;
private Duration delay = DEFAULT_DELAY;
private @Nullable Duration jitter;
private Double multiplier = DEFAULT_MULTIPLIER;
private Duration maxDelay = DEFAULT_MAX_DELAY;
private @Nullable Function<Builder, RetryPolicy> factory; // 扩展点
public RetryPolicy createRetryPolicy() {
PropertyMapper map = PropertyMapper.get();
RetryPolicy.Builder builder = RetryPolicy.builder();
map.from(this::getExceptionIncludes).to(builder::includes);
map.from(this::getExceptionExcludes).to(builder::excludes);
map.from(this::getExceptionPredicate).to(builder::predicate);
map.from(this::getMaxRetries).to(builder::maxRetries);
map.from(this::getDelay).to(builder::delay);
map.from(this::getJitter).to(builder::jitter);
map.from(this::getMultiplier).to(builder::multiplier);
map.from(this::getMaxDelay).to(builder::maxDelay);
return (this.factory != null) ? this.factory.apply(builder) : builder.build();
}
}
createRetryPolicy() 的设计有两个亮点:
1. PropertyMapper 桥接 :用 Spring Boot 的 PropertyMapper API 把字段值"有条件地"映射到 Builder。PropertyMapper.get() 默认行为是"只有值不为 null 时才调用 setter",这让字段的默认值治理变得直观。
2. factory 扩展点:如果预置的 8 个字段还不够,可以注入自定义函数:
java
RetryPolicySettings settings = new RetryPolicySettings();
settings.setDelay(Duration.ofSeconds(2));
settings.setMultiplier(2.0);
settings.setFactory((builder) -> builder
.timeout(Duration.ofMinutes(1)) // 加一个整体超时
.backOff(myCustomBackOff) // 或者完全替换 BackOff 策略
.build()
);
RetryPolicy policy = settings.createRetryPolicy();
RetryPolicySettings 自身不绑定 任何 application.yml 配置键(没有一个 spring.retry.* 的统一前缀)。它被设计为一个"可编程中间层":各模块(RabbitMQ、Kafka、Redis)在自己的 *Properties 中定义各自的配置结构,然后通过 RetryPolicySettings 统一转成 RetryPolicy。这么设计的原因是:不同中间件的重试语义不同(消息发送重试 vs 消息消费重试 vs 连接恢复重试),强行统一配置前缀反而会让用户困惑。
四、各模块的集成方式
4.1 RabbitMQ:最完整的重试集成
RabbitMQ 是 Spring Boot 4 中对新重试 API 集成度最高的模块,分两个层面:
Template 层(消息发送重试):
yaml
# application.yml
spring:
rabbitmq:
template:
retry:
enabled: true
max-retries: 5
initial-interval: 2s
multiplier: 2.0
max-interval: 30s
源码链路(RabbitTemplateConfigurer.java:131-146):
java
protected RetryTemplate createRetryTemplate(RabbitProperties.Retry properties) {
// 1. 把 RabbitProperties.Retry 翻译成 RetryPolicySettings
RabbitRetryTemplateSettings retrySettings = new RabbitRetryTemplateSettings(
properties.initializeRetryPolicySettings());
// 2. 回调所有 RabbitTemplateRetrySettingsCustomizer
if (this.retrySettingsCustomizers != null) {
for (RabbitTemplateRetrySettingsCustomizer customizer : this.retrySettingsCustomizers) {
customizer.customize(retrySettings);
}
}
// 3. 用 settings 创建 RetryPolicy
RetryPolicy retryPolicy = retrySettings.getRetryPolicySettings().createRetryPolicy();
// 4. 创建 RetryListener(支持多个,自动组合为 CompositeRetryListener)
RetryListener retryListener = createRetryListener(retrySettings.getRetryListeners());
// 5. 组装 RetryTemplate
RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
if (retryListener != null) {
retryTemplate.setRetryListener(retryListener);
}
return retryTemplate;
}
然后通过 template.setRetryTemplate(createRetryTemplate(...)) 设置到 RabbitTemplate 上。
Listener 层(消息消费重试):
java
// RabbitProperties.java
public static class ListenerRetry extends Retry {
private boolean stateless = true; // 无状态重试 vs 有状态重试
}
// AbstractRabbitListenerContainerFactoryConfigurer.java
RetryInterceptorBuilder.stateless() // 或 .stateful()
.retryPolicy(createRetryPolicy(retryConfig))
.recoverer(messageRecoverer)
.build();
自定义方式:
java
// 自定义 Template 重试
@Bean
RabbitTemplateRetrySettingsCustomizer templateRetryCustomizer() {
return (settings) -> {
settings.getRetryPolicySettings().setMultiplier(1.5);
settings.getRetryListeners().add(new MyLoggingRetryListener());
};
}
// 自定义 Listener 重试(注意:参数直接是 RetryPolicySettings,不是 RabbitRetryTemplateSettings)
@Bean
RabbitListenerRetrySettingsCustomizer listenerRetryCustomizer() {
return (settings) -> {
settings.setMaxRetries(10L);
};
}
4.2 Kafka:Retry Topic 的 BackOff 构建
Kafka 的重试是通过 Retry Topic 机制(失败消息投递到重试主题,延迟后重新消费),Spring Boot 4 用新的 RetryPolicy.Builder 来构建 BackOff 策略:
java
// KafkaAutoConfiguration.java:236-244
static BackOff getBackOff(Backoff retryTopicBackoff) {
PropertyMapper map = PropertyMapper.get();
RetryPolicy.Builder builder = RetryPolicy.builder()
.maxRetries(Long.MAX_VALUE); // Kafka 重试由 topic 层控制总次数
map.from(retryTopicBackoff.getDelay()).to(builder::delay);
map.from(retryTopicBackoff.getMaxDelay())
.when(Predicate.not(Duration::isZero)).to(builder::maxDelay);
map.from(retryTopicBackoff.getMultiplier()).to(builder::multiplier);
map.from(retryTopicBackoff.getJitter())
.when(Predicate.not(Duration::isZero)).to(builder::jitter);
return builder.build().getBackOff(); // 只取 BackOff,不取 RetryPolicy
}
配置:
yaml
spring:
kafka:
retry:
topic:
enabled: true
attempts: 5
backoff:
delay: 1s
multiplier: 2.0
max-delay: 10s
4.3 Redis:消息监听器恢复
Redis 的消息监听器在连接断开后需要"恢复"(recovery),同样用了新的重试 API:
java
// RedisMessageListenerContainerConfigurer.java:66-74
static BackOff getRecoveryBackOff(Recovery recovery) {
RetryPolicy.Builder builder = RetryPolicy.builder()
.maxRetries(recovery.getMaxRetries());
PropertyMapper map = PropertyMapper.get();
map.from(recovery.getDelay()).to(builder::delay);
map.from(recovery.getMaxDelay())
.when(Predicate.not(Duration::isZero)).to(builder::maxDelay);
map.from(recovery.getMultiplier()).to(builder::multiplier);
map.from(recovery.getJitter())
.when(Predicate.not(Duration::isZero)).to(builder::jitter);
return builder.build().getBackOff();
}
4.4 集成全景对比
| 模块 | 配置前缀 | RetryPolicySettings | Customizer | 生效位置 |
|---|---|---|---|---|
| RabbitMQ Template | spring.rabbitmq.template.retry.* |
直接使用 | RabbitTemplateRetrySettingsCustomizer |
RabbitTemplate.setRetryTemplate() |
| RabbitMQ Listener | spring.rabbitmq.listener.retry.* |
直接使用 | RabbitListenerRetrySettingsCustomizer |
RetryInterceptorBuilder |
| Kafka | spring.kafka.retry.topic.* |
间接使用(仅取 BackOff) |
无 | RetryTopicConfiguration |
| Redis | spring.data.redis.listener.recovery.* |
间接使用(仅取 BackOff) |
无 | RedisMessageListenerContainer.setRecoveryBackoff() |
五、实战:手写一个带重试的 HTTP 调用
假设有一个订单服务需要调用库存接口,网络抖动时应该自动重试。在 Spring Boot 4 中不需要任何额外依赖:
java
package com.example.orderservice;
import org.springframework.core.retry.RetryPolicy;
import org.springframework.core.retry.RetryTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
import java.time.Duration;
@Service
public class InventoryClient {
private final RestClient restClient;
private final RetryTemplate retryTemplate;
public InventoryClient(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder
.baseUrl("http://inventory-service:8080")
.build();
// 构建重试策略:网络异常重试 3 次,退避 1s→2s→4s
// 注意:RestClient 将底层 I/O 异常包装为 RestClientException(RuntimeException)
// 因此用 includes(RestClientException.class) 才能正确触发重试
RetryPolicy retryPolicy = RetryPolicy.builder()
.maxRetries(3)
.delay(Duration.ofSeconds(1))
.multiplier(2.0)
.maxDelay(Duration.ofSeconds(10))
.jitter(Duration.ofMillis(200))
.includes(RestClientException.class)
.build();
this.retryTemplate = new RetryTemplate(retryPolicy);
}
/**
* 查询库存------带自动重试(读操作幂等,安全重试)
*/
public InventoryResponse queryInventory(Long productId) {
return retryTemplate.invoke(() ->
restClient.get()
.uri("/api/inventory/{productId}", productId)
.retrieve()
.body(InventoryResponse.class)
);
}
/**
* 扣减库存------因为涉及写操作,要更谨慎
*/
public DeductResponse deductInventory(DeductRequest request) {
// 写操作:只重试超时类异常,避免重复扣减
// predicate 检查异常链中是否包含连接超时(RestClient 将其包装为 RestClientException)
RetryPolicy writeRetryPolicy = RetryPolicy.builder()
.maxRetries(2)
.delay(Duration.ofMillis(500))
.multiplier(1.0) // 固定延迟,不翻倍
.predicate(ex -> {
Throwable cause = ex;
while (cause != null) {
if (cause instanceof java.net.SocketTimeoutException
|| cause instanceof java.net.http.HttpTimeoutException) {
return true;
}
cause = cause.getCause();
}
return false;
})
.build();
RetryTemplate writeTemplate = new RetryTemplate(writeRetryPolicy);
return writeTemplate.invoke(() ->
restClient.post()
.uri("/api/inventory/deduct")
.body(request)
.retrieve()
.body(DeductResponse.class)
);
}
}
对比一下 Spring Boot 3 需要什么:
- Spring Boot 3:
@EnableRetry+@Retryable(maxAttempts = 3, backoff = ...)+spring-retry依赖 +spring-aspects依赖 - Spring Boot 4: 只需要
spring-core(已在 classpath 上)
| 维度 | Spring Boot 3 (spring-retry) |
Spring Boot 4 (spring-core) |
|---|---|---|
| 额外依赖 | spring-retry + spring-aspects |
无(spring-core 自带) |
| 启用方式 | @EnableRetry 注解 |
无需启用,直接 new RetryTemplate |
| 使用方式 | @Retryable 注解 + AOP |
函数式 template.invoke(() -> ...) |
| 调用栈 | 切面代理 → 反射 → 重试逻辑 | 直接调用,链更短 |
| 单测友好 | 需要 Spring 容器加载 | 纯 POJO,直接 new 测 |
| 自定义策略 | RetryTemplate + RetryPolicy Bean |
RetryPolicy.builder() 一行链式调用 |
六、源码追踪:从配置到重试执行的完整调用链
以 RabbitMQ 发送消息重试为例,追踪完整调用链路:
scss
1. application.yml
spring.rabbitmq.template.retry.enabled = true
│
2. RabbitProperties.Retry.enabled = true(属性绑定)
│
3. RabbitTemplateConfigurer.configure(template, connectionFactory)
│
4. templateProperties.getRetry().isEnabled() == true
│
5. createRetryTemplate(templateProperties.getRetry())
│
6. RabbitProperties.Retry.initializeRetryPolicySettings()
│ └─ 把 maxRetries=3, initialInterval=1s, multiplier=1.0, maxInterval=10s
│ 映射到 RetryPolicySettings 的 maxRetries/delay/multiplier/maxDelay
│
7. RetryPolicySettings.createRetryPolicy()
│ └─ RetryPolicy.builder()
│ .maxRetries(3).delay(1s).multiplier(1.0).maxDelay(10s)
│ .build()
│ 生成的是 DefaultRetryPolicy + ExponentialBackOff(含 BackOff 计算逻辑)
│
8. new RetryTemplate(retryPolicy)
│
9. template.setRetryTemplate(retryTemplate)
│
10. 业务代码调用 rabbitTemplate.convertAndSend(...)
│
11. RabbitTemplate 在发送消息遇到可重试异常时
│
12. retryTemplate.execute(retryable) → RetryPolicy.shouldRetry(ex) → BackOff 等待 → 重试
关键源码位置汇总:
| 类 | 文件位置 | 作用 |
|---|---|---|
RetryPolicy |
spring-core-7.0.8.jar!/org/springframework/core/retry/RetryPolicy |
重试决策接口 |
RetryTemplate |
spring-core-7.0.8.jar!/org/springframework/core/retry/RetryTemplate |
重试执行器 |
Retryable |
spring-core-7.0.8.jar!/org/springframework/core/retry/Retryable |
函数式接口 |
RetryPolicySettings |
core/spring-boot/.../retry/RetryPolicySettings.java |
Boot 重试配置桥接 |
RabbitProperties.Retry |
module/spring-boot-amqp/.../RabbitProperties.java |
RabbitMQ 重试属性 |
RabbitTemplateConfigurer |
module/spring-boot-amqp/.../RabbitTemplateConfigurer.java |
RabbitMQ 模板配置 |
KafkaAutoConfiguration |
module/spring-boot-kafka/.../KafkaAutoConfiguration.java |
Kafka 重试主题配置 |
RedisMessageListenerContainerConfigurer |
module/spring-boot-data-redis/.../RedisMessageListenerContainerConfigurer.java |
Redis 恢复配置 |
七、当前限制与迁移决策
7.1 目前没有 @Retryable 注解
org.springframework.core.retry.Retryable<R> 是一个函数式接口 ,不是注解。如果期望类似旧 spring-retry 那样在方法上加 @Retryable(maxAttempts = 3) 就能自动重试------在 Spring Framework 7.0.8 中还不支持。
做法是显式创建 RetryTemplate 并用 Lambda 包装:
java
// 可行
RetryTemplate template = new RetryTemplate(policy);
template.invoke(() -> someMethod());
// 尚不可行(Spring Framework 7.0.8 中没有这个注解)
@Retryable(maxAttempts = 3)
public void someMethod() { ... }
Spring 团队选择"先把基础 API 放进 spring-core":有了 RetryPolicy + RetryTemplate 基础设施,上层可以逐步叠加 AOP 注解或 Spring Boot Starter。这是"自下而上"的演进路线。
7.2 目前没有限流和熔断
Spring Framework 7.0.8 的 spring-core 中没有 Rate Limiter 和 Circuit Breaker 的实现。在整个 Spring Boot 4.1.0 代码仓库中,也没有 resilience4j 依赖声明。
如果需要完整的"重试 + 熔断 + 限流"能力栈,目前仍然需要引入 Resilience4j:
xml
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
不过 RetryPolicy.builder() 的 API 设计已经为未来扩展留了空间------getBackOff() 返回的是 BackOff 接口,未来完全可以实现一个 RateLimitedBackOff 或 CircuitBreakerBackOff 而不改变 API。
7.3 从 spring-retry 迁移的决策树
ruby
你现在在用 spring-retry 吗?
│
├─ 只是简单的"这种异常重试 N 次"?
│ └─ 可以迁移到 spring-core RetryTemplate
│ 省掉 spring-retry 依赖,调用栈更短
│
├─ 用了 @Retryable 注解,方法上挂了十几个?
│ └─ 暂时保留 spring-retry
│ 等 Spring Framework 未来加注解支持再迁
│
└─ 用了 RetryTemplate + RecoveryCallback + RetryContext?
└─ 需要评估------spring-core 的 Retryable 接口
比 RetryCallback 简单,但少了 RetryContext
八、总结
Spring Framework 7.0 把重试搬进 spring-core,容错从"可选的扩展"变成了框架基础设施的一部分。
三个关键变化:
- 零依赖重试 :任何 Spring Boot 4 应用都自带
RetryPolicy+RetryTemplate,不需要再加spring-retry - API 现代化 :
RetryPolicy.builder()一条链式调用搞定策略配置,告别SimpleRetryPolicy+ExponentialBackOffPolicy的组合式痛苦 - 函数式执行 :
template.invoke(() -> doSomething())替代了 AOP + 注解,调用栈更短,调试更直观
三个尚待补齐:
- 还没有
@Retryable注解(Retryable是函数式接口) - 还没有限流和熔断的内置实现
RetryPolicySettings没有统一的spring.retry.*配置前缀