Spring Batch处理器中如何每次调用时初始化/清空列表,避免Chunk数据残留?
问题解决方法
你的问题根源在于类级别的processedList是Spring单例Bean的成员变量,整个Step运行期间只会初始化一次,所以每个Chunk处理时都会往同一个列表里累加数据,导致传给Writer的列表包含之前所有Chunk的内容。
最规范的解决方式是遵循Spring Batch的Chunk处理设计,不要在Processor里维护全局列表,具体步骤如下:
1. 修正Step的泛型配置
把Step的泛型从<InputObject, List<ProcessedObject>>改成<InputObject, ProcessedObject>,因为Processor的职责是处理单个输入项,返回单个处理后的项,Spring Batch会自动把当前Chunk的所有处理后的项收集成列表传给Writer:
// Step @Bean public Step step1(StepBuilderFactory stepBuilderFactory, Processor myProcessor, Writer myWriter, ItemReader<InputObject> myReader) { // 补上缺失的reader引用 return stepBuilderFactory.get("step1") .<InputObject, ProcessedObject>chunk(15) .reader(myReader) .processor(myProcessor) .writer(myWriter) .build(); }
2. 改造Processor,去掉类级列表
Processor只负责处理单个输入项,返回单个处理结果,不需要维护全局列表:
@Override public ProcessedObject process(InputObject input) throws Exception { ProcessedObject o = new ProcessedObject(); try{ // 这里设置元数据到processed object } catch (Exception ex) { o.setErrorFlag(true); o.setErrorMessage(ex.getMessage()); } return o; }
3. 在Writer中处理分文件逻辑
Writer会自动收到当前Chunk的所有ProcessedObject列表,你可以直接遍历这个列表,根据每个项的条件写入不同文件:
@Override public void write(List<? extends ProcessedObject> chunkItems) throws Exception { // 遍历当前Chunk的所有项,按条件分文件写入 for (ProcessedObject item : chunkItems) { if (item.isErrorFlag()) { // 写入错误数据文件 } else { // 写入正常数据文件 } } }
如果因为特殊需求,你必须在Processor中维护当前Chunk的列表,那可以通过ChunkListener来实现每个Chunk开始前初始化列表:
改造Processor实现ChunkListener
@Component public class MyProcessor implements ItemProcessor<InputObject, List<ProcessedObject>>, ChunkListener { private List<ProcessedObject> processedList; // 每个Chunk开始前调用,初始化空列表 @Override public void beforeChunk(ChunkContext chunkContext) { processedList = new ArrayList<>(); } @Override public List<ProcessedObject> process(InputObject input) throws Exception { ProcessedObject o = new ProcessedObject(); try{ // 设置元数据到processed object } catch (Exception ex) { o.setErrorFlag(true); o.setErrorMessage(ex.getMessage()); } processedList.add(o); return processedList; } // Chunk处理完成后清理列表(可选) @Override public void afterChunk(ChunkContext chunkContext) { processedList.clear(); } }
在Step中注册Listener
@Bean public Step step1(StepBuilderFactory stepBuilderFactory, MyProcessor myProcessor, Writer myWriter, ItemReader<InputObject> myReader) { return stepBuilderFactory.get("step1") .<InputObject, List<ProcessedObject>>chunk(15) .reader(myReader) .processor(myProcessor) .writer(myWriter) .listener(myProcessor) // 注册ChunkListener .build(); }
⚠️ 注意:这种方式下,Processor会被调用chunkSize次(比如15次),每次返回的列表元素从1个增加到15个,Writer会收到15个这样的列表,这通常不符合常规需求,所以更推荐第一种规范的实现方式。
内容的提问来源于stack exchange,提问作者FAIZAN
相关产品推荐
相关产品推荐

