Spring Batch中ResourceAware模型多文件处理文件名异常问题
这个场景我之前处理过,核心问题通常出在状态复用或者ResourceAware的正确使用上,咱们从几个关键环节排查:
1. 检查ItemProcessor的作用域与状态管理
Spring Batch默认所有组件(包括ItemProcessor)都是单例的。如果你在处理器中用成员变量存储文件名,那么处理第二个文件时这个变量不会自动重置,始终保留第一个文件的值。
错误示例(单例处理器存状态)
@Component public class MyItemProcessor implements ItemProcessor<MyModel, MyModel> { // 单例下这个变量会被所有文件处理共享,导致文件名不更新 private String currentFileName; @Override public MyModel process(MyModel item) throws Exception { item.setFileName(currentFileName); // 这里用的是旧值 return item; } // 假设你在某个地方设置这个值,但只触发了一次 public void setCurrentFileName(String fileName) { this.currentFileName = fileName; } }
正确做法:从Item本身获取Resource
因为你的MyModel实现了ResourceAware,Spring Batch会自动将当前处理的文件资源注入到每个Item实例中。所以处理器应该直接从Item里获取文件名,同时给处理器加上@StepScope确保状态隔离:
@Component @StepScope // 关键:每个文件处理阶段拥有独立的处理器实例 public class MyItemProcessor implements ItemProcessor<MyModel, MyModel> { @Override public MyModel process(MyModel item) throws Exception { // 直接从Item的Resource中获取当前文件名 String fileName = item.getResource().getFilename(); item.setFileName(fileName); return item; } }
2. 验证ResourceAware的实现是否正确
确保你的模型类正确实现了ResourceAware,并且Resource字段是每次读取都被重新赋值的,而不是复用旧值:
public class MyModel implements ResourceAware { private Resource resource; private String fileName; // 其他字段... @Override public void setResource(Resource resource) { this.resource = resource; // 也可以在这里直接设置文件名,避免处理器里再处理 // this.fileName = resource.getFilename(); } // Getter & Setter public Resource getResource() { return resource; } public void setFileName(String fileName) { this.fileName = fileName; } }
注意:如果你的ItemReader(比如FlatFileItemReader)开启了对象复用,要确保setResource方法每次都会被调用,覆盖旧的Resource。
3. 检查MultiResourceItemReader的配置
如果你用MultiResourceItemReader处理多个文件,要确保它的delegate reader正确返回实现了ResourceAware的Item实例,并且没有阻止Resource的注入:
@Bean public MultiResourceItemReader<MyModel> multiResourceItemReader() { MultiResourceItemReader<MyModel> reader = new MultiResourceItemReader<>(); reader.setResources(new PathMatchingResourcePatternResolver().getResources("file:/path/to/files/*.csv")); reader.setDelegate(flatFileItemReader()); // 确保这个参数为true(默认就是true),让MultiResourceItemReader注入Resource到Item中 reader.setDelegateIsResourceAware(true); return reader; } @Bean public FlatFileItemReader<MyModel> flatFileItemReader() { FlatFileItemReader<MyModel> reader = new FlatFileItemReader<>(); reader.setLineMapper(new DefaultLineMapper<MyModel>() {{ setLineTokenizer(new DelimitedLineTokenizer()); setFieldSetMapper(new BeanWrapperFieldSetMapper<MyModel>() {{ setTargetType(MyModel.class); }}); }}); return reader; }
重点确认setDelegateIsResourceAware(true),这个参数确保MultiResourceItemReader会把当前处理的文件资源传递给delegate reader返回的Item(只要Item实现了ResourceAware)。
4. 排查ItemWriter的批量处理缓存
如果你的ItemWriter是批量写入(比如JdbcBatchItemWriter),要确保写入的每个Item的文件名已经被正确设置。可以在处理器里加日志,打印每个Item的文件名,验证是否在进入Writer前就已经是正确的。比如:
@Override public MyModel process(MyModel item) throws Exception { String fileName = item.getResource().getFilename(); item.setFileName(fileName); System.out.println("Processing item from file: " + fileName); // 日志验证 return item; }
如果日志显示文件名正确,但数据库里还是旧值,那就要检查Writer的配置,比如是否有错误的字段映射或者缓存机制。
内容的提问来源于stack exchange,提问作者M06H

