升 Boot4 后,Spring AMQP 重试已从 Spring Retry 迁到 Spring Framework。本稿讲旧 RabbitRetryTemplateCustomizer 怎么认改名、怎么迁到 Template/Listener 两个 Customizer、怎么验,不写 Kafka jitter 或 CLASSIC loader。
同系列:Kafka retry 属性怎么改、CLASSIC 加载器怎么删、DevTools 热重载怎么开、endpoint 启用属性怎么改------一句对照,不写进本稿交付。
Boot4 升后 RabbitRetryTemplateCustomizer 为什么失效?
Spring Boot 4.0 Migration Guide · Spring AMQP Retry Features 写得很直:
Spring AMQP has moved its retry capabilities from Spring Retry to Spring Framework.
Spring Boot offers a customization hook-point for retry features used by the
RetryTemplateand message listeners. To make it more explicit, two dedicated customizers have been introduced:RabbitTemplateRetrySettingsCustomizer, andRabbitListenerRetrySettingsCustomizer.If you were using
RabbitRetryTemplateCustomizerto customize the retry settings according to a target, you will need to migrate to either of those interfaces.
也就是说:不是 AMQP 重试能力被拿掉 ,而是实现底座从 Spring Retry 迁到 Spring Framework 后,Boot 把「按 Target 定制一份 RetryTemplate」拆成两个更明确的钩子------发模板一侧、监听一侧各一个。以前靠 RabbitRetryTemplateCustomizer(配合 Target.SENDER / Target.LISTENER)做定制的代码,升到 Boot4 后常见现象是:
| 现象 | 大致原因 |
|---|---|
编译找不到 RabbitRetryTemplateCustomizer / 旧包名 |
接口已由两个新 Customizer 替代 |
| Bean 还在,但 retry 不再按旧分支生效 | Guide:须迁到 Template / Listener 专用接口 |
只改了 YAML 里的 spring.rabbitmq.*.retry.*,自定义 Bean 未动 |
属性与 Customizer 是两层;本稿盯 Customizer 改名 |
误以为是 Kafka random→jitter 同一坑 |
不是(见后文分列) |
API 落点(since 4.0.0):
RabbitTemplateRetrySettingsCustomizer--- 定制RabbitTemplate侧 retry settingsRabbitListenerRetrySettingsCustomizer--- 定制 message listener 侧 retry settings
本稿只盯 AMQP retry Customizer 怎么认改名、怎么迁、怎么与 Kafka retry 区分,其它 Boot4 主题见文首内链。
先过一张迁移总览图:

图:旧 RabbitRetryTemplateCustomizer → Boot4 迁到 Spring Framework → 拆成 Template / Listener 两接口 → 发送/监听重试冒烟。
旧写法长什么样(Boot3,勿再沿用)
Boot 3.x 常见做法是声明一个 RabbitRetryTemplateCustomizer Bean,在回调里按 Target 分支------SENDER 对应 RabbitTemplate,LISTENER 对应消息监听容器。旧包名在 org.springframework.boot.autoconfigure.amqp。
java
package com.example.amqp;
import org.springframework.boot.autoconfigure.amqp.RabbitRetryTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
/**
* Boot 3.x 旧写法示意------升 Boot4 后请删除/替换。
* 旧接口按 Target 定制同一份 org.springframework.retry.support.RetryTemplate。
*/
@Configuration
public class LegacyAmqpRetryConfig {
@Bean
RabbitRetryTemplateCustomizer legacyRabbitRetryTemplateCustomizer() {
return (target, retryTemplate) -> {
if (target == RabbitRetryTemplateCustomizer.Target.SENDER) {
tune(retryTemplate, 3);
} else if (target == RabbitRetryTemplateCustomizer.Target.LISTENER) {
tune(retryTemplate, 5);
}
};
}
private static void tune(RetryTemplate retryTemplate, int maxAttempts) {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
policy.setMaxAttempts(maxAttempts);
retryTemplate.setRetryPolicy(policy);
ExponentialBackOffPolicy backOff = new ExponentialBackOffPolicy();
backOff.setInitialInterval(1000L);
backOff.setMultiplier(2.0);
backOff.setMaxInterval(10_000L);
retryTemplate.setBackOffPolicy(backOff);
}
}
升 Boot4 后这段的问题不在「重试次数设多少」,而在:接口与回调签名整条换了 。再继续实现 RabbitRetryTemplateCustomizer,要么编不过,要么 Bean 挂不上新的自动配置钩子。Guide 口径是 migrate to either of those interfaces------按场景拆,而不是找一个「同名兼容别名」。
新写法:Template 与 Listener 各一个 Customizer
包名也变了:新接口在 org.springframework.boot.amqp.autoconfigure。Template 侧回调参数是 RabbitRetryTemplateSettings;Listener 侧直接是 RetryPolicySettings(org.springframework.boot.retry)。两者都是 @FunctionalInterface,可以用 lambda。
发模板重试:RabbitTemplateRetrySettingsCustomizer
java
package com.example.amqp;
import java.time.Duration;
import org.springframework.boot.amqp.autoconfigure.RabbitTemplateRetrySettingsCustomizer;
import org.springframework.boot.retry.RetryPolicySettings;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Boot 4:定制 RabbitTemplate 操作所用的 retry settings。
* API:customize(RabbitRetryTemplateSettings) since 4.0.0
*/
@Configuration
public class AmqpTemplateRetryConfig {
@Bean
RabbitTemplateRetrySettingsCustomizer rabbitTemplateRetrySettingsCustomizer() {
return retrySettings -> {
RetryPolicySettings policy = retrySettings.getRetryPolicySettings();
policy.setMaxRetries(3L);
policy.setDelay(Duration.ofSeconds(1));
policy.setMultiplier(2.0);
policy.setMaxDelay(Duration.ofSeconds(10));
};
}
}
官方参考还写明:也可以通过配置属性打开模板重试,例如 spring.rabbitmq.template.retry.enabled=true;程序化定制则声明上述 Customizer Bean。属性开关注与 Customizer 互补,不要以为「只改 YAML 就等于迁完了旧 Bean」。
监听消费重试:RabbitListenerRetrySettingsCustomizer
java
package com.example.amqp;
import java.time.Duration;
import org.springframework.boot.amqp.autoconfigure.RabbitListenerRetrySettingsCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Boot 4:定制 message listener 所用的 RetryPolicy settings。
* API:customize(RetryPolicySettings) since 4.0.0
*/
@Configuration
public class AmqpListenerRetryConfig {
@Bean
RabbitListenerRetrySettingsCustomizer rabbitListenerRetrySettingsCustomizer() {
return retrySettings -> {
retrySettings.setMaxRetries(5L);
retrySettings.setDelay(Duration.ofMillis(500));
retrySettings.setMultiplier(2.0);
retrySettings.setMaxDelay(Duration.ofSeconds(8));
};
}
}
Listener 侧重试默认同样是关的;官方文档说明可用配置打开,并可用本 Customizer 程序化改 RetryPolicy。耗尽后的 recover 行为(如 RejectAndDontRequeueRecoverer)属监听框架默认策略,本稿不展开成另一条主线------先把 Customizer 迁对。
选型对照(一张表记牢)
| 你以前在旧 Bean 里分支的 Target | Boot4 应实现的接口 | 回调参数 |
|---|---|---|
Target.SENDER(RabbitTemplate) |
RabbitTemplateRetrySettingsCustomizer |
RabbitRetryTemplateSettings → 再 getRetryPolicySettings() |
Target.LISTENER(监听容器) |
RabbitListenerRetrySettingsCustomizer |
RetryPolicySettings |
| 两边都定制过 | 两个 Bean 都要,不要指望一个接口兼顾 | 分文件/分配置类更清晰 |
旧代码若是「一个 Customizer 里 if/else 两个 Target」,升版时拆成两个 @Bean 最不容易漏。只发消息、从不 @RabbitListener 的服务,可以只迁 Template 侧;纯消费服务则相反。
Template 和 Listener 该用哪个新 Customizer?
按调用栈症状选,不要按「类名里有没有 RetryTemplate」猜:
- 生产者 / 出站 :业务代码走
RabbitTemplate.convertAndSend(或 AmqpTemplate)失败后的自动重试 → Template Customizer;同时核对spring.rabbitmq.template.retry.*是否已启用。 - 消费者 / 入站 :
@RabbitListener方法抛异常后的投递重试 → Listener Customizer;核对spring.rabbitmq.listener.simple.retry.*(或 direct 容器对应项)是否启用。 - 两边都有:两个 Bean 都注册;MR 描述里写清「SENDER→Template,LISTENER→Listener」,方便评审对照 Guide 原文。
不要把「监听重试次数」写进 Template Customizer 里指望两边生效------Boot4 拆接口的目的就是显式 。同样,也不要再引入已移除依赖管理路径上的 org.springframework.retry.support.RetryTemplate 去「手搓兼容层」当长期方案;Guide 对 Spring Retry 依赖管理移除是另一条说明,本稿只一句:若业务仍直连 Spring Retry,需显式版本 并考虑迁 Framework------不是本稿主线。
和 Kafka retry 的 random→jitter 是同一坑吗?
不是。 169145369《Boot4升后Kafka retry属性怎么改》 落在 Migration Guide 的 Spring Kafka Retry Features :配置键 spring.kafka.retry.topic.backoff.random removed in favor of ...backoff.jitter。那是 YAML/属性键替换。
本稿是 Spring AMQP Retry Features :Java 回调接口改名/拆分 (RabbitRetryTemplateCustomizer → 两个 *RetrySettingsCustomizer)。
| 维度 | AMQP(本稿) | Kafka(169145369) |
|---|---|---|
| Guide 小节 | Spring AMQP Retry Features | Spring Kafka Retry Features |
| 变更形态 | Customizer 接口拆分 | 配置键 random→jitter |
| 典型症状 | 旧接口编不过 / Bean 不生效 | 旧键不绑定 / 告警 |
| 改法 | 迁到 Template 或 Listener Customizer | 改写 jitter 属性 |
| 是否混判 | 勿混 | 勿混 |
两边都提到「retry 从 Spring Retry 迁到 Spring Framework」,但交付物完全不同 。升版评审拆两条 checklist:一条扫 RabbitRetryTemplateCustomizer,一条扫 backoff.random。绑同一个 MR 时,失败日志关键字也对不上号。
怎么快速核自己的 AMQP 配置 / Bean?
验收不要只看「应用能启」。Customizer 踩坑在类型迁移与钩子是否挂上,建议写成可勾选步骤:

图:对照 Guide → 搜旧 Customizer 残留 → 按场景迁两接口 → 模板发送/监听重试冒烟 → 边界(勿混 Kafka jitter / CLASSIC / DevTools)。
1. 对照官方源
打开 Migration Guide 的 Spring AMQP Retry Features,确认仍是「moved ... to Spring Framework」+ 两个 dedicated customizers +「migrate to either of those interfaces」。API 页核 since 4.0.0。
2. 搜残留并替换
- 全仓搜
RabbitRetryTemplateCustomizer、RabbitRetryTemplateCustomizer.Target、Target.SENDER、Target.LISTENER(限定在 AMQP 配置类); - 旧 import
org.springframework.boot.autoconfigure.amqp.RabbitRetryTemplateCustomizer一律清掉; - 按上表改成 Template / Listener 新接口;不要留一个空的旧 Bean「占位」。
3. 本地冒烟(文字 checklist)
- 确认相关
spring.rabbitmq.template.retry.enabled/spring.rabbitmq.listener.*.retry.enabled按环境打开(与你们现网策略一致); - 模板路径 :故意触发一次可恢复失败(如短暂断连测试桩),观察是否按 Customizer 中的
maxRetries/delay节奏重试; - 监听路径 :
@RabbitListener内抛受控异常,确认重试次数与间隔符合 Listener Customizer;耗尽后进你们既定的 DLQ / reject 策略; - 启动日志 / Actuator(若有)确认没有「找不到旧 Customizer 类型」一类错误;
- 对照:Guide 小节 + 新 Bean 生效,即本条升版项闭环。
4. 全文搜「假绿」残留
| 检查项 | 期望 |
|---|---|
无 RabbitRetryTemplateCustomizer 源码引用 |
通过 |
| Template / Listener 新接口按场景注册 | 通过 |
与 Kafka backoff.random 清单 |
分列,勿同一行改完 |
| 父工程 / 公司 starter 是否仍导出旧类型 | 一并扫 |
和 CLASSIC / DevTools / endpoint 怎么区分?
主线不同,症状域也不同。
| 主题 | 你在排查什么 | 该看哪篇 |
|---|---|---|
| AMQP Retry Customizer(本稿) | 旧 RabbitRetryTemplateCustomizer 失效 |
本稿:迁两接口 |
| Kafka retry 属性 | backoff.random / jitter |
169145369 |
| CLASSIC loader | 构建里 loaderImplementation=CLASSIC |
169615117 |
| DevTools Live Reload | 浏览器不自动刷新 | 169536598 |
| endpoint access | Actuator 启用语义 | 169441413 |
实务口诀:
- 报错 / 编译点名 RabbitRetryTemplateCustomizer → 本稿;
- 配置键带 kafka...backoff.random/jitter → Kafka 专文;
- 构建关键字 CLASSIC / loaderImplementation → CLASSIC 专文;
- 现象在 热刷 / Actuator → 各走专文。
Session MongoDB 直持移除、Spring Retry dependency management removed (需显式版本 / 迁 Framework)都是 Guide 里的邻条,仅作 fallback 知晓:不是本稿交付范围,升版 runbook 可另立条目,勿塞进本 MR 主描述。
常见误判怎么排?
1. 以为「改个 YAML 就等于迁完 Customizer」
属性开关管「开不开重试」;旧 Java 钩子仍必须换成新接口。两层都要核。
2. 把 Kafka jitter 改法套到 AMQP
AMQP 这条不是换 random→jitter 键,而是换 Customizer 类型。分列。
3. 只迁了 Template,忘了 Listener(或相反)
对照旧代码里出现过的 Target 枚举;两边都用过就两个 Bean。
4. 继续依赖 org.springframework.retry.* 手写「兼容层」当永久方案
过渡可以,长期应按 Guide 迁到 Framework retry;依赖管理移除是邻条,另开任务。
5. 和 CLASSIC / DevTools / endpoint 绑同一 MR
症状域完全不同。分 MR,回滚更干净。
团队升版清单(AMQP · retry Customizer 条)
把下面几行贴进 Boot4 升版 runbook 的「消息 / AMQP」小节即可:
- Migration Guide 确认 Spring AMQP Retry Features 与两个新 Customizer;
- 全仓删除 / 替换
RabbitRetryTemplateCustomizer; Target.SENDER→RabbitTemplateRetrySettingsCustomizer;Target.LISTENER→RabbitListenerRetrySettingsCustomizer;- 核对
spring.rabbitmq.template.retry.*/listener.*.retry.*与 Bean 一致; - 模板发送 + 监听抛错各冒烟一次;
- 注明:与 Kafka jitter(169145369)、CLASSIC(169615117)、DevTools(169536598)、endpoint(169441413)分列;
升版窗口怎么排期更稳?
建议把 AMQP Customizer 迁改单独成 MR:先对照 Guide 确认两个新接口,再全仓清 RabbitRetryTemplateCustomizer,最后才开模板/监听冒烟。父工程或公司 starter 若仍导出旧类型,要和业务侧一起扫,避免「业务已迁、starter 还编不过」。Kafka jitter、CLASSIC、DevTools、endpoint 各走专文 checklist,不要绑进同一回滚单元。
FAQ
Boot4 升后 RabbitRetryTemplateCustomizer 为什么失效?
Migration Guide:AMQP retry 已迁到 Spring Framework;须改用 RabbitTemplateRetrySettingsCustomizer 或 RabbitListenerRetrySettingsCustomizer。
Template 和 Listener 该用哪个新 Customizer?
发模板 / RabbitTemplate 侧重试用 Template;@RabbitListener 消费重试用 Listener;两边都定制过就两个都要。
和 Kafka retry 的 random→jitter 是同一坑吗?
不是。Kafka 是配置键替换;AMQP 是 Customizer 接口拆分。分列,勿混。
Spring Retry 依赖管理移除和这次 Customizer 改名是什么关系?
同属「retry 迁 Spring Framework」大背景;Customizer 改名是 AMQP 专用钩子调整。依赖管理移除(需显式版本)是邻条,不是本稿主线。
怎么快速核自己的 AMQP 配置 / Bean?
对照 Guide → 搜旧接口 → 按 Target 迁到新接口 → 打开对应 retry 开关 → 发送/监听各冒烟一次。
小结 checklist
- 认清变更:AMQP retry 迁到 Spring Framework ;旧
RabbitRetryTemplateCustomizer须替换 - Template 侧:实现
RabbitTemplateRetrySettingsCustomizer(since 4.0.0) - Listener 侧:实现
RabbitListenerRetrySettingsCustomizer(since 4.0.0) - 旧
Target.SENDER/LISTENER分支拆成两个 Bean(按需) - 对照源:Migration Guide · Spring AMQP Retry Features
- 冒烟:模板发送重试 + 监听消费重试
- 与 Kafka jitter(169145369)分列勿混
- 勿抢 CLASSIC(169615117)/ DevTools(169536598)/ endpoint(169441413)
- Session Mongo / Spring Retry BOM 仅邻条,非本稿主线
按「对照 Guide → 删旧 Customizer → 按场景挂两个新接口 → 发送/监听冒烟」改完,这类踩坑通常一次就能闭环。别在 Kafka jitter / CLASSIC 两侧来回猜。其它 Boot4 能力见前述内链,本文不展开。