ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Batch - Scope
    🌱 spring/🚛 spring batch 2022. 12. 16. 01:13

    ⭐ Scope


    Spring Batch 의 Scope 는 @StepScope, @JobScope 를 이야기 한다.

     

    JobParameter 와 Scope

    Spring Batch의 경우 외부 혹은 내부에서 파라미터를 받아 Batch 컴포넌트에서 사용할 수 있게 지원하고 있다.

    이런 파라미터를 Job Parameter라고 한다.

    ❗Job Parameter를 사용하기 위해서는 항상 Spring Batch 전용 Scope를 선언해야만 한다.

    크게 2가지

    • @StepScope
    • @JobScope

    사용법

    @Value("#{jobParameters[parameter_name]}")
    

     

    JobScope

      @Bean
      public Job scopeJob() {
        return jobBuilderFactory.get("scopeJob")
            .start(scopeStep1(null))
            .next(scopeStep2())
            .build();
      }
      
      @Bean
      @JobScope
      public Step scopeStep1(@Value("#{jobParameters[requestDate]}") String requestDate) {
        return stepBuilderFactory.get("scopeStep1")
            .tasklet((contribution, chunkContext) -> {
              log.info("===== scopeStep1 =====");
              log.info("===== requestDate = {} =====", requestDate);
              return RepeatStatus.FINISHED;
            })
            .build();
      }
    • @JobScope 는 Step 선언문에서 사용 가능하다.

     

    StepScope

      @Bean
      public Step scopeStep2() {
        return stepBuilderFactory.get("scopeStep2")
            .tasklet(scopeStep2Tasklet(null))
            .build();
      }
      
      @Bean
      @StepScope
      public Tasklet scopeStep2Tasklet(@Value("#{jobParameters[requestDate]}") String requestDate) {
        return ((contribution, chunkContext) -> {
          log.info("===== scopeStep2 =====");
          log.info("===== requestDate = {} =====", requestDate);
          return RepeatStatus.FINISHED;
        });
      }
    • @StepScope는 Tasklet이나 ItemReader, ItemWriter, ItemProcessor 에서 사용할 수 있다.

    JobParameter의 타입으로 사용할 수 있는 것

    • Double
    • Long
    • Date
    • String

    LocalDate 와 LocalDateTime은 String으로 받아 타입변환을 해서 사용해야 한다.

     

    호출하는 쪽에서 null을 할당 ❓

    • Job Parameter의 할당이 어플리케이션 실행시에 하지 않기 때문에 가능하다
    • 이게 무슨말이지..?
    • 아래에서 알아보자

    @StepScope & @JobScope

    Spring Batch는 @StepScope 와 @JobScope 라는 Bean Scope를 지원

    Spring Bean의 기본 Scope는 singleton 이다.

     

    하지만 Spring Batch 컴포넌트(Tasklet, ItemReader, ItemWriter, ItemProcessor 등)에 @StepScope를 사용하면

    • 지정된 Step의 실행시점에 해당 컴포넌트를 Spring Bean으로 생성한다.

    @JobScope 역시 Job 실행시점에 Bean이 생성된다.

     

    즉, Bean의 생성 시점을 지정된 Scope가 실행된 시점으로 지연시킨다.

    • 실행될 때 생성, 끝날 때 삭제

    지연 시켜서 얻는 장점 ❓

    1. JobParameter의 Late Binding이 가능하다.
      • Job Parameter를 StepContext 또는 JobExecutionContext 레벨에서 할당시킬 수 있다.
      • Application이 실행되는 시점이 아니더라도 Controller 나 Service 같은 비즈니스 로직 처리 단계에서 Job Parameter를 할당시킬 수 있다.
    2. 동일한 컴포넌트를 병렬 혹은 동시에 사용할 때 유용하다.
      • Step 안에 Tasklet이 있고, 이 Tasklet은 멤버 변수와 이 멤버 변수를 변경하는 로직이 있다고 가정
      • @StepScope 없이 Step을 병렬로 실행시키게 된다면
        • 서로 다른 Step에서 하나의 Tasklet을 두고 상태를 변경하고자 한다.
      • 하지만 @StepScope가 있다면
        • 각각의 Step에서 별도의 Tasklet을 생성하고 관리하기 때문에 서로의 상태를 침범할 일이 없다.

     

    Job Parameter

    Job Parameter는 @Value 를 통해서 가능

    • Step, Tasklet, Reader 등 Batch 컴포넌트 Bean의 생성 시점에 호출할 수 있다.
    • 정확히는 Scope Bean을 생성할때만 가능하다!!!

    @StepScope, @JobScope Bean을 생성할 때만 Job Parameter가 생성되어 사용할 수 있다.

     

    메서드가 아닌 클래스에서 직접 Bean을 생성하는 것으로 코드 수정

    private final SimpleJobTasklet tasklet1;
    
    @Bean
    public Job simpleJob() { 
      return jobBuilderFactory.get("simpleJob") 
          .start(simpleStep1())
          .next(simpleStep2(null))
          .build();
    }
    
    // @Bean
    // @JobScope
    public Step simpleStep1() {
      log.info("===== simple Step1 =====");
      return stepBuilderFactory.get("simpleStep1")
          .tasklet(tasklet1)
          .build();
    }

     

    SimpleJobTasklet.java

    @Slf4j
    @Component
    @StepScope
    public class SimpleJobTasklet implements Tasklet {
    
      @Value("#{jobParameters[requestDate]}")
      private String requestDate;
    
      public SimpleJobTasklet() {
        log.info("====== class - tasklet 생성 =======");
      }
    
      @Override
      public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
          throws Exception {
    
        log.info("===== step!!!! =====");
        log.info("===== requestDate = {} ====", requestDate);
        return RepeatStatus.FINISHED;
      }
    }

    → 메소드의 파라미터로 Job Parameter를 할당받지 않고, 클래스의 멤버 변수로 Job Parameter를 할당 받도록 하였다.

     

    실행 결과

     

    SimpleJobTasklet Bean을 singleton Bean으로 생성한 경우

    @Slf4j
    @Component
    //@StepScope
    public class SimpleJobTasklet implements Tasklet {
    
      @Value("#{jobParameters[requestDate]}")
      private String requestDate;
    
      public SimpleJobTasklet() {
        log.info("====== class - tasklet 생성 =======");
      }
    
      @Override
      public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
          throws Exception {
    
        log.info("===== step!!!! =====");
        log.info("===== requestDate = {} ====", requestDate);
        return RepeatStatus.FINISHED;
      }
    }

    • ‘jobParameter’ cannot be found 에러 발생

     

    Bean 을 메소드나 클래스 어느 것을 통해서 생성해도 무방하나 Bean의 Scope는 Step이나 Job이어야 한다!!!!

     

    JobParameter vs 시스템 변수

    Job Parameter를 왜 사용해야 하나❓

    • 기존 Spring Boot에서 사용하던 여러 환경변수 혹은 시스템 변수를 사용하면 되지 않을까?
    • CommandLineRunner를 사용시
      • java jar application.jar -D파라미터
      • 시스템 변수 지정

    차이점

    1. 시스템 변수를 사용할 경우 Spring Batch의 Job Parameter 관련 기능을 못 쓰게 된다.
      • ex)
        • 같은 Job Parameter로 같은 Job을 두 번 실행하지 않는 기능이 작동하지 않는다.
        • Spring Batch에서 자동으로 관리해주는 Parameter 관련 메타 테이블이 관리되지 않는다.
    2. Command line이 아닌 다른 방법으로 Job을 실행하기가 어렵다.
      • 만약 실행해야 한다면 전역 상태(시스템 변수 혹은 환경 변수)를 동적으로 계속해서 변경시킬 수 있도록 Spring Batch를 구성해야 한다.
      • 여러 Job을 동시에 실행하려는 경우 또는 테스트 코드로 Job을 실행할 때 문제가 발생할 수 있다.
    3. Job Parameter의 장점을 사용하지 못한다.
      • Job Parameter를 못쓰면 Late Binding을 못하게 된다.
      • 외부에서 넘겨주는 파라미터에 따라 Batch가 다르게 작동해야 한다고 하면
        • 이를 시스템 변수로 하기에는 너무 어렵다.
        • 하지만 Job Parameter를 이용한다면 아주 쉽게 해결할 수 있다.

     

    Controller

    @Slf4j
    @RequiredArgsConstructor
    @RestController
    public class JobLauncherController {
    
      private final JobLauncher jobLauncher;
      private final Job job;
    
      @GetMapping("/launchjob")
      public String handle(@RequestParam("fileName") String fileName) {
        try {
          JobParameters jobParameters = new JobParametersBuilder()
              .addString("input.file.name", fileName)
              .addLong("time", System.currentTimeMillis())
              .toJobParameters();
          jobLauncher.run(job, jobParameters);
        } catch (Exception e) {
          log.info(e.getMessage());
        }
        return "";
      }
    }
    

     

    • Controller에서 Request Parameter로 받은 값을 Job Parameter로 생성한다.
    JobParameters jobParameters = new JobParametersBuilder()
              .addString("input.file.name", fileName)
              .addLong("time", System.currentTimeMillis())
              .toJobParameters();
    

     

    • 그리곤 Job Parameter로 Job을 수행한다.
    jobLauncher.run(job, jobParameters);
    

    즉, Job Parameter를 개발자들이 원할 때 생성하고 Job을 수행할 수 있다.

    • 각각의 Batch 컴포넌트들이 Job Parameter를 사용하면 되니 변경이 심한 경우에도 쉽게 대응할 수 있다.

     

    참고자료

    https://jojoldu.tistory.com/330?category=902551

    https://ahndy84.tistory.com/25

    https://velog.io/@lxxjn0/Spring-Batch-Guide-05.-Spring-Batch-Scope-Job-Parameter

     

    '🌱 spring > 🚛 spring batch' 카테고리의 다른 글

    SpringBatch - ItemReader  (0) 2022.12.16
    Spring Batch - Chunk 지향 처리  (1) 2022.12.16
    Spring Batch - Job Flow  (0) 2022.12.16
    메타데이터 테이블  (0) 2022.12.16
    MySQL 환경 - Spring Batch  (0) 2022.12.16

    댓글

Designed by Tistory.