Spring Batch中RetryContext设置值后获取为null的问题排查
你的核心问题出在RetryContext的生命周期管理和RetryContextCache的使用方式上,具体原因及修正方案如下:
错误原因
自定义RetryContext是单例Bean,不符合重试上下文的生命周期
RetryContext是Spring Retry框架为每次重试操作单独创建的实例,用于存储当前重试的上下文数据。你手动定义的RetryContext是全局单例,会被多线程(你使用了taskExecutor)并发覆盖,且框架本身的重试流程不会使用这个自定义Bean,导致存储的属性和重试时获取的上下文完全不匹配。错误使用业务ID作为RetryContextCache的Key
Spring Batch的RetryContextCache的Key并非业务自定义的bookingId,而是框架根据重试上下文(如Item标识、重试次数等)生成的RetryContextCache.Key对象。你直接用bookingId存值,不符合框架的缓存逻辑,即使containsKey返回true,获取到的也不是正确的重试上下文。手动操作RetryContextCache多余
你配置的retryContextCache会由框架自动管理上下文的缓存,不需要手动调用put方法,手动操作反而会破坏框架的缓存逻辑。
修正方案
1. 移除自定义的RetryContext Bean
删除你定义的RetryContext Bean,框架会自动为每次重试创建对应的上下文实例:
// 移除以下Bean定义 /* @Bean RetryContext retryContext() { return new RetryContextSupport(null); } */
2. 修改Processor代码,使用框架提供的RetryContext
通过RetrySynchronizationManager获取当前线程绑定的RetryContext,而不是注入自定义Bean:
@Component public class CorrectionProcessor implements ItemProcessor<String, List<BookingInfo>> { public List<BookingInfo> process(String bookingId) throws Exception { List<BookingInfo> list = new ArrayList<>(); // 获取当前线程绑定的重试上下文 RetryContext currentContext = RetrySynchronizationManager.getContext(); // 从上下文获取之前存储的数据 if (currentContext != null) { list = (List<BookingInfo>) currentContext.getAttribute(bookingId); } // 如果没有缓存数据,从数据库查询 if (list == null) { // 从数据库查询并填充list的逻辑 } try { // 对list执行操作的逻辑 } catch (Exception e) { // 修改list后存入当前重试上下文 if (currentContext != null) { currentContext.setAttribute(bookingId, list); } throw e; // 抛出异常触发重试 } return list; } }
额外说明
RetrySynchronizationManager会将RetryContext绑定到当前线程,多线程场景下每个线程的上下文独立,不会出现并发冲突。- 你之前配置的
retryContextCache会被框架自动用来缓存RetryContext实例,无需手动干预。
内容的提问来源于stack exchange,提问作者Rahul Raj

