(Spring Batch) cli 실행 - JobLauncherApplicationRunner
SpringBoot 안쓰는 경우 - CommandLineJobRunner
- 공식 docs 참고 -https://docs.spring.io/spring-batch/docs/4.3.6/reference/html/job.html#runningAJob
- https://kwonnam.pe.kr/wiki/springframework/batch/commandlinejobrunner
SpringBoot 쓰는 경우 - yml 이용한 세팅
- SpringBoot 쓰는데 CommandLineJobRunner를 쓰게 되면 프로그램 실행 인입점이 main이 아니라 CommandLineJobRunner가 된다. => SpringBoot Auto-config가 제대로 적용되지 않는다.
- 따라서 부트에서는 CommandLineJobRunner가 아니라 아래 구성요소들을 이용해야 한다.
- BatchAutoConfiguration.html
- JobLauncherCommandLineRunner.html (Spring Boot 2)
- JobLauncherApplicationRunner.html (Spring Boot 3)
- 여러 job들을 ,로 명시해서 실행하는 기능은 제거된 듯.https://github.com/spring-projects/spring-boot/issues/25373
- https://jojoldu.tistory.com/328
- https://kwonnam.pe.kr/wiki/springframework/springboot/batch
[!info] 즉, SpringBoot3에서는 JobLauncherApplicationRunner를 사용하면 된다.
BatchAutoConfiguration에서 JobLauncherApplicationRunner bean을 만들어준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ConditionalOnMissingBean(value = DefaultBatchConfiguration.class, annotation = EnableBatchProcessing.class)
public class BatchAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true)
public JobLauncherApplicationRunner jobLauncherApplicationRunner(JobLauncher jobLauncher, JobExplorer jobExplorer,
JobRepository jobRepository, BatchProperties properties) {
JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobLauncher, jobExplorer, jobRepository);
String jobNames = properties.getJob().getName();
if (StringUtils.hasText(jobNames)) {
runner.setJobName(jobNames);
}
return runner;
}
- SpringBoot 3 기준, BatchAutoConfiguration에서 JobLauncherApplicationRunner를 Bean으로 등록해준다.
- Spring Boot 3 부터 @EnableBatchProcessing를 붙이면 auto-config 기능이 비활성화 된다 !
- Spring Boot 2와 정반대로, 애너테이션을 없애야 auto-config가 적용되어 Bean이 생성된다.
- 상단의
@ConditionalOnMissingBean
보면 알 수 있다.
SpringBoot 3 부터는 spring.batch.job.enabled=true 일 때, jobName 누락하더라도 전체 job이 실행되지 않는다.
SpringBoot 2 까지는 spring.batch.job.enabled=false
로 만들어야 jobName을 누락하는 경우 전체 job 실행되지 않았던 것 같은데,
SpringBoot 3 부터는 spring.batch.job.enabled=true
여도 jobName 누락하더라도 전체 job이 실행되지 않는다. 옵션을 true로 놓아도 무방하다.
1
2
Caused by: java.lang.IllegalArgumentException: Job name must be specified in case of multiple jobs
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.validate
JobLauncherApplicationRunner의 문제점? - 알 수 없는 job name을 넘겨도 배치가 실패하지 않는다.
따라서 별도 PostConstructor를 이용해 검사하거나, 아예 JobLauncherApplicationRunner 빈 자체를 직접 생성하면서 검사하는 것이 좋아보인다. 요구사항을 만족하는 빈을 직접 생성하기로 했다.
JobLauncherApplicationRunner 생성 빈 직접 정의하기
요구사항 정리
JobParameter 목록에 현재시각을 자동으로 추가해주어야 한다.- 생각해보니 이 요구사항은 제거해도 될 것 같다.
- 배치에서 사용할 기준 시간이 필요해서 받는거라면, 이를 꼭
JobParameter
로 받을 필요는 없다.- 하지만 static 변수나 싱글턴 변수 보다는 param으로 받는게 테스트 하기에는 더 낫지 않나 싶다.
- 같은 파라미터로 재수행 불가하기 때문에 파라미터 바꿔주기 위해 받는거라면, 이는
allowStartIfComplete
설정이나,Increment
를 넣어주면 된다.
- cli에서 key1=value1 key2=value2 형식으로 잡파라미터 받을 수 있어야 한다.
- 알 수 없는 job name이 들어왔을 때, 실패해야 한다. (ExitCode도 비정상 반환)
- 배치 중간에 실패했을 때, 애플리케이션 ExitCode를 비정상으로 반환해야 한다.
직접 JobLauncherApplicationRunner Bean 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@ConditionalOnMissingBean(value = DefaultBatchConfiguration.class, annotation = EnableBatchProcessing.class) // BatchAutoConfiguration 없이 단독으로 JobLauncherApplicationRunner bean만 생성되는 경우를 방지하기 위해 방어로직 추가
@EnableConfigurationProperties(BatchProperties::class)
@Configuration
class JobLauncherApplicationRunnerConfig {
@Bean
fun jobLauncherApplicationRunner(
jobLauncher: JobLauncher, jobExplorer: JobExplorer, jobRepository: JobRepository,
properties: BatchProperties, jobs: Collection<Job>
): JobLauncherApplicationRunner {
val runner = JobLauncherApplicationRunner(jobLauncher, jobExplorer, jobRepository)
val jobName = properties.job.name
if (!jobs.map { it.name }.contains(jobName)) {
logger.error { "### job Bean [$jobName] 찾을 수 없음" }
throw IllegalArgumentException("### job Bean [$jobName] 찾을 수 없음")
}
runner.setJobName(jobName)
return runner
}
}
[!warning]
DefaultBatchConfiguration
가 존재하면BatchAutoConfig
가 적용되지 않고
BatchAutoConfig
가 적용되지 않으면JobExecutionExitCodeGenerator
를 비롯한 여러 빈들이 생성되지 않는다.
i.e.ExitCodeGenerator
빈이 없으면 exit code가 정상적으로 세팅되지 않는 등 JobLauncherApplicationRunner가 예상한대로 동작하지 않으므로
ConditionalOnMissingBean
으로BatchAutoConfiguration
이 적용 되었는지 방어로직 꼭 넣어주는 것이 좋다.
1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication
class MyBatchApplication
fun main(args: Array<String>) {
try {
val applicationContext = runApplication<ApprovalBatchApplication>(*args)
val exitCode = SpringApplication.exit(applicationContext)
exitProcess(exitCode)
} catch (e: Exception) {
exitProcess(101) // java.lang.IllegalStateException: Failed to execute ApplicationRunner 케이스
}
}
1
2
3
4
5
6
spring:
jdbc:
isolation-level-for-create: DEFAULT
job:
enabled: true
name: \${job.name:NONE}
1
$ batch.jar --job.name=실행할JobName arg1=value1 arg2=value2
위와 같이 세팅하면 요구사항을 만족 할 수 있다.
contribution
이런 기능은 아예 JobLauncherApplicationRunner에서 제공해주는게 좋아보여 이슈를 올렸는데.
https://github.com/spring-projects/spring-boot/issues/35940
갑자기 나타난 인도인이 PR을 먼저 올리면서 그 친구가 담당해서 처리하게 되었다…
Test시에는 어떻게?
https://www.baeldung.com/spring-junit-prevent-runner-beans-testing-execution
참고) executeLocalJobs와 executeRegisteredJobs의 차이?
1
2
3
4
5
6
7
// local jobs는 JobLauncherApplicationRunner 빈 생성 시점에 DI 받아 확정
@Autowired(required = false)
public void setJobs(Collection<Job> jobs) {
this.jobs = jobs;
}
// 반면 registered jobs는 런타임에 등록된 job들에 대한 컨테이너. (JobRegistry 통해 런타임에 잡 등록 가능)