扩展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回答给别人参考
- 我创建了一个
AbstractBatchItemReader<T>
class 具有基本配置
- 扩展基本配置的具体 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 逻辑保留在一个地方
谢谢
[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回答给别人参考
- 我创建了一个
AbstractBatchItemReader<T>
class 具有基本配置 - 扩展基本配置的具体 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 逻辑保留在一个地方
谢谢