[Spring Batch / Kotlin] Multi Job 등록하고 실행해보기

yesjm·2023년 5월 22일
0

기존에 스케줄러에서 jobLauncher.run(job, jobParameters) 을 실행했을때 잡의 이름같은것을 선택하지도 않았는데 동작하는것을 보고 의문이 들어서 찾아봤다.

  • @Autowired lateinit var job: Job 을 이용하면 모든 job이 실행
  • jobRegistry를 사용하면 원하는 여러 개의 job 중 특정 name의 job 만 실행 가능

여러개의 job을 등록할때엔 @Bean에 name을 설정해주어야 한다.

소스코드

MyBatchConfig.kts

@Configuration
class MyBatchConfig(
    private val entityManagerFactory: EntityManagerFactory,
) {

    companion object {
        const val chuckSize = 3
        const val JOB_NAME = "MY_JOB"
        const val STEP_NAME = "MY_STEP"
    }

    @Bean(name = [JOB_NAME])
    fun myJob(jobRepository: JobRepository, @Qualifier("MY_STEP") step: Step): Job {
        return JobBuilder("myJob", jobRepository)
            .start(step)
            .build()
    }

    @Bean(name = [STEP_NAME])
    fun myStep(jobRepository: JobRepository, transactionManager: PlatformTransactionManager, entityManagerFactory: EntityManagerFactory): Step {
        return StepBuilder("myStep", jobRepository)
            .chunk<Member, String>(chuckSize, transactionManager)
            .reader(reader(null))
            .processor(processor(null))
            .writer(writer(null))
            .build()
    }

    @Bean
    @StepScope // Bean의 생성 시점이 스프링 애플리케이션이 실행되는 시점이 아닌 @JobScope, @StepScope가 명시된 메서드가 실행될 때까지 지연
    // jobParameters 사용하려면 필요
    fun reader(@Value("#{jobParameters[requestDate]}") requestDate: String?): JpaPagingItemReader<Member> {
        println("==> reader: $requestDate")
        return JpaPagingItemReaderBuilder<Member>()
            .name("reader")
            .entityManagerFactory(entityManagerFactory)
            .pageSize(chuckSize)
            .queryString("SELECT m FROM Member m")
            .build()
    }

    @Bean
    @StepScope
    fun processor(@Value("#{jobParameters[requestDate]}") requestDate: String?): ItemProcessor <Member, String> {
        println("==> processor: $requestDate")
        return ItemProcessor<Member, String> { item: Member ->
            item.name
        }
    }

    @Bean
    @StepScope
    fun writer(@Value("#{jobParameters[requestDate]}") requestDate: String?): ItemWriter<String> {
        println("==> writer: $requestDate")
        return ItemWriter<String> { items ->
            for (item in items) {
                println("name: $item")
            }
        }
    }
}

OtherBatchConfig.kts

@Configuration
class OtherBatchConfig(
    private val entityManagerFactory: EntityManagerFactory,
): DefaultBatchConfiguration() {
    companion object {
        const val chuckSize = 3
        const val JOB_NAME = "OTHER_JOB"
        const val STEP_NAME = "OTHER_STEP"
    }

    @Bean(name = [JOB_NAME])
    fun otherJob(jobRepository: JobRepository, @Qualifier("OTHER_STEP") step: Step): Job {
        return JobBuilder("otherJob", jobRepository)
            .start(step)
            .build()
    }

    @Bean(name = [STEP_NAME])
    fun otherStep(jobRepository: JobRepository, transactionManager: PlatformTransactionManager, entityManagerFactory: EntityManagerFactory): Step {
        return StepBuilder("otherStep", jobRepository)
            .chunk<Member, String>(chuckSize, transactionManager)
            .reader(otherReader(null))
            .processor(otherProcessor(null))
            .writer(otherWriter(null))
            .build()
    }

    @Bean
    @StepScope
    fun otherReader(@Value("#{jobParameters[requestDate]}") requestDate: String?): JpaPagingItemReader<Member> {
        println("==> reader: $requestDate")
        return JpaPagingItemReaderBuilder<Member>()
            .name("reader")
            .entityManagerFactory(entityManagerFactory)
            .pageSize(chuckSize)
            .queryString("SELECT m FROM Member m")
            .build()
    }

    @Bean
    @StepScope
    fun otherProcessor(@Value("#{jobParameters[requestDate]}") requestDate: String?): ItemProcessor <Member, String> {
        println("==> processor: $requestDate")
        return ItemProcessor<Member, String> { item: Member ->
            item.name
        }
    }

    @Bean
    @StepScope
    fun otherWriter(@Value("#{jobParameters[requestDate]}") requestDate: String?): ItemWriter<String> {
        println("==> writer: $requestDate")
        return ItemWriter<String> { items ->
            for (item in items) {
                println("name: $item")
            }
        }
    
}

JobRegistryJobRegistryBeanPostProcessor 설정을 해주고 실행하고싶은 잡 이름을 설정했다.

MyScheduler.kts

@EnableScheduling
@Component
class MyScheduler(
    private val jobRegistry: JobRegistry,
	private val jobLauncher: JobLauncher
) {
    @Bean
    fun jobRegistryBeanPostProcessor(): JobRegistryBeanPostProcessor {
        val postProcessor = JobRegistryBeanPostProcessor()
        postProcessor.setJobRegistry(jobRegistry)
        return postProcessor
    }
    
    @Scheduled(fixedDelay = 30000)
    fun startMyJob() {
        println("======= my job ========")
        val parameter = mapOf(
            "requestDate" to JobParameter(OffsetDateTime.now().toString(), String::class.java),
            "jobName" to JobParameter("myJob", String::class.java)
        )
        val jobParameters = JobParameters(parameter)
        jobLauncher.run(jobRegistry.getJob("myJob"), jobParameters)
    }

    @Scheduled(fixedDelay = 50000)
    fun startOtherJob() {
        println("======= other job ========")
        val parameter = mapOf(
            "requestDate" to JobParameter(OffsetDateTime.now().toString(), String::class.java),
            "jobName" to JobParameter("otherJob", String::class.java)
        )
        val jobParameters = JobParameters(parameter)
        jobLauncher.run(jobRegistry.getJob("otherJob"), jobParameters)
    }

}

실행 결과

my job과 other job 출력한게 잘 나와있는걸 확인할 수 있다.

BATCH_JOB_INSTANCE 테이블에도 실행된 job name이 기록되어 있다.

.
.
.
참고

profile
yesjm's second brain

0개의 댓글