카테고리 없음

ItemWriter Bean이 확장 될 때 런타임에 @StepScope로 ItemWriter Bean을 동적으로 초기화하는 방법은 무엇입니까?

기록만이살길 2021. 2. 19. 16:16
반응형

ItemWriter Bean이 확장 될 때 런타임에 @StepScope로 ItemWriter Bean을 동적으로 초기화하는 방법은 무엇입니까?

질문(문제점):

[New to Spring Batch] 다른 형식의 다른 csv를 가지고 있습니다. 앞으로 더 많은 csv가 추가 될 수 있으므로 각 csv 형식에 대해 FlatFileItemReader<T>정의하는 대신 공통으로 생각 @Bean하고 각 csv 유형에 대해 기본 구성 클래스를 만든 다음 구체적인 클래스를 만들었습니다. .

Reader bean을로 정의했기 때문에 @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예외로 인해 실패합니다. 예외는 독자가 전달한 것이 아니라 패키지의 첫 번째 클래스에 의해 자동으로 초기화 되었기 때문입니다. 단계로

이 디자인 패턴이 올바른지 제안하십시오.이를 달성하는 쉬운 방법이있을 수 있습니다.

해결방안:

다른 사람에게 참고로 답변을 게시하고 싶습니다.

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

CsvInformation 인터페이스 :

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

}

Each implementation of interface has to be annotated with @Component so that Concrete Reader class picks it up via DI

Benefit of having such an approach is, it can be scaled to as many csv type as required and also the Reader logic stays at one place

Thanks

65954907
반응형