Spring Batch 문법

Spring Batch 순차적으로 Step 시행하기

@Configuration
@RequiredArgsConstructor
@Slf4j
public class JobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job testJob() {
        return jobBuilderFactory.get("testJob")
                .start(step1())
                .next(step2()) // step1()이 정상 종료되면 step2()로 이동
                .next(step3()) // steo2()가 정상 종료되면 step3()로 이동
                .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                .tasklet(((contribution, context) -> {
                    log.info("*** step1 ***");
                    return RepeatStatus.FINISHED;
                })).build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                .tasklet(((contribution, context) -> {
                    log.info("*** step2 ***");
                    return RepeatStatus.FINISHED;
                })).build();
    }
    
    @Bean
    public Step step3() {
        return stepBuilderFactory.get("step3")
                .tasklet(((contribution, context) -> {
                    log.info("*** step3 ***");
                    return RepeatStatus.FINISHED;
                })).build();
    }
}

 

jobBuilderFactory.get()

JobBuilderFactory로 JobBuilder 객체를 생성하여 빌더를 반환한다.

 

 

start(step())

start() 메서드는 step() 메서드의 반환 값인 Step 정보를 Job의 첫 번째 Step으로 저장한다. (steps 컬렉션)

 

 

next(step())

steps 컬레션에 정보를 추가할 때 쓰인다. start() 메서드 같은 경우 첫 번째로 추가할 때 쓰고 그 이후는 next() 메서드를 이용하여 step 정보를 추가한다. 만약 전 Step 작업이 정상 종료되지 않은 경우 작업을 중단한다.

메서드 체이닝을 지원한다.

 

 


 

분기별로 Step 시행하기

방법1: 기본

@Configuration
@RequiredArgsConstructor
@Slf4j
public class JobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
	
    /**
     * step1 FAIL    : step1() -> step3()
     * step1 SUCCESS : step1() -> step2() -> step3()
     */
    @Bean
    public Job testJob() {
        return jobBuilderFactory.get("testJob")
                .start(step1())
                	.on("FAILED")  // steo1()이 FAILED인 경우
                    .to(step3())   // step3()로 이동
                    .on("*")       // step3() 결과 상관 없이
                    .end()         // step3()로 이동시 종료
                .from(step1())     // step1()의 결과가
                	.on("*")       // 모든 결과인 경우 (위에서 FAILED 조건있기 때문에 제외)
                    .to(step2())   // step2()로 이동
                    .next(step3()) // step2() 정상 종료되면 step3()로 이동
                    .on("*")       // step3() 결과 상관 없이
                    .end()         // step3()로 이동시 종료
                .end()             // JOB 종료
                .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                .tasklet(((contribution, context) -> {
                    log.info("*** step1 ***");
                    
                    /**
                     * contribution.setExitStatus(ExitStatus.FAILED);
                     */
                    
                    return RepeatStatus.FINISHED;
                })).build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                .tasklet(((contribution, context) -> {
                    log.info("*** step2 ***");
                    return RepeatStatus.FINISHED;
                })).build();
    }
    
    ...
}

on()

상태 값(ExitStatus)을 체크한다.

 

 

to() / from()

  • to()
    • 다음 작업할 Step
    • 상태 값이 일치하는 경우 to()에 지정된 Step으로 이동한다.
  • from()
    • 이벤트 리스터 역할을 한다.
    • step1() 메서드가 어떤 결과든  step2() 메서드로 이동한다. 위에서 step1() 메서드가 FAILED인 경우를 체크했으므로 논리상 FAILED인 경우를 제외한 나머지 조건인 경우란 의미일 것이다.

 

 

end()

조건 흐름 Builder를 반환하는 end()와 끝내는 end() 두 종류가 있다. 글로 이론적인 설명을 적는 것보다 위 예제를 보는 것이 이해가 더 쉬울 것이다.

 

 

contribution.setExitStatus(ExitStatus.FAILED)

  • 분기 처리를 위한 ExitStatus 값을 조정할 수 있다.

 

 

on()가 체크하는 상태값 (ExitStatus)

.on("FAILED")

위 코드에서 on() 메서드가 참조하는 상태값은 BatchStatus가 아니라 Step의 ExitStatus임에 주의해야 한다.

ExitStatus는 Step의 실행 후 상태이다. 즉 위 코드를 해석하면 'Step의 exitCode가 "FAILED"라면' 이라는 뜻이다.

BatchStatus는 Job 또는 Step의 실행 결과를 Spring에서 기록할 때 사용하는 Enum 클래스이다.

 

Spring Batch는 Step의 BatchStatus와 ExitStatus의 exitCode가 같도록 설정되어 있어서 간단한 분기 적용을 할 때는 이에 대한 고려를 하지 않아도 된다. 하지만 개발자가 자신의 커스텀한 exitCode를 생성해야 한다면 별도의 Listener를 생성해야 하고 Job Flow에 등록해야하여 번거로울 것이다. 때문에 이 방법보다는 아래 방법2를 사용하는 것을 권장한다.

 

 

 

방법2 : Step의 분기를 담당하는 JobExecutionDecider 활용 (추천)

Spring Batch에서는 Step 들의 분기만 담당하는 타입인 JobExecutionDecider를 제공한다.

 

@Configuration
@RequiredArgsConstructor
@Slf4j
public class DeciderJobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job deciderJob() {
        return jobBuilderFactory.get("deciderJob")
            .start(step1)
            .next(decider())    // random number 홀,짝 체크
            .from(decider())    // decider 결과가
                .on("Step2")    // Step2 라면
                .to(step2())    // Step2로 이동
            .from(decider())    // decider 결과가
                .on("Step3")    // Step3 라면
                .to(step3())    // Step3로 이동
            .end()
            .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("StartStep")
            .tasklet((contribution, chunkContext) -> {
                log.info("*** step1 ***");
                return RepeatStatus.FINISHED;
            })
            .build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("Step2")
            .tasklet((contribution, chunkContext) -> {
                log.info("*** Step2 ***");
                return RepeatStatus.FINISHED;
            })
            .build();
    }

    @Bean
    public Step step3() {
        return stepBuilderFactory.get("Step3")
            .tasklet((contribution, chunkContext) -> {
                log.info("*** Step3 ***");
                return RepeatStatus.FINISHED;
            })
            .build();
    }

    @Bean
    public JobExecutionDecider decider() {
        return new StepDecider();
    }

    public static class StepDecider implements JobExecutionDecider {
        @Override
        public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
            
            int randomNumber = new Random().nextInt(99) + 1;
            if(randomNumber % 2 == 0) {
                return new FlowExecutionStatus("Step2");
            } else {
                return new FlowExecutionStatus("Step3");
            }
        }
    }

}
  • from()
    • 이벤트 리스너 역할을 한다.
    • decider의 결과를 보고 일치하는 상태(on()라면 다음 작업으로 이동(to()) 한다.

 

 


 

파라미터 적용하기 : JobParameter

Spring Batch에서는 파라미터를 받아 Batch 컴포넌트에서 사용할 수 있게 Job Parameter를 지원한다.

 

@RequiredArgsConstructor
@Slf4j
@Configuration
public class TestJobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job testJob() {
		return jobBuilderFactory.get("testJob")
        		.start(testStep1(null))
        		.build();
    }

    @Bean
    @JobScope
    public Step testStep1(@Value("#{jobParameters[requestParam]}") String requestParam) {
        return stepBuilderFactory.get("testStep")
            	.tasklet((contribution, chunkContext) -> {
                
                	log.info("Parameter: {}", requestParam);
                    
                	return RepeatStatus.FINISHED;
            	}).build();
    }
    
    @Bean
    public Step testStep2() {
    	return stepBuilderFactory.get("testStep")
            	.tasklet(testStep2Tasklet(null))
                .build();
    }
    
    @Bean
    @StepScope
    public Tasklet scopeStep2Tasklet(@Value("#{jobParameters[requestParam]}") String requestParam) {
    	return (contribution, chunkContext) -> {
        	
            log.info("Parameter: {}", requestParam);
            
            return RepeatStatus.FINISHED;
        };
    }
}

 

Scope 선언

Job Parameter를 사용하기 위해선 Spring Batch 전용 Scope를 선언해야 한다.

Spring Batch 전용 Scope : @JobScope, @StepScope

 

  • @JobScope
    • Step 선언문에 적용하는 어노테이션
  • @StepScope
    • Tasklet, ItemReader, ItemWriter, ItemProcessor에 적용하는 어노테이션

 

  • @Value("#{jobParameters[파라미터명]}") 
    • Job Parameter의 타입은 String, Long, Double, Date만 가능하다.