当itemwriterbean被扩展时,如何在运行时用@stepscope动态初始化itemwriterbean?

b1zrtrql  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(218)

[新加入spring批处理]我有不同格式的csv,将来可以添加更多的csv,所以我想有一个共同的 FlatFileItemReader<T> 而不是定义 @Bean 对于每个csv格式,我创建了一个基本配置类,然后为每个csv类型创建了一个具体的类。
因为我将readerbean定义为 @StepScope ,在批处理作业运行时,它使用包中的第一个具体类自动初始化bean,这里讨论了相同的问题,但答案与我的案例无关
在作业运行期间,如何将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 异常很明显是因为读取器是由包中的第一个类自动初始化的,而不是我传递给步骤的那个类
也请建议,如果这个设计模式是正确的,可能有一个简单的方法来实现这一点。

k2fxgqgv

k2fxgqgv1#

我想把答案贴出来作为参考。
我创造了一个 AbstractBatchItemReader<T> 具有基本配置的类
扩展基本配置类的具体类 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 所以具体的reader类通过di获取它
有这样一种方法的好处是,它可以根据需要扩展到任意多个csv类型,并且读卡器逻辑保持在一个位置
谢谢

相关问题