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만 가능하다.
'Spring Batch' 카테고리의 다른 글
Spring Batch 메타 테이블 데이터 (0) | 2023.01.26 |
---|---|
Spring Batch 설정하기 (0) | 2023.01.26 |
Spring Batch 개념 (0) | 2023.01.25 |