반응형
StepBuilderFactory
1. StepBuilderFactory
- StepBuilder 를 생성하는 팩토리 클래스로서 get(String name) 메서드 제공
- StepBuilderFactory.get(“stepName")
- “stepName” 으로 Step 을 생성
2. StepBuilder
- Step을 구성하는 설정 조건에 따라 다섯 개의 하위 빌더 클래스를 생성하고 실제 Step 생성을 위임한다
- TaskletStepBuilder
- TaskletStep 을 생성하는 기본 빌더 클래스
- SimpleStepBuilder
- TaskletStep 을 생성하며 내부적으로 청크기반의 작업을 처리하는 ChunkOrientedTasklet 클래스를 생성한다
- PartitionStepBuilder
- PartitionStep 을 생성하며 멀티 스레드 방식으로 Job 을 실행
- JobStepBuilder
- JobStep 을 생성하여 Step 안에서 Job 을 실행한다
- FlowStepBuilder
- FlowStep 을 생성하여 Step 안에서 Flow 를 실행한다
3.SetpBuilder의 하위 Step의 생성 흐름도
- tasklet()은 이전 예외에서 익명 객체를 만들어서 계속 사용해 왔다.
- Tasklet이 사용되는 경우는 TaskleteStepBuildir가 사용된다.
- 2번째로 chunk(chunkSize)와 chunk(competePolicy)를 사용해서 simplerStepBuilder 클래스를 생성한다. 둘 다 TaskleteStepBuilder를 생성하며각자 다른 StepBuilder를 생성한다고 이해하자
- 세 번째로 Partiioner(StepName, Partioner)와 Patitioner(Step)을 이용해서 PartitionStepBuilder를 생성한다.
- 네 번째로 flow(flow)를 사용해서 FlowStepBuilder를 생성한다.
4.StepBuilderFactory의 클래스 구조
- JobBuilderFactory와 유사한 구조로 이루어져있다.
- StepBuilder는 StepBuilderHelper를 상속한다.
- CoomonStepProperties에는 공통으로 사용하는 정보가 저장되어있다.
- StepBuilderFactory는 StepBuilderHelper를 참조하고 있다.
- StepBuilderHelper는 자신의 메타데이터 정보를 StepJobRepository에 저장한다.
- AbstractTaskletStepBuilder는 StepBuilderHelper를 참조한다.
- 맨 밑에 위의 5개의 Builder(TaskletStepBuilder, SimplerStepBuilder, PartitionStepBuilder, JobStepBuilder, FlowStepBuilder)는 각각의 Step(TaskletStep, PartionStep, JobStep, FlowStep)을 생성한다.
예제 코드
- 주석의 각각의 StepBuilder가 어떤 경우에 사용되는지만 확인하면 된다.
package com.example.springbatch_4_1_batchinitconfiguration;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.builder.FlowBuilder;
import org.springframework.batch.core.job.flow.Flow;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.*;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* packageName : com.example.springbatch_4_1_batchinitconfiguration
* fileName : StepBuilderconfiguration
* author : namhyeop
* date : 2022/07/25
* description :
* StepBuilder Factory와 관련된 예제를 진행해보는 코드이다.
* 개념에서는 StepBuilderFactory에 5개의 종류가 있다고 설명하였다. 5개의 StepBuilderFactory를 걸치는 과정을 확인해보자
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
@Configuration
@RequiredArgsConstructor
public class StepBuilderConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job batchJob(){
return this.jobBuilderFactory.get("batchJob")
.incrementer(new RunIdIncrementer())
.start(step1())
.next(step2())
// .next(step3())
.next(step4())
.next(step5())
.build();
}
//tasklket -> TaskletStepBuilder
private Step step1() {
return stepBuilderFactory.get("step1")
.tasklet((contribution, chunkContext) -> {
System.out.println("step1 has executed");
return RepeatStatus.FINISHED;
})
.build();
}
//chunk -> SimpleStepBuilder 사용
@Bean
public Step step2(){
return stepBuilderFactory.get("step2")
.<String, String>chunk(3)
//데이터를 읽는 영역
.reader(new ItemReader<String>() {
@Override
public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
return null;
}
})
//데이터 로직을 처리하는 영역
.processor(new ItemProcessor<String, String>() {
@Override
public String process(String s) throws Exception {
return null;
}
})
//로직을 처리한 데이터를 쓰는 영역
.writer(list->{})
.build();
}
//partiioner -> PartitionStepBuilder 반환
@Bean
public Step step3(){
return stepBuilderFactory.get("step3")
.partitioner(step1())
.gridSize(2)
.build();
}
//job -> JobStepBuilder 반환
@Bean
public Step step4(){
return stepBuilderFactory.get("step4")
.job(job())
.build();
}
//flow -> FlowStepBuilder 반환
@Bean
public Step step5(){
return stepBuilderFactory.get("step5")
.flow(flow())
.build();
}
@Bean
public Job job(){
return this.jobBuilderFactory.get("job")
.start(step1())
.next(step2())
// .next(step3())
.build();
}
@Bean
public Flow flow(){
FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow");
flowBuilder.start(step2()).end();
return flowBuilder.build();
}
}
TaskletStep-개념 및 API 소개
1. 기본개념
- 스프링 배치에서 제공하는 Step 의 구현체로서 Tasklet 을 실행시키는 도메인 객체
- RepeatTemplate 를 사용해서 Tasklet의 구문을 트랜잭션 경계 내에서 반복해서 실행함
- 쉽게 말해서 실행할 로직을 담을 수 있다는 의미이다. (Tasklet안에 로직 작성했던것을 기억해내자)
- 또한 Transaction 경계내에서 작업이 이루어지므로 별도의 Transaction을 선언해주지 않아도 된다.
- Task 기반과 Chunk 기반으로 나누어서 Tasklet 을 실행함
- 이전에 만들었던 사용자 정의용 Task와 Chunk로 나누어진다.
2. Task vs Chunk 기반 비교
- 스프링 배치에서 Step의 실행 단위는 크게 2가지로 나누어짐 → Chunk, Task
- chunk 기반
- 하나의 큰 덩어리를 n개씩 나눠서 실행한다는 의미로 대량 처리를 하는 경우 효과적으로 설계 됨
- ItemReader, ItemProcessor, ItemWriter 를 사용하며 청크 기반 전용 Tasklet 인 ChunkOrientedTasklet 구현체가 제공된다
- Task 기반
- ItemReader 와 ItemWriter 와 같은 청크 기반의 작업 보다 단일 작업 기반으로 처리되는 것이 더 효율적인 경우
- 주로 Tasklet 구현체를 만들어 사용
- 대량 처리를 하는 경우 chunk 기반에 비해 더 복잡한 구현 필요
💡 대용량 처리를 한다면 chunk를, 단순한 단일 작업 처리라면 Task를 사용하자
3.TaskletStepBuilder의 메소드
4.이번에 살펴볼 TaskletStepBuilder의 부분을 그림으로 확인하자
예제코드
package com.example.springbatch_4_1_batchinitconfiguration;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
/**
* packageName : com.example.springbatch_4_1_batchinitconfiguration
* fileName : TaskletStepConfiguration
* author : namhyeop
* date : 2022/07/25
* description :
* Task, Chunk 타입 두 가지 비교테스트를 작성한다.
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
@Configuration
@RequiredArgsConstructor
public class TaskletStepConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job batchJob(){
return this.jobBuilderFactory.get("batchJob")
.start(taskStep())
.build();
}
@Bean
public Step taskStep(){
return stepBuilderFactory.get("taskStep")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("step was executed");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step chunkStep(){
return stepBuilderFactory.get("chunkStepNamePosition")
.<String, String>chunk(10)
.reader(new ListItemReader<>(Arrays.asList("item1","item2","item3","item4","item5")))
.processor(new ItemProcessor<String, String>() {
@Override
public String process(String item) throws Exception {
return item.toUpperCase();
}
})
.writer(new ItemWriter<String>() {
@Override
public void write(List<? extends String> items) throws Exception {
items.forEach(item -> System.out.println(items));
}
}).build();
}
}
- Task와 Chunk 두 가지 비교테스트를 작성해보았다.
- 각각의 멀티스레드 또는 단일 스레드 상황에 따라 알맞게 chunk와 Task를 사용하자
TaskletStep-tasklet()
1. 기본개념
- Tasklet 타입의 클래스를 설정한다
- Tasklet
- Step 내에서 구성되고 실행되는 도메인 객체로서 주로 단일 태스크를 수행하기위한 것
- TaskletStep 에 의해 반복적으로 수행되며 반환값에 따라 계속 수행 혹은 종료한다
- RepeatStatus - Tasklet 의 반복 여부 상태 값
- RepeatStatus.FINISHED - Tasklet 종료, RepeatStatus 을 null 로 반환하면 RepeatStatus.FINISHED로 해석됨
- RepeatStatus.CONTINUABLE - Tasklet 반복
- RepeatStatus.FINISHED가 리턴되거나 실패 예외가 던져지기 전까지 TaskletStep 에 의해 while 문 안에서 반복적으로 호출됨 (무한루프 주의)
- Tasklet
- 익명 클래스 혹은 구현 클래스를 만들어서 사용한다
- 이 메소드를 실행하게 되면 TaskletStepBuilder 가 반환되어 관련 API 를 설정할 수 있다.
- Step에 오직 하나의 Tasklet 설정이 가능하며 두 개 이상을 설정 했을 경우 마지막에 설정한 객체가 실행된다
2.구조
3.Tasklet API 예제
예제 코드
package com.example.SpringBatchTasklet;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* packageName : com.example.springbatch_4_1_batchinitconfiguration
* fileName : Taskletconfiguration
* author : namhyeop
* date : 2022/07/25
* description :
* Tasklet 테스트 파일
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
@Configuration
@RequiredArgsConstructor
public class TaskletConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job BatchJob(){
return this.jobBuilderFactory.get("batchJob")
.incrementer(new RunIdIncrementer())
.start(step1())
.next(step2())
.build();
}
private Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step step2(){
return stepBuilderFactory.get("step2")
.tasklet(new CustomTasklet())
.build();
}
}
package com.example.SpringBatchTasklet;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
/**
* packageName : com.example.springbatch_4_1_batchinitconfiguration
* fileName : CustomTasklet
* author : namhyeop
* date : 2022/07/25
* description :
* Tasklet Custom 동작파일
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
public class CustomTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String stepName = contribution.getStepExecution().getStepName();
String jobName = chunkContext.getStepContext().getJobName();
System.out.println("stepName = " + stepName);
System.out.println("jobName = " + jobName);
return RepeatStatus.FINISHED;
}
}
- Tasklet에 사용자 정의 Tasklet을 동작하는 예제이다.
TaskletStep-startLimit()/allowStartIfComplete()
Start
1.기본개념
- Step의 실행 횟수를 조정할 수 있다
- Step 마다 설정할 수 있다
- 설정 값을 초과해서 다시 실행하려고 하면 StartLimitExceededException이 발생
- start-limit의 디폴트 값은 Integer.MAX_VALUE
2.API
allowStartIfComplte()
1. 기본개념
- 재시작 가능한 job 에서 Step의 이전 성공 여부와 상관없이 항상 step 을 실행하기 위한 설정
- Job은 성공이나 실패를 할 수 있다.
- 성공할 경우 작업은 다시 실행되지 않는다.
- Step도 성공이나 실패 둘 중 한 가지이다.
- Job과 마차간지로 성공이나 실패를 할 수 있다.
- 이런 두 작업을 성공과 실패 상관없이 매번 반복적으로 실행할 때 사용하는게 allowStartIfComplte이다.
- Job은 성공이나 실패를 할 수 있다.
- 실행 마다 유효성을 검증하는 Step이나 사전 작업이 꼭 필요한 Step에 사용한다.
- 정리하면 기본적으로 COMPLETED 상태를 가진 Step은 Job 재시작시 실행하지 않고 스킵한다
- 그러나 allowStartifCcomplete가 “true”로 설정된 step은 항상 실행한다
2.흐름도
- Job에서 Step이 실행되고 해당 Step마다 먼저 작업이 성공했는지 아닌지 구분한다.
- 작업이 성공되지 않은 작업이라면 Tasklet이 실행되고 작업이 성공했다면 allowStartComplete 옵션을 확인한다.
- allowStartComplete 옵션이 YES일 경우에는 해당 작업이 한 번 더 실행되고 그렇지 않다면 NextStep을 통해 다음 Step으로 넘어간다.
3.allowStartInfComplete API 예제
- 위처럼 성공할 경우 성공한 Step도 반복적으로 수행해준다.
예제코드
package com.example.SpringBatch_5_4_Step_startLimitAndAllowStartIfComplte;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* packageName : com.example.SpringBatch_5_4_Step_startLimitAndAllowStartIfComplte
* fileName : Limit_AllowConfiguration
* author : namhyeop
* date : 2022/07/25
* description :
* allowStartIfComplete와 startLimite를 쓴 복합 예제 코드
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
@RequiredArgsConstructor
@Configuration
public class Limit_AllowConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job batchJob(){
return this.jobBuilderFactory.get("batchJob")
.start(step1())
.next(step2())
.build();
}
private Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("stepContribution = " + contribution + ", chunkContext = " + chunkContext);
return RepeatStatus.FINISHED;
}
})
.allowStartIfComplete(true)
.build();
}
private Step step2(){
return stepBuilderFactory.get("step2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("stepContribution = " + contribution + ", chunkContext = " + chunkContext);
throw new RuntimeException("");
// return RepeatStatus.FINISHED;
}
})
.startLimit(3)
.build();
}
}
- 테스트 스토리
- Step1에 allowStatrtIfComplter를 주석처리하고 작업을 돌리는 경우
- 위 상태에서 Step2에 startLimit를 주석처리하고 작업을 돌렸을 경우.
- 위 상태에서 Step2에 startLimit를 주석처리 안하고 돌렸을 경우
- Step1에 allowStatrtIfComplter를 주석처리 안하고 작업을 돌리는 경우
- 위 상태에서 Step2에 startLimit를 주석처리하고 작업을 돌렸을 경우.
- 위 상태에서 Step2에 startLimit를 주석처리 안하고 돌렸을 경우
- Step1에 allowStatrtIfComplter를 주석처리하고 작업을 돌리는 경우
💡 startLImit는 말 그대로 작업에 대한 총 반복횟수이기 때문에 DB에 저장된 StepExcution 작업을 기준으로 한다. 나는 Step안에서 돌아가는 작업의 반혹횟수로 착각하여 잘못이해하는 바람에 시간을 낭비했다.
💡 allowStartIfComplter를 안하고 동작할 경우 다음 Step 자체가 동작하지 않기 때문에 이 부분을 기억하고 예제 테스트를 진행하자. 나중에 이 기록에 다시 한번 작업을 확인하는데 도움을 줄것이다.
TaskletStep 아키텍쳐
- 이 쳅터에서는 TaskletStep 아키텍쳐에 대해서 설명한다.
1.Step의 로직 순서
- 먼저 Job이 실행되고 Step이 실행된다.
- 이후 RepeatTemplate의 의해서 Tasklet이 반복적으로 수행된다.
- Tasklet의 비즈니스 로직이 수행되면서 예외가 발생할 경우에는 반복이 종료되고 Step 자체가 종료된다.
- 그렇지 Exception 없이 정상적으로 수행이 되었다면 RepeatStatus의 상태에 따라 다음 수행이 결정된다.
- RepeatStatus.FINISHED인 경우에는 반복이 종료 다음 Step으로 이동된다.
- RepeatStatus.CONTINUABLE인 경우에는 RepeatTEmplate으로 이동해서 Tasklet이 반복적으로 수행된다.
2.TaskletStep의 로직 순서
- TaskletStep의 Job과 관계에서 ExcutionContenxt에 대한 메타데이터가 생성된다.
- 이때 배치상태에 대한 설정이 이루어진다.
- 이후 TaskletStep과 RepecatTemplate과 관계에서 StepListener 을 호출하게 된다.
- 이후 RepeatTemplate과 Tasklet 상태에서 Repeat 설정에 따라 반복적으로 수행될지 아니면 반환할지 결정한다.
- 반환되면서 위와 같은 로직순서를 한번더 실행한다.
3.클래스 상속 관계도(현재 TaskletStepBuilder를 학습하고 있다.)
예제 코드
package com.example.SpringBatch_5_5_Step_TaskletStepArchitecture;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.job.DefaultJobParametersExtractor;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* packageName : com.example.SpringBatch_5_5_Step_TaskletStepArchitecture
* fileName : TaskletStepArchitectureCongiguration
* author : namhyeop
* date : 2022/07/25
* description :
* TaskletArchitecture에 대한 흐름을 파악하는 예제
* 디버깅하면서 쳅터에서 배운 개념들의 클래스를 확인해보자
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
@RequiredArgsConstructor
@Configuration
public class TaskletStepArchitectureCongiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job batchJob() {
return this.jobBuilderFactory.get("batchJob")
.start(jobStep(null))
.next(step2())
.build();
}
@Bean
public Step jobStep(JobLauncher jobLauncher){
return stepBuilderFactory.get("step1")
.job(childJob())
.launcher(jobLauncher)
.listener(new StepExecutionListener() {
@Override
public void beforeStep(StepExecution stepExecution) {
stepExecution.getExecutionContext().putString("name", "user1");
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null; // null == RepeatStatus.Finished
}
})
.parametersExtractor(jobParamtersExtractor())
.build();
}
@Bean
public Job childJob(){
return this.jobBuilderFactory.get("childJob")
.start(step1())
.build();
}
private Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
// throw new RuntimeException("step1 was failed");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step step2(){
return stepBuilderFactory.get("step2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("step2 was failed");
throw new RuntimeException("");
// return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public DefaultJobParametersExtractor jobParamtersExtractor() {
DefaultJobParametersExtractor extractor = new DefaultJobParametersExtractor();
extractor.setKeys(new String[]{"name"});
return extractor;
}
}
- 학습한 개념들의 클래스를 디버깅하면서 확인해보자
JobStep
1. 기본개념
- Job 에 속하는 Step 중 외부의 Job 을 포함하고 있는 Step
- 외부의 Job 이 실패하면 해당 Step 이 실패하므로 결국 최종 기본 Job도 실패한다
- 모든 메타데이터는 기본 Job과 외부 Job 별로 각각 저장된다.
- 커다란 시스템을 작은 모듈로 쪼개고 job의 흐름를 관리하고자 할 때 사용할 수 있다
2.API 소개
3.예제 코드의 설계 분석
- ParentJob에서 JobStepd을 먼저 실행한다. 이후 JobStep의 JobLauncher에서 childJob()을 실행한다.
- 이후 childJob의 Step1이 실행되고 Step1의 Tasklet 로직이 수행된다.
- 이때 childJob이 실패할 경우 ParentJob으로 돌아가게 되고 성공할 경우 다음 Step인 Step2를 수행한다.
4.이번에 살펴볼 JobStepBuilder의 부분을 그림으로 확인하자
5.클래스 상속 관계도(현재 JobStepBuilder를 학습하고 있다.)
예제 코드
package com.example.SpringBatch_5_6_Step_JobStep;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.job.DefaultJobParametersExtractor;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* packageName : com.example.SpringBatch_5_5_Step_TaskletStepArchitecture
* fileName : TaskletStepArchitectureCongiguration
* author : namhyeop
* date : 2022/07/25
* description :
* JobStep에 대한 흐름을 파악하는 예제
* 디버깅하면서 쳅터에서 배운 개념들의 클래스를 확인해보자
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022/07/25 namhyeop 최초 생성
*/
@RequiredArgsConstructor
@Configuration
public class JobStepConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job parentJob() {
return this.jobBuilderFactory.get("parentJob")
.start(jobStep(null))
.next(step2())
.build();
}
@Bean
public Step jobStep(JobLauncher jobLauncher) {
return this.stepBuilderFactory.get("jobStep")
.job(childJob())
.launcher(jobLauncher)
.listener(new StepExecutionListener() {
//name이란 키값에 value라는 값이 user1이라는 의미이다. parametersExtractor를 이용해 추출할 수 있다.
//name=user1 매개변수를 설정했다고 생각하면 이해하기 쉽다.
@Override
public void beforeStep(StepExecution stepExecution) {
stepExecution.getExecutionContext().putString("name", "user1");
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
})
.parametersExtractor(jobParametersExtractor())
.build();
}
@Bean
public Job childJob() {
return this.jobBuilderFactory.get("childJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
//(1) throw new RuntimeException("step1 was failed");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
//(2) throw new RuntimeException("step2 was failed");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public DefaultJobParametersExtractor jobParametersExtractor() {
DefaultJobParametersExtractor extractor = new DefaultJobParametersExtractor();
extractor.setKeys(new String[]{"name"});
return extractor;
}
}
실행옵션(paramter 자리에 줘야함. Active profiles에 줘서 뻘짓함)
- 먼저 1번의 주석을 해제하고 수행해보자
- 이럴 경우 1번 step1에서 예외처리가 발생하기 때문에 ParentJob Execution은 Faild 상태가 된다. 또한 step2 는 실행되지 않는다.
Job_execution_table
Job_execution_params
- childJob에서 실행할때 설정한 파라미터가 하나 더 추가된것을 확인할 수 있다.
Batch_Step_execution
- 다음으로 1번을 주석처리하고 2번을 주석해제하고 수행해보자
- 이럴 경우 step2에서 예외처리가 발생하기 때문에 ParentJob Execution은 Faild 상태가 된다. Step2는 수행되며 ChildJob 처리 자체는 성공적이므로 ChildJob의 Execution은 성공처리된다. 그러나 Step2가 실패하였으므로 ParentJob_Execution은 실패처리된다.
Job_execution_table
Job_execution_params
Batch_Step_execution
반응형
'Spring Batch' 카테고리의 다른 글
7.Spring Batch의 Chunk와 동작원리 살펴보기 (0) | 2024.11.08 |
---|---|
6.Spring Batch의 Flow (1) | 2024.11.07 |
4.Spring Batch의 Job (0) | 2024.11.06 |
3.Spring Batch 도메인 이해하기 (0) | 2024.11.02 |
2.Spring Batch 시작하기 (0) | 2024.11.02 |