ExecutionContext 개념
https://devfunny.tistory.com/485
실습해보기
- ExecutionContextConfiguration.java
- ExecutionContextTasklet1.java
- ExecutionContextTasklet2.java
- ExecutionContextTasklet3.java
- ExecutionContextTasklet4java
ExecutionContextConfiguration.java
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
--job.name=executionContextTestJob
*/
@Configuration
@RequiredArgsConstructor
public class ExecutionContextConfiguration {
// job 생성
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
/* tasklet */
private final ExecutionContextTasklet1 executionContextTasklet1;
private final ExecutionContextTasklet2 executionContextTasklet2;
private final ExecutionContextTasklet3 executionContextTasklet3;
private final ExecutionContextTasklet4 executionContextTasklet4;
@Bean
public Job executionContextTestJob() {
return this.jobBuilderFactory.get("executionContextTestJob")
.start(executionContextTestStep1())
.next(executionContextTestStep2())
.next(executionContextTestStep3())
.next(executionContextTestStep4())
.build();
}
@Bean
public Step executionContextTestStep1() {
return stepBuilderFactory.get("executionContextTestStep1")
.tasklet(executionContextTasklet1)
.build();
}
@Bean
public Step executionContextTestStep2() {
return stepBuilderFactory.get("executionContextTestStep2")
.tasklet(executionContextTasklet2)
.build();
}
@Bean
public Step executionContextTestStep3() {
return stepBuilderFactory.get("executionContextTestStep3")
.tasklet(executionContextTasklet3)
.build();
}
@Bean
public Step executionContextTestStep4() {
return stepBuilderFactory.get("executionContextTestStep4")
.tasklet(executionContextTasklet4)
.build();
}
}
ExecutionContextTasklet1.java
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.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Component
public class ExecutionContextTasklet1 implements Tasklet {
/**
* 비즈니스 로직 구현
* @param stepContribution
* @param chunkContext
* @return
* @throws Exception
*/
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step1 was executed");
/* ExecutionContext */
ExecutionContext jobExecutionContext = stepContribution.getStepExecution()
.getJobExecution().getExecutionContext();
ExecutionContext stepExecutionContext = stepContribution.getStepExecution()
.getExecutionContext();
/* jobName */
String jobName = chunkContext.getStepContext().getStepExecution()
.getJobExecution().getJobInstance().getJobName();
/* stepName */
String stepName = chunkContext.getStepContext().getStepExecution()
.getStepName();
// jobExecutionContext 안에 jobName 을 key 로 하는 데이터를 저장한 경우가 없을때
// job 최초 실행시 put 이 될것이다.
if (jobExecutionContext.get("jobName") == null) {
jobExecutionContext.put("jobName", jobName);
}
// job 최초 실행시 put 이 될것이다.
if (stepExecutionContext.get("stepName") == null) {
stepExecutionContext.put("stepName", stepName);
}
System.out.println("jobName = " + jobExecutionContext.get("jobName"));
System.out.println("stepName = " + stepExecutionContext.get("stepName"));
return RepeatStatus.FINISHED;
}
}
1) jobExecutionContext
ExecutionContext jobExecutionContext = stepContribution
.getStepExecution()
.getJobExecution()
.getExecutionContext();
2) stepExecutionContext
ExecutionContext stepExecutionContext = stepContribution
.getStepExecution()
.getExecutionContext();
3) jobName 가져오기
/* jobName */
String jobName = chunkContext
.getStepContext()
.getStepExecution()
.getJobExecution()
.getJobInstance()
.getJobName();
4) stepName 가져오기
/* stepName */
String stepName = chunkContext
.getStepContext()
.getStepExecution()
.getStepName();
5) jobExecutionContext에 데이터 저장
jobExecutionContext에 "jobName"을 key로 하는 데이터를 저장한 경우가 없을때 데이터를 put() 하자.
if (jobExecutionContext.get("jobName") == null) {
jobExecutionContext.put("jobName", jobName);
}
6) stepExecutionContext에 데이터 저장
stepExecutionContext에 "stepName"을 key로 하는 데이터를 저장한 경우가 없을때 데이터를 put() 하자.
if (stepExecutionContext.get("stepName") == null) {
stepExecutionContext.put("stepName", stepName);
}
7) 5)~6) 결과 로그 출력
System.out.println("jobName = " + jobExecutionContext.get("jobName"));
System.out.println("stepName = " + stepExecutionContext.get("stepName"));
ExecutionContextTasklet2.java
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.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Component
public class ExecutionContextTasklet2 implements Tasklet {
/**
* 비즈니스 로직 구현
* @param stepContribution
* @param chunkContext
* @return
* @throws Exception
*/
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step2 was executed");
/* ExecutionContext */
ExecutionContext jobExecutionContext = stepContribution.getStepExecution()
.getJobExecution().getExecutionContext();
ExecutionContext stepExecutionContext = stepContribution.getStepExecution()
.getExecutionContext();
/* Tasklet1 에서 설정한 ExecutionContext 확인 */
System.out.println("jobName : " + jobExecutionContext.get("jobName"));
System.out.println("stepName : " + stepExecutionContext.get("stepName"));
/* stepName */
String stepName = chunkContext.getStepContext().getStepExecution()
.getStepName();
// step 끼리 공유가 안되므로 null 일 것이다.
if (stepExecutionContext.get("stepName") == null) {
stepExecutionContext.put("stepName", stepName);
}
return RepeatStatus.FINISHED;
}
}
1) ExecutionContextTasklet1 에서 저장한 데이터를 꺼내보자
- JobExecutionContext은 Step 끼리 공유할 수 있다.
- StepExecutionContext은 Step 끼리 공유할 수 없다. Job 끼리만 공유가 가능하다.
System.out.println("jobName : " + jobExecutionContext.get("jobName"));
System.out.println("stepName : " + stepExecutionContext.get("stepName")); // null
결과
jobName = executionContextTestJob
stepName = executionContextTestStep1
2) 다시 "stepName"을 key로 데이터를 저장하자.
위 1)번에서 step 끼리는 공유가 안되므로 stepExecutionContext.get("stepName")은 null이다.
/* stepName */
String stepName = chunkContext.getStepContext().getStepExecution()
.getStepName();
// step 끼리 공유가 안되므로 null 일 것이다.
if (stepExecutionContext.get("stepName") == null) {
stepExecutionContext.put("stepName", stepName);
}
결과
jobName : executionContextTestJob
stepName : null
ExecutionContextTasklet3.java
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;
import org.springframework.stereotype.Component;
@Component
public class ExecutionContextTasklet3 implements Tasklet {
/**
* 비즈니스 로직 구현
* @param stepContribution
* @param chunkContext
* @return
* @throws Exception
*/
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step3 was executed");
// 최초일 경우, 저장한적 없으므로 null
Object name = chunkContext.getStepContext().getStepExecution()
.getJobExecution().getExecutionContext().get("name");
if (name == null) {
chunkContext.getStepContext().getStepExecution()
.getJobExecution().getExecutionContext().put("name", "user1");
throw new RuntimeException("step3 was failed"); // 예외 발생시키기
}
return RepeatStatus.FINISHED;
}
}
1) 최초 실행의 경우 name 은 null이다.
Object name = chunkContext.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext()
.get("name");
2) 최초 실행의 경우, 에러를 고의로 발생시킨다.
여기서 "name"을 key로 "user1"을 value로 데이터를 저장한 후에 예외를 발생시켰다.
if (name == null) {
chunkContext.getStepContext().getStepExecution()
.getJobExecution().getExecutionContext().put("name", "user1");
throw new RuntimeException("step3 was failed"); // 예외 발생시키기
}
이때 에러를 발생시킨 이유는, 다음에 실행될 ExecutionContextTasklet4 에서 재실행했을때 name 을 가져올 수 있는지를 테스트해보기 위해서다.
오류 발생
java.lang.RuntimeException: step3 was failed
Batch 재실행
- 최초 실행시
ExecutionContextTasklet1 | 성공 |
ExecutionContextTasklet2 | 성공 |
ExecutionContextTasklet3 | 실패 |
ExecutionContextTasklet4 | ExecutionContextTasklet3에서 에러 발생했으므로 미실행 (배치 재수행시, ExecutionContextTasklet3 부터 수행) |
ExecutionContextTasklet3.java
1) name 에 값이 들어있으므로 PASS
if (name == null) {
chunkContext.getStepContext().getStepExecution()
.getJobExecution().getExecutionContext().put("name", "user1");
throw new RuntimeException("step3 was failed"); // 예외 발생시키기
}
결과
step3 was executed
ExecutionContextTasklet4.java
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;
import org.springframework.stereotype.Component;
@Component
public class ExecutionContextTasklet4 implements Tasklet {
/**
* 비즈니스 로직 구현
* @param stepContribution
* @param chunkContext
* @return
* @throws Exception
*/
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step4 was executed");
// 재실행했을때 tasklet3 에서 실패했지만 name 은 executionContext 에 저장되어있는데, 정상적으로 출력될까?
// tasklet3 에서 name 을 저장 후에 오류가 발생했으므로 정상적으로 찍힌다.
Object name = chunkContext.getStepContext().getStepExecution()
.getJobExecution().getExecutionContext().get("name");
System.out.println("name : " + name);
return RepeatStatus.FINISHED;
}
}
1) 실행 로그 확인
ExecutionContextTasklet3에서 에러 발생 전에 저장한 "name" 데이터가 정상 출력된다.
step4 was executed
name : user1
최종 결과 DB 테이블 조회
1) BATCH_JOB_EXECUTION
JOB_EXECUTION_ID | STATUS | EXIT_CODE |
27 | FAILED | FAILED |
28 (재수행) | COMPLETED | COMPLETED |
2) BATCH_JOB_INSTANCE
COLUMN | VALUE |
JOB_NAME | executionContextTestJob |
3) BATCH_STEP_EXECUTION
STEP_EXECUTION_ID | STEP_NAME | JOB_EXECUTION_ID | STATUS | EXIT_CODE |
33 | executionContextTestStep1 | 27 | COMPLETED | COMPLETED |
34 | executionContextTestStep2 | 27 | COMPLETED | COMPLETED |
35 | executionContextTestStep3 | 27 | FAILED | FAILED |
36 (재수행) | executionContextTestStep3 | 28 | COMPLETED | COMPLETED |
37 (재수행) | executionContextTestStep4 | 28 | COMPLETED | COMPLETED |
'Coding > Spring Batch' 카테고리의 다른 글
[SpringBatch 실습] 8. JobRepository 를 사용하여 특정 JobName, JobParameters 에 해당하는 최신에 수행된 Job 정보 가져오기 (0) | 2022.05.26 |
---|---|
[SpringBatch 실습] 7. BATCH 테이블 Prefix 변경해보기 (0) | 2022.05.26 |
[SpringBatch 실습] 5. Batch Job 수행시 Step 실패하는 경우2 (Step 실패와 StepExceution 관계) (0) | 2022.05.23 |
[SpringBatch 실습] 4. Tasklet 클래스를 생성하여 Job에 설정하기 (0) | 2022.05.22 |
[SpringBatch 실습] 3. Batch Job 수행시 Step 실패하는 경우 (0) | 2022.05.22 |