JobParameter 불러오기
1
2
3
4
5
6
7
8
9
| @JobScope @Bean
fun jobListener(
// 방법 1. @Value 애너테이션으로 불러온다.
@Value("#{jobParameters[fileName]}") fileName: String?,
): JobExecutionListener {
return object : JobExecutionListener {
override fun beforeJob(jobExecution: JobExecution) {
// 방법2. jobExecution이나 stepExecution 이용해서 불러온다.
jobExecution.jobParameters.getParameter("fileName")
|
jobParameter는 런타임에 실체화되는 JobScope, StepScope Bean에서만 불러올 수 있다.
일반 Bean은 애플리케이션 시작 시점에 생성되는데, 이 시점에는 jobParameter 참조가 불가능하다.
(Spring Batch) scope bean의 이해
JobParameter 관리
잡파라미터를 묶어주는 클래스를 어떤 기준으로 묶어서 그룹핑?
요구사항
1
2
3
| MainJob (step1, step2, step3)
VerificationJob (step2, step3)
SendJob (step3)
|
배치는 보통 중간 실패 시 재수행을 고려해서 위와 같이 스텝 진행에 따라 subset 잡들을 구성하는 경우도 있는데 이 때 JobParameter 관리는 어떻게 되어야 하는가?
- 여러 스텝에 걸쳐 공통으로 쓰는 파라미터도 있고, 특정 스텝에서만 쓰는 파라미터도 있는 상황
- 어떤 스텝이 A잡에 포함되어 실행 될 때와, B잡에 포함되어 실행될 때 각각 파라미터 초기화가 달라야 하는 상황 (e.g., MainJob 실행 할 때는 시간 지정 불가. 이후 subset Job 실행 할 때는 재수행을 위해 시간 지정 가능)
[!info] silver bullet은 없다.
아래 안이 유효한 것은 어디까지나 이와 비슷한 요구사항을 만족해야 할 때다.
요구사항이 달라지면 최적의 설계도 달라진다.
1안. 잡파라미터 관리 클래스 없고, 잡파라미터 받아서 초기화 하는 부분이 각 step 별로 찢어져 있다면?
- 단점
- 같은 의미의 파라미터를 step 별로 따로 정의하게 되는 등 관리가 어려움.
- Job 실행 할 때 어떤 param들이 필요한지를 알아보려면 step 별로 찾아다니면서 봐야 함. (한 눈에 이 잡이 필요로 하는 파라미터를 파악 하기 어려움)
- fail-fast가 안됨. 파라미터를 잘못 넘기면 step 일부 진행하다가 중간에 실패.
- 메서드 파라미터로 바로 받기 때문에IDE Go To 기능 사용 할 수 없음.
- 유지보수하기 가장 나쁨.
2안. 스텝 별로 JobParameter 관리 클래스를 둔다면?
1
2
3
| class Step1Params
class Step2Params
class Step3Params
|
- 초기화, 유효성 검증은 JobListener에서 호출.
- 장점
- 어떤 잡을 실행 할 때 어떤 잡파라미터가 필요한지 JobListener 참고하면 바로 알 수 있음.
- 어차피 JobConfig에서 Step 불러와야 하는데, 이 때 StepParam도 불러와서 생성해주는게 유지보수 편하고 자연스러움.
- 각 Step에서는 자기 자신 StepParam만 보면 되므로 구조가 간단해짐. Step으로 응집되는 구조.
- 단점
- 모든 step에서 써야하는 공통 필드가 각 StepParams에 위치하면서 중복이 늘어난다는게 단점.
- 제일 괜찮아 보이는 방법.
3안. Job 묶음 별로 JobParameter 관리 클래스를 둔다면? (step1 + step2 + step3에서 필요한 파라미터를 관리하는 하나의 클래스)
- MainJob, SendJob은 같은 JobMetaData를 사용하지만, 그 안에서 어떤 필드가 required이고 어떻게 초기화 되어야 하는지는 MainJob, SendJob이 각자 다를 수 있음.
- 따라서 JobMetaData 초기화 부분은 Job 마다 독립적인 JobListener에서 수행 해야만 함. (유효성 검증도)
- 장점
- step1~3에서 모두 써야 하는 공통 필드를 중복 없이 관리 할 수 있음.
- 단점
- MainJob(step1 + step2 + step3)이고 SendJob(step3) 이면, SendJob을 실행 할 때는 일부 필드만 초기화, 나머지 필드는 초기화X 상태가 됨. (항상 모든 job parameter를 필요하지도 않은데 넘기도록 하는 것은 실행 시 너무 불편하기 때문에 이렇게 할 수 밖에 없음.)
- 나쁘진 않지만 복잡도가 좀 있다.
4안. 각각의 sub Job 별로 JobParameter 클래스를 따로 둔다면?
1
2
3
| class MainJobParams
class VerificationJobParams
class SendJobParams
|
- 이걸 다 찢어놓으면 각 스텝에서 참조 하기가 곤란하다는게 문제. 모든 JobMeta를 일단 참조하면서 현재 step이 어떤 Job 아래에서 실행되고 있는지를 확인하고 그에 맞는 JobMeta를 가져와 써야 하는데 구조가 어색함.
- Step에서 JobParam 관리 클래스 참조는 Step 별 interface로 하고, 현재 실행하는 Job에 따라 해당하는 JobParam Bean만 생성할 수 있으면 가능하긴 함. (다른 JobParam Bean은 생성되어서는 안됨)
- 아무튼 좀 번거롭고 구조가 어색해보임.
2안이 가장 나아보임.
JobParameter 관리 객체, Bean으로? static object로?
- JobParameter는 런타임에 결정되는 값이라
final
변수에 할당 할 수 없음. - JobParameter는 JobScope에 종속적인 값이므로 이를 묶어주는 VO도 JobScope/StepScope Bean으로 관리하는게 타당하나… Scope Bean은 스프링 외부(e.g., DTO)에서 참조가 불가능해 곤란한 경우가 종종 있다.
- 하지만 원칙적으로는 Scope Bean을 쓰는게 좋아보인다.
- Delegates.initOnlyOnce 같은 패턴을 이용하면 도움이 될 수도.
JobParameter 유효성 검증
유효성 검증을 JobListener에서 하면 좋은 이유
- 유효성 검증을 Listener가 아닌 JobParameter 관리 클래스에서 한다면?
- step1에서는 param1이 필수인데, step3에서는 param1을 사용하지 않는 등 케이스 충돌이 발생함.
- 각 StepListener에서 한다면?
- param1이 존재하는지 체크 정도는 StepListener에서도 가능하지만 JobParameter 관리 클래스에서 임의값으로 초기화한건지, user input으로 초기화한건지는 이미 초기화가 끝난 다음이라 구분하기 어렵다.
- fail-fast 불가능하다.
- JobListener에서 한다면? 👍
- 각 Job 마다 필수로 요구하는 파라미터만 선택적으로 유효성 검증 할 수 있음.
- 파라미터가 정상이 아니면 fail-fast 가능
- JobListener에서 Programmatic 하게 빈 생성 하면 빈 충돌 막을 수 있음.
- 따라서 Job을 보고 어떤 파라미터가 필요한지 한눈에 알 수 있음.👍
1
2
3
4
5
| @StepScope @Component
class FileDownloadStepParams {
lateinit var fileName: String,
...
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| @JobScope @Bean
fun jobListener(
@Value("#{jobParameters[fileName]}") fileName: String?,
): JobExecutionListener {
return object : JobExecutionListener {
override fun beforeJob(jobExecution: JobExecution) {
require(!fileName.isNullOrBlank())
fileDownloadStepParams.fileName = fileName
fileReadAndDbLoadStepParams.fileName = fileName
fileReadAndDbLoadStepParams.isRepurchase = false
logger.info...
|
JobParametersValidator
https://devfunny.tistory.com/779
JobParametersValidator
라는게 있어서 이를 사용하는 것을 권장.
- 직접 정의해도 되고, DefaultJobParametersValidator를 사용해도 됨.
- Default~의 경우 required, optional에 대한 유무 체크 정도를 제공하는데, enum 파라미터의 경우 일단 string으로 들어오게 되면 validator에서 걸리지는 않지만 해당 enum으로 mapping 할 때 에러 발생함.