Spring Retry 重试机制:优雅解决接口调用失败问题

Luca Ju
2026-01-16 / 0 评论 / 1 阅读 / 正在检测是否收录...

在日常开发中,我们经常会遇到第三方接口不稳定、网络抖动导致的调用失败场景。很多人第一反应是在 try-catch 里写 for 循环重试,再搭配 Thread.sleep() 控制间隔——这种写法不仅冗余,还难以维护。

今天给大家推荐 Spring Retry 框架,它基于 AOP 实现,能让你零侵入式地为方法添加重试功能,大幅简化代码!

一、快速上手:三步集成 Spring Retry

1. 添加 Maven 依赖

Spring Retry 核心依赖 + AOP 依赖(因为其底层是 AOP 实现),这里推荐使用 2.0.12 稳定版本:

<!-- Spring Retry 核心依赖 -->
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.12</version>
</dependency>

<!-- AOP 依赖(Spring Boot 项目推荐此 starter) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 启用 Spring Retry 功能

在 Spring Boot 主启动类上添加 @EnableRetry 注解,一键开启重试功能:

import org.springframework.retry.annotation.EnableRetry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableRetry // 启用重试功能
@SpringBootApplication
public class SpringRetryDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringRetryDemoApplication.class, args);
    }
}

3. 核心注解:@Retryable 标记重试方法

在需要重试的方法上添加 @Retryable 注解,即可实现重试逻辑。

基础用法

import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class RetryDemoService {

    // 标记该方法需要重试
    @Retryable
    public void basicRetry() {
        int random = (int) (Math.random() * 10);
        System.out.println("当前随机数:" + random);
        // 模拟异常:随机数为偶数时抛出异常
        if (random % 2 == 0) {
            throw new RuntimeException("随机数为偶数,触发异常");
        }
        System.out.println("方法执行成功!");
    }
}

基础用法说明

  • 未指定异常类型时,方法抛出任何异常都会触发重试
  • 默认重试次数:3次(包含首次执行,实际重试 2 次)。
  • 默认重试间隔:1秒
  • 当重试次数耗尽仍失败时,会抛出 ExhaustedRetryException 异常。

二、进阶配置:灵活定制重试策略

@Retryable 注解提供了丰富的属性,可根据业务需求精准控制重试逻辑。

1. @Retryable 核心属性说明

属性名作用示例
value/retryFor指定触发重试的异常类型retryFor = RuntimeException.class
includevalue,优先级更高include = {NullPointerException.class}
exclude指定不触发重试的异常类型exclude = IllegalArgumentException.class
maxAttempts最大重试次数(包含首次执行)maxAttempts = 5
backoff配置重试间隔策略@Backoff(delay = 1000, multiplier = 2)
stateful是否有状态重试(异常信息保留)stateful = true

2. 实战示例:指数退避重试

需求:调用第三方接口时,仅在抛出 RuntimeException 时重试,最大重试 5 次,重试间隔按 1s → 2s → 4s → 8s 指数增长(避免高频重试压垮接口)。

import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RetryDemoController {

    @GetMapping("/test/retry")
    // 仅对RuntimeException重试,最大5次,指数退避间隔
    @Retryable(
            retryFor = RuntimeException.class,
            maxAttempts = 5,
            backoff = @Backoff(delay = 1000, multiplier = 2.0)
    )
    public String testRetry() {
        int random = (int) (Math.random() * 10);
        System.out.println("[" + System.currentTimeMillis() + "] 当前随机数:" + random);
        if (random % 2 == 0) {
            throw new RuntimeException("随机数为偶数,触发重试");
        }
        return "调用成功!随机数:" + random;
    }
}

三、兜底处理:@Recover 重试失败后的恢复逻辑

当重试次数耗尽仍失败时,我们需要一个兜底方法来处理最终的失败(比如记录日志、返回默认结果),这时候就需要 @Recover 注解。

1. @Recover 用法规则

  1. 恢复方法和 @Retryable 方法应该在同一个类中
  2. 后续参数需和 @Retryable 方法的参数列表完全一致
  3. 返回值需和 @Retryable 方法的返回值完全一致

2. 实战示例:重试失败后返回默认结果

import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class RetryDemoService {

    @Retryable(
            retryFor = RuntimeException.class,
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000)
    )
    public String callThirdPartyApi(String param) {
        System.out.println("调用第三方接口,参数:" + param);
        // 模拟接口调用失败
        throw new RuntimeException("第三方接口超时");
    }

    // 重试失败后的恢复方法
    @Recover
    public String recover(RuntimeException e, String param) {
        System.out.println("重试次数耗尽,执行兜底逻辑!异常信息:" + e.getMessage());
        System.out.println("请求参数:" + param);
        // 返回默认结果
        return "接口调用失败,已触发兜底策略";
    }
}

四、注意事项(避坑指南)

  1. @Retryable 不能修饰 private 方法:因为 Spring AOP 无法代理 private 方法,重试逻辑会失效。
  2. 避免同类方法调用:如果在同一个类中调用 @Retryable 方法(非代理调用),重试逻辑也会失效。
  3. 重试策略要合理:避免设置过短的间隔和过多的重试次数,增加服务压力。

五、总结

Spring Retry 凭借注解化的方式,让我们摆脱了手写重试逻辑的繁琐,实现了代码的优雅和解耦。核心要点如下:

  1. 三步集成:加依赖 → 启注解 → 标记方法。
  2. 灵活配置:通过 @Retryable 属性定制重试次数、间隔、触发异常。
  3. 兜底保障:通过 @Recover 处理重试失败的最终逻辑。

掌握 Spring Retry,能让你在应对不稳定接口时更加从容,大幅提升系统的健壮性!

1

评论 (0)

取消