Spring/Spring Batch

SpringBatch(2) : 코드 설명 및 아키텍처 알아보기

kchabin 2024. 10. 30. 09:40

스프링 배치 3.0 이상 버전을 이용하므로 @EnableBatchProcessing이 필요하지 않다

기본 배치 어플리케이션

지난번에 만든 스프링배치 프로젝트 활용

lombok 설정

과정

  • Tasklet 구현체 생성
  • @Configuration 을 통해서 생성할 Batch Bean을 스프링에 등록한다.
  • Job, Step을 생성하고 빈에 등록
  • 실행 결과 확인

Tasklet 구현체 생성

Tasklet → Step 내에서 구성되고 실행되는 도메인 객체, 단일 task를 수행하기 위해 사용된다.

import lombok.extern.slf4j.Slf4j;
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.beans.factory.InitializingBean;

@Slf4j
public class GreetingTasklet implements Tasklet, InitializingBean {

    @Override
    public RepeatStatus execute(StepContribution contribution,
                                ChunkContext chunkContext) throws Exception{
        log.info("======================Taks Execute======================");
        log.info("GreetingTask: {}, {}", contribution, chunkContext);
        
        return RepeatStatus.FINISHED;
    }
    
    @Override
    public void afterPropertiesSet() throws Exception{
        log.info("====================== After Properties Sets()=====================");
    }
}

  • Tasklet, InitializingBean 인터페이스 구현
  • Tasklet → execute()
    • StepContribution , ChunkContext 를 파라미터로 받음
    • RepeatStatus 를 반환한다.
      • FINISHED : tasklet 종료
      • CONTINUABLE : 계속 태스크 수행
      • continueIf(condition) : 조건에 따라 종료/지속 결정
  • InitializingBean → afterPropertiesSet()
    • Task를 수행 시 프로퍼티를 설정하고 난 뒤에 수행되는 메서드.
    • 없어도 됨

@Configuration 을 통해서 생성할 배치 빈을 스프링에 등록하기

@Slf4j
@Configuration
public class BasicTaskJobConfiguration {
    
}
  • 부트업되면 @Configuration 을 찾고 빈 등록 작업을 수행한다.

Job, Step 생성 및 빈 등록

//프로그래밍적 트랜잭션: PlatformTransactionManager 구현체를 Bean에 넘긴다.
    //TransactionDefinition, TransacntionStatus를 사용해서 롤백, 커밋
    @Autowired
    PlatformTransactionManager transactionManager;

    @Bean
    public Tasklet greetingTasklet() {
        return new GreetingTasklet();
    }
  • 이전에 생성한 Tasklet도 빈으로 등록
  • PlatformTransactionManager transactionManager

step bean 등록

@Bean
    public Step step(JobRepository jobRepository, PlatformTransactionManager transactionManager){
        log.info("=======================Init step=======================");

        return new StepBuilder("myStep", jobRepository)
                .tasklet(greetingTasklet(), transactionManager)
                .build();
    }
  • JobRepository와 PlatformTransactionManager를 파라미터로 받는다.
    • 데이터 소스와 함께 작업
  • StepBuilder로 myStep이란 이름의 스텝을 jobRepository에 저장한다.
  • tasklet을 step에 추가하고, greetingTasklet() 을 통해서 step 내 tasklet 주입
  • build를 통해 step을 생성하고 빈으로 등록, 리턴한다.

Step 기본 구현 클래스

이 코드에선 Tasklet()을 사용해서 구현하는데, 우린 greetingTasklet()을 만들어놨기에 이를 주입해서 사용한다.

 

StepBuilderFactory는 StepBuilder를 생성하는 팩토리 클래스.

  • StepBuilderFactory.get(”stepName”)
    • “stepName”으로 step 생성
  • @Bean public Step step() { return stepBuilderFactory.get("step") .tasklet(new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContex chunkContext) throws Exception { return RepeatStatus.FINISHED; } }) .build();

Job 등록

@Bean
    public Job myJob(Step step, JobRepository jobRepository){
        log.info("======================Init job=======================");
        return new JobBuilder("myJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(step)
                .build();
    }
  • step, jobRepository가 파라미터로 들어간다.
  • Job은 JobRepository에 등록된다.
  • JobBuilder로 myJob이라는 이름의 job을 생성한다.
  • incrementer 는 job이 지속적으로 실행될 때, job의 unique성을 구분할 수 있는 방법을 설정한다.
    • RunIdIncrementer는 job의 아이디를 실행할 때 지속적으로 증가시키면서 유니크한 잡을 실행하게 된다.
  • start()를 통해서 job의 시작 포인트를 잡고, 우리가 파리미터로 받은 step을 등록했다.
  • 마찬가지로 build를 통해서 job 생성하고 빈으로 등록하도록 리턴한다.

실행

/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dmanagement.endpoints.jmx.exposure.include=* -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=61517:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/kchabin/Desktop/spring-textbook/spring-batch/out/production/classes:/Users/kchabin/Desktop/spring-textbook/spring-batch/out/production/resources:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.34/ec547ef414ab1d2c040118fb9c1c265ada63af14/lombok-1.18.34.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-batch/3.3.4/38f47d83bf881149c3995af421779a0071014499/spring-boot-starter-batch-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/com.h2database/h2/2.2.224/7bdade27d8cd197d9b5ce9dc251f41d2edc5f7ad/h2-2.2.224.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-jdbc/3.3.4/b6bc324d5af04af752a6f3e44ea8e4cd96b28343/spring-boot-starter-jdbc-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/3.3.4/f02312a6816fb439a55d2920b9af8f0f8abf5bac/spring-boot-starter-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.batch/spring-batch-core/5.1.2/461b30be2f5e0daaa7fb96e9fbcb98b2a1c12e24/spring-batch-core-5.1.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jdbc/6.1.13/11811219be3452cfd989c57d3de4529f6927352f/spring-jdbc-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/com.zaxxer/HikariCP/5.1.0/8c96e36c14461fc436bb02b264b96ef3ca5dca8c/HikariCP-5.1.0.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/3.3.4/979234a0f3035fe60d5e505018789f98a7ec7ee3/spring-boot-autoconfigure-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/3.3.4/f06c6950aa5766b63328e821641f5c7d71be819d/spring-boot-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/3.3.4/c83073c0f5574b2146695ab716bcef3d6baa45e6/spring-boot-starter-logging-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/2.1.1/48b9bda22b091b1f48b13af03fe36db3be6e1ae3/jakarta.annotation-api-2.1.1.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/6.1.13/ddbd765408d2665f47017c8f05a7682012f91da3/spring-core-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/2.2/3af797a25458550a16bf89acc8e4ab2b7f2bfce0/snakeyaml-2.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.batch/spring-batch-infrastructure/5.1.2/bfc8439a45a0681f44771d3b15f5c61fe4751db7/spring-batch-infrastructure-5.1.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/6.1.13/d5163ddae069c5d78c841cd3a60c4db25bf16401/spring-context-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/6.1.13/ad2c631e1717fdc6d91d56c06d4fd61e0215c19a/spring-aop-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-tx/6.1.13/ed041ecf9a5f480e1b11d0b58a06d8034ff8cfea/spring-tx-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/6.1.13/90bbee6278b46b145e64e5ae29316bdd459c6826/spring-beans-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-core/1.13.4/6a333b0ac647afa33616e142797da950ff295b7b/micrometer-core-1.13.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-observation/1.13.4/2673c9b181ab2512002b23b7ad0f1dd02212696c/micrometer-observation-1.13.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.16/172931663a09a1fa515567af5fbef00897d3c04/slf4j-api-2.0.16.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.5.8/229bb1ef6b14dfbf74ff443af9097d836dc5f0dd/logback-classic-1.5.8.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.23.1/425ad1eb8a39904d2830e907a324e956fb456520/log4j-to-slf4j-2.23.1.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/2.0.16/6d57da3e961daac65bcca0dd3def6cd11e48a24a/jul-to-slf4j-2.0.16.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/6.1.13/37291ff008a66a9dbf2c98e11bd468cfadaa7ebc/spring-jcl-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.retry/spring-retry/2.0.9/fc605474a6002a501a6d0ea67c04df1db14d22af/spring-retry-2.0.9.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/6.1.13/be30254712ca0549cbd539ba05c57064917253f3/spring-expression-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-commons/1.13.4/edcf69518a4c382c48e19c7fb7d4aedfb115c0c3/micrometer-commons-1.13.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.5.8/3fce599197de3b6f387cc9bee412ead2b4994a46/logback-core-1.5.8.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.23.1/9c15c29c526d9c6783049c0a77722693c66706e1/log4j-api-2.23.1.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.hdrhistogram/HdrHistogram/2.2.2/7959933ebcc0f05b2eaa5af0a0c8689fa257b15c/HdrHistogram-2.2.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.latencyutils/LatencyUtils/2.0.3/769c0b82cb2421c8256300e907298a9410a2a3d3/LatencyUtils-2.0.3.jar org.kchabin.springbatch.SpringBatchApplication

  .   ____          _            __ _ _
 /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
 \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.4)

2024-10-14T19:23:42.466+09:00  INFO 26389 --- [spring-batch] [           main] o.k.springbatch.SpringBatchApplication   : Starting SpringBatchApplication using Java 17.0.7 with PID 26389 (/Users/kchabin/Desktop/spring-textbook/spring-batch/out/production/classes started by kchabin in /Users/kchabin/Desktop/spring-textbook/spring-batch)
2024-10-14T19:23:42.467+09:00  INFO 26389 --- [spring-batch] [           main] o.k.springbatch.SpringBatchApplication   : No active profile set, falling back to 1 default profile: "default"
2024-10-14T19:23:42.599+09:00  INFO 26389 --- [spring-batch] [           main] o.s.b.c.c.annotation.BatchRegistrar      : Finished Spring Batch infrastructure beans configuration in 1 ms.
2024-10-14T19:23:42.689+09:00  WARN 26389 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'jobRegistry' of type [org.springframework.batch.core.configuration.support.MapJobRegistry] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:23:42.720+09:00  INFO 26389 --- [spring-batch] [           main] o.kchabin.springbatch.GreetingTasklet    : ====================== After Properties Sets()=====================
2024-10-14T19:23:42.728+09:00  INFO 26389 --- [spring-batch] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-10-14T19:23:42.798+09:00  INFO 26389 --- [spring-batch] [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:testdb user=SA
2024-10-14T19:23:42.799+09:00  INFO 26389 --- [spring-batch] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-10-14T19:23:42.804+09:00  INFO 26389 --- [spring-batch] [           main] o.s.b.c.r.s.JobRepositoryFactoryBean     : No database type set, using meta data indicating: H2
2024-10-14T19:23:42.812+09:00  INFO 26389 --- [spring-batch] [           main] o.k.s.BasicTaskJobConfiguration          : =======================Init step=======================
2024-10-14T19:23:42.822+09:00  INFO 26389 --- [spring-batch] [           main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP
2024-10-14T19:23:42.823+09:00  INFO 26389 --- [spring-batch] [           main] o.k.s.BasicTaskJobConfiguration          : ======================Init job=======================
2024-10-14T19:23:42.825+09:00  INFO 26389 --- [spring-batch] [           main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP
2024-10-14T19:23:42.826+09:00  INFO 26389 --- [spring-batch] [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
2024-10-14T19:23:42.870+09:00  INFO 26389 --- [spring-batch] [           main] o.k.springbatch.SpringBatchApplication   : Started SpringBatchApplication in 0.594 seconds (process running for 0.917)
2024-10-14T19:23:42.872+09:00  INFO 26389 --- [spring-batch] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-10-14T19:23:42.873+09:00  INFO 26389 --- [spring-batch] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

종료 코드 0(으)로 완료된 프로세스

After Properties Sets → Init step → Init job 순으로 실행됨

  • No TaskExecutor has been set,

SpringBatchApplication의 @EnableBatchProcessing 제거

배치 3.0 이상부터는 @EnableBatchProcessing 이 필요하지 않은데, 추가해놔서 문제가 생긴 것이라고 추측 → 제거

/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dmanagement.endpoints.jmx.exposure.include=* -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=61742:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/kchabin/Desktop/spring-textbook/spring-batch/out/production/classes:/Users/kchabin/Desktop/spring-textbook/spring-batch/out/production/resources:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.34/ec547ef414ab1d2c040118fb9c1c265ada63af14/lombok-1.18.34.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-batch/3.3.4/38f47d83bf881149c3995af421779a0071014499/spring-boot-starter-batch-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/com.h2database/h2/2.2.224/7bdade27d8cd197d9b5ce9dc251f41d2edc5f7ad/h2-2.2.224.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-jdbc/3.3.4/b6bc324d5af04af752a6f3e44ea8e4cd96b28343/spring-boot-starter-jdbc-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/3.3.4/f02312a6816fb439a55d2920b9af8f0f8abf5bac/spring-boot-starter-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.batch/spring-batch-core/5.1.2/461b30be2f5e0daaa7fb96e9fbcb98b2a1c12e24/spring-batch-core-5.1.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jdbc/6.1.13/11811219be3452cfd989c57d3de4529f6927352f/spring-jdbc-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/com.zaxxer/HikariCP/5.1.0/8c96e36c14461fc436bb02b264b96ef3ca5dca8c/HikariCP-5.1.0.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/3.3.4/979234a0f3035fe60d5e505018789f98a7ec7ee3/spring-boot-autoconfigure-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/3.3.4/f06c6950aa5766b63328e821641f5c7d71be819d/spring-boot-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/3.3.4/c83073c0f5574b2146695ab716bcef3d6baa45e6/spring-boot-starter-logging-3.3.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/2.1.1/48b9bda22b091b1f48b13af03fe36db3be6e1ae3/jakarta.annotation-api-2.1.1.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/6.1.13/ddbd765408d2665f47017c8f05a7682012f91da3/spring-core-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/2.2/3af797a25458550a16bf89acc8e4ab2b7f2bfce0/snakeyaml-2.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.batch/spring-batch-infrastructure/5.1.2/bfc8439a45a0681f44771d3b15f5c61fe4751db7/spring-batch-infrastructure-5.1.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/6.1.13/d5163ddae069c5d78c841cd3a60c4db25bf16401/spring-context-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/6.1.13/ad2c631e1717fdc6d91d56c06d4fd61e0215c19a/spring-aop-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-tx/6.1.13/ed041ecf9a5f480e1b11d0b58a06d8034ff8cfea/spring-tx-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/6.1.13/90bbee6278b46b145e64e5ae29316bdd459c6826/spring-beans-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-core/1.13.4/6a333b0ac647afa33616e142797da950ff295b7b/micrometer-core-1.13.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-observation/1.13.4/2673c9b181ab2512002b23b7ad0f1dd02212696c/micrometer-observation-1.13.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.16/172931663a09a1fa515567af5fbef00897d3c04/slf4j-api-2.0.16.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.5.8/229bb1ef6b14dfbf74ff443af9097d836dc5f0dd/logback-classic-1.5.8.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.23.1/425ad1eb8a39904d2830e907a324e956fb456520/log4j-to-slf4j-2.23.1.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/2.0.16/6d57da3e961daac65bcca0dd3def6cd11e48a24a/jul-to-slf4j-2.0.16.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/6.1.13/37291ff008a66a9dbf2c98e11bd468cfadaa7ebc/spring-jcl-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework.retry/spring-retry/2.0.9/fc605474a6002a501a6d0ea67c04df1db14d22af/spring-retry-2.0.9.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/6.1.13/be30254712ca0549cbd539ba05c57064917253f3/spring-expression-6.1.13.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-commons/1.13.4/edcf69518a4c382c48e19c7fb7d4aedfb115c0c3/micrometer-commons-1.13.4.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.5.8/3fce599197de3b6f387cc9bee412ead2b4994a46/logback-core-1.5.8.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.23.1/9c15c29c526d9c6783049c0a77722693c66706e1/log4j-api-2.23.1.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.hdrhistogram/HdrHistogram/2.2.2/7959933ebcc0f05b2eaa5af0a0c8689fa257b15c/HdrHistogram-2.2.2.jar:/Users/kchabin/.gradle/caches/modules-2/files-2.1/org.latencyutils/LatencyUtils/2.0.3/769c0b82cb2421c8256300e907298a9410a2a3d3/LatencyUtils-2.0.3.jar org.kchabin.springbatch.SpringBatchApplication

  .   ____          _            __ _ _
 /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
 \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.4)

2024-10-14T19:35:09.253+09:00  INFO 26527 --- [spring-batch] [           main] o.k.springbatch.SpringBatchApplication   : Starting SpringBatchApplication using Java 17.0.7 with PID 26527 (/Users/kchabin/Desktop/spring-textbook/spring-batch/out/production/classes started by kchabin in /Users/kchabin/Desktop/spring-textbook/spring-batch)
2024-10-14T19:35:09.254+09:00  INFO 26527 --- [spring-batch] [           main] o.k.springbatch.SpringBatchApplication   : No active profile set, falling back to 1 default profile: "default"
2024-10-14T19:35:09.444+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.455+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.456+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.456+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'jdbcConnectionDetails' of type [org.springframework.boot.autoconfigure.jdbc.PropertiesJdbcConnectionDetails] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.462+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'dataSource' of type [com.zaxxer.hikari.HikariDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.464+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.466+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration' of type [org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.467+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'transactionExecutionListeners' of type [org.springframework.boot.autoconfigure.transaction.ExecutionListenersTransactionManagerCustomizer] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.468+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'spring.transaction-org.springframework.boot.autoconfigure.transaction.TransactionProperties' of type [org.springframework.boot.autoconfigure.transaction.TransactionProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.468+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'platformTransactionManagerCustomizers' of type [org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.470+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'transactionManager' of type [org.springframework.jdbc.support.JdbcTransactionManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.470+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'spring.batch-org.springframework.boot.autoconfigure.batch.BatchProperties' of type [org.springframework.boot.autoconfigure.batch.BatchProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-14T19:35:09.472+09:00  WARN 26527 --- [spring-batch] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration$SpringBootBatchConfiguration' of type [org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration$SpringBootBatchConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [jobRegistryBeanPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead.
2024-10-14T19:35:09.476+09:00  INFO 26527 --- [spring-batch] [           main] o.kchabin.springbatch.GreetingTasklet    : ====================== After Properties Sets()=====================
2024-10-14T19:35:09.481+09:00  INFO 26527 --- [spring-batch] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-10-14T19:35:09.549+09:00  INFO 26527 --- [spring-batch] [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:testdb user=SA
2024-10-14T19:35:09.550+09:00  INFO 26527 --- [spring-batch] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-10-14T19:35:09.581+09:00  INFO 26527 --- [spring-batch] [           main] o.k.s.BasicTaskJobConfiguration          : =======================Init step=======================
2024-10-14T19:35:09.591+09:00  INFO 26527 --- [spring-batch] [           main] o.k.s.BasicTaskJobConfiguration          : ======================Init job=======================
2024-10-14T19:35:09.639+09:00  INFO 26527 --- [spring-batch] [           main] o.k.springbatch.SpringBatchApplication   : Started SpringBatchApplication in 0.524 seconds (process running for 0.695)
2024-10-14T19:35:09.640+09:00  INFO 26527 --- [spring-batch] [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2024-10-14T19:35:09.656+09:00  INFO 26527 --- [spring-batch] [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=myJob]] launched with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}]
2024-10-14T19:35:09.665+09:00  INFO 26527 --- [spring-batch] [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [myStep]
2024-10-14T19:35:09.668+09:00  INFO 26527 --- [spring-batch] [           main] o.kchabin.springbatch.GreetingTasklet    : ======================Taks Execute======================
2024-10-14T19:35:09.668+09:00  INFO 26527 --- [spring-batch] [           main] o.kchabin.springbatch.GreetingTasklet    : GreetingTask: [StepContribution: read=0, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING], ChunkContext: attributes=[], complete=false, stepContext=SynchronizedAttributeAccessor: [], stepExecutionContext={batch.version=5.1.2, batch.taskletType=org.kchabin.springbatch.GreetingTasklet, batch.stepType=org.springframework.batch.core.step.tasklet.TaskletStep}, jobExecutionContext={batch.version=5.1.2}, jobParameters={run.id=1}
2024-10-14T19:35:09.672+09:00  INFO 26527 --- [spring-batch] [           main] o.s.batch.core.step.AbstractStep         : Step: [myStep] executed in 6ms
2024-10-14T19:35:09.673+09:00  INFO 26527 --- [spring-batch] [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 13ms
2024-10-14T19:35:09.675+09:00  INFO 26527 --- [spring-batch] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-10-14T19:35:09.690+09:00  INFO 26527 --- [spring-batch] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

종료 코드 0(으)로 완료된 프로세스

After Properties Set ~ Task Execute 까지 잘 실행되는 것을 확인할수 있다.


Spring Batch Architecture

스프링 배치 모델

Tasklet model

  • 단순한 처리 모델을 가지고 있으며, 로직 자체가 단순한 경우에 주로 사용한다.
  • 다양한 데이터 소스나 파일을 한번에 처리해야 하는 경우 유연함이 있다.

Chunk model

  • 데이터양이 매우 큰 경우 효과적으로 처리 가능하다.
  • Reader/Processor/Writer 플로우 방식으로 처리된다.

Job

스프링 배치에서 일괄 적용을 위한 일련의 프로세스를 요약하는 단일 실행 단위

Step

  • Job과 Step은 1:N 관계이다.
    • 하나의 Job에 여러 Step을 재사용, 병렬화, 조건분기 등을 수행할 수 있다.
  • Step은 tasklet 모델/chunk 모델의 구현체가 탑재되어 실행된다.

JobLauncher

  • Job을 수행하기 위한 인터페이스. 사용자에 의해 직접 수행된다.
  • CommandLineJobRunner 를 실행하여 단순하게 배치 프로세스가 수행될 수 있다.

ItemReader

  • chunk 단위 모델에서 사용, 소스 데이터를 읽어들이는 역할 수행

ItemProcessor

  • 읽어들인 청크 데이터를 처리한다.
  • 데이터 변환 수행, 데이터 정제 등의 역할 담당
  • 옵션

ItemWriter

  • chunk 데이터를 읽어들였거나, 처리된 데이터를 실제 쓰기작업을 담당한다.
  • 데이터베이스로 저장, 수정 역할, 파일로 처리 결과를 출력할 수도 있음

Tasklet

  • 단순하고 유연하게 배치 처리를 수행하는 task 수행

JobRepository

  • Job과 step의 상태를 관리하는 시스템
  • 스프링 배치에서 사용하는 테이블 스키마를 기반으로 상태정보를 저장하고 관리한다.

스프링배치 흐름

Main Processing Flow

  1. JobLauncher는 JobScheduler로부터 초기화된다.
  2. JobLauncher에서 Job이 execute.
  3. Job에서 Step이 실행된다.
  4. Step은 ItemReader를 사용해서 input data를 가져온다.
  5. 가져온 input data를 ItemProcessor를 사용해서 처리한다.
  6. Step은 ItemWriter로 처리된 데이터를 출력한다.

Job 정보의 흐름

  1. JobLauncher는 JobRepository를 통해서 JobInstance 정보를 데이터베이스에 등록한다.
  2. JobLauncher는 JobRepository를 통해서 DB에 시작된 Job execution을 등록한다. → Job 수행, 실행 정보를 데이터베이스에 저장한다.
  3. JobStep은 JobRepository를 통해서 I/O 레코드 수 및 상태정보를 업데이트한다.
  4. job이 완료되면 JobRepository를 통해서 데이터베이스에 완료 정보를 저장한다.

스프링배치 저장 정보

JobInstance

  • 잡 이름과 전달 파라미터에 의해 정의된다.
    • 동일한 Job name과 argument는 동일한 JobInstance으로 식별되고, Job은 이전 activation의 연속으로 실행된다.
  • 중단되는 경우 다음 실행 시 중단 이후부터 실행하도록 지원
  • Job이 재실행을 지원하지 않거나, 성공적으로 처리된 경우 배치를 재실행한다면 중복 수행되지 않도록 종료함.

JobExecution / ExecutionContext

  • JobExecution
    • Job의 물리적인 실행을 나타냄
    • JobInstance와 달리 동일한 Job이 여러번 수행될 수 있음. → JobInstacne와 JobExecution은 1:N 관계
  • ExecutionContext
    • 각각의 JobExecution에서 처리 단계와 같은 메타 정보들을 공유하는 영역.
    • 주로 스프링배치가 프레임워크 상태를 기록하는데 사용하며, 애플리케이션에서 ExecutionContest에 접근하는 수단도 제공된다.
    • 저장되는 객체는 java.io.Serialized 를 구현하는 클래스여야 한다.

StepExecution

  • StepExecution
    • Step의 물리적인 실행
    • Job은 여러 Step을 수행하는 1:N 관계이다.
  • ExecutionContext
    • Step 내부에 데이터를 공유해야 하는 공유 영역
    • 데이터의 지역화 관점 → 여러 단계에 공유할 필요없는 정보는 Step 내의 ExecutionContext를 사용해야 한다.
    • 저장되는 데이터는 반드시 java.io.Serializable 을 구현해야 한다.

JobRepository

  • JobExecution, StepExecution 등과 같이 배치 실행정보나 상태, 결과정보들이 데이터베이스에 저장될 때 처리하는 것
  • → 스프링배치를 수행하기 위해 이를 저장할 DB가 필요하다.
  • 저장된 정보를 활용하여 스프링배치는 배치 잡을 재실행하거나, 정지된 상태 후부터 수행할 수 있는 수단을 제공한다.

'Spring > Spring Batch' 카테고리의 다른 글

Spring Batch (1) : 스프링 배치 빠르게 시작하기  (1) 2024.10.08