
本文详解为何手动创建 Retry 实例(函数式配置)在 Spring Boot 中无法触发重试,明确指出核心问题在于未将重试逻辑注入调用链——必须通过 @Retry 注解声明或显式装饰并执行,而非仅构造 Retry 对象后直接调用原始方法。
本文详解为何手动创建 retry 实例(函数式配置)在 spring boot 中无法触发重试,明确指出核心问题在于未将重试逻辑注入调用链——必须通过 `@retry` 注解声明或显式装饰并执行,而非仅构造 `retry` 对象后直接调用原始方法。
在 Spring Boot 项目中集成 Resilience4j 的 Retry 模块时,一个高频误区是:误以为创建 Retry 实例并调用 decorateCheckedSupplier() 就能自动生效。正如提问者所遇——尽管成功构建了 RetryConfig、注册了 RetryRegistry、甚至调用了 Retry.decorateCheckedSupplier(),但最终 myService.bar() 仍只执行一次并抛出异常,重试完全未发生。
根本原因在于:Retry.decorateCheckedSupplier() 返回的是一个被重试逻辑“包装”后的 CheckedSupplier,它本身不会自动执行;你必须显式调用该装饰器的 get() 方法,才能触发含重试机制的执行流程。 而提问代码中最后一行 myService.bar(); 调用的是原始方法,完全绕过了所有装饰逻辑。
✅ 正确做法(函数式编程风格,无需注解):
@Service
@Slf4j
public class MyService {
private final Retry retry;
public MyService() {
// 1. 定义重试策略
RetryConfig config = RetryConfig.custom()
.maxAttempts(6)
.waitDuration(Duration.ofMillis(100))
.failAfterMaxAttempts(true) // 可选:达到最大重试次数后抛出 RetryException
.build();
// 2. 创建 Registry 并获取实例(注意:需确保为单例 Bean,避免重复注册)
RetryRegistry registry = RetryRegistry.of(config);
this.retry = registry.retry("bar-retry");
}
public String barWithRetry() throws Exception {
// 3. 关键:必须使用装饰后的 Supplier,并调用 get()
CheckedSupplier<string> decorated = Retry.decorateCheckedSupplier(
this.retry,
() -> {
log.error("Bar is called (attempt #{})", this.retry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt() + 1);
Thread.sleep(3000);
throw new RuntimeException("bar exception");
}
);
// 4. 执行装饰器 → 此处才会真正触发重试逻辑
return decorated.get();
}
}</string>
⚠️ 注意事项:
- @Retry 注解方式更推荐用于 Spring Boot 场景:它由 resilience4j-spring-boot2 Starter 自动代理,底层依赖 Spring AOP。务必确保已引入 spring-boot-starter-aop(否则注解完全无效);
- 函数式配置需自行管理生命周期:若将 RetryRegistry 或 Retry 声明为 @Bean,应标注 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) 避免重复初始化;
- 异常类型需匹配:默认情况下,Retry 仅对 RuntimeException 和 Error 重试;若需对受检异常(如 IOException)重试,须显式配置 retryExceptions;
- 避免“假装饰”陷阱:decorateSupplier(...) / decorateCheckedSupplier(...) 仅返回可执行对象,不自动执行——这是函数式设计的核心契约,也是最容易忽略的坑。
? 补充:若选择声明式 @Retry 方式(更符合 Spring 生态习惯),配置与使用如下:
# application.yml
resilience4j:
retry:
instances:
bar-retry:
max-attempts: 6
wait-duration: 100ms
enable-exponential-backoff: false
@Service
@Slf4j
public class MyService {
@Retry(name = "bar-retry") // ← 显式绑定配置实例
public String bar() throws InterruptedException {
log.error("Bar is called");
Thread.sleep(3000);
throw new RuntimeException("bar exception");
}
}
? 总结:Resilience4j Retry 的生效,本质是控制流的接管——无论是函数式装饰还是注解代理,目标都是让业务调用“经过”重试状态机。跳过装饰器执行、遗漏 AOP 依赖、或混淆配置作用域,都会导致重试静默失效。理解其“装饰器模式”本质,是规避此类问题的关键。










