
Spring Batch 在启用容错(fault-tolerant)时,若发生异常会默认重试失败项,导致跳过的记录被多次处理;正确做法是使用 SkipListener 统一捕获并写入错误记录,避免在 Reader/Processor/Writer 中自行写文件引发重复。
spring batch 在启用容错(fault-tolerant)时,若发生异常会默认重试失败项,导致跳过的记录被多次处理;正确做法是使用 `skiplistener` 统一捕获并写入错误记录,避免在 reader/processor/writer 中自行写文件引发重复。
在 Spring Batch 中,当配置 .faultTolerant().skipPolicy(...) 后,框架对跳过的项默认执行一次重试(retry)——即:若某条记录在处理过程中抛出可跳过异常,Batch 会先尝试重试该记录(由 RetryTemplate 控制),重试失败后才触发跳过逻辑。而如果你在 ItemReader(或 ItemProcessor/ItemWriter)中直接用 try-catch + FileOutputStream 写错误日志,就会因重试机制导致同一错误记录被写入多次。
✅ 正确解法:将错误记录写入逻辑完全委托给 SkipListener,它是 Spring Batch 官方指定的、线程安全且仅在最终确认跳过时调用的扩展点。
以下为推荐实现方式:
@Bean
public Step mainStep(StepBuilderFactory stepBuilderFactory,
ItemReader<employee> reader,
ItemProcessor<employee employee> processor,
ItemWriter<employee> writer,
SkipListener<employee employee> skipListener) {
return stepBuilderFactory.get("mainStep")
.<employee employee>chunk(2)
.reader(reader)
.processor(processor)
.writer(writer)
.faultTolerant()
.skipPolicy(new AlwaysSkipItemSkipPolicy())
.skipListener(skipListener) // ✅ 关键:注册 SkipListener
.build();
}
// 自定义 SkipListener,确保每条跳过记录只写一次
@Component
public class FileSkipListener implements SkipListener<employee employee> {
private final PrintWriter errorWriter;
public FileSkipListener(@Value("${error.output.path:./errors.txt}") String outputPath) throws IOException {
this.errorWriter = new PrintWriter(
Files.newBufferedWriter(Paths.get(outputPath), StandardOpenOption.CREATE, StandardOpenOption.APPEND)
);
}
@Override
public void onSkipInRead(Throwable t) {
// 若 Reader 抛异常(如解析失败),此处不传 item,可记录堆栈
errorWriter.printf("[READ_ERROR] %s%n", t.getMessage());
}
@Override
public void onSkipInProcess(Employee item, Throwable t) {
errorWriter.printf("[PROCESS_SKIP] %s | %s%n", item, t.getMessage());
}
@Override
public void onSkipInWrite(Employee item, Throwable t) {
errorWriter.printf("[WRITE_SKIP] %s | %s%n", item, t.getMessage());
}
@PreDestroy
public void close() {
if (errorWriter != null) {
errorWriter.close();
}
}
}</employee></employee></employee></employee></employee></employee>
⚠️ 重要注意事项:
- 禁止在 Reader/Processor/Writer 中手动写错误文件:它们可能被多次调用(尤其开启 retry 时),破坏幂等性;
-
SkipListener的onSkipInXxx()方法仅在跳过决策最终确定后调用一次,天然规避重复写入; - 若需保证错误文件线程安全(如多线程 Step),建议使用
synchronized块、ConcurrentHashMap缓存或基于Resource的BufferedWriter(配合@StepScope); - 如需关闭自动重试,可显式禁用:
.retryLimit(0)(但通常不推荐,因跳过前重试有助于区分瞬时故障与永久错误)。
总结:Spring Batch 的容错设计强调“关注点分离”——跳过策略负责判定,SkipListener 负责响应。将错误落地逻辑收敛到 SkipListener,既是最佳实践,也是解决重复写入问题的根本方案。










