首页 > 解决方案 > 扩展ItemWriter Bean时如何在运行时使用@StepScope动态初始化ItemWriter Bean?

问题描述

[Spring Batch 的新手] 我有不同格式的不同 csv,将来可能会添加更多 csv 所以我想有一个通用的FlatFileItemReader<T>而不是@Bean为每个 csv 格式定义,我创建了一个基本配置类,然后为每个 csv 类型创建了具体类.

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

如何在作业运行期间将 ItemReader 的特定具体类类型传递给我的步骤?

这是我的基本配置类:

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();

    }
 }

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

@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();
   }
 }

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

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

如果我尝试运行另一种类型的 csv,则整个作业循环仅针对包中的第一个具体类Unexpected token required n found n运行到步骤

还请建议这种设计模式是否正确,可能有一种简单的方法来实现这一点。

标签: springspring-bootspring-batch

解决方案


我想发布答案作为其他人的参考。

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

3.接口与Csv信息方法和类实现每种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);
}
}

具体类:

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

Csv信息接口:

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

}

接口的每个实现都必须注解,@Component以便具体阅读器类通过 DI 获取它

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

谢谢


推荐阅读