扩展ItemWriter Bean时如何在运行时用@StepScope动态初始化ItemWriter Bean?

How to dynamically initialize ItemWriter Bean with @StepScope at runtime when ItemWriter Bean is extended?

[Spring 批处理的新功能] 我有不同格式的不同 csv,将来可以添加更多 csv,所以我想到了使用通用 FlatFileItemReader<T> 而不是定义 @Bean 对于每个 csv 格式,我创建了一个基本配置 class,然后为每个 csv 类型创建了具体的 class。

因为我已经将 Reader bean 定义为 @StepScope ,在批处理作业 运行 期间,它会使用包中的第一个具体 class 自动初始化 bean,相同讨论了某种问题 但答案与我的情况无关

如何在作业 运行 期间将特定 class 类型的项目 Reader 传递到我的步骤?

这是我的基本配置 class:

public abstract class AbstractBatchItemReader<T> {

private CsvInformation csvInformation;

protected AbstractBatchItemReader(CsvInformation csvInformation) {
    this.csvInformation = csvInformation;
}

@Bean
@StepScope
//fileName is retrieved from jobParameters during runtime
public FlatFileItemReader<T> getItemReader(@Value("#{jobParameters['input.file.name']}") String fileName) {
    return new FlatFileItemReaderBuilder<T>()
            .name("invoiceHeaderItemReader")
            .resource(new FileSystemResource(fileName))
            .linesToSkip(1)
            .delimited()
            .names(csvInformation.getHeaders().split(","))
            .fieldSetMapper(new BeanWrapperFieldSetMapper<T>() {{
                setConversionService(new StringToLocalDateConversion().convert());
                setTargetType(csvInformation.getERPClass());
            }})
            .build();

    }
 }

这是扩展基本配置的具体 class:

@Configuration
public class InvoiceHeaderReader extends AbstractBatchItemReader<ERPInvoiceHeader> {
protected InvoiceHeaderReader(InvoiceHeaderCsvInformation csvInformation) {
    super(csvInformation);
  }
}

这是我的基本步骤配置:

public abstract class AbstractBatchStep<T> {

private final AbstractBatchItemReader<T> reader;
private final AbstractBatchItemWriter<T> writer;
private final StepBuilderFactory stepBuilderFactory;

protected AbstractBatchStep(AbstractBatchItemReader<T> reader,
                            AbstractBatchItemWriter<T> writer,
                            StepBuilderFactory stepBuilderFactory) {
    this.reader = reader;
    this.writer = writer;
    this.stepBuilderFactory = stepBuilderFactory;
}

public Step getStep() {
    afterPropertiesSet();
    return stepBuilderFactory.get("Batch Step")
            .<T, T>chunk(BatchConfiguration.READER_CHUNK_SIZE)
            //fileName is passed during runtime
            .reader(reader.getItemReader(null))
            .writer(writer.getItemWriter())
            .build();
   }
 }

这里是扩展步骤配置的具体class:

@Configuration("invoice_header")
public class InvoiceHeaderStep extends AbstractBatchStep<ERPInvoiceHeader> {
protected InvoiceHeaderStep(InvoiceHeaderReader reader, InvoiceHeaderWriter writer, StepBuilderFactory stepBuilderFactory) {
    super(reader, writer, stepBuilderFactory);
 }
}

整个工作周期运行仅针对包中的第一个具体class如果我尝试运行另一种类型的csv它会失败并出现异常..Unexpected token required n found n 异常显然是因为 reader 是由包中的第一个 class 自动初始化的,而不是我传递给 Step

的那个

另请建议此设计模式是否正确,或者是否有一种简单的方法可以实现此目的。

我想post回答给别人参考

  1. 我创建了一个 AbstractBatchItemReader<T> class 具有基本配置
  2. 扩展基本配置的具体 classes class TypeOneCsvReader extends AbstractBatchItemReader<TypeOneEntity>

3.Interface 使用 Csv 信息方法和 Classes 为每个 Csv 类型实现接口

这是代码示例:

AbstractBatchItemReader:

public abstract class AbstractBatchItemReader<T> {

private CsvInformation csvInformation;
protected AbstractBatchItemReader(CsvInformation csvInformation) {
    this.csvInformation = csvInformation;
}

 FlatFileItemReader<T> getItemReader() {
    return new FlatFileItemReaderBuilder<T>()
            .name("Batch Reader")
            .resource(resource(null))
            .linesToSkip(1)
            .delimited()
            .quoteCharacter(BatchConfiguration.READER_QUOTE_CHARACTER)
            .names(csvInformation.getHeaders().split(","))
            .fieldSetMapper(new BeanWrapperFieldSetMapper<T>() {{
                setConversionService(StringToLocalDateConversion.convert());
                setTargetType(csvInformation.getEntityClass());
            }})
            .build();

}

@Bean
@StepScope
public Resource resource(@Value("#{jobParameters['input.file.name']}") String fileName) {
    return new FileSystemResource(fileName);
}
}

具体Class:

@Configuration
public class TypeOneCsvReader extends AbstractBatchItemReader<TypeOneEntity> {
protected TypeOneCsvReader(TypeOneCsv csvInformation) {
    super(csvInformation);
}
}

Csv信息接口:

public interface CsvInformation {
String getHeaders();
Class getEntityClass();

}

接口的每个实现都必须用 @Component 注释,以便 Concrete Reader class 通过 DI

获取它

采用这种方法的好处是,它可以根据需要扩展到尽可能多的 csv 类型,而且 Reader 逻辑保留在一个地方

谢谢