Open 0JUUU opened 2 years ago
Step을 순차적으로 구성하는 것이 아닌 특정한 상태에 따라 흐름을 전환하도록 구성 ➡️ FlowJobBuilder에 의해 생성
Flow : 흐름 정의하는 역할 (e.g. start(), from(), next())
Transition : 조건에 따라 흐름을 전환시키는 역할 (e.g. on(), to(), stop(), fail(), end(), stopAndRestart())
@Bean
public Job batchJob() {
return jobBuilderFactory.get("batchJob")
.start(step1())
.on("COMPLETED").to(step2())
.from(step1())
.on("FAILED").to(step3())
.end()
.build();
}
start
/ next
: on
,from
,... 등과는 다르게 Step 중 하나라도 실패할 경우 전체 Job이 실패하게 된다.@RequiredArgsConstructor
@Configuration
public class TransitionConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job batchJob() {
return jobBuilderFactory.get("batchJob")
.start(step1())
.next(step2())
.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println(">> step1 has executed");
return RepeatStatus.FINISHED;
}
}).build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step2 has executed");
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}).build();
}
}
on(String Pattern)
호출 ➡️ TransitionBuilder 반환 ➡️ Transition Flow 구성 가능 @Bean
public Job batchJob() {
return this.jobBuilderFactory.get("batchJob")
.start(step1())
.on("FAILED")
.to(step2())
.on("FAILED")
.stop()
.from(step1())
.on("*")
.to(step3())
.next(step4())
.from(step2())
.on("*")
.to(step5())
.end()
.build();
}
// step1()
contribution.setExitStatus(ExitStatus.FAILED);
afterStep()
메서드 : Custom exitCode 생성
후 새로운 ExitStatus 반환JobExecutionDecider
를 사용하는게 더 좋다❗️ @Bean
public Job batchJob() {
return jobBuilderFactory.get("batchJob")
.start(step1())
.on("FAILED")
.to(step2())
.on("PASS")
.stop()
.end()
.build();
}
.from(step2())
.on("*")
.stop()
public class PassCheckingListener implements StepExecutionListener {
@Override
public void beforeStep(StepExecution stepExecution) {
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
String exitCode = stepExecution.getExitStatus().getExitCode();
if (!exitCode.equals(ExitStatus.FAILED.getExitCode())) {
return new ExitStatus("PASS");
}
return null;
}
}
public class CustomDecider implements JobExecutionDecider {
private int count = 0;
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
count++;
System.out.println(">> count " + count);
if(count % 2 == 0) {
return new FlowExecutionStatus("EVEN");
}
return new FlowExecutionStatus("ODD");
}
}
private int count = 1;
@RequiredArgsConstructor
@Configuration
public class SimpleFlowConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return jobBuilderFactory.get("batchJob")
.start(flow1())
.on("COMPLETED")
.to(flow2())
.from(flow1())
.on("FAILED")
.to(flow3())
.end()
.build();
}
@Bean
public Flow flow1() {
FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow1");
flowBuilder.start(step1())
.next(step2())
.end();
return flowBuilder.build();
}
@Bean
public Flow flow2() {
FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow2");
flowBuilder.start(flow3())
.next(step5())
.next(step6())
.end();
return flowBuilder.build();
}
@Bean
public Flow flow3() {
FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow3");
flowBuilder.start(step3())
.next(step4())
.end();
return flowBuilder.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step1 has executed");
// throw new RuntimeException("step1 was failed");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step2 has executed");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step step3() {
return stepBuilderFactory.get("step3")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step3 has executed");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step step4() {
return stepBuilderFactory.get("step4")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step4 has executed");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step step5() {
return stepBuilderFactory.get("step5")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step5 has executed");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step step6() {
return stepBuilderFactory.get("step6")
.tasklet((contribution, chunkContext) -> {
System.out.println(">> step6 has executed");
return RepeatStatus.FINISHED;
}).build();
}
}
JobStep
)@RequiredArgsConstructor
@Configuration
public class FlowStepConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return jobBuilderFactory.get("batchJob")
.start(flowStep())
.next(step2())
.build();
}
private Step flowStep() {
return stepBuilderFactory.get("flowStep")
.flow(flow())
.build();
}
private Flow flow() {
FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow");
flowBuilder.start(step1())
.end();
return flowBuilder.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("step1 was executed");
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 executed");
return RepeatStatus.FINISHED;
}
})
.build();
}
}
@Scope(value = "job", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Value("#{jobParameters[파라미터명]}")
, @Value("#{jobExecutionContext[파라미터명]}")
, @Value("#{stepExecutionContext[파라미터명]}")
public class CustomJobListener implements JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
jobExecution.getExecutionContext().putString("name", "user1");
}
@Override
public void afterJob(JobExecution jobExecution) {
}
}
public class CustomStepExecutionListener implements StepExecutionListener {
@Override
public void beforeStep(StepExecution stepExecution) {
stepExecution.getExecutionContext().putString("name2", "user2");
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
}
@RequiredArgsConstructor
@Configuration
public class JobScope_StepScope_Configuration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return jobBuilderFactory.get("batchJob")
.start(step1(null))
.next(step2())
.listener(new CustomJobListener())
.build();
}
@Bean
@JobScope
public Step step1(@Value("#{jobParameters['message']}") String message) {
System.out.println("jobParameters['message'] : " + message);
return stepBuilderFactory.get("step1")
.tasklet(tasklet1(null))
.build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2")
.tasklet(tasklet2(null))
.listener(new CustomStepExecutionListener())
.build();
}
@Bean
@StepScope
public Tasklet tasklet1(@Value("#{jobExecutionContext['name']}") String name) {
System.out.println(">> jobExecutionContext['name'] = " + name);
return (stepContribution, chunkContext) -> {
System.out.println(">> tasklet1 has executed");
return RepeatStatus.FINISHED;
};
}
@Bean
@StepScope
public Tasklet tasklet2(@Value("#{stepExecutionContext['name2']}") String name2) {
System.out.println(">> stepExecutionContext['name2'] = " + name2);
return (stepContribution, chunkContext) -> {
System.out.println(">> tasklet2 has executed");
return RepeatStatus.FINISHED;
};
}
}