Spring Batch中如何在ItemReader中获取资源及当前文件名?
在Spring Batch中通过MultiResourceItemReader获取当前文件名的解决方案
嘿,这个场景我之前做批处理的时候刚好遇到过!当用MultiResourceItemReader批量处理多个文件时,要在FlatFileItemReader里拿到当前处理的文件名,其实Spring Batch已经给我们提供了现成的机制,下面给你两种实用的实现方式:
方案一:通过ExecutionContext + SpEL注入当前资源
MultiResourceItemReader在切换处理每个文件时,会自动把当前的Resource对象存入StepExecution的ExecutionContext中,对应的key是常量MultiResourceItemReader.CURRENT_RESOURCE(值为current.resource)。我们可以利用Spring的SpEL表达式,在StepScope下把这个资源注入到读取器或处理器中。
代码示例
首先,假设你已经配置了MultiResourceItemReader,接下来可以这样配置FlatFileItemReader和对应的处理逻辑:
- 自定义一个
FieldSetMapper,接收当前文件名作为参数:
public class CustomFieldSetMapper implements FieldSetMapper<YourDomainObject> { private String currentFileName; // 提供setter方法用于注入文件名 public void setCurrentFileName(String currentFileName) { this.currentFileName = currentFileName; } @Override public YourDomainObject mapFieldSet(FieldSet fieldSet) throws BindException { YourDomainObject obj = new YourDomainObject(); // 这里可以使用currentFileName来处理数据 obj.setSourceFileName(currentFileName); obj.setField1(fieldSet.readString("field1")); obj.setField2(fieldSet.readInt("field2")); // 其他字段映射... return obj; } }
- 在配置类中,通过SpEL注入当前资源的文件名:
@Configuration @EnableBatchProcessing public class BatchConfig { // 省略其他配置(比如Job、Step、MultiResourceItemReader的配置)... @Bean @StepScope public FlatFileItemReader<YourDomainObject> flatFileItemReader( @Value("#{stepExecution.executionContext['current.resource']}") Resource currentResource) { String fileName = currentResource.getFilename(); return new FlatFileItemReaderBuilder<YourDomainObject>() .name("flatFileItemReader") .delimited() .names("field1", "field2") // 你的文件列名 .fieldSetMapper(customFieldSetMapper(fileName)) .build(); } @Bean public CustomFieldSetMapper customFieldSetMapper(String fileName) { CustomFieldSetMapper mapper = new CustomFieldSetMapper(); mapper.setCurrentFileName(fileName); return mapper; } }
这样,每当MultiResourceItemReader切换到新文件时,flatFileItemReader会重新初始化(因为是@StepScope),并注入当前文件的资源,进而把文件名传递给CustomFieldSetMapper,在映射每行数据时就能用上文件名了。
方案二:利用ThreadLocal + ReadListener传递文件名
如果你的场景不适合用StepScope注入,也可以通过ItemReadListener来监听读取事件,把当前文件名存入ThreadLocal,然后在FlatFileItemReader的处理逻辑中获取。
代码示例
- 定义一个ThreadLocal工具类来存储当前文件名:
public class FileNameHolder { private static final ThreadLocal<String> currentFileName = new ThreadLocal<>(); public static void setFileName(String fileName) { currentFileName.set(fileName); } public static String getFileName() { return currentFileName.get(); } public static void clear() { currentFileName.remove(); } }
- 实现
ItemReadListener,在读取前获取当前资源并设置文件名:
public class FileReadListener implements ItemReadListener<YourDomainObject> { @Value("#{stepExecution.executionContext['current.resource']}") private Resource currentResource; @Override public void beforeRead() { if (currentResource != null) { FileNameHolder.setFileName(currentResource.getFilename()); } } @Override public void afterRead(YourDomainObject item) { // 可选:如果需要在读取后清理,也可以在这里clear,但注意多文件切换时的时机 } @Override public void onReadError(Exception ex) { FileNameHolder.clear(); } }
- 在Step配置中注册这个Listener,并在
FieldSetMapper中使用:
@Bean public Step fileProcessingStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) { return new StepBuilder("fileProcessingStep", jobRepository) .<YourDomainObject, YourDomainObject>chunk(100, transactionManager) .reader(multiResourceItemReader()) .processor(customItemProcessor()) .writer(customItemWriter()) .listener(fileReadListener()) // 注册Listener .build(); } // 然后在CustomFieldSetMapper中直接获取: @Override public YourDomainObject mapFieldSet(FieldSet fieldSet) throws BindException { String fileName = FileNameHolder.getFileName(); // 用文件名处理数据... }
注意事项
- 记得使用
@StepScope来确保每次处理新文件时,相关的Bean能重新获取最新的current.resource。 - 如果是XML配置,原理也是一样的,通过SpEL表达式
#{stepExecution.executionContext['current.resource']}来注入资源即可。
内容的提问来源于stack exchange,提问作者VIJAYKUMAR SUBRAMANI
相关产品推荐
相关产品推荐

