본문 바로가기
백엔드/Spring

[Spring Batch] @JobScope 와 @StepScope 개념 및 예제

by 작은소행성 2023. 9. 23.

@JobScope 와 @StepScope

@JobScope 와 @StepScope는 스프링의 기본 Scope 인 싱글톤과 대치되는 역할이다. 

@JobScope 와 @StepScope이 선언되면

Bean의 생성 시점이 애프리케이션이 구동되는 시점이 아닌 Bean의 실행 시점에서 이루어진다. 

@JobScope, @StepScope이 명시된 메서드가 실행될 때까지 지연시키는 것을 의미한다. 이러한 행위는 Late Binding 이라고 한다. 

 

Scope 란 

스프링 컨테이너에서 빈이 관리되는 범위를 뜻한다. 

 

@JobScope 

Step 선언시에 사용한다. 

@Value : JobParameter, jobExecutionContext만 사용이 가능하다. 

 

@StepScope

Tasklet 이나 ItemReader, ItemWriter, ItemProcessor 선언문에 사용한다.

@Value : jobParameter, jobExecutionContext, stepExecutionContext 사용 가능하다.

 

@JobScope는 Step 선언문에서만 사용이 가능하고
@StepScope는 Step을 구성하는 ItemReader, ItemProcessor, ItemWriter에서 사용 가능하다.

 

 

특징 

1. JobParameter를 사용하면 

애프리케이션이 구동되는 시점이 아니라 비즈니스 로직이 구현되는 시점에 할당함으로써 유연한 설계가 가능하다. 

 

2. 병렬처리에 좋다. 

Step 의 구성요소인 ItemReader, ItemProcessor, ItemWriter이 있고, ItemReader에서 데이터를 읽어 오는 메서드를 서로 다른 Step으로 부터 동시에 병렬 실행이 된다면 서로 상태를 간섭받게 될 수 있는데 

@StepScope 을 적용하면 각각의  Step에서 실행될 때 서로의 상태를 침범하지 않고 처리 할 수 있다. 

 

 

예제 

단일 Step

@Slf4j
@Configuration
@RequiredArgsConstructor
@Primary
@EnableBatchProcessing
public class JobStepConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;


    // Job
    @Bean
    public Job jobScopeJob() {
        return jobBuilderFactory.get("jobScopeJob")
                .start(jobStep())
                .build();
    }

    // Step
    @Bean
    public Step jobStep() {
        return stepBuilderFactory.get("jobStep")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
                        log.info("jobStep has execute");
                        return RepeatStatus.FINISHED;
                    }
                }).build();
    }

}

 

 

다중 Step 

@Slf4j
@Configuration
@RequiredArgsConstructor
@Primary
@EnableBatchProcessing
public class JobStepConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;


    // Job
    @Bean
    public Job jobScopeJob() {
        return jobBuilderFactory.get("jobScopeJob")
                .start(startStep())
                .next(jobScopeSuccessStep1())
                .next(jobScopeSuccessStep2())
                .build();
    }

    // Start Step
    @JobScope
    @Bean
    public Step startStep() {
        return stepBuilderFactory.get("startStep")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
                        log.info("startStep has execute");
                        return RepeatStatus.FINISHED;
                    }
                }).build();
    }
    
    // Step1
    @JobScope
    @Bean
    public Step jobScopeSuccessStep1() {
        return stepBuilderFactory.get("jobScopeSuccessStep1")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
                        log.info("jobScopeSuccessStep1 has execute");
                        return RepeatStatus.FINISHED;
                    }
                }).build();
    }

    //Step2
    @JobScope
    @Bean
    public Step jobScopeSuccessStep2() {
        return stepBuilderFactory.get("jobScopeSuccessStep2")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
                        log.info("jobScopeSuccessStep2 has execute");
                        return RepeatStatus.FINISHED;
                    }
                }).build();
    }
}

 

 

플로우를 통한 Step 구성하기

@Slf4j
@Configuration
@RequiredArgsConstructor
@Primary
@EnableBatchProcessing
public class JobStepConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
    private final Step jobStep; 


    // Job
    @Bean
    public Job jobScopeJob() {
        return jobBuilderFactory.get("jobScopeJob")
                .start(jobStep)
                   .on("COMPLETED") // jobStep가 COMPLETED 이면
                   .to(jobScopeSuccessStep()) // 해당 step 실행
                .from(jobStep) // jobStep 결과가 COMPLETED 가 아니면
                   .on("*") // 모든 경우 
                   .to(jobScopeFailStep()) // 해당 step 실행
                   .end() // 해당 플로우를 종료
                .build();
    }

    // Step1
    @JobScope
    @Bean
    public Step jobScopeSuccessStep() {
        return stepBuilderFactory.get("jobScopeSuccessStep")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
                        log.info("jobScopeSuccessStep has execute");
                        return RepeatStatus.FINISHED;
                    }
                }).build();
    }

    //Step2
    @JobScope
    @Bean
    public Step jobScopeFailStep() {
        return stepBuilderFactory.get("jobScopeFailStep")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
                        log.info("jobScopeFailStep has execute");
                        return RepeatStatus.FINISHED;
                    }
                }).build();
    }
}

 

 

 

반응형