You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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和对应的处理逻辑:

  1. 自定义一个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;
    }
}
  1. 在配置类中,通过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的处理逻辑中获取。

代码示例

  1. 定义一个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();
    }
}
  1. 实现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();
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:00:31