TIL(Today I Learned)

[Today I Learned - 8] Spring Batch에서 Step간 데이터 공유

lazy man 2024. 1. 16. 15:09

1. Execution Context


스프링 배치에서는 Execution Context를 통해 데이터를 공유할 수 있으며 Execution Context는 Job 전체에 유지되는 Job Execution Context와 각 Step에서만 유지되는 Step Execution 으로 구분할 수 있다.

 

  • Job Execution Context 는 각 Step이 종료되는 시점에 업데이트
  • Step Execution Context 는 각 Tasklet? Chunk? 가 종료되는 시점에 업데이트

 

Step 간의 데이터를 공유하기 위해서는 Job 단위로 데이터가 유지되어야 하기 때문에 Job Execution Context를 사용해야한다.

 

 

2. ExecutionContextPromotionListener


Job Context Execution을 직접 참조하여 공유할 데이터를 설정할 수 있지만 이러한 경우 Job과 Step 간에 강한 결합이 생기는 문제가 생긴다. 또한 Step 이 정상적으로 종료되는 경우 데이터가 손실될 수 있기 때문에 스프링배치에서는 ExecutionContextPromotionListener를 이용하여 Step간 데이터를 공유하도록 권장하고 있다.

 

ExecutionContextPromotionListener는 Step이 끝나는 시점에 미리 설정한 key 값이 Step Context Execution에 존재한다면 이 데이터를 Job Step Execution으로 승격시키는 역할을 수행한다. 

 

ExecutionContextPromotionListener 적용

Listener를 정의하고 Step에 설정하는 코드

@Bean
public ExecutionContextPromotionListener promotionListener() {
    ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
    listener.setKeys(new String[]{CANCELED_DUE_TO_REJECT_TAX, NTS_SEND_FAIL_TAX});
    return listener;
}
@Bean
public Step ntsSendFailNotifyStep() {
    return new StepBuilder("ntsSendFailNotifyStep", jobRepository)
            .tasklet(ntsSendFailNotifyTasklet(), transactionManager)
            .listener(promotionListener())
            .build();
}

 

 

3. Step 간 데이터 공유


ExecutionContextPromotionListener에서 설정한 키 값(NTS_SEND_FAIL_TAX, CANCELED_DUE_TO_REJECT_TAX) 으로 Step Execution Context에 "data1"과 "data2"를 저장하고 있다. 

@Bean
public Tasklet save_tasklet() {
    return (contribution, chunkContext) -> {
        ExecutionContext executionContext = contribution.getStepExecution().getExecutionContext();
        executionContext.put(NTS_SEND_FAIL_TAX, "data1");
        executionContext.put(CANCELED_DUE_TO_REJECT_TAX, "data2");

        return RepeatStatus.FINISHED;
    };
}

 

ExecutionContextPromotionListener에 의해 Step Execution Context에서 Job Execution Context로 승격했기 때문에 저장한 값을 참조할 때는 Job Execution Context를 통해 값을 참조해야 한다.

@Bean
public Tasklet get_tasklet() {
    return (contribution, chunkContext) -> {
        JobExecution jobExecution = contribution.getStepExecution().getJobExecution();
        ExecutionContext executionContext = jobExecution.getExecutionContext();

        String data1 = (String) executionContext.get(NTS_SEND_FAIL_TAX);
        String data2 = (String) executionContext.get(CANCELED_DUE_TO_REJECT_TAX);

        return RepeatStatus.FINISHED;
    };
}