
spring batch 的 retrycontext 作用域仅限于重试内部,无法在处理器中提前写入并在后续重试中读取;应改用 @stepscope 管理跨重试周期的业务状态,配合 skiplistener 在跳过时恢复或处理暂存数据。
spring batch 的 retrycontext 作用域仅限于重试内部,无法在处理器中提前写入并在后续重试中读取;应改用 @stepscope 管理跨重试周期的业务状态,配合 skiplistener 在跳过时恢复或处理暂存数据。
在 Spring Batch 中,RetryContext 并非设计用于用户手动管理业务状态——它由 RetryTemplate 在每次重试执行前自动创建、在重试结束后销毁,其生命周期严格绑定于单次重试操作。因此,像原代码中那样在 catch 块中调用 retryContext.setAttribute() 并试图通过 retryContextCache 持久化,本质上是无效的:RetryContextSupport 实例不会被复用,MapRetryContextCache 中缓存的也并非可跨重试访问的“状态容器”,而是框架内部用于统计与上下文传递的临时结构。
✅ 正确做法是将业务状态解耦出重试机制,使用 Spring Batch 提供的 @StepScope 作用域 Bean 来实现 Step 级别的状态共享:
@Bean
@StepScope
public Map<string list>> bookingStateMap() {
return new ConcurrentHashMap();
}</string>
该 Bean 在每个 Step 实例启动时创建,在 Step 结束时销毁,天然支持重试过程中多次调用(如 processor 被重复执行)对同一状态 Map 的读写。
在 ItemProcessor 中,直接注入并使用该 Map:
@Component
@RequiredArgsConstructor
public class CorrectionProcessor implements ItemProcessor<string list>> {
private final Map<string list>> bookingStateMap;
@Override
public List<bookinginfo> process(String bookingId) throws Exception {
List<bookinginfo> list = bookingStateMap.get(bookingId);
if (list == null) {
// 首次处理:从数据库加载
list = fetchFromDatabase(bookingId);
bookingStateMap.put(bookingId, list);
}
try {
// 执行核心业务逻辑(可能失败)
businessLogic(list);
return list;
} catch (Exception e) {
// 失败时已确保 state 已缓存,直接抛出触发重试
throw e;
}
}
private List<bookinginfo> fetchFromDatabase(String id) { /* ... */ }
private void businessLogic(List<bookinginfo> list) { /* ... */ }
}</bookinginfo></bookinginfo></bookinginfo></bookinginfo></string></string>
⚠️ 注意事项:
- 不要再尝试操作 RetryContext 或 RetryContextCache 存储业务数据;
- @StepScope Bean 必须通过构造器注入(推荐)或 @Autowired + @Lazy,避免代理问题;
- 若需在重试失败后(即跳过)清理或补偿状态,应结合 SkipListener —— 如题中所示,在 @OnSkipInProcess 回调中获取并处理暂存数据,之后及时 remove() 避免内存泄漏。
最后,在 Step 定义中注册监听器:
@Bean
public Step correctionStep(JpaTransactionManager transactionManager) {
return new StepBuilder("correction-step", jobRepository)
.<string list>>chunk(10, transactionManager)
.reader(customItemReader())
.processor(correctionProcessor())
.writer(customItemWriter())
.taskExecutor(correctionTaskExecutor())
.faultTolerant()
.retryPolicy(retryPolicy())
.backOffPolicy(exponentialBackOffPolicy())
.skipPolicy(skipPolicy())
.listener(customSkipListener()) // ← 注册监听器
.build();
}</string>
这种方式既符合 Spring Batch 的设计哲学,又确保了状态一致性、线程安全(ConcurrentHashMap)与生命周期可控性,是处理“重试中需保留中间状态”场景的标准实践。










