์ ํด์ง ์๊ฐ์ ์ผ๊ด์ ์ผ๋ก ์์
์ ์ฒ๋ฆฌํ ์ ์๋๋ก ํด์ฃผ ํ๋ก๊ทธ๋จ์ผ๋ก ์ฃผ๋ก ๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฌ๋ค.
ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์์ ์ฒ๋ฆฌํ๊ฑฐ๋, ์ผ์ ์๊ฐ ๋ค์ ์ฒ๋ฆฌํ๊ณ ์ ํ ๋ ์ฌ์ฉํ ์ ์๊ณ ,๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋ ํธ๋ํฝ์ด ์ ์ ์๊ฐ๋์ ์๋ฒ ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์ฌ์ฉํ๋ค.
(์ฃผ๋ก ETL:Extract-Transform-Load, ๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ ์จ์ดํ์ฐ์ค์ ์ ์ฅ.)
์๋ฐ ๊ธฐ๋ฐ ํ์ค ๋ฐฐ์น ๊ธฐ์ ์ ๋ถ์ฌ๋ก ๋ฐฐ์น ์ฒ๋ฆฌ์์ ์๊ตฌํ๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฐ ๊ธฐ๋ฐ ๋ฐฐ์น ์ํคํ
์ฒ ํ์ค์ ํ์์ฑ์ด ๋๋๋์๊ณ ,
Accenture์์ ์์ ํ๊ณ ์๋ ๋ฐฐ์น ์ฒ๋ฆฌ ์ํคํ
์ฒ ํ๋ ์์ค๋ฅด๋ฅผ ์คํ๋ง ๋ฐฐ์น ํ๋ก์ ํธ์ ๊ธฐ์ฆํ์๋ค.
๊ฐ๋ณ๊ณ ๋ค์ํ ๊ธฐ๋ฅ์ ๊ฐ์ง ๋ฐฐ์น ํ๋ ์์ํฌ๋ก, ๊ฒฌ๊ณ ํ ๋ฐฐ์น ์ดํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์ด ๊ฐ๋ฅํ๋๋ก ๋์์ธ ๋์ด์๋ค.
์ต๊ทผ ๊ธฐ์
์์คํ
์ด์์ ํ์์ ์ด๋ผ๊ณ ํ ์ ์๋ค.
๊ธฐ์กด Spring ํ๋ก์ ํธ์ ๋ชจ๋์ ํ์ฉํ ์ ์๋ค๋ ์ฅ์ ์ ๊ฐ์ง๋ค.(์๋ก์ด ์ธ์ด๋ก ์ฒ๋ฆฌ๋ฅผ ์๋ก ๊ตฌํํ์ง ์์๋ ๋๋ค.)
๋ฐฐ์น ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ก์ง์ ์๋ก ๋ง๋ค์ง ์๊ณ ์คํ๋ง ๋ฐฐ์น์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.
-
- Read: DB, ํ์ผ, ํ ๋ฑ์์ ๋ค๋์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋๋ค.
- Process: ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ๋ค.
- Write: ๊ฐ๊ณต๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ ์ฅํ๋ค.
-
- ๋ฐฐ์น ํ๋ก์ธ์ค๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ์ปค๋ฐํ๋ค.(ํจ์จ์ ์ธ ์ปค๋ฐ ์ ๋ต.)
- ๋์ ๋ค๋ฐ์ ์ธ Job ์ ๋ฐฐ์น ์ฒ๋ฆฌ, ๋ณ๋ ฌ ์ฒ๋ฆฌ.
- ์คํจ ํ ์ค์ผ์ค๋ง์ ์ํด ์ฌ์์๋๋ค.
- ์์กด๊ด๊ณ๊ฐ ์๋ step๋ค์ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
- ์กฐ๊ฑด์ ๋ฐ๋ผ ํ๋ฆ์ ๊ตฌ์ฑํ๋ ๋ฑ ์ฒด๊ณ์ ์ด๊ณ ์ ์ฐํ ๋ฐฐ์น ๋ชจ๋ธ์ ๊ตฌ์ฑํ๋ค.
- ๋ฐ๋ณตํ๊ฑฐ๋, ์ฌ์๋, Skip ์ฒ๋ฆฌ(์ค์ํ์ง ์์ ์์ธ๋ฅผ ์คํต, ๊ณ์ ์คํ๋ ์ ์๋๋ก)๋ฑ..
JobLauncher: Job์ ์คํ์ํค๋ ์ปดํฌ๋ํธJob: ๋ฐฐ์น ์์ .JobRepository: Job์ ์คํ๊ณผ Job, Step์ ์ ์ฅ.Step: ๋ฐฐ์น ์์ ์ ๋จ๊ณ. ItemReader, ItemProcessor, ItemWriter๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ , ์ฒ๋ฆฌํ๊ณ , ์ฐ๋ ๊ตฌ์ฑ์ ํ๋์ฉ ๊ฐ์ง๋ค.
-
Application
๋น์ฆ๋์ค, ์๋น์ค ๋ก์ง, Core, Infrastructure์ ์ด์ฉํ์ฌ ๋ฐฐ์น ๊ธฐ๋ฅ์ ๋ง๋ ๋ค.
๊ฐ๋ฐ์๋ ์ ๋ฌด ๋ก์ง์ ๊ตฌํ์๋ง ์ง์คํ๊ณ ๊ณตํต์ ์ธ ๊ธฐ์ ๊ธฐ๋ฐ์ ํ๋ ์ ์ํฌ๊ฐ ๋ด๋นํ๋๋ก ํ๋ค. -
Core
๋ฐฐ์น ์์ ์ ์์ํ๊ณ ์ ์ดํ๋ ํ์ ํด๋์ค(Job, Step, JobLauncher, Flow)
Job์ ์คํํ๊ณ ๋ชจ๋ํฐ๋ง, ๊ด๋ฆฌํ๋ API๋ก ๊ตฌ์ฑ๋์ด ์๋ค. -
Infrastructure
์ธ๋ถ์ ์ํธ์์ฉํ๋ ๋ ์ด์ด, (ItemReader, ItemWriter, RetryTemplate, Skip)
Application, Core ๋ชจ๋ ๊ณตํต Infrastructure ์์์ ๋น๋ํ๋ค. Job ์คํ์ ํ๋ฆ๊ณผ ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ ์ ๊ณตํ๋ค.์ค์ ๋ก ํจํค์ง ๊ตฌ์กฐ๋ฅผ ์ด์ด ํ์ธํด ๋ณผ ์ ์๋ค.
-
- ์ ์ฒด ๋ฐฐ์น ํ๋ก์ธ์ค๋ฅผ ์บก์ํํ ๋๋ฉ์ธ์ผ๋ก, Step์ ์์๋ฅผ ์ ์ํ๋ค.
JobParameters๋ฅผ ๋ฐ๋๋ค.- JobParameters๋ฅผ ๋ฐ์ JobInstance๊ฐ ์์ฑ๋๊ณ , JobExecution์ผ๋ก ๋๋์ด์ ธ ์คํ๋๋ค.
-
- ์์ ์ ์ฒ๋ฆฌ ๋จ์.
- Chunk | Tasklet ๊ธฐ๋ฐ์ผ๋ก ํ๋์ ํธ๋์ญ์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค.
- commitInterval ๋งํผ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ , ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ค, ChunkSize ๋งํผ ํ๋ฒ์ Write ํ๋ค.
์คํ๋ง ๋ฐฐ์น๊ฐ ์คํ ๋ฐ ๊ด๋ฆฌ๋ฅผ ์ํ ๋ชฉ์ ์ผ๋ก ์ฌ๋ฌ ๋๋ฉ์ธ(Job, Step, Execution, Instance JobParams ...) ์ ์ ๋ณด๋ฅผ ์ ์ฅํ ์ ์๋ ์คํค๋ง๋ฅผ ์ ๊ณตํ๋ค.
Job ์ ์ด๋ ฅ(์ฑ๊ณต, ์คํจ), ํ๋ผ๋ฏธํฐ ๋ฑ ์คํ ๊ฒฐ๊ณผ๋ฅผ ์กฐํํ ์ ์๋ค. -> ๋ฆฌ์คํฌ ๋ฐ์์ ๋น ๋ฅธ ๋์ฒ ๊ฐ๋ฅ.
DB์ ์ฐ๋ํ ๊ฒฝ์ฐ ํ์์ ์ผ๋ก ๋ฉํ ํ ์ด๋ธ์ด ์์ฑ๋์ด์ผ ํ๋ฉฐ ์คํค๋ง ํ์ผ์ ์์น๋ /org/springframework/batch/core/schema-*.sql ์ด๋ค.(DB ์ ํ๋ณ๋ก ์ ๊ณต)
-
-
BATCH_JOB_INSTANCE
Job ์ด ์คํ๋ ๋ JobInstance ์ ๋ณด๊ฐ ์ ์ฅ๋๋ฉฐ, job_name๊ณผ job_key๋ก ํ์ฌ ํ๋์ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋๋ค (์ธ์คํด์ค๋ ์ ์ผ)
- version: ์ ๋ฐ์ดํธ ๋ง๋ค 1์ฉ ์ฆ๊ฐํ๋ ๊ฐ
- job_name: job์ ๊ตฌ์ฑํ ๋ ๋ถ์ฌํ ์ด๋ฆ.
- job_key: name ๊ณผ parmas๋ฅผ ํฉ์ณ ํด์ฑํ ๊ฐ
-
BATCH_JOB_EXECUTION
Job์ ์คํ ์ ๋ณด(์์ฑ, ์์, ์ข ๋ฃ ์๊ฐ, ์คํ ์ํ, ์ข ๋ฃ ์ฝ๋, ์คํจ ์์ธ ๋ฉ์์ง, ๋ง์ง๋ง ์คํ ์์ ๋ฑ)
-
BATCH_JOB_EXECUTION_PARAMS
Job๊ณผ ํจ๊ป ์คํ๋๋ JobParams ์ ๋ณด๋ฅผ ์ ์ฅ.
- type_cd : String, Long, Date ๋ฑ์ ํ์ ์ ๋ณด
- key_name: ํ๋ผ๋ฏธํฐ ํค ๊ฐ.
- string_val: ํ๋ผ๋ฏธํฐ ๋ฌธ์ ๊ฐ
- data_val: ํ๋ผ๋ฏธํฐ ๋ ์ง ๊ฐ.
- long_val
- double_val
- identifying: ์๋ณ ์ฌ๋ถ (boolean)
-
BATCH_JOB_EXECUTION_C์ ONTEXT
Job์ ์คํ๋์ ์ฌ๋ฌ๊ฐ์ง ์ํ์ ๋ณด, ๊ณต์ ๋ฐ์ดํฐ๋ฅผ JSON ํ์์ผ๋ก ์ง๋ ฌํํ์ฌ ์ ์ฅํ๋ค. Step๊ฐ์ ๊ณต์ ๊ฐ ๊ฐ๋ฅํ๋ค.
- short_context: job์ ์คํ ์ํ์ ๋ณด, ๊ณต์ ๋ฐ์ดํฐ ๋ฑ์ ์ ๋ณด๋ฅผ ๋ฌธ์์ด๋ก ์ ์ฅ
- serialized_context: ์ง๋ ฌํ ๋ ์ ์ฒด ์ปจํ ์คํธ
-
BATCH_STEP_EXECUTION
- Step์ ์คํ ์ ๋ณด(์์ฑ, ์์, ์ข ๋ฃ ์๊ฐ, ์คํ ์ํ, ์ข ๋ฃ ์ฝ๋, ์คํจ ์์ธ ๋ฉ์์ง, ๋ง์ง๋ง ์คํ ์์ ๋ฑ)
- ๋ถ๋ชจ(Job)์ ID
- ํธ๋์ญ์ ๋น Commit, Read, Write, Filter, Read skip, Write skip, ProcessSkip, Rollback ์
-
BATCH_STEP_EXECUTION_CONTEXT
Job์ ๊ฒฝ์ฐ์ ๋์ผํ์ง๋ง, Step ๋ณ๋ก ์ ์ฅ๋๋ฉฐ Step๊ฐ ๊ณต์ ํ ์ ์๋ค.
ํ ์ด๋ธ๊ฐ์ ๊ด๊ณ(1:N) ์ ์ฃผ์ํ์ฌ ์ดํด๋ณด์.
-
-
- ์๋ ์์ฑ: ์ฟผ๋ฆฌ ๋ณต์ฌ ํ ์ง์ ์์ฑ.
- ์๋ ์์ฑ: properties ์์ spring.batch.jdbc.initialize-schema ์ค์ .
- ALWAYS
์คํฌ๋ฆฝํธ ํญ์ ์คํ, RDBMS ์ค์ ์ด ๋์ด์์ ๊ฒฝ์ฐ ๋ด์ฅ DB๋ณด๋ค ์ฐ์ ์ ์ผ๋ก ์คํํ๋ค.
- EMBEDDED
๋ด์ฅ DB ์ผ๋๋ง ์คํ๋๋ค. (๊ธฐ๋ณธ๊ฐ)
- NEVER
- ์คํฌ๋ฆฝํธ๋ฅผ ํญ์ ์คํํ์ง ์๋๋ค. ํ ์ด๋ธ์ด ์๋ค๊ฑฐ๋ ๋ด์ฅ DB ๋ผ๋ฉด ์ค๋ฅ ๋ฐ์.
- ์ด์์์ ์๋์ผ๋ก ์คํฌ๋ฆฝํธ ์์ฑ ํ ์ค์ ํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
- ALWAYS
-
implementation 'org.springframework.boot:spring-boot-starter-batch' testImplementation 'org.springframework.batch:spring-batch-test'
-
@EnableBatchProcessing @SpringBootApplication public class SpringBatchApplication { public static void main(String[] args) { SpringApplication.run(SpringBatchApplication.class, args); } }
-
์คํ๋ง ๋ฐฐ์น๋ฅผ ์๋์ํค๊ธฐ ์ํด ์ ์ธํ๋ ์ ๋ ธํ ์ด์ ์ผ๋ก, ์ด 4๊ฐ์ ์ค์ ํด๋์ค๋ฅผ ์คํ์ํค๋ฉฐ ์คํ๋ง ๋ฐฐ์น์ ๋ชจ๋ ์ด๊ธฐํ ๋ฐ ์คํ ๊ตฌ์ฑ์ด ์ด๋ฃจ์ด์ง๋ค.
-
์คํ๋ง ๋ถํธ ๋ฐฐ์น์ ์๋์ค์ ํด๋์ค๊ฐ ์คํ๋์ด ๋ฑ๋ก๋ ๋ชจ๋ Job์ ๊ฒ์ํ์ฌ ์ด๊ธฐํํ๊ณ ๋์์ Job ์ ์ํํ๋๋ก ๊ตฌ์ฑํ๋ค.
-
-
์คํ๋ง ๋ฐฐ์น๊ฐ ์ด๊ธฐํ ๋ ๋ ์๋์ผ๋ก ์คํ, Job์ ์ํํ๋ JobLauncherApplicationRunner ๋น์ ์์ฑํ๋ค.(ApplicationRunner๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ์คํ๋ง์ด ์คํ์ํจ๋ค.)
-
- JobBuilderFactory ์ StepBuilderFactory๋ฅผ ์์ฑํ๋ค.
- ์คํ๋ง ๋ฐฐ์น์ ์ฃผ์ ๊ตฌ์ฑ ์์๋ฅผ ์์ฑํ๋ค.(ํ๋ก์ ๊ฐ์ฒด๋ก ์์ฑ๋๋ค.) - jobRepository, jobLauncher, hobRegistry, jobExplorer
-
- BasicBatchConfigurer
SimpleBatchConfiguration ์์ ์์ฑํ ํ๋ก์ ๊ฐ์ฒด์ ์ค์ ํ๊ฒ์ ์์ฑํ๋ ์ค์ ํด๋์ค.
- JpaBatchConfigurer
JPA ๊ด๋ จ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์ค์ ํด๋์ค.
- BasicBatchConfigurer
-
-
-
@RequiredArgsConstructor @Configuration public class JobConfig { // #1 private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; @Bean public Job myJob() { return jobBuilderFactory.get("myJob") // #2 .start(myStep()) .next(myStep2()) .build(); } @Bean public Step myStep() { return stepBuilderFactory.get("myStep1") // #2 .tasklet((contribution, chunkContext) -> { System.out.println("================ My Step1 ============="); return RepeatStatus.FINISHED; // #3 }) .build(); } @Bean public Step myStep2() { return stepBuilderFactory.get("muStep2") .tasklet((contribution, chunkContext) -> { System.out.println("================ My Step2 ============="); return RepeatStatus.FINISHED; }) .build(); } }
-
๋ชจ๋ Job๊ณผ Step์ ๋น์ผ๋ก ๋ฑ๋ก๋์ด์ผ ํ๋ค.
-
(#1): Job, Step์ ์์ฑํ๋ ๋น๋ ํฉํน๋ฆฌ
-
(#2): Job, Step์ ์ด๋ฆ์ ์ง์ ํด์ค๋ค.
-
(#3): tasklet์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฌดํ๋ฐ๋ณตํ๋ค. ๋๋ฌธ์ ์ด์ ๊ฐ์ ๊ฐ์ ๋ฐํํ์ฌ ํ๋ฒ ์คํ ํ ์ข ๋ฃํ ์ ์๋๋ก ํ๋ค.(๋ฐ๋ณต false)
-
Job Configuration์ ์ํด ์์ฑ๋๋ ๊ฐ์ฒด ๋จ์๋ก, ๋ฐฐ์น ๊ณ์ธต ๊ตฌ์กฐ์์ ๊ฐ์ฅ ์์์ ์๋ ๊ฐ๋
์ด๋ฉฐ ํ๋์ ๋ฐฐ์น์์
์์ฒด์ ํด๋นํ๋ค.(์ต์์ ์ธํฐํ์ด์ค)
๋ฐฐ์น ์์
์ ์ด๋ป๊ฒ ๊ตฌ์ฑํ๊ณ ์คํํ ์ง๋ฅผ ์ค์ ํ๊ณ ๋ช
์ธํด ๋์ ๊ฐ์ฒด๋ก ์ฌ๋ฌ step์ ํฌํจํ๋ ์ปจํ
์ด๋ ๋ก์์ ์ญํ ์ ํ๋ค. (1๊ฐ ์ด์์ Step)
-
- name : Job ์ด๋ฆ - restartable: ์ฌ์์ ์ฌ๋ถ ๊ธฐ๋ณธ๊ฐ true - JobRepository: ๋ฉํ๋ฐ์ด๋ ์ ์ฅ์ - JobExecutionListener: Job ์ด๋ฒคํธ ๋ฆฌ์ค๋ - JobParametersIncrementer: JobParameter ์ฆ๊ฐ๊ธฐ - JobParametersValidator: JobParameter ๊ฒ์ฆ๊ธฐ - SimpleStepHandler: Step์ ์คํํ๋ ํธ๋ค๋ฌ.- SimpleJob
- ์์ฐจ์ ์ผ๋ก Step์ ์คํ์ํค๋ Job์ผ๋ก, ํ์ค ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ค.(steps๋ฅผ ๊ฐ์ง๊ณ ์์)
- FlowJob
- ํน์ ์กฐ๊ฑด๊ณผ ํ๋ฆ์ ๋ฐ๋ผ Step์ ๊ตฌ์ฑํ๋ Job์ผ๋ก, Flow ๊ฐ์ฒด๋ฅผ ์คํ์์ผ ์์ ์ ์งํํ๋ค.
JobLauncher์ run(job, jobParameters) ๋ฉ์๋์์ job์ ๋ฐ์ ์คํ์ํค๊ฒ ๋๋๋ฐ, job.execute(execution)๋ก step์ ํ๋ํ๋ ์คํ์ํจ๋ค.
๊ตฌํ์ฒด์ธ SimpleJobLauncher ์ฝ๋๋ฅผ ๋ณด๋ฉด jobRepository์์ ํด๋น ์ก์ ๋ง์ง๋ง Execution์ ๊ฐ์ ธ์ ์ํ๋ฅผ ํ์ธํ ํ ์๋ก์ด JobExecution์ ์์ฑํ๊ณ ,์์ฑ๋ JobExecution์ผ๋ก Job์ ์คํํ๋ค. Job์ execute(AbstractJob ์) ์์๋ ๊ตฌํ์ฒด์ doExecute()๋ฅผ ํธ์ถํ๊ณ , ํด๋น ๋ฉ์๋์์ handleStep(step, jobExecution)์ ์คํ์ํจ๋ค.handleStep ์์๋ ๋ง์ฐฌ๊ฐ์ง๋ก AbstractStep ์ execute ๋ฅผ ํธ์ถํ๊ณ , ๊ตฌํ์ฒด์ doExecute๊ฐ ํธ์ถ๋๋ค.
- SimpleJob
Job์ด ์คํ๋ ๋ ์์ฑ๋๋ ๋
ผ๋ฆฌ์ ์คํ ๋จ์ ๊ฐ์ฒด๋ก ๊ณ ์ ํ๊ฒ ์๋ณ ๊ฐ๋ฅํ ์์
์คํ์ ๋ํ๋ธ๋ค.
๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค(BATCH_JOB_INSTANCE)์ ์ ์ฅํ๊ธฐ ์ํด ์์ฑ๋๋ ์ธ์คํด์ค์ด๋ค.
์ฒ์ ์์ํ๋ Job + JobParameter์ ๊ตฌ์ฑ์ผ ๊ฒฝ์ฐ ์๋ก์ด JobInstance๋ฅผ ์์ฑํ๊ณ , ์ด์ ๊ณผ ๋์ผํ ๊ตฌ์ฑ์ด๋ผ๋ฉด ์ด๋ฏธ ์กด์ฌํ๋ JobInstance๋ฅผ ๋ฆฌํดํ๋ค.
(๋์ผํ ๊ตฌ์ฑ์ผ๋ก ์คํํ ์ ์์ด ์์ธ๊ฐ ๋ฐ์ํ๊ณ Job์ ์คํ์ ์ค๋จํ๋ค ) A job instance already exists and is complete for parameters={ ... }
์คํ๋ ํ๋ผ๋ฏธํฐ๋ BATCH_JOB_EXECUTION_PARAMS์์ ํ์ธํ ์ ์์ผ๋ฉฐ ๋ด๋ถ์ ์ผ๋ก๋ job_name + params_key ์ ํด์๊ฐ์ ๊ฐ์ง๊ณ ์ธ์คํด์ค ๊ฐ์ฒด๋ฅผ ์๋ณํ๋ค.
Job์ ์คํํ ๋ ํจ๊ป ์ฌ์ฉ๋๋ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง ๋๋ฉ์ธ ๊ฐ์ฒด๋ก, ํ๋์ JobInstance๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํ ์ฉ๋๋ก ์ฌ์ฉ๋๋ค.
-
JobParameters:
LinkedHashMap<String, Parameter>๋ฅผ ๋ฉค๋ฒ๋ณ์๋ก ๊ฐ์ง๋ Wrapper ํด๋์ค. -
JobParameter:
Object parameter,ParameterType parameterType,boolean identifying -
ParameterType:
String,Date,Long,Double -
- ์ดํ๋ฆฌ์ผ์ด์
์คํ์ ์ต์
์ผ๋ก ์ฃผ์
.
Java -jar batch.jar name=user1 seq(long)=2L date(date)=2022/03/28 weight(double)=70.5
- ์ฝ๋์์ ์์ฑ
JobParameterBuilder,DefaultJobParametersConverterJobParameters jobParameters = new JobParametersBuilder() .addString("name", "kim2") .addLong("seq", 1L) .addDate("data", new Date()) .addDouble("weight", 70.5) .toJobParameters();
- SpEL ์ด์ฉ
- @Value("#{jobParameter[requestDate]}")
- ์ดํ๋ฆฌ์ผ์ด์
์คํ์ ์ต์
์ผ๋ก ์ฃผ์
.
-
// StepContribution์์ ๊บผ๋ด๊ธฐ JobParameters jobParameters = contribution.getStepExecution().getJobExecution().getJobParameters(); jobParameters.getParameters() // Map<String, parameter> jobParameters.getString("key"); jobParameters.getDate("key"); jobParameters.getLong("key"); jobParameters.getDouble("key"); // ChunkContext ์์ ๊บผ๋ด๊ธฐ Map<String, Object> chunkJobParameters = chunkContext.getStepContext().getJobParameters();
JobInstance์ ๋ํ ํ๋ฒ์ ์๋๋ฅผ ์๋ฏธํ๋ ๊ฐ์ฒด๋ก, ์คํ ์ค์ ๋ฐ์ํ ์ ๋ณด๋ค์ ์ ์ฅํ๊ณ ์๋ ๊ฐ์ฒด์ด๋ค.
- ์์ ์๊ฐ, ์ข ๋ฃ์๊ฐ, ์ํ(์์?, ์๋ฃ?, ์คํจ?), ์ข ๋ฃ์ํ
JobExecution์ ์คํ ๊ฒฐ๊ณผ๊ฐ COMPLETED ์ด๋ฉด ์ธ์คํด์ค์ ์คํ์ด ์๋ฃ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํด์ ์ฌ ์คํํ ์ ์๋ค.
FAILED๋ผ๋ฉด, ์คํ์ด ์๋ฃ๋์ง ์์ ๊ฒ์ด๋ฏ๋ก ์ฌ์คํ์ด ๊ฐ๋ฅํ๋ค.(JobParameter๊ฐ ๊ฐ๋๋ผ๋) ์ฆ, ์คํ ๊ฒฝ๊ณผ๊ฐ COMPLETED๊ฐ ๋ ๋๊น์ง ์คํ์ด ๊ฐ๋ฅํ๋ค.
(ํ Instance ๋ด์์ ์ฌ๋ฌ๋ฒ์ ์๋๊ฐ ๋ฐ์ํ ์ ์์, JobInstance์ N:1)
๋์ผํ Job Instance์ ๋ํด ์ฑ๊ณปํ ๋๊น์ง Execution์ด ์์ฑ๋จ์ ํ์ธํ ์ ์๋ค.
Batch Job์ ๊ตฌ์ฑํ๋ ๋
๋ฆฝ์ ์ธ ํ๋์ ๋จ๊ณ๋ก, ์ค์ ๋ฐฐ์น๋ด ์ฒ๋ฆฌํ๋ ๋ชจ๋ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋ ๋๋ฉ์ธ ๊ฐ์ฒด์ด๋ค.
๋ฐฐ์น์์
์ ์ด๋ป๊ฒ ๊ตฌ์ฑํ๊ณ ์คํํ ๊ฒ์ธ์ง ์ธ๋ถ์์
์ Task ๊ธฐ๋ฐ์ผ๋ก ์ค์ ํ๊ณ ๋ช
์ธํด ๋์ ๊ฐ์ฒด.
-
- name
- startLimit: ์คํ ์ ํ ํ์.
- allowStartIfComplete: ์๋ฃ ํ ์ฌ์คํ ๊ฐ๋ฅ์ฌ๋ถ.
- stepExecutionListener: ์ด๋ฒคํธ ๋ฆฌ์ค๋.
- jobRepository: ๋ฉํ๋ฐ์ดํฐ ์ ์ฅ.
-
- TaskletStep: ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ์ฒด, Taklet ํ์ ์ ๊ตฌํ์ฒด๋ฅผ ์ ์ดํ๋ค.
- PartitionStep: ๋ฉํฐ ์ค๋ ๋ ๋ฐฉ์์ผ๋ก ์คํ ์ ์ฌ๋ฌ๊ฐ๋ก ๋ถ๋ฆฌ ์คํํ๋ค.
- JobStep: Step ๋ด์์ Job์ ์คํํ๋ค.( Job -> Step -> Job .. )
- FlowStep: Step ๋ด์์ Flow๋ฅผ ์คํํ๋๋ก ํ๋ค.
Step์ ์คํ์ํค๋ execute(StepExecution)๊ฐ ์๊ณ , StepExecution์๋ ์คํ ๊ฒฐ๊ณผ์ ์ํ๊ฐ ์ ์ฅ๋๋ค.
-
- Tasklet ์ง์ ์์ฑ
stepBuilderFactory.get("myStep1") .tasklet(myTasklet()) .build();
- ChunkOrientedTasklet
stepBuilderFactory.get("myStep3") .<String, String>chunk(100) // <input, output> .reader(reader()) .processor(processor()) .writer(writer()) .build();
- JobStep
stepBuilderFactory.get("jobStep") .job(myJob()) .launcher(jobLauncher) .parametersExtractor(jobParametersExtractor())2 .build();
- FlowStep
stepBuilderFactory.get("jobStep") .flow(myFlow()) .build();
- Tasklet ์ง์ ์์ฑ
- Step์ ๋ํ ํ๋ฒ์ ์๋๋ฅผ ์๋ฏธํ๋ ๊ฐ์ฒด๋ก ์คํ์ค ๋ฐ์ํ ์ ๋ณด๋ค์ ์ ์ฅํ๊ณ ์๋ ๊ฐ์ฒด. (์์,์ข ๋ฃ ์๊ฐ, ์ํ, commit count, rollback count ...)
- Job์ด ์ฌ์์ ๋๋๋ผ๋ ์ด๋ฏธ ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ Step์ skip ํ๊ณ , ์คํจํ๋ Step๋ง ์คํ๋๋ค.(allowStartIfComplete ๋ก ์ค์ ๊ฐ๋ฅ.)
- ๋ชจ๋ StepExecution์ด ์ฑ๊ณตํด์ผ JobExecution๋ ์ฑ๊ณต์ผ๋ก ๋๋๋ค.
-
์ฒญํฌ ํ๋ก์ธ์ค์ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ฅํด๋๋ค๊ฐ StepExecution์ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํ๋ ๋๋ฉ์ธ ๊ฐ์ฒด์ด๋ค.
-
์ฒญํฌ ์ปค๋ฐ ์ง์ ์ StepExecution์ apply()๋ฅผ ํธ์ถํ์ฌ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํ๋ค.
-
์ฌ์ฉ์ ์ ์ ExitStatus๋ฅผ ์ง์ ํ ์ ์๋ค.
-
- stepExecution
- read, write, filter(ItemProcessor์ ์ํด ํํฐ๋ง๋) count
- parent(StepExecution), read, write, process SkipCount
- ExitStatus
TaskletStep -> StepExecution -> StepContribution ์์ผ๋ก ์์ฑ๋๊ณ ,
chunkOrientedTasklet๊ณผ ๊ฐ์ ๊ตฌํ์ฒด์์ ์คํ๋ ItemReader, Processor, Writer ์ ์ํ๋ค์ด StepContribution์ ์ ์ฅ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ์ต์ข ์ ์ผ๋ก ์ปค๋ฐ๋๊ธฐ ์ ์ StepExecution์ ์ ์ฅํด๋๋ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํ๋ค.
Step, Job Execution ๊ฐ์ฒด์ ์ํ๋ฅผ ์ ์ฅํ๋ ๊ณต์ ๊ฐ์ฒด๋ก key:value ์์ผ๋ก ๋ ์ปฌ๋ ์ ์ด๋ฉฐ DB์ ์ง๋ ฌํ ํ ๊ฐ์ผ๋ก ์ ์ฅ๋๊ฒ ๋๋ค.
-
StepExecution ์ ๊ฐ์ Step ๊ฐ ๊ณต์ ๋ถ๊ฐ๋ฅ.
-
JobExecution ์ ๊ฐ์ Job ๊ฐ ๊ณต์ ๋ ์๋์ง๋ง, Job์ Step๊ฐ ๊ณต์ ๋ ๊ฐ๋ฅํ๋ค.(ํ์ํ ์ ๋ณด๋ฅผ ์ ์ฅํด๋๋ค ๊บผ๋ด์ฐ๊ธฐ์ ์ ์ฉํ ๊ฒ ๊ฐ๋ค)
Job ์ฌ์์์ ์ด๋ฏธ ์ฒ๋ฆฌํ ๋ฐ์ดํฐ๋ฅผ Skipํ๊ณ ์ํํ ๋ ํด๋น ์ํ ์ ๋ณด๋ฅผ ํ์ฉํ๋ค.
-
ExecutionContext ๊ฐ์ ธ์ค๊ธฐ.
ExecutionContext jobExecutionContext = contribution.getStepExecution().getJobExecution().getExecutionContext(); ExecutionContext stepExecutionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();
ChunkContext, Contribution ๊ฐ์ฒด ๋๋ค์์ ๊ฐ์ ธ์ค๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
get, put ๋ฉ์๋๋ ExecutionContext์ Map<String,Object> ์์ ๊ฐ์ ๋ฃ๊ณ , ๊ฐ์ ธ์ค๋ ๋ฉ์๋์ด๋ค. ์ปค๋ฐ ์์ ์ DB์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค.
JobInstance ๊ฐ ๋์ผํ๊ณ , ์ด์ ์คํ์ด COMPLETED ์ํ๊ฐ ์๋๋ผ๋ฉด ์ด์ ๊น์ง์ ExecutionContext์ ์ ์ฅ๋ ๊ฐ์ ๋ถ๋ฌ์จ ํ, ๋๋จธ์ง Step์ ๋ค์ ์คํํ๋ค. -
getJob(Step)ExecutionContext?
Map<String, Object> jobExecutionContext = chunkContext.getStepContext().getJobExecutionContext(); Map<String, Object> stepExecutionContext = chunkContext.getStepContext().getStepExecutionContext();
์๊ธฐ์ getJobExecutionContext, getStepExecutionContext๋ ExecutionContext๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ด ์๋ ์ ์ฅ๋์ด ์๋ ๊ฐ์ ๋ณต์ฌํด ๋๋ ค์ฃผ๋ ๋ฉ์๋์ด๋ค.
์ค์ ๋ก ๋ฉ์๋๋ฅผ ์ดํด๋ณด์์ ๋ Map์ ๋ง๋ค์ด ๋ด์ฉ์ ๋ณต์ฌํ๊ณ ์ด๋ฅผ unmodifiableMap ์ผ๋ก ๋๋ ค์ค์ ํ์ธํ ์ ์์๋ค.
๋ฐฐ์น ์์
์ค์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ์ ์ฅ์๋ก, ๋ฐฐ์น ์์
์ ์ํ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค.
JobLauncher, Job, Step ๊ตฌํ์ฒด ๋ด๋ถ์์ CRUD ๊ธฐ๋ฅ์ ์ฒ๋ฆฌํ๋ค.
-
- isJobInstanceExist(jobName, jobParameters)
- createJobExecution(jobName, jobParameters)
- getLastJobExecution(jobName, jobParameters)
- getLastStepExecution(jobInstance, stepName)
- update(jobExecution): Job์ ์คํ ์ ๋ณด ์ ๋ฐ์ดํธ
- update(stepExecution)
- add(stepExecution): ์คํ ์ค์ธ Step์ ์๋ก์ด stepExecution ์ ์ฅ.
- updateExecutionContext(jobExecution)
- updateExecutionContext(stepExecution)
@EnableBatchProcessing ์ ๋
ธํ
์ด์
์ ์ ์ธํ๋ฉด JobRepository๊ฐ ์๋์ผ๋ก ๋น์ผ๋ก ๋ฑ๋ก๋๋ค.
BatchConfigurer ์ธํฐํ์ด์ค๋ ๊ตฌํ์ด๋ค BasicBatchConfigurer๋ฅผ ์์ํ์ฌ jobRepository๋ฅผ ์ปค์คํ
ํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
-
JDBC ๋ฐฉ์์ผ๋ก ์ค์ ํ๊ธฐ ์ํด์๋
JobRepositoryFactoryBean์ ์ฌ์ฉํ๋๋ฐ, AOP ๋ฐฉ์์ผ๋ก ํธ๋์ญ์ ์ฒ๋ฆฌ๊ฐ ์ด๋ฃจ์ด์ง๋ค. ๊ฒฉ๋ฆฌ ๋ ๋ฒจ์ ๊ธฐ๋ณธ์ ์ผ๋กSERIALIZEBLE์ด๊ณ , ๋ค๋ฅธ ๋ ๋ฒจ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅํ๋ค.
ํ ์ด๋ธ์ ๊ธฐ๋ณธ prefix๋ "BATCH_"์ด๋ฉฐ ๋ณ๊ฒฝ ๊ฐ๋ฅํ๋ค.@Configuration public class CustomBatchConfigurer extends BasicBatchConfigurer { private final DataSource dataSource; protected CustomBatchConfigurer(BatchProperties properties, DataSource dataSource, TransactionManagerCustomizers transactionManagerCustomizers) { super(properties, dataSource, transactionManagerCustomizers); this.dataSource = dataSource; } @Override protected JobRepository createJobRepository() throws Exception { JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean(); factoryBean.setDataSource(dataSource); // ์ค์ ํ์ง ์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค์ ๋จ. factoryBean.setTransactionManager(getTransactionManager()); // BasicBatchConfigurer์ ์๋ ๋ฉ์๋ factoryBean.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED"); factoryBean.setTablePrefix("LOG_BATCH"); return factoryBean.getObject(); } }
-
DB์ ์ ์ฅ๊น์ง๋ ํ์๊ฐ ์๋ค๋ฉด
MapJobRepositoryFactoryBean์ ์ฌ์ฉํ์ฌ ์ธ๋ฉ๋ชจ๋ฆฌ๋ก ์ฌ์ฉํ ์๋ ์๋ค. -
JobExecution lastJobExecution = jobRepository.getLastJobExecution(jobName, jobParameters); if(lastJobExecution != null) { lastJobExecution.getStepExecutions() .forEach(s -> System.out.println(s.getExitStatus())); }
Job๊ณผ ํ๋ผ๋ฏธํฐ๋ฅผ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ ๋ฐฐ์น ์์
์ ์คํ์ํจ ํ ํด๋ผ์ด์ธํธ์๊ฒ JobExecution์ ๋ฐํํ๋ค.
์คํ๋ง ๋ถํธ ๋ฐฐ์น๊ฐ ๊ตฌ๋๋๋ฉด ์๋์ผ๋ก ๋น์ด ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ๋ง๋ค์ด์ฃผ์ง ์์๋ ๋๋ค.
ApplicationRunner๋ฅผ ๊ตฌํํ JobLauncherApplicationRunner๊ฐ JobLauncher๋ฅผ ์๋์ผ๋ก ์คํ์ํค๊ฒ ๋๋ค.
๋๊ธฐ์ (SyncTaskExecutor), ๋น๋๊ธฐ์ (SimpleAsyncExecutor) ์คํ์ด ๊ฐ๋ฅํ๋ฉฐ ๊ธฐ๋ณธ๊ฐ์ ๋๊ธฐ์ ์คํ์ด๋ค.
๋ ๋ฐฉ์์ ์ฐจ์ด๋ ์ธ์ JobExecution์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋๋์ด๋ค. ๋๊ธฐ์ ๋ฐฉ์์ ๋ฐฐ์น ์ฒ๋ฆฌ๊ฐ ์ต์ข
์ ์ผ๋ก ์๋ฃ๋๋ฉด ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ์ง๋ง,
๋น๋๊ธฐ์ ์คํ์์๋ JobExecution์ ํ๋ํ๋ฉด ๋ฐ๋ก ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ค.(ExitStatus.UNKNOWN)
๋๊ธฐ์ ์คํ์ ์ค์ผ์ค๋ฌ์ ์ํ ๋ฐฐ์น์ฒ๋ฆฌ์ ๊ฐ์ด ๋ฐฐ์น์ฒ๋ฆฌ์๊ฐ์ด ๊ธธ์ด๋ ์๊ด ์๋ ๊ฒฝ์ฐ์ ์ ํฉํ๊ณ , ๋น ๋๊ธฐ์ ์คํ์ HTTP์์ฒญ์ ์ํ ๋ฐฐ์น ์ฒ๋ฆฌ์ ์ ํฉํ๋ค.
- ๋น ๋๊ธฐ์ ์คํ
@RequiredArgsConstructor
@RestController
public class JobLauncherController {
private final Job job;
private final BasicBatchConfigurer basicBatchConfigurer;
@PostMapping("/batch")
public String launch(@RequestBody Member member)
throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
SimpleJobLauncher jobLauncher = (SimpleJobLauncher) basicBatchConfigurer.getJobLauncher();
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.run(job, new JobParametersBuilder()
.addString("id", member.getId())
.addDate("date", new Date())
.toJobParameters());
return "batch completed";
}setTaskExecutor๋ JobLauncher์ ๋ฉ์๋๊ฐ ์๋ SimpleJobLauncher์ ๋ฉ์๋์ด๊ธฐ ๋๋ฌธ์ ๋น์ผ๋ก ์ฃผ์
๋ฐ์ ์ฌ์ฉํ ์ ์๋ค.
JobLauncher ์ธํฐํ์ด์ค๋ก ์ฃผ์
๋ฐ๋๋ผ๋ ํ๋ก์ ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ์ SimpleJobLauncher ๋ก์ ๊ฐ์ ํ๋ณํ ๋ํ ๋ถ๊ฐ๋ฅํ๋ค.
๋๋ฌธ์ BasicBatchConfigurer์์ ํ๋ก์๊ฐ ์๋ ์ค์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ ํ์
์บ์คํ
์ ํด์ค๋ค.
- ApplicationRunner์ ๊ตฌํ์ฒด๋ก BatchAutoConfifuration์์ ์์ฑ๋๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ๋น์ผ๋ก ๋ฑ๋ก๋ ๋ชจ๋ job์ ์คํ์ํจ๋ค.(ํน์ job๋ง ์คํํ๋๋ก ์ค์ ๋ ๊ฐ๋ฅ.)
- ํ๊ฒฝ ์ค์ ํด๋์ค๋ก job ์ด๋ฆ, ์คํค๋ง ์ด๊ธฐํ ์ค์ , ํ ์ด๋ธ prefix ๋ฑ ์ค์ ๊ฐ๋ฅ.
- properties | yml ํ์ผ์ ์ค์ ๊ฐ๋ฅํ๋ค.
batch.job.names,batch.initialize-schema: never | always | embedded,batch.tablePrefix:...
Spring.batch.jhob.names: ${job.name:NONE}์ง์ ํ Job๋ง ์คํํ๋๋ก ํ๋ค.- NONE๋ ์์์ ๋ฌธ์.
--job.name=name1, name2- properties๋ฅผ ์ค์ ํด๋๊ณ ์ต์
์ ์ฃผ์ง ์์ผ๋ฉด ์๋ฌด Job๋ ์คํ๋์ง ์๋๋ค.
Job์ ์ฝ๊ฒ ์์ฑํ๊ณ ์ค์ ํ ์ ์๋๋ก util ์ฑ๊ฒฉ์ ๋น๋ ํด๋์ค์ธ JobBuilderFactory๋ฅผ ์ ๊ณตํ๋ค.
JobBuilderFactory ์์๋ JobBuilder(SimpleJobBuilder, FlowBuilder)๋ฅผ ์์ฑํ์ฌ Job์ ์์ฑ์ ์์ํ๋ค.
-
JobBuilderFactory๋ฅผ ํตํด์ JobBuilder๋ฅผ ์์ฑํ๊ณ
start(step)๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด SimpleJobBuilder๊ฐ ์์ฑ๋๊ณ ์ต์ข ์ ์ผ๋กSimpleJob์ด ์์ฑ๋๋ค. -
JobBuilder์์
start(flow)๋๋flow(step)์ ์คํํ๋ฉด FlowJobBuilder๋ฅผ ์์ฑํ๊ณ , ์ต์ข ์ ์ผ๋กFlowJob์ด ์์ฑ๋๋ค.
FlowJobBuilder ์์๋ ๋ ๋ด๋ถ์ ์ผ๋ก JobFlowBuilder -> FlowBuilder๋ฅผ ์์ฑํ๊ณ , ์ฌ๊ธฐ์Flow๋ฅผ ์์ฑํ๊ฒ ๋๋ค.
public JobBuilder get(String name) {
JobBuilder builder = new JobBuilder(name).repository(jobRepository);
return builder;
}SimpleJobBuilder์ FlowJobBuilder๋ JobBuilderHelper ํด๋์ค๋ฅผ ์์ํ๋ฉฐ, ํด๋น ํด๋์ค๋ค์์ ๋ง๋ค์ด์ง Job๋ค์ SimpleJobRepository๊ฐ ์ ๋ฌ๋์ด CRUD๋ฅผ ํตํด ๋ฉํ์ ๋ณด๋ค์ ๊ธฐ๋กํ๊ฒ ๋๋ค.
-
- start() ์์ ์ฒ์ ์คํํ step์ ์ค์ ํ๊ณ SImpleJobBuilder๋ฅผ ์์ฑ, ๋ฐํํ๋ค, ํ next() ์์๋ ์์ฐจ์ ์ผ๋ก ์คํํ step์ ๋ฑ๋กํ๋ค.
-
- JobParameters์ ๊ฐ์ ์ฆ๊ฐ์์ผ ๋ค์์ ์ฌ์ฉ๋ ๊ฐ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
- Long ๊ฐ์ ๋ฃ์ด์ฃผ๊ณ , ์คํํ ๋ ๋ง๋ค ํด๋น ๊ฐ์ ์ฆ๊ฐ์ํจ๋ค.
- ๊ธฐ์กด์ ํ๋ผ๋ฏธํฐ ๊ฐ์๋ ๋ณํ๊ฐ ์์ง๋ง ๋ฃ์ด์ค Long ๊ฐ์ด ๋ณํ๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ๋ฒ ์คํ ๊ฐ๋ฅํ๋ค.
- RunIdIncrementer ๊ตฌํ์ฒด๋ฅผ ์ง์ํ๋ฉฐ, ํ์ํ๋ค๋ฉด ์ธํฐํ์ด์ค๋ฅผ ์ง์ ๊ตฌํํ์ฌ ์ ์ํ ์ ์๋ค.
@Override public JobParameters getNext(JobParameters parameters) { String date = new SimpleDateFormat("yyyyMMdd-hhmmss").format(new Date()); return new JobParametersBuilder(parameters).addString("run.date", date).toJobParameters(); }
- ๐ก incrementer.getNext()๊ฐ ์ ์ฉ๋ ํ ApplicationRunner๋ค์ ์คํํ๊ธฐ ๋๋ฌธ์ Runner ํด๋์ค์์ jobParameters๋ฅผ ๋ฃ์ด์ค๋ค๋ฉด
incrementer์ด ์ ์ฉ๋์ง ์์ ์ ์๋ค.(๋ฎ์ด์์ ์ง)
- ๐ก incrementer.getNext()๊ฐ ์ ์ฉ๋ ํ ApplicationRunner๋ค์ ์คํํ๊ธฐ ๋๋ฌธ์ Runner ํด๋์ค์์ jobParameters๋ฅผ ๋ฃ์ด์ค๋ค๋ฉด
- JobParameters์ ๊ฐ์ ์ฆ๊ฐ์์ผ ๋ค์์ ์ฌ์ฉ๋ ๊ฐ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
-
- restartable ์ default ๊ฐ์ true, .preventRestart() ํ๋ฉด false๋ก ๋ณ๊ฒฝ๋จ.
- ํด๋น ์ต์ ์ false๋ก ์ฃผ๊ฒ ๋๋ฉด job์ ์คํ์ด ์คํจํด๋ ์ฌ์์์ด ๋ถ๊ฐ๋ฅํ๋ค. (JobRestartException ๋ฐ์)
- SimpleJobLaunch์์ lastJobExecution์ ๊ฐ์ ธ์จ ๋ค ์กฐ๊ฑด์ ํ์ธํ๋ค.
-
- DefaultJobParametersValidator ๊ตฌํ์ฒด๋ฅผ ์ง์ํ๋ค.
public DefaultJobParametersValidator(String[] requiredKeys, String[] optionalKeys)- ํ์ํค๊ฐ ์๊ฑฐ๋, ํ์ํค, ์ต์ ํค ๋๋ค์ ์๋ ํ๋ผ๋ฏธํฐ๊ฐ ๋ค์ด์ค๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
- ์ปค์คํ
ํ ์ ์ฝ ์กฐ๊ฑด์ ์์ฑํ๊ณ ์ถ๋ค๋ฉด ์ธํฐํ์ด์ค๋ฅผ ์ง์ ๊ตฌํํ ์๋ ์๋ค.
@Override public void validate(JobParameters parameters) throws JobParametersInvalidException { if(parameters.getString("name") == null) { throw new JobParametersInvalidException("name parameters is null"); } }
- DefaultJobParametersValidator ๊ตฌํ์ฒด๋ฅผ ์ง์ํ๋ค.
-
@Component public class JobListener implements JobExecutionListener { @Override public void beforeJob(JobExecution jobExecution) { } @Override public void afterJob(JobExecution jobExecution) { } }
- JobLauncher ์์ Job, JobParameter๋ฅผ ๊ฐ์ง๊ณ JobInstance๋ฅผ ์์ฑ
- JobExecution์ ์์ฑํ๊ณ , ExecutionContext ํ ๋น.
- JobExecutionListener.beforeJob()
- ๊ฐ Step์ด ์คํ๋๋ฉฐ StepExecution,ExecutionContext ์์ฑ
- StepExecution์ ์ต์ข ์ํ ์ ๋ฐ์ดํธ.
- JobListener.afterJob() ํธ์ถ
- JobExecution์ ์ต์ข ์ํ ์ ๋ฐ์ดํธ.(Status, ExitStatus)
- JobLauncher์ ๋ฐํ.
StepBuilder๋ฅผ ์์ฑํ๋ ํฉํ ๋ฆฌ ํด๋์ค. ๊ตฌ์กฐ๋ JobBuilderFactory์ ์ ์ฌํ๋ค.
-
-
- API: tasklet(tasklet())
-
- TaskletStepBuilder์ ๋ง์ฐฌ๊ฐ์ง๋ก TaskletStep์ ์์ฑํ์ง๋ง, ๋ด๋ถ์ ์ผ๋ก ์ฒญํฌ๊ธฐ๋ฐ์ ์์ ์ ์ฒ๋ฆฌํ๋ ChunkOrientedTasklet์ ์์ฑํ๋ค.
- API: chunk(chunkSize) | chunk(completionPolicy)
-
- PartitionStep์ ์์ฑํ๋ฉฐ ๋ฉํฐ ์ค๋ ๋ ๋ฐฉ์์ผ๋ก Job์ ์คํํ๋ค.
- API: partitioner(stepName, partitioner) | partitioner(step)
-
- JobStep์ ์์ฑํ๊ณ , Step์์์ Job์ ์คํํ๋ค.
- API: job(job)
-
- FlowStep์ ์์ฑํ๊ณ , Step์์์ Flow๋ฅผ ์คํํ๋ค.
- API: flow(flow)
TaskletStepBuilder์ SimpleStepBuilder๋ StepBuilderHelper๋ฅผ ์์๋ฐ์ AbstractTaskletStepBuilder๋ฅผ ์์๋ฐ๊ณ ,
๋๋จธ์ง ๋น๋๋ค์ StepBuilderHelper๋ฅผ ์ง์ ์์ ๋ฐ๋๋ค. -
Tasklet์ ์คํ๋ง ๋ฐฐ์น์์ ์ ๊ณตํ๋ Step์ ๊ตฌํ์ฒด๋ก Tasklet์ ์คํ์ํจ๋ค.
Task ๊ธฐ๋ฐ๊ณผ Chunk ๊ธฐ๋ฐ์ด ์์ผ๋ฉฐ, RepeatTEmplate๋ฅผ ์ฌ์ฉํ์ฌ Tasklet ๊ตฌ๋ฌธ์ ํธ๋์ญ์
๋ด์์ ๋ฐ๋ณต ์คํํ๋ค.
-
- ๋จ์ผ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋๋ ๊ฒ์ด ๋ ๋์ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ค.
- Tasklet ๊ตฌํ์ฒด๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉํ๋ค.
@Bean public Step myStep() { return stepBuilderFactory.get("myStep") .tasklet(new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { return RepeatStatus.FINISHED; } }) .build(); }
-
- n๊ฐ์ ์กฐ๊ฐ์ผ๋ก ๋๋์ด ์คํํ๋ค, ๋๋ ์ฒ๋ฆฌ์ ํจ๊ณผ์ ์ผ๋ก ๋์ฒํ ์ ์๋๋ก ์ค๊ณ ๋์๋ค.
- ChunkOrientedTasklet ๊ตฌํ์ฒด๊ฐ ์ ๊ณต๋๋ฉฐ, ItemReader, ItemProcessor, ItemWriter์ ์ฌ์ฉํ๋ค.
@Bean public Step chunkStep() { return stepBuilderFactory.get("chunkStep") .<String, String>chunk(3) .reader(new ItemReader<String>() { @Override public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { if(!strings.isEmpty()) { return strings.remove(0); } return null; } }) .processor(new ItemProcessor<String, String>() { @Override public String process(String item) throws Exception { return item.toUpperCase(Locale.ROOT); } }) .writer(new ItemWriter<String>() { @Override public void write(List<? extends String> items) throws Exception { items.forEach(System.out::println); } }) .build(); }
-
-
- ๋ฐ๋ณต์ ์ผ๋ก ์ํ๋๋ Tasklet ํ์ ์ ํด๋์ค๋ฅผ ์ค์ ํ๋ค.
- ๋ฐํ ๊ฐ์ ๋ฐ๋ผ ๋ฐ๋ณต ์ฌ๋ถ๋ ๋ณ๊ฒฝ ๊ฐ๋ฅ,
RepeatStatus.FINISHED,RepeatStatus.CONTINUABLE - ํ๊ฐ๋ง ์ค์ ์ด ๊ฐ๋ฅํ๊ณ , ์ฌ๋ฌ๊ฐ ์ค์ ์ ๋ง์ง๋ง ์ค์ ๋ง ์คํ๋๋ค.
- ์ต๋ช ๋๋ Tasklet์ ๊ตฌํํ๋ค.
- execute()๋ StepContribution, ChunkContext๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
-
- Step์ ์ต๋ ์คํ ํ์ ์ค์ , ๊ธฐ๋ณธ๊ฐ์ INTEGER.MAX_VALUE, ์ด๊ณผํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
- Step ๋ง๋ค ๊ฐ๋ณ๋ก ์ค์ ํ๋ค.
- ๋์ผํ JobInstance์์ ์คํ๋๋ ๋์ผํ Step์ ๋ํ ์คํ ํ์ ์ ํ์ด๋ค.
-
- Job์ ์ฌ์์ํ ๋ Step์ ์ด์ ์คํ์ ์ฑ๊ณต ์ฌ๋ถ์ ์๊ด ์์ด ํญ์ Step์ ์คํํ๊ธฐ ์ํ ์ค์ ์ด๋ค.
- Step 1~4์ค์ 1,2๊น์ง ์ฑ๊ณตํ๊ณ ์คํจํ์ฌ Job์ ์ฌ์คํํ ์ ์์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก๋ 3๋ถํฐ ๋ค์ ์์ํ๋ค.
ํ์ง๋ง 1,2์ ์์ ์ด ๋ฌด์กฐ๊ฑด ์ ํ๋์ด์ผ ํ๋ Flow๋ผ๋ฉด, ํด๋น ์ต์ ์ ํ์ฑํํ์ฌ 1๋ถํฐ ์์ํ๋๋ก ๋ณ๊ฒฝํ ์ ์๋ค.
-
- Step์ ์คํ ์ ํ์ ์ฝ๋ฐฑ.
-
- ExecutionContext๋ฅผ ๊ฐ์ง๋ StepExecution์ด ์์ฑ๋๋ค.
- TaskletStep์์ StepExecution์ ๋ฐ์ Step์ ์คํ์ํจ๋ค.
- StepExecutionListener.beforeStep()์ ํธ์ถํ๋ค.
- RepeatTemplate ์์ Tasklet์ ๋ฐ๋ณต ์คํํ๋ค.
loop ์์๋ RepeatStatus๋ฅผ ํ์ธํ์ฌ FINISHED ๋ผ๋ฉด ๋ฃจํ๋ฅผ ๋น ์ ธ๋์ค๊ณ CONTINUABLE ์ด๋ผ๋ฉด ๋ค์ RepeatTemplate์์ Tasklet์ ๋ฐ๋ณต์ํจ๋ค. - StepExecution์ Status๋ฅผ ์ ๋ฐ์ดํธ ํ๋ค.
- StepExecutionListener.afterStep()์ ํธ์ถํ๋ค.
- StepExecution์ ExitStatus๋ฅผ ์ ๋ฐ์ดํธ ํ๋ค.
๋ ๋ค๋ฅธ Job์ ์คํ์ํค๋ Step์ผ๋ก, ์์คํ ์ ์์ ๋ชจ๋๋ก ์ชผ๊ฐ Job์ ํ๋ฆ์ ๋๋๊ณ ์ ํ ๋ ์ฌ์ฉํ๋ค.
-
-
- ์คํํ Job์ ์ค์ ํ๋ค.
- ์ถ๊ฐํ Job๋ Bean ์ผ๋ก ๋ฑ๋กํ๋ฉด ์๋์ผ๋ก ์คํ๋์ด 2๋ฒ ์คํ๋ ์ ์์ผ๋ฏ๋ก ์ค์ ์ด ํ์ํ๋ค.
-
- Job์ ์คํํ JobLauncher๋ฅผ ์ค์ ํ๋ค.
- null์ ๋๊ฒจ ์ฃผ๋ฉด SimpleJobLauncher๋ก ์คํํ๋ค.
// JobBuilder if (jobLauncher == null){ SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); ... }
-
-
Step์ ExecutionContext์์ ๊ฐ์ ์ถ์ถํด JobParameters๋ก ๋ณํํ๋ค.
-
์ ๊ณต๋๋ DefaultJobParametersExtractor๋
JobParametersExtractor๋ฅผ ๊ตฌํํ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ๋ค. -
๋ถ๋ชจ์ JobParameter๋ค์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ถ๊ฐ๋๊ณ , setKeys() ๋ฅผ ์ด์ฉํด StepExecution์ ExecutionContext์์ ๊ฐ์ ์ฐพ์ ํ๋ผ๋ฏธํฐ์ ์ถ๊ฐํ ์ ์๋ค.
// ํด๋น Step์ ExecutionContext์ ๊ฐ ์ถ๊ฐ. .listener(new StepExecutionListener() { @Override public void beforeStep(StepExecution stepExecution) { stepExecution.getExecutionContext().put("date", new Date()); } @Override public ExitStatus afterStep(StepExecution stepExecution) { return null; } })
private JobParametersExtractor jobParametersExtractor() { DefaultJobParametersExtractor extractor = new DefaultJobParametersExtractor(); extractor.setKeys(new String[] {"date"}); return extractor; }
๋ถ๋ชจ Job(7), jobStep์ Job(8)
-
-
Step์ ์์ฐจ์ ์คํ์ด ์๋๋ผ ์ํ์ ๋ฐ๋ผ ํ๋ฆ์ ์ ํํ๋๋ก ๊ตฌ์ฑํ ์ ์๋ค.
- Step์ด ์คํจํ๋๋ผ๋ Job์ ์คํจํ์ง ์๊ฒ ํ ์ ์๋ค.
- ๋ค์์ ์คํํ step์ ๊ตฌ๋ถํ์ฌ ์คํํ ์ ์๋ค.
- ex) ์ฑ๊ณต์ Step2, ์คํจ์ Step3 ...
- ํน์ Step์ ์คํ๋์ง ์๊ฒ ๊ตฌ์ฑํ ์ ์๋ค.
JobBuilderFactory โถ JobBuilder โถ JobFlowBuilder โถ FlowBuilder โถ FlowJob
-
-
- ์ด์ ์ ์ ์ํ Step์ flow๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ์ ์ํ๋ค.(Transition์ ์๋กญ๊ฒ ์ ์.)
-
- Step์ ExitStatus๋ฅผ ์บ์นํ๊ณ ํจํด๊ณผ ๋งค์นญ๋๋ฉด,
TransitionBuilder๋ฅผ ๋ฐํํ๋ค. - Step์ ExitStatus๊ฐ on()์ ์ด๋ค Pattern ๊ณผ๋ ๋งค์นญ์ด ๋์ง ์๋๋ค๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๊ณ Job์ ์คํจํ๊ฒ ๋๋ค.
*: ์์ผ๋ ์นด๋, ๋ชจ๋ ExitStatus์ ๋งค์นญ(C*, F*, *),?: ์ ํํ 1๊ฐ์ ๋ฌธ์์ ๋งค์นญ (C?T)- ์์ผ๋ ์นด๋๋
else์ฒ๋ผ ์ฌ์ฉ๋ ์ ์๋ค. step1์ on("COMPLETED") to(step2())๋ฅผ ์คํํ๋ค๊ณ ์ง์ ํด๋๊ณ ,
from(step1()).on("*") ์ฌ์ฉํ๋ฉด COMPLETED๋ฅผ ์ ์ธํ ๋ชจ๋ ํจํด์ ๋ปํ๊ฒ ๋๋ค.
- TransitionBuilder๋ ์๋์ API๋ฅผ ๊ฐ์ง๋ค.
-
- ๋ค์์ผ๋ก ์คํํ ๊ฒ์ ์ง์ ํ๋ค.
-
- flow๋ฅผ ์ค์ง, ์คํจ, ์ข ๋ฃํ๋๋ก ํ๋ค.
- FlowExecutionStatus๊ฐ ๊ฐ๊ฐ
STOPPED,FAILED,COMPLETED๋ก ์ข ๋ฃ๋๋ค. - stopAndRestart() ๋ ํ์ฌ๊น์ง์ Step์ COMPLETED๋ก, ์ดํ๋ ์คํํ์ง ์๊ณ STOPPED ์ํ๋ก Job์ ์ข ๋ฃํ๋ค.(์ดํ ์ฌ์์์ COMPLETED๋ Skip)
- ์ค์ Step์ด FAILED๋ก ์ข ๋ฃ๋์๋๋ผ๋ Job์ BatchStatus๋ฅผ COMLETED๋ก ์ข ๋ฃํ๋๋ก ํ ์ ์๋ค.(์ฌ์์ ๋ถ๊ฐ๋ฅ ํด์ง)
- Step์ ExitStatus๋ฅผ ์บ์นํ๊ณ ํจํด๊ณผ ๋งค์นญ๋๋ฉด,
-
- FlowBuilder ๋ฅผ ์ข ๋ฃํ๊ณ SimpleFlow ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
- FlowJobBuilder์์๋ flowJob์ ์์ฑํ๊ณ Simpleflow๋ฅผ ์คํ์ํจ๋ค.
start, next, from ์ flow๋ฅผ ์ ์ํ๊ณ , on, to, stop, fail, end, stopAndRestart๋ ์กฐ๊ฑด์ ๋ฐ๋ผ ํ๋ฆ์ ์ ํ์ํจ๋ค.
on()์ ํธ์ถํ๋ฉด TransitionBuilder๊ฐ ์์ฑ๋๊ณ , to, stop, fail, end, stopAndRestart๋ฅผ ์ค์ ํ ์ ์๋ค.
@Bean
public Job flowJob() {
return jobBuilderFactory.get("flowJob")
.start(myStep1())
.on("COMPLETED").to(myStep3())
.from(myStep1())
.on("FAILED").to(myStep2())
.end()
.build();
}FlowJob์ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑํ๋ฉด myStep1์ด ์ฑ๊ณตํ๋ฉด myStep3๋ก, ์คํจํ๋ฉด myStep2๋ฅผ ์คํํ๋ค๋ ํ๋ฆ์ด ๋ง๋ค์ด์ง๋ค.

DB์ ์ ์ฅ๋ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ํ์ธํ๋ฉด myStep 1๊ณผ 3์ด ์คํ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
์ด๋ฒ์๋ myStep1 ์์ ์์ธ๋ฅผ ๋ฐ์์์ผ ์ผ๋ถ๋ฌ ์คํจํ ํ ๋ฉํ๋ฐ์ดํฐ ๊ฐ์ ์ดํด๋ณด๊ฒ ๋ค.

on์ FAILED ํจํด๊ณผ ๋งค์นญ๋์ด myStep2๊ฐ ์คํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
ํ ๊ฐ์ง ๋ ํน์ด ์ฌํญ์ด ์๋ค๋ฉด, FlowJob์์๋ Step์ ์คํจ๊ฐ Job์ ์คํจ๋ก ์ฐ๊ฒฐ๋์ง ์๋๋ค๋ ๊ฒ์ด๋ค. ์์ ์ํฉ์์ JobExecution์ ํ์ธํด ๋ณด์๋ค.

๋ถ๋ช
myStep1์ ์คํจ์์ผฐ์ง๋ง Job์ ์ฑ๊ณต์ ์ผ๋ก ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ชจ๋ ์ํฉ์์ ์ด๋ฐ ๊ฒ์ ์๋๊ณ , ์คํจํ์ ๊ฒฝ์ฐ ์ด๋ค ๊ฒ์ ํ๋์ง ์ ์๊ฐ ๋์ด์์ ๋๋ง ํด๋นํ๋ค.
์ค์ ๋ก COMPLETED์ ์กฐ๊ฑด๋ง์ ์ฃผ๊ณ Step์ ์คํจ์์ผฐ์ ๋์๋ Job ๋ํ ์คํจํ๋ค.
Flow ๋ด Step์ ์กฐ๊ฑด๋ถ ์ ํ์ ์ ์ํ๋ค. on()์ ํธ์ถํ๋ฉด TransitionBuilde ๊ฐ ๋ฐํ๋๊ณ , ํด๋น ๊ฐ์ฒด์ API๋ฅผ ํธ์ถํ์ฌ Transition Flow๋ฅผ ๊ตฌ์ฑํ ์ ์๋ค.
-
-
Job, Step์ ์ข
๋ฃ ํ ์ต์ข
๊ฒฐ๊ณผ ์ํ๋ก, SimpleJob ์์๋ ๊ฐ์ฅ ๋ง์ง๋ง์ ์คํ ๋ Step์ ์ํ๊ฐ๋๊ณ ,
FlowJob ์์๋ ๋ง์ง๋ง Flow์ FlowExecutionStatus ๊ฐ์ด ๋๋ค.COMPLETE, STARTING, STARTED, STOPPED, FAILED, ABANDONED(์คํจ, ๊ทธ๋ฌ๋ ์ฌ์์์ ๊ฑด๋ ๋ฐ์ด์ผํ๋ ๋จ๊ณ), UNKOWN
-
์ด๋ค ์ํ๋ก ์ข
๋ฃ๋์๋์ง๋ฅผ ์๋ฏธํ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก๋ BatchStatus์ ๋์ผํ ๊ฐ์ผ๋ก ์ค์ ๋์ง๋ง, ์์๋ก ๋ณ๊ฒฝํ ์ ์๋ค.(contribution.setExitStatus()
SimpleJob, FlowJob์์์ ๊ฐ์ ์ค์ ์ BatchStatus์ ๊ฐ๋ค.COMPLETED, FAILED, STOPPED, EXECUTING, UNKNOWN
-
FlowExecution์ ์์ฑ์ผ๋ก FLow ์คํ ํ ๊ฒฐ๊ณผ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
Flow ๋ด์ Step์ ExitStatus ๊ฐ์ FlowExecutionStatus ๊ฐ์ผ๋ก ์ ์ฅํ๋ฉฐ FlowJob์ ๋ฐฐ์น ๊ฒฐ๊ณผ ์ํ์ ๊ด์ฌํ๋ค.(Step์๋ ์ํฅ์ ์ฃผ์ง ์๋๋ค)COMPLETED, STOPPED, FAILED, UNKNOWN
-
Job, Step์ ์ข
๋ฃ ํ ์ต์ข
๊ฒฐ๊ณผ ์ํ๋ก, SimpleJob ์์๋ ๊ฐ์ฅ ๋ง์ง๋ง์ ์คํ ๋ Step์ ์ํ๊ฐ๋๊ณ ,
๊ธฐ๋ณธ์ ์ผ๋ก ์ ์๋์ด ์๋ ExitStatus ์ด์ธ์ exitCode๋ฅผ ์๋กญ๊ฒ ์ ์ ํ ์ ์๋ค.
StepExecutionListener์ afterStep() ์์ ์์ฑํ ํ์ ๋ง๋ค์ด์ง ExitStatus๋ฅผ ๋ฐํํ ์ ์๋ค.
new ExitStatus("CUSTOM_STATUS")afterStep()) ์์ ์๋ก์ด ExitStatus๋ฅผ ๋ฐํํ๋ฉด TaskletStep์ exitStatus๋ฅผ ์ธํ
ํ๋ ๋ถ๋ถ์์ ์ด๋ฅผ ๋ฐ์ํ๋ค.
์๋์ ExitStatus๋ฅผ ์ค์ ํ ํ afterStep()์ ํธ์ถํ์ฌ ๋ค์ ExitStatus๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ๋ฎ์ด ์์์ง๋ค.
@Bean
public Flow flowA() {
FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flowA");
return flowBuilder
.start(myStep1())
.on("COMPLETED")
.to(myStep2())
.on("PASS")
.stop()
.next(myStep3())
.end();
}@Bean
public Step myStep2() {
return stepBuilderFactory.get("myStep2")
.tasklet(new MyTasklet("myStep2"))
.listener(new PasscheckingListener())
.build();
}public class PasscheckingListener implements StepExecutionListener {
...
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
ExitStatus exitCode = stepExecution.getExitStatus();
if(!exitCode.getExitCode().equals(ExitStatus.FAILED.getExitCode())) {
return new ExitStatus("PASS");
}
return exitCode;
}
}myStep2์ EXIT_CODE๊ฐ PASS๋ก ๋ณ๊ฒฝ๋์๋ค.
myStep1์ด COMPLETED๋ก ๋๋ myStep2๊ฐ ์คํ๋๊ณ ๋ง์ฐฌ๊ฐ์ง๋ก COMPLETED๋ก ๋๋๊ธฐ ๋๋ฌธ์ afterStep() ์์ ExitStatus๊ฐ PASS ๋ก ๋ณ๊ฒฝ๋๋ค.
on("PASS") ํจํด์ ๋งค์นญ๋์ด .stop()์ด ํธ์ถ๋๊ณ , Job์ STOPPED ์ํ๋ก ๋ง์น๊ฒ ๋๋ค.
ExitStatus์ ์กฐ์์ด๋ StepEcecutionListener์ ๋ฑ๋ก์์ด Transition ์ฒ๋ฆฌ๋ฅผ ์ํ ํด๋์ค๋กStep๊ณผ Transition์ ์ญํ ์ ๋ช ํํ๊ฒ ๋ถ๋ฆฌํ ์ ์๊ฒ ํด์ค๋ค.
๊ธฐ์กด์๋ Step์ ExitStatus๊ฐ JobExecutionStatus์ ์ํ ๊ฐ์ ๋ฐ์๋๊ณ , ์ด ๊ฐ์ด JobFlow์ ๋ฐ์ํ๋ ๊ฒ๊ณผ ๋ฌ JobExecutionDecider์์ FlowExecutionStatus ์ํ๊ฐ์ ์๋กญ๊ฒ ์์ฑํด์ ๋ฐํํ๋ค.
@Bean
public Job job() {
return jobBuilderFactory.get("job")
.incrementer(new RunIdIncrementer())
.start(firstStep())
.next((decider()))
.on("ODD").to(oddStep())
.on("EVEN").to(evenStep())
.end()
.build();
}
@Bean
public JobExecutionDecider decider() {
return new CustomDecider();
}public class CustomDecider implements JobExecutionDecider {
private int count = 0;
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
if (++count % 2 == 0) {
return new FlowExecutionStatus("EVEN");
}
return new FlowExecutionStatus("ODD");
}
}์ด์ ์ API๋ฅผ ์ด์ฉํ์ฌ ExitStatus ์ฝ๋์ ๋ฐ๋ผ flow๋ฅผ ์งํํ๋ ๋ฐฉ์๊ณผ ๋์ผํ๊ฒ ๋์ํ๋ค. Job์ ๊ตฌ์ฑํ๋ ์ํฉ์ ๋ฐ๋ผ ๋ ์๋ง๋ค๊ณ ์๊ฐ๋๋ ๋ฐฉ๋ฒ์ ์ ํํ๋ฉด ๋๊ฒ ๋ค.
๋๋ถ๋ถ์ SimpleJob๊ณผ ๋์ผํ๋ค.
๋ค๋ฅธ ์ ์ SimpleFlow ์์ State๋ผ๋ ์์ฑ์ ๊ฐ์ง๋ค๋ ๊ฒ๊ณผ, ์์
์ด ์ข
๋ฃ๋์์ ๋ StepExecution์ ์ํ๋ฅผ ๋ฐ์ํ๋ ๊ฒ์ด ์๋๋ผ FlowExecutionStatus
์ ์ํ๋ก ์
๋ฐ์ดํธ ํ๋ค๋ ๊ฒ์ด๋ค.
Flow์ ๊ตฌํ์ฒด๋ก Step, Flow, JobExecutionDecider์ ๋ด๊ณ ์๋ State๋ฅผ ์คํ์ํค๋ ๋๋ฉ์ธ ๊ฐ์ฒด๋ก, FlowBuilder๋ฅผ ํตํด ์์ฑ๋๋ค.
Flow๋ ์ค์ฒฉ๋ ์ ์๋ค
@Bean
public Job flowJob() {
return jobBuilderFactory.get("flowJob")
.start(flowA()) // SimpleFlowA
.end() // SimpleFlow ์์ฑ
.build();
}๊ฒฐ๊ณผ์ ์ผ๋ก FlowJob ( SimpleFlow( SimpleFlowA ) )์ ๊ฐ์ ํํ๊ฐ ๋๋ค.
-
getName()getStatus(stateName)FlowExecution start(flowExcecutor): Flow๋ฅผ ์คํ.resume(stateName, flowExecutor): ๋ค์์ ์คํํ State๋ฅผ ๊ตฌํด FlowExecutor ์๊ฒ ์คํ์ ์์ํ๋ค.getStates(): Flow๊ฐ ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ State๋ฅผ Collection ์ผ๋ก ๋ฐํ.
-
String nameState startState: ๊ฐ์ฅ ์ฒ์์ผ๋ก ์์ํ State(StepState, FlowState, DecisionState, SplitState)Map<String, Set<StateTransition>> transitionMap: State ์ด๋ฆ์ผ๋ก ๋งคํ State ๋ณ Transition SetMap<String, State> stateMap: ์ด๋ฆ์ผ๋ก ๋งคํ๋์ด ์๋ State MapList<StateTransition> stateTransitions: State + Transition ์ ๋ณด๋ฅผ ๊ฐ์ง ๊ฐ์ฒด์ ๋ฆฌ์คํธ.
StateTransition ์ ํ์ฌ State ์(state) on()์ ๋งค์นญ๋๋ ํจํด(pattern), ๋ค์ State(next) ์ ์์ฑ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
-
@Bean public Flow flow() { FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow"); return flowBuilder .start(myStep1()) .next(myStep()) .end(); }
๋๋ flowBuilder.build()๋ฅผ return ํด๋ ๋๋ค. (end() ๊ฐ ๋ด๋ถ์ ์ผ๋ก๋ build()๋ฅผ ํธ์ถํ์ฌ SimpleFlow ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.)
start(), next(), from() ์ ๋ฌ๋๋ ๊ฐ์ฒด์ ๋ฐ๋ผ State ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ์ ๋ฌ๋ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ค.
์ด๋ ๊ฒ ์์ฑ๋ State๋ SimpleFlow ์์ StateTransition ๊ฐ์ฒด๋ก ๊ด๋ฆฌ๋๋ฉฐ, ํด๋น ๊ฐ์ฒด๋ฅผ ํ ๋๋ก SimpleFlow์ ๋ค๋ฅธ ์์ฑ๋ค์ ๊ฐ์ ์ค์ ํ๊ฒ ๋๋ค.
SimpleFlow๊ฐ State ๋ฅผ ์คํ์์นธ๋ค.(StateTransition ์ ์ฐธ๊ณ ํ์ฌ currentState๋ฅผ ์คํํ๋ค. Map์ ์ ์ฅ๋ ๋ชจ๋ State๋ฅผ ์ํํ๋ฉฐ ์คํ.)
State ์์๋ Step, Flow, JobExecutionDecider ์์๋ค์ ์ ์ฅํ๋ฉฐ,Flow๋ฅผ ๊ตฌ์ฑํ๋ฉด ์๋์ผ๋ก State๊ฐ ์์ฑ๋๋ฉฐ Transition๊ณผ ์ฐ๋๋๋ค.
handle() ๋ฉ์๋๋ฅผ ํตํด ์คํ ํ FlowExecutionStatus๋ฅผ ๋ฐํํ๋ค. ๋ง์ง๋ง ์คํ ์ํ๊ฐ FlowJob์ ์ต์ข
์ํ๊ฐ ๋๋ค.
- SimpleFlow๋ ๋ SimpleFlow๋ฅผ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ์ ์ค์ฒฉ๋์ด ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ฉฐ ์คํ๋๋ค.
- SplitState ๋ ์ฌ๋ฌ๊ฐ์ SimpleFlow๋ฅผ ๊ฐ์ง๊ณ ๋ณ๋ ฌ์ ์ผ๋ก ์คํ์ํฌ ์ ์๋ค.
1. SimpleFlow์ start() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ฒซ State๋ฅผ ์คํ์ํจ๋ค.
2. ๊ทธ ์ดํ resume() ๋ฉ์๋ ์์๋ loop๋ฅผ ๋๋ฉฐ ๋ค์์ ์คํํ State๊ฐ ์๋ค๋ฉด ์คํ์ํค๊ณ , null์ด๊ฑฐ๋ ์คํ ๋ถ๊ฐ๋ฅํ ์ํ๋ผ๋ฉด ์ข
๋ฃํ๋ค.
3. nextState๋ฅผ ํธ์ถํ์ฌ StateMap์์ ๋ค์ State๋ฅผ ์คํํ๋ค.
Step ๋ด์ Flow๋ฅผ ๊ฐ์ง๊ณ ์๋ ๋๋ฉ์ธ ๊ฐ์ฒด. FlowStep์ Status ๋ค์ Flow์ ์ต์ข ์ํ๊ฐ์ ๋ฐ๋ผ ๊ฒฐ์ ๋๋ค.
StepBuilderFactory โถ StepBuilder โถ FlowStepBuilder โถ FlowStep
@Bean
public Step flowStep() {
return stepBuilderFactory.get("flowStep")
.flow(flowA()) // FlowStepBuilder ๋ฐํ.
.build(); // FlowStep ๋ฐํ.
}@JobScope ์ @StepScope๋ ๋น์ ์์ฑ๊ณผ ์คํ์ ๊ด์ฌํ๋ฉฐ, ๋น์ ์์ฑ ์์ ์ ์กฐ์ํ๋ค.(๊ตฌ๋์์ -> ๋น์ ์คํ ์์ )
๋ Scope ์ ๋
ธํ
์ด์
์ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด ์๋ค. @Scope(value="job | step", proxyMode = ScopedProxyMode.TARGET_CLASS
์ ์์์ ๋ณผ ์ ์๋ฏ ํด๋น ์ ๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด ๊ตฌ๋์์ ์๋ ํ๋ก์ ๊ฐ์ฒด๋ก ์์ฑ๋๊ณ , ์คํ ์์ ์ ์ค์ ๋น์ ํธ์ถํ์ฌ ๋ฉ์๋๋ฅผ ์คํํ๋ค.
-
@Values ๋ฅผ ์ฃผ์ ํด์ ๋น์ ์คํ ์์ ์ ํน์ ๊ฐ์ ์ฐธ์กฐํ๋๊ฒ ๊ฐ๋ฅํด์ง๋ค.(Lazy Binding, ํ๋ ๋๋ ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์ ๋ฐ๋๋ค)
@Values("#{jobParameters[paramName]}"),@Values("#{jobExecutionContext[paramName]}"),@Values("#{stepExecutionContext[paramName]}")
-
- Step์ ์ ์ธ๋ฌธ์ ์ ์
- jobParameter, jobExecutionContext ๊ฐ์ ๋ฐ์ธ๋ฉ ํ ์ ์๋ค.
@JobScope @Bean public Step myStep1(@Value("#{jobParameters['message']}") String message) { System.out.println("Parameter[message]: " + message); return stepBuilderFactory.get("myStep1") .tasklet(tasklet(null, null)) .build(); }
๋ฐํ์ ์์ ์ ๊ฐ์ด ๋ฐ์ธ๋ฉ ๋๊ธฐ ๋๋ฌธ์ null์ ๋๊ฒจ์ฃผ์ด ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐฉ์งํด์ค๋ค.
-
- Tasklet, Item Reader, Writer, Processor ์ ์ธ๋ฌธ์ ์ ์ํ๋ค.
- jobParameter, jobExecutionContext, stepExecutionContext ๊ฐ์ ๋ฐ์ธ๋ฉ ํ ์ ์๋ค.
@Bean @StepScope public Tasklet tasklet(@Value("#{jobExecutionContext['name']}") String jobName, @Value("#{stepExecutionContext['name']}") String stepName) { return ((contribution, chunkContext) -> { System.out.println("tasklet has execute"); System.out.println("jobName: " + jobName + ", " + "stepName: " + stepName); return RepeatStatus.FINISHED; }); }
job, stepExecutionContext์ ๊ฐ์ ๊ฐ ExecutionListener์์ ๋ฃ์ด์ค ์ ์๋ค.
Proxy ๊ฐ์ฒด์ ์ค์ ๋์์ด ๋๋ Bean์ ๋ฑ๋กํ๊ณ , ํด์ ํ๋ ์ญํ ์ ํ๋ JobScope, StepScope ํด๋์ค๊ฐ ์กด์ฌํ๋ค.
ํด๋น ํด๋์ค๋ค์ ์ค์ ๋น์ ์ ์ฅํ๊ณ ์๋ JobContext์ StepContext๋ฅผ ๊ฐ์ง๊ณ ์๋ค. (๋ง์น Spring์ ApplicationContext์ ๊ฐ์ด)
์ดํ๋ฆฌ์ผ์ด์
๊ตฌ๋ โถ ApplicationContext์์ ๋น์ ์์ฑ โถ @JobScope, StepScope๊ฐ ์๋๊ฐ? โถ ์์ผ๋ฉด proxy, ์์ผ๋ฉด Singleton Bean ์์ฑ
์คํ๋ง ์ด๊ธฐ์ ์๋ฃ, Job์คํ โถ Job ์์ Proxy ํธ์ถ โถ proxy์์ ์ค์ Step Bean ์ฐธ์กฐ โถ Step Bean ์ด ์๋ค๋ฉด ๊บผ๋ด์ฃผ๊ณ ์๋ค๋ฉด beanFactory ์์ ์์ฑ(@Value ๋ฐ์ธ๋ฉ๋ ์ด๋)
โถJobScope ํด๋์ค์์ ์ค์ Bean์ JobContext์ ๋ฑ๋ก, ๊ด๋ฆฌ
Chunk ๋ ์ฌ๋ฌ๊ฐ์ ์์ดํ
์ ๋ฌถ์ ๋ฉ์ด๋ฆฌ ๋ธ๋ก์ผ๋ก, ์์ดํ
์ ์
๋ ฅ๋ฐ์ ๋ฉ์ด๋ฆฌ๋ก ๋ง๋ ํ Chunk ๋จ์๋ก ํธ๋์ญ์
์ ์ฒ๋ฆฌํ๋ค.
์ผ๋ฐ์ ์ผ๋ก ๋์ฉํฅ ๋ฐ์ดํฐ๋ฅผ ํ๋ฒ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์๋ chunk ๋จ์๋ก ์ชผ๊ฐ์ด ๋ฐ๋ณต ์
์ถ๋ ฅ ํ ๋ ์ฌ์ฉ๋๋ค.
Chunk<I>๋ItemReader๋ก๋ถํฐ ์ฝ์ ์์ดํ ์Chunk Size๋งํผ ๋ฐ๋ณตํด์ ์ ์ฅํ๋ค.Chunk<O>๋ItemReader๋ก ๋ถํฐ ์ ๋ฌ๋ฐ์Chunk<I>๋ฅผ ์ฐธ์กฐํ์ฌItemProcessor์์ ๊ฐ๊ณต๋ ์์ดํ ๋ค์ItemWriter์๊ฒ ์ ๋ฌํ๋ค.- ItemReader ์ Processor ๋ ์์ดํ ์ ๊ฐ๋ณ์ ์ผ๋ก ์ฒ๋ฆฌํ์ง๋ง ItemWriter ๋ ์ผ๊ด์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.(List ๋ฅผ ๋ฐ์)
@Bean
public Step chunkStep() {
return stepBuilderFactory.get("chunkStep")
.<String, String>chunk(5)
.reader(new ListItemReader<>(Arrays.asList("item1", "item2", "item3", "item4", "item5")))
.processor(new ItemProcessor<String, String>() {
@Override
public String process(String item) throws Exception {
// Do Something
return ...
}
})
.writer(new ItemWriter<String>() {
@Override
public void write(List<? extends String> items) throws Exception {
// Do Something (์ถ๋ ฅ, DB ์ ์ฅ, ํ์ผ ์ฐ๊ธฐ ๋ฑ..)
}
})
.build();
}-
- List Items
- List skips: ์ค๋ฅ ๋ฐ์์ผ๋ก ์คํต๋ ์์ดํ
- List errors
- iterator()
Inner Class์ธ ChunkIterator๊ฐ ๋ฐํ๋๋ค.
์คํ๋ง ๋ฐฐ์น์์ ์ ๊ณตํ๋ Tasklet์ ๊ตฌํ์ฒด๋ก, Chunk ๊ธฐ๋ฐ ํ๋ก์ธ์ฑ์ ๋ด๋นํ๋ ๋๋ฉ์ธ ๊ฐ์ฒด์ด๋ค.
Tasklet์ ์ํด ๋ฐ๋ณต์ ์ผ๋ก ์คํ๋๋ฉฐ ์คํ๋ ๋๋ง๋ค ์๋ก์ด ํธ๋์ญ์
์ด ์์ฑ๋์ด ์ฒ๋ฆฌ๊ฐ ์ด๋ฃจ์ด์ง๋ค. ๋๋ฌธ์ ์์ธ ๋ฐ์์ผ๋ก ๋กค๋ฐฑ์ด ์ด๋ฃจ์ด์ ธ๋ ์ด์ ์ ์ปค๋ฐํ Chunk๋ ๋กค๋ฐฑ๋์ง ์๋๋ค.
๋ด๋ถ์๋ ItemReader ๋ฅผ ํธ๋ค๋งํ๋ ChunkProvider ์ ItemProcessor, ItemWriter ๋ฅผ ํธ๋ค๋งํ๋ ChunkProcessor ํ์
์ ๊ตฌํ์ฒด๊ฐ ์กด์ฌํ๋ค.
TaskletStep โถ ChunkOrientedTasklet.execute() โถ ChunkProvider โถ ItemReader๋ฅผ ํตํด read(chunkSize ๋งํผ ๋ฐ๋ณต) โถ ChunkProcessor๋ฅผ ํตํด process(inputs)
โถ ItemProcessor์๊ฒ ์ฒ๋ฆฌ ์์ Iterator๋ก ์ํํ๋ฉฐ ์ฒ๋ฆฌ โถ ItemWriter โถ ChunkOrientedTasklet ์ผ๋ก ๋์๊ฐ ์์ดํ
์ด ์์ ๋ ๊น์ง ๋ฐ๋ณต
Chunk๋ฅผ ์งํํ๋ฉฐ ChunkContext ์ item ๋ค์ ์บ์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณ ์์ธ๊ฐ ๋ฐ์ํ์ฌ ์ฌ ์๋ํ ๊ฒฝ์ฐ ์์ดํ
์ ๋ค์ ์ฝ์ด์ค๋ ๊ฒ์ด ์๋๋ผ ์บ์ฑ๋ ์์ดํ
์ ๊บผ๋ด ๋ค์ ์ฒ๋ฆฌํ๋ค.
์บ์ฑ๋ ๋ฐ์ดํฐ๋ ํด๋น Chunk๊ฐ ๋ชจ๋ ์ํ๋์์ ๊ฒฝ์ฐ ์ ๊ฑฐํ๊ฒ ๋๋ค.
-
- input, output ์ ๋ค๋ฆญ ํ์ ์ ์ค์ , commit interval ์ง์ .
- SimpleStepBuilder ๋ฅผ ๋ฐํํ๋ค.
-
- Chunk ํ๋ก์ธ์ค๋ฅผ ์๋ฃํ๊ธฐ ์ํ ์ ์ฑ ์ ์ค์ ํ๋ ํด๋์ค.
-
- Processor๋ ํ์์ ์ผ๋ก ์ฌ์ฉํ์ง ์์๋ ๋๋ค.
-
- ์ฌ์์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ์ฝ๋ฐฑ์ ๋ํ ์คํธ๋ฆผ.
-
- MQS, JMS ๊ฐ์ด ํธ๋์ญ์ ์ธ๋ถ์์ ์ฝ๊ณ , ์บ์ํ ๊ฒ์ธ์ง์ ์ฌ๋ถ, ๊ธฐ๋ณธ์ false ์ด๋ค.
-
ItemReader๋ฅผ ์ฌ์ฉํด์ ์์ค๋ก๋ถํฐ ์์ดํ ์ chunk size ๋งํด ์ฝ์ด ์ ๊ณตํ๋ ๋๋ฉ์ธ ๊ฐ์ฒด์ด๋ค.
Chunk<I>๋ฅผ ๋ง๋ค๊ณ ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํด ItemReader.read()๋ฅผ ํธ์ถํ๋ฉฐ ์์ดํ ์ chunk์ ์๊ณ , ์ฌ์ด์ฆ๋งํผ ์์ดํ ์ฝ๊ธฐ๋ฅผ ๋ง์น๋ฉด ChunkProcessor๋ก ๋์ด๊ฐ๋ค.
๋ง์ฝ ๋์ด์ ์ฝ์ ์์ดํ ์ด ์๋๊ฒฝ์ฐ(null) chunk ํ๋ก์ธ์ค๋ฅผ ์ข ๋ฃํ๋ค.๊ธฐ๋ณธ ๊ตฌํ์ฒด๋ก SimpleChunkProvider, FaultTolerantChunkProvider(์์ธ ๋ฐ์์ skip, retry) ์ด ์๋ค.
-
ItemProcessor๋ฅผ ์ฌ์ฉํด์ Item์ ๊ฐ๊ณตํ๊ณ , ItemWriter๋ฅผ ์ฌ์ฉํด์ Chunk ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ, ์ถ๋ ฅํ๋ค.
Chunk<O>๋ฅผ ์์ฑํ๊ณ ๋์ด์จChunk<I>์์ ์์ดํ ์ ํ ๊ฑด์ฉ ๊บผ๋ด ์ฒ๋ฆฌํ ํChunk<O>์ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ค.
ItemProcessor๋ ํ์ ์ฌํญ์ด ์๋๊ธฐ ๋๋ฌธ์ ์๋ค๋ฉด ์๋ฌด์ฒ๋ฆฌ ์์ด ๊ทธ๋๋กChunk<O>์ ์ ์ฅ๋๊ฒ ๋๋ค.
ItemWriter ๊น์ง์ ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋๋ฉด ํด๋น Chunk ํธ๋์ญ์ ์ด ์ข ๋ฃ๋๊ณ , ๋ค์ ChunkOrientedTasklet์ด ์คํ๋๋ค.๊ธฐ๋ณธ ๊ตฌํ์ฒด๋ก๋ SimpleChunkProcessor ์ FaultTolerantChunkProcessor๊ฐ ์๋ค.
-
- csvm txt ๋ฑ์ ํ๋ซ ํ์ผ
- XML, JSON
- DB
- JMS์ ๊ฐ์ Message Queuing ์๋น์ค
- Custom Reader
๋ฑ์ ๋ค์ํ ์์ค์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด ์ ๊ณตํ๋ ์ธํฐํ์ด์ค๋ก ์์ดํ ์ ํ๋์ฉ์ฝ์ด ๋ฐํํ๊ณ , ๋ ์ด์ ์๋ค๋ฉด null์ ๋ฐํํ๋ค.
ExecutionContext์ read์ ๊ด๋ จ๋ ์ฌ๋ฌ ์ํ ์ ๋ณด๋ฅผ ์ ์ฅํด ์ฌ์์์ ๋ค์ ์ฐธ์กฐํ๋๋ก ์ง์ํ๋ค. -
ItemReader ์์ ์ฝ์ ์์ดํ ๋ค์ ๋ฆฌ์คํธ๋ก ์ ๋ฌ๋ฐ์ ์ถ๋ ฅํ๋ค. ์ถ๋ ฅ์ด ์๋ฃ๋๊ณ ํธ๋์ญ์ ์ด ์ข ๋ฃ๋๋ฉด ์๋ก์ด Chunk ๋จ์ ํ๋ก์ธ์ค๋ฅผ ์งํํ๋ค.
-
๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํ๊ธฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณต, ๋ณํ, ํํฐ๋ง(null์ ๋ฐํํ๋ฉด ํํฐ๋ง ๋๋ค) ํ๋ค. ItemReader ์ ItemWriter ์ ๋ ๋ฆฝ๋์ด ๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌํํ๋ค. Reader ์์ ๋ฐ์ ์์ดํ ์ ํน์ ํ์ ์ผ๋ก ๋ณํํ์ฌ Wirter์ ๋๊ฒจ์ค๋ค.
์ค๊ฐ ์ฒ๋ฆฌ์ ์ญํ ์ด๊ธฐ ๋๋ฌธ์ ํ์์์๊ฐ ์๋๊ณ , Processor ๊ฐ ์์ผ๋ฉด ์์ดํ ์ ๊ทธ๋๋ก Writer์ ์ ๋ฌ๋๋ค.
๋๋ถ๋ถ ItemReader์ ItemWriter๋ ์คํ๋ง์์ ์ ๊ณตํ๋ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ณ , ItemProcessor๋ ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๊ธฐ ๋๋ฌธ์ ์ง์ ๊ตฌํํ๋ค.
ExecutionContext ๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ItemReader, ItemWriter ์ฒ๋ฆฌ์ ์ํ๋ฅผ ์ ์ฅํ๊ณ , ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ํด๋น ์ํ๋ฅผ ์ฐธ์กฐํ์ฌ ์ฌ์์ ํ๋๋ก ์ง์ํ๋ค.
ItemReader, ItemWriter ์ ๊ตฌํ์ฒด๋ ItemSteam ์ ๊ตฌํํด์ผ ํ๋ค.(ItemStreamReader, ItemStreamWriter ์ ๊ตฌํํ๋ฉด ๋๋ค.)
-
- read(), write() ์ ์ ํ์ผ์ด๋ ์ปค๋ฅ์ ์ดํ์ํ ๋ฆฌ์์ค์ ์ ๊ทผํ๋๋ก ์ด๊ธฐํํ๋ ์์ .
-
- ํ์ฌ๊น์ง์ ์ํ๋ฅผ ์ ์ฅ
-
- ์ด๋ ค์๋ ๋ฆฌ์์ค ํด์ . (์์ธ๊ฐ ๋ฐ์ํ์ ๋๋ ํธ์ถ๋์ด ๋ฆฌ์์ค๋ฅผ ํด์ ํ๋ค)
ItemReader, ItemWriter ๊ฐ ๋์ํ๊ธฐ ์ ์ ItemStream์์ open() ์ ํตํด ๋ฆฌ์์ค๋ฅผ ์ด๊ณ ์ด๊ธฐํ ํ๋ค.
๊ทธ ํ ์์ดํ
์ ์ฝ์ด์ฌ ๋, ์ธ ๋ chunk ๋ง๋ค update()๋ฅผ ํธ์ถํ์ฌ DB์ ์ ์ฅํ๋ค.
public class CustomItemReader implements ItemStreamReader<Member> {
private final List<Member> items;
private int index;
private boolean restartable;
public CustomItemReader(List<Member> items) {
this.items = new ArrayList<>(items);
this.index = 0;
this. restartable = false;
}
@Override
public Member read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
Member item = null;
if(this.index < this.items.size()) {
item = this.items.get(index++);
}
if(index == 8 && !restartable) {
throw new RuntimeException("Restart is required");
}
return item;
}
@Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
if(executionContext.containsKey("index")) {
index = executionContext.getInt("index");
this.restartable = true;
}
else {
executionContext.put("index", index);
}
}
@Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
executionContext.put("index", index);
}
@Override
public void close() throws ItemStreamException {
System.out.printf("๋ฆฌ์์ค ํด์ .");
}
}- open() ์์ ์ด์ ์ ์คํํด index๊ฐ ์กด์ฌํ๋ค๋ฉด ํด๋น index ๊ฐ์ ๋ถ๋ฌ์ ๊ทธ ์์น๋ถํฐ ์คํํ๋ค.
- close(): ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ํธ์ถ๋์ด, ๋ฆฌ์์ค๋ฅผ ํด์ ์ํจ๋ค.
Chunk Size๋ฅผ 2๋ก ์ฃผ๊ณ , ๋ฆฌ์์ค๋ก 10๊ฐ์ ์์ดํ
์ ์ฃผ์๋ค.
8๋ฒ์งธ ์์ดํ
์ ์ฝ์ ํ์ ์์ธ๊ฐ ๋ฐ์ํ๋๋ก ์ค์ ํด ๋ณด์๋ค. ์๋ ์ด๋ฏธ์ง๋ ํ
์คํธ์ ๊ฒฐ๊ณผ์ด๋ค.
์ต์ด์ Reader์ Writer์ Stream์ด Open ๋๊ณ , Update ๊ฐ ํ๋ฒ ํธ์ถ ๋๋ค.
๊ทธ ๋ค์ ์์ดํ
์ ์ฒญํฌ ์ฌ์ด์ฆ๋งํผ ์ฝ๊ณ , Processor๊ฐ ๋์ํ ํ Write๊ฐ ์ด๋ฃจ์ด ์ง๋ค.(USER1, USER2 ์ ๊ฐ์ด ์ถ๋ ฅํ๋๋ก ํจ)
read(), process(), write() ๊ฐ ํ chunk์ ๋ํด ๋ชจ๋ ์คํ๋๋ฉด Reaader, Writer์ Stream์์ Update()๊ฐ ํธ์ถ๋์ด ์ํ๋ฅผ ์ ์ฅํ๋ค.
8๋ฒ์งธ ์์ดํ
์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด Close()๋ฅผ ํธ์ถํ์ฌ ๋ฆฌ์์ค๋ฅผ ํด์ ํ๊ณ ์ข
๋ฃํ๋ค.
๋ฌผ๋ก ์์ธ๊ฐ ๋ฐ์ํ์ง ์์๋ Close()๋ฅผ ํธ์ถํ์ฌ ๋ฆฌ์์ค๋ฅผ ํด์ ํ๋ค.
๋ค์์ ํด๋น ์ธ์คํด์ค๋ฅผ ๋ค์ ์คํํ๊ฒ ๋๋ฉด ExecutionContext ์์ ์ธ๋ฑ์ค๋ฅผ ๊ฐ์ ธ์ค๊ณ (8์ ์คํํ๋ค ๋กค๋ฐฑ ๋์์ผ๋ฏ๋ก ์ด์ Chunk์ธ 6๊น์ง ์ ์ฅ๋์๋ค.)
restartable์ด true๋ก ๋ฐ๋๊ธฐ ๋๋ฌธ์ item 10 ๊น์ง ์ ์์ ์ผ๋ก ์คํ๋๋ค.
์ค๋ช ์ ์์์ ๊ณ์ ํ์ผ๋ ์๋ตํ๋ค.
ํ์ ๊ฐ์ 2์ฐจ์ ๋ฐ์ดํฐ๋ก ํํ๋ ์ ํ์ ํ์ผ์ ์ฒ๋ฆฌํ๋ค. ์ผ๋ฐ์ ์ผ๋ก ๊ณ ์ ์์น๋ก ์ ์๋ ๋ฐ์ดํฐ๋, ํน์ ๋ฌธ์์ ์ํค ๊ตฌ๋ณ๋ ๋ฐ์ดํฐ์ ํ์ ์ฝ๋๋ค.
Resource(์ฝ์ด์ผํ ๋ฐ์ดํฐ)์ LineMapper(Line String to Object) ๊ฐ ํ์ํ๋ค.
-
-
- ํ์ผ ์๋จ๋ถํฐ ๋ฌด์ํ ๋ผ์ธ ์ (ํค๋ ๋ฑ์ ์คํตํ ๋ ์ฌ์ฉ)
- LineCallbackHandler ๋ฅผ ํธ์ถํ์ฌ ๊ฑด๋๋ด๋ค.
-
- ํด๋น ๋ฌธ์๊ฐ ์๋ ๋ผ์ธ์ ๋ฌด์ํ๋ค.
-
- FileSystemResource, ClassPathResource ...
-
- Line์ ์ฝ์ด ๊ฐ์ฒด๋ก ๋ณํํ๋ค.
LineTokenizer- ๋ผ์ธ์ FieldSet ์ผ๋ก ๋ณํํ๋ค. ํ์ผ ํ์์ ๋ง์ถฐ FieldSet ์ผ๋ก ๋ณํํ๋ ์์ ์ ์ถ์ํํด์ผํ๋ค.
- ๊ตฌ๋ถ์๋ฅผ ์ด์ฉํ๋ DelimitedLineTokenizer, ๊ณ ์ ๊ธธ์ด ๋ฐฉ์์ FixedLengthTokenizer ๊ฐ ์๋ค.
FieldSet- ๋ผ์ธ์ ๊ตฌ๋ถ์๋ก ๊ตฌ๋ถํด์ ํ ํฐ ๋ฐฐ์ด์ ์์ฑํ๋ค.
FieldSetMapper- FieldSet์ ๊ฐ์ฒด์ ๋งคํํ์ฌ ๋ฐํํ๋ค.(๊ฐ์ฒด์ ํ๋๋ช ๊ณผ ๋งคํ, BeanWrapperFieldSetMapper๋ฅผ ์ฌ์ฉํ๋ค.)
-
- .name(String name)
- ExecutionContext ๋ด์์ ๊ตฌ๋ถํ๊ธฐ ์ํ key๋ก ์ ์ฅ๋๋ค.
- .resource(Resource)
- .delimited().delimiter()
- .fixedLength()
- ๊ธธ์ด๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ผ์ ์ฝ์
- .addColumns(Range)
- ๊ณ ์ ๊ธธ์ด์ ๋ฒ์
- .names(String[] fieldNames)
- ๋งคํ๋ ๊ฐ์ฒด์ ํ๋๋ช
- .targetType(Class)
- .addComment(String comment)
- ๋ฌด์ํ ๋ผ์ธ์ ๊ธฐํธ ์ค์ .
- .stric(false)
- ๋ผ์ธ์ ์ฝ์ ๋ ํ์ฑ ์์ธ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ๊ฒ์ฆ ์๋ต ์ค์ . ๊ธฐ๋ณธ์ true
- .encoding(String encoding)
- .lineToSkip(num)
- .saveState(false)
- ์ํ ์ ๋ณด๋ฅผ ์ ์ฅํ ๊ฒ์ธ์ง, ๊ธฐ๋ณธ์ true
- .setLineMapper(LineMapper)
- .setFieldSetMapper(FieldSetMapper)
- .setLineTokenizer(LineTokenizer)
- .name(String name)
@Bean
public ItemReader<? extends Member> itemReader() {
FlatFileItemReader<Member> itemReader = new FlatFileItemReader<>();
DefaultLineMapper<Member> lineMapper = new DefaultLineMapper<>();
lineMapper.setLineTokenizer(new DelimitedLineTokenizer()); // ๊ธฐ๋ณธ ๊ตฌ๋ถ์ ','
lineMapper.setFieldSetMapper(new MemberFieldSetMapper());
itemReader.setLineMapper(lineMapper);
itemReader.setResource(new ClassPathResource("/member.csv"));
itemReader.setLinesToSkip(1);
return itemReader;
}- LineMapper
@Setter
public class DefaultLineMapper<T> implements LineMapper<T> {
private LineTokenizer lineTokenizer;
private FieldSetMapper<T> fieldSetMapper;
@Override
public T mapLine(String line, int lineNumber) throws Exception {
return fieldSetMapper.mapFieldSet(lineTokenizer.tokenize(line));
}
}- FieldSetMapper
public class MemberFieldSetMapper implements FieldSetMapper<Member> {
@Override
public Member mapFieldSet(FieldSet fieldSet) throws BindException {
if(fieldSet == null){
return null;
}
Member member = new Member();
member.setName(fieldSet.readString(0));
member.setId(fieldSet.readString(1));
return member;
}
}ํ์ผ์์ ํ ์ค์ ์ฝ์ด์ด โถ LineTokenizer ์์ ํ์ฑํด ํ ํฐ ๋ฐฐ์ด ์์ฑ(DefaultFieldSet) โถ FieldSetMapper ์์ fieldSet์ ํ ๋๋ก ๊ฐ์ฒด ์์ฑ, ๋ฐํ
โถ ํ์ผ์ ๋๊น์ง ๋ฐ๋ณต
names๋ฅผ ๋ฃ์ด์ฃผ์ง ์์๊ธฐ ๋๋ฌธ์ ์ธ๋ฑ์ค๋ก ๊ฐ์ ๊ฐ์ ธ์๋ค. LineTokenizer์ setNames()๋ฅผ ์ค์ ํด์ฃผ๋ฉด ํ๋๋ช ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ค.
ํ๊ธฐ์ ๊ฐ์ด Builder๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๊น๋ํ๊ฒ ๊ตฌ์ฑํ ์ ์๋ค.
@Bean
public ItemReader itemReader() {
return new FlatFileItemReaderBuilder<Member>()
.name("flatFile")
.resource(new ClassPathResource("/member.csv"))
.fieldSetMapper(new BeanWrapperFieldSetMapper<>())
.targetType(Member.class)
.delimited().delimiter(",")
.names("name", "id")
.linesToSkip(1)
.build();
}์ฝ๋๋ฅผ ๋ณด๋ฉด LineMapper๋ฅผ ์ค์ ํ๋ ๋ถ๋ถ์ด ๋น ์ก๋ค๋ ๊ฒ์ ์ ์ ์๋๋ฐ, ์ฌ์ค ๋ฐ๋ก ์์ฑํด์ ๋ฃ์ด์ฃผ์ง ์์๋, ์คํ๋ง์์ ์ ๊ณตํ๋ DefaultLineMapper๊ฐ ์กด์ฌํ๊ณ ,์ด๋ฅผ ์ฌ์ฉํ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก FieldSetMapper ๋ํ ์คํ๋ง์์ ์ ๊ณตํ๋ BeanWrapperFieldSetMapper๋ฅผ ์ฌ์ฉํ๊ณ , ํ๊ฒ ํด๋์ค๋ฅผ ์ง์ ํด ์ฃผ๋ฉด ํ๋๋ช
์ ๋ง๊ฒ ๋งคํํด์ค๋ค.
-
๋ฌธ์์ด์ด ์๋์ ์ฃผ์ํ์.
.fixedLength() // fixedLengthBuilder ๋ฐํ .names("name", "id") .addColumns(new Range(1-5)) .addCloumns(new Range(6-10))
IncorrectTokenCountException- ๋ฃ์ด์ค ํ ํฐ ํ๋์ ์ด๋ฆ(names)์ ์๋ณด๋ค ์ฝ์ด๋ค์ธ ํ ํฐ์ ์๊ฐ ๋ค๋ฅผ ๋ ๋ฐ์ํ๋ค.
IncorrectLineLengthException- ์ง์ ํด์ค ์ปฌ๋ผ๋ค์ ๊ธธ์ด๋ณด๋ค ๋ผ์ธ ์ ์ฒด ๊ธธ์ด๊ฐ ์ผ์นํ์ง ์์ ๋ ๋ฐ์ํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก๋ stric ์ต์
์ด true ์ด๊ธฐ ๋๋ฌธ์ ํ ํฐํ๋ฅผ ์ํํ ๋ ์ด๋ฅผ ๊ฒ์ฆํ๊ฒ ๋๊ณ , ์์ธ๋ฅผ ๋ฐ์์ํจ๋ค. ํ์ง๋ง ํด๋น ์ต์
์ false๋ก ์ฃผ๊ฒ ๋๋ค๋ฉด
๋ผ์ธ ๊ธธ์ด๋ ์ปฌ๋ผ๋ช
์ ๊ฒ์ฆํ์ง ์๊ฒ๋๊ธฐ ๋๋ฌธ์ ์์ธ๋ฅผ ๋ฐ์์ํค์ง ์๊ณ , ๋ฒ์๋ ์ด๋ฆ์ ๋ง์ง ์๋ ์ปฌ๋ผ์ ๋น ํ ํฐ์ ๊ฐ์ง๊ฒ ๋๋ค.
Streaming API for XML, DOM ๊ณผ SAX ์ ์ฅ, ๋จ์ ์ ๋ณด์ํ API ๋ชจ๋ธ๋ก PUSH, PULL ๋ฐฉ์์ ๋ชจ๋ ์ ๊ณตํ๋ค.
XNL ํ์ผ์ ํญ๋ชฉ์ ์ง์ ์ด๋ํ๋ฉด์ Stax ํ์๊ธฐ๋ฅผ ํตํด ๊ตฌ๋ฌธ์ ๋ถ์ํ๋ค.
- Iterator API ๋ฐฉ์
- XMLEventReader์ nextEvent()๋ฅผ ํธ์ถํด ์ด๋ฒคํธ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์จ๋ค.
- Cursor API ๋ฐฉ์
- JDBC Resultset ์ฒ๋ผ ๋์, XMLStreamReader๋ XML ๋ฌธ์์ ๋ค์ ์์๋ก ์ปค์๋ฅผ ์ด๋ํ๋ค.
- ์ปค์์์ ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ํ์ฌ ์ด๋ฒคํธ์ ์ ๋ณด๋ฅผ ์ป๋๋ค.
์คํ๋ง ๋ฐฐ์น์์๋ XML ๋ฐ์ธ๋ฉ์ Spring OXM์๊ฒ ์์ํ๊ณ , ๋ฐ์ธ๋ฉ ๊ธฐ์ ์ ์ ๊ณตํ๋ ๊ตฌํ์ฒด๋ฅผ ์ ํํด์ ์ฒ๋ฆฌํ๋๋ก ํ๋ค.
Marshaller(๊ฐ์ฒด -> XML), UnMarchaller(XML -> ๊ฐ์ฒด)๋ฅผ ์ง์ํ๋ ์คํ์์ค๋ก๋ JaxB2, Castor, XmlBeans, Xstream ... ์ด ์๋ค.
์คํ๋ง ๋ฐฐ์น๋ StAX ๋ฐฉ์์ผ๋ก ๋ฌธ์๋ฅผ ์ฒ๋ฆฌํ๋ StaxEventItemReader๋ฅผ ์ ๊ณตํ๋ค.
XML ๋ฌธ์๋ฅผ ์กฐ๊ฐ(fragment) ๋จ์๋ก ๋ถ์ํ์ฌ ์ฒ๋ฆฌํ๋ค.(root element ๋ฅผ ํ๋์ ์กฐ๊ฐ์ผ๋ก)
์กฐ๊ฐ์ ์ฝ์ ๋๋ DOM์ Pull ๋ฐฉ์์ ์ฌ์ฉํ๊ณ , ์ด๋ฅผ ๊ฐ์ฒด๋ก ๋ฐ์ธ๋ฉ ํ ๋๋ SAX์ Push ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
fragment ๋จ์๋ก ์ฝ์ด๋ค์ธ ํ SpringOXM ์๊ฒ ๊ฐ์ฒด ๋งคํ์ ์์ํ๋ค.
๋ฃจํธ ์๋ฆฌ๋จผํธ๋ฅผ ๊ฐ์ฒด๋ก, ๋ด๋ถ์ ์์ ์๋ฆฌ๋จผํธ๋ค์ ๋งคํ๋ ๊ฐ์ฒด์ ํ๋๋ก ๋งคํํ๋ค.
-
- FragmentEventReader
- XML ์กฐ๊ฐ์ ๋ ๋ฆฝํ XML ๋ฌธ์๋ก ์ฒ๋ฆฌํ๋ ์ด๋ฒคํธ ํ๋ ๊ธฐ
- XMLEventReader
- XML ์ด๋ฒคํธ ๊ตฌ๋ฌธ ๋ถ์์ ์ํ ์ต์์ ์ธํฐํ์ด์ค
- Unmarshaller
- XML to Object
- Resource
- List fragmentRootElementNames
- ์กฐ๊ฐ ๋จ์์ ๋ฃจํธ ์๋ฆฌ๋จผํธ๋ช ์ ๋ด์ ๋ฆฌ์คํธ.
- FragmentEventReader
-
StaxEventItemRedaderBuilder<T> ๋ฅผ ์ฌ์ฉํ๋ค.
- .name(String)
- .resource(Resource)
- .addFragmentRootElements(String ...)
- root Elemnet๋ฅผ ์ง์ ํ๋ค.
- .unmarshaller(Unmarshaller)
- ํ๊ฒ ๊ฐ์ฒด ์ค์ .
- .saveState(false)
- ์ํ ์ ๋ณด ์ ์ฅ์ ์ฌ๋ถ, ๊ธฐ๋ณธ๊ฐ์ true ์ด๋ค.
implementation 'com.thoughtworks.xstream:xstream:1.4.19'
implementation 'org.springframework:spring-oxm:5.3.16'<?xml version="1.0" encoding="UTF-8" ?>
<members>
<member id="1">
<id>1</id>
<name>user1</name>
</member>
<member>
<id>2</id>
<name>user2</name>
</member>
...
</members>
@Bean
public ItemReader<? extends Member> itemReader() {
return new StaxEventItemReaderBuilder<Member>()
.name("staXml")
.resource(new ClassPathResource("/member.xml"))
.addFragmentRootElements("member")
.unmarshaller(itemUnmarshaller())
.build();
}
@Bean
public Unmarshaller itemUnmarshaller() {
Map<String, Class<?>> aliases = new HashMap<>();
aliases.put("member", Member.class);
aliases.put("id", String.class);
aliases.put("name", String.class);
XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
xStreamMarshaller.setAliases(aliases);
return xStreamMarshaller;
}Map ์ ์ฒ์์ผ๋ก ๋ค์ด๊ฐ๋ ์์๋ RootElement์ ํด๋นํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฒด์ ๋งคํ๋๊ณ ๊ทธ ๋ค์์ ์์๋ค์ ๊ฐ๊ฐ ๊ฐ์ฒด์ ํ๋์ ๋งคํ๋๋ค.
Json ๋ฐ์ดํฐ์ ํ์ฑ, ๋ฐ์ธ๋ฉ์ JsonObjectReader ๊ตฌํ์ฒด์๊ฒ ์์ํ์ฌ ์ฒ๋ฆฌํ๋ค.
- GsonJsonObjectReader
- JacksonJsonObjectReader
- itemType: Json ๋ฐ์ดํฐ๋ฅผ ๋งคํํ ๊ฐ์ฒด์ ํ์
- JsonParser
- ObjectMapper
- InputStream : Json ํ์ผ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ค๋ ์ ๋ ฅ ์คํธ๋ฆผ.
@Bean
public ItemReader<? extends Member> itemReader() {
return new JsonItemReaderBuilder<Member>()
.name("JsonReader")
.resource(new ClassPathResource("/member.json"))
.jsonObjectReader(new JacksonJsonObjectReader<>(Member.class))
.build();
}[
{
"name": "user1",
"id": "1"
},
{
"name": "user2",
"id": "2"
},
...
]JDBC ResultSet์ ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉํ๋ค. ํ์ฌ ํ์ ์ปค์๋ฅผ ์ ์งํ๋ฉฐ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ๋ฉด ๋ค์ ํ์ผ๋ก ์ปค์๋ฅผ ์ด๋ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ Streaming ๋ฐฉ์์ I/O ์ด๋ค.
DB Connection์ด ์ฐ๊ฒฐ๋๋ฉด ๋ฐฐ์น๊ฐ ์๋ฃ๋ ๋ ๊น์ง ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ค๊ธฐ ๋๋ฌธ์ ์์ผ ํ์์์์ ์ด์ ๋ง๊ฒ ์ค์ ํ์ผ ํ๋ค.
- ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ํ ๋นํ๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋ง๋ค.
- Connection ์ฐ๊ฒฐ ์ ์ง ์๊ฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ด ์ถฉ๋ถํ๋ค๋ฉด ๋์ฉ๋ ๋ฐ์ดํฐ์ ์ฒ๋ฆฌ์ ์ ํฉํ๋ค.(fatchSize๋ก ํ๋ฒ์ ๊ฐ์ ธ์ค๋ ์ ์ค์ ๊ฐ๋ฅ)
ํ์ด์ง ๋จ์๋ก ๋ฐ์ดํฐ๋ฅผ ์กฐํ, PageSize ๋งํผ ํ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค๋๊ณ , ํ ๊ฐ์ฉ ์ฝ๋๋ค.
Cursor์ ๋ฌ๋ฆฌ ํ ํ์ด์ง๋ฅผ ์ฝ์ ๋ ๋ง๋ค Connection์ ์ฌ์ฐ๊ฒฐ ํ๋ค.
-
ํ์ด์ง ๋จ์์ ๊ฒฐ๊ณผ๋ง ๋ฉ๋ชจ๋ฆฌ์ ํ ๋นํ๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋ ์ ์ ์ ์๋ค.
-
์ปค๋ค๊ฒฌ ์ฐ๊ฒฐ ์ ์ง์๊ฐ์ด ์ ๊ณ , ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ์ ์ ํฉํ๋ค.
-
์ปค์ ๊ธฐ๋ฐ์ JDBC ๊ตฌํ์ฒด๋ก ResultSet๊ณผ ํจ๊ป ์ฌ์ฉ๋๋ฉฐ, Datasource์์ Connection์ ์ป์ด์ SQL์ ์คํํ๋ค.
Thread-safe ํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์ ๋๊ธฐํ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.Step์์ read() ๊ฐ ํธ์ถ ๋๋ฉด, JdbcCursorItemReader ์์ fetchSize(chunkSize) ๋งํผ ์ฝ์ด์จ ํ ๋๋ ค์ค๋ค.
-
JdbcCursorItemReaderBuilde() ๋ฅผ ์ฌ์ฉํ๋ค.
- .name(name)
- .fetchSize(size)
- .dataSource(DataSource)
- .rowMapper(RowMapper)
- ๋ฐํ๋๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด์ ๋งคํํ๊ธฐ ์ํ ์ค์ .
- .beanRowMapper()
- RowMapper ๋์ ํด๋์ค ํ์ ์ผ๋ก ์ค์ ํ๋ค.
- .sql(sql)
- .queryArguments(Object ...)
- ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ์ค์
- .maxItemCount(int)
- .currentItemCount(int)
- ์กฐํ item์ ์์ ์ง์ .
- maxRows(int)
- ResultSet ์ค๋ธ์ ํธ๊ฐ ํฌํจ ํ ์ ์๋ ์ต๋ ํ์ ์.
@Bean public ItemReader<Member> itemReader() { return new JdbcCursorItemReaderBuilder<Member>() .name("jdbcCursorItemReader") .fetchSize(chunkSize) .sql("select id, name from member where name like ? order by id") .queryArguments("user%") .beanRowMapper(Member.class) .dataSource(dataSource) .build(); }
-
-
SpringBatch 4.3 ๋ถํฐ ์ง์ํ๋ค.
EntityManagerFactory๊ฐ์ฒด๋ฅผ ํ์๋กํ๋ฉฐ ์ฟผ๋ฆฌ๋JPQL๋ก ์์ฑํ๋ค.
ItemStream์์ Query๋ฅผ ํตํด ์์ฑ๋ ๊ฒฐ๊ณผ๋ฅผ ResultStream ์ผ๋ก ๊ฐ์ ธ์จ๋ค. ๊ทธ ํ JpaCursorItemReader ์์ Iterator๋ก ResultStream์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฝ์๋ธ๋ค.-
JpaCursorItemReaderBuilder() ๋ฅผ ์ฌ์ฉํ๋ฉฐ ๊ธฐ๋ณธ์ ์ธ API๋ JDBC ๋ฐฉ์๊ณผ ๋น์ทํ๋ค.
- .queryString(String JPQL)
- .EntityManagerFactory(EMF)
- .parameterValue(Map<String, Object>)
...
@Bean public ItemReader<Member> itemReader() { Map<String, Object> params = new HashMap<>(); params.put("name", "user%"); return new JpaCursorItemReaderBuilder<Member>() .name("jpaCursorItemReader") .entityManagerFactory(entityManagerFactory) .queryString("select m from Member m where name like :name") .parameterValues(params) .build(); }
-
-
ํ์ด์ง ๊ธฐ๋ฐ์ ๊ตฌํ์ฒด๋ก ์์ row์ ๋ฒํธ(offset)์ ๋ฐํํ row์ ์(limit)๋ฅผ ์ง์ ํ์ฌ ์คํํ๋ค.
ํ์ด์ง ๋จ์๋ก ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋๋ง๋ค ์๋ก์ด ์ฟผ๋ฆฌ๊ฐ ์คํ๋๊ณ , ์ปค๋ฅ์ ์ ๋งบ๋๋ค. Thread-safe ํ๋ค.์๋ ํ๋ก์ธ์ค๋ Cursor ์ ์ ์ฌํ์ง๋ง ๋ค๋ฅธ ์ ์ด๋ผ๋ฉด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ํ์ด์ง ๋จ์๋ก ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ Mapper์ ์ํด ๊ฐ์ฒด์ List๊ฐ ์์ฑ๋๋ค.
-
์ฟผ๋ฆฌ๋ฌธ์ ItemReader์๊ฒ ์ ๊ณตํ๋ ํด๋์ค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง๋ค ๋ค๋ฅธ ์ข ๋ฅ๋ฅผ ์ฌ์ฉํ๋ค.
-
JdbcPagingItemReaderBuilder() ๋ฅผ ์ฌ์ฉํ๋ค.
- name(String)
- pageSize(int)
- dataSource(DataSource)
- queryProvider(PagingQueryProvider)
- rowMapper(Class)
- parameterValues(Map<String, Object>)
-
- selectClause(String), fromClause(String), whereClause(String), groupClause(String)
- select, from, where, group ์ .
- sortKeys(Map<String, Order>)
- ์ ๋ ฌ์ ์ํ ํค ์ค์ . ํ์๋ก ์ง์ ํด ์ฃผ์ด์ผ ํ๋ค. ...
@Bean public ItemReader<Member> itemReader() throws Exception { Map<String, Object> params = new HashMap<>(); params.put("name", "user%"); return new JdbcPagingItemReaderBuilder<Member>() .name("jdbcPagingItemReader") .pageSize(5) .dataSource(dataSource) .rowMapper(new BeanPropertyRowMapper<>(Member.class)) .queryProvider(createQueryProvider()) .parameterValues(params) .build(); } @Bean public PagingQueryProvider createQueryProvider() throws Exception { Map<String, Order> sortKeys = new HashMap<>(); sortKeys.put("id", Order.ASCENDING); SqlPagingQueryProviderFactoryBean providerFactoryBean = new SqlPagingQueryProviderFactoryBean(); providerFactoryBean.setDataSource(dataSource); providerFactoryBean.setSelectClause("id, name"); providerFactoryBean.setFromClause("member"); providerFactoryBean.setWhereClause("name like :name"); providerFactoryBean.setSortKeys(sortKeys); return providerFactoryBean.getObject(); }
- selectClause(String), fromClause(String), whereClause(String), groupClause(String)
-
-
ํ์ด์ง ๊ธฐ๋ฐ์ JPA ๊ตฌํ์ฒด๋ก JpaCursor ๋ฐฉ์๊ณผ API๋ pageSize ์ค์ ์ ์ ์ธํ๊ณ ๋ ๋์ผํ๋ค.
@Bean public ItemReader<Member> itemReader() { Map<String, Object> params = new HashMap<>(); params.put("name", "user%"); return new JpaPagingItemReaderBuilder<Member>() .name("jpaCursorItemReader") .pageSize(5) .entityManagerFactory(entityManagerFactory) .queryString("select m from Member m where name like :name") .parameterValues(params) .build(); }
๋ฐฐ์น Job ์์์ ์ด๋ฏธ ์๋ ๊ธฐ์กด์ DAO๋ ๋ค๋ฅธ ์๋น์ค๋ฅผ ItemReader ์์์ ์ฌ์ฉํ๊ณ ์ ํ ๋ ์คํ์ ์์ํ๋ ์ญํ ์ ํ๋ค.
(MemberService ์ joinMember() ๋ฉ์๋์ ํธ์ถ๊ณผ ๊ฐ์ด), Java์ ๋ฆฌํ๋ ์
๊ธฐ์ ์ ์ฌ์ฉํ๋ค.
@Bean
public ItemReader<Member> itemReader() {
ItemReaderAdapter<Member> reader = new ItemReaderAdapter<>();
reader.setTargetObject(MemberService());
reader.setTargetMethod("readMember");
return reader;
}public class MemberService {
private long id= 0;
public Member readMember() {
if(id < 10) {
return new Member(String.valueOf(++id), "user");
}
return null;
}
}๊ธฐ์กด์ MemberService์ readMember() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํ๊ฑด์ฉ ์ฝ์ด์ฌ ์ ์๋ค.
10 ๋ฒ๋ง ์ฝ๊ธฐ ์ํด id < 10 ์กฐ๊ฑด์ ์ถ๊ฐํ์๋ค. ์ฝ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ ํด๋น ๋ฆฌ์คํธ๋ฅผ remove() ํ๋ฉฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์๋ค.
๊ณ ์ ์์น ๋๋ ํน์ ๋ฌธ์์ ์ํด ๊ตฌ๋ณ๋ ๋ฐ์ดํฐ์ ํ์ ๊ธฐ๋กํ๋ค.
์์ฑํด์ผํ Resource์ Object๋ฅผ String์ผ๋ก ๋ณํํด์ฃผ๋ LineAggregator๊ฐ ํ์ํ๋ค.
-
encoding
-
append
- ํ์ผ์ด ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ ๊ฒ์ธ์ง ์ฌ๋ถ (๊ธฐ๋ณธ๊ฐ false, ๋ฎ์ด์์)
-
Resource
-
LineAggregator
- Item ์ ๋ฐ์ String ์ผ๋ก ๋ณํํ๋ค.
- ๊ตฌํ์ฒด
- PassThroughLineAggregator: ๋จ์ ๋ฌธ์์ด๋ก ๋ฐํ
- DelimitedLineAggregator: ๊ตฌ๋ถ์๋ฅผ ์ถ๊ฐํ์ฌ ๋ฌธ์์ด์ ์์ฑ.
- ๊ธฐ๋ณธ๊ฐ์
,์ด๋ฉฐ,delimited().delimiter(String)์ผ๋ก ๊ตฌ๋ถ์๋ฅผ ์ง์ ํ ์ ์๋ค.
- ๊ธฐ๋ณธ๊ฐ์
- FormatterLineAggregator: ๊ณ ์ ๊ธธ์ด๋ก ๊ตฌ๋ถํ์ฌ ๋ฌธ์์ด ์์ฑ
formatted().format("%-3s|%-2s")์ ๊ฐ์ด ํ์์ ์ง์ ํ๋ค.
- ๋ด๋ถ์ ์ผ๋ก
FieldExtractor๋ฅผ ์ฌ์ฉํ๋ค.- ๊ฐ์ฒด์ ํ๋๋ฅผ ์ถ์ถํด์ ๋ฐฐ์ด๋ก ๋ง๋ค์ด ๋ฐํํ๋ค.
- BeanWrapperFieldExtractor: ๊ฐ์ฒด์ ํ๋๋ฅผ ๋ฐฐ์ด๋ก ๋ฐํ.
- PassThroughFieldExtractor: ์ ๋ฌ๋ฐ์ Collection์ ๋ฐฐ์ด๋ก ๋ฐํ.
- Item ์ ๋ฐ์ String ์ผ๋ก ๋ณํํ๋ค.
-
FlatFileHeaderCallback, FlatFileFooterCallback
- ํค๋, ํธํฐ๋ฅผ ํ์ผ์ ์ฐ๊ธฐ์ํ ์ธํฐํ์ด์ค.
FieldExtractor ์์ ํ๋๋ฅผ ์ถ์ถํด ๋ฐฐ์ด์ ์์ฑํด ๋๊ฒจ์ฃผ๋ฉด LineAggregator ์์ ๊ตฌ๋ถ์๋ฅผ ์ถ๊ฐํ์ฌ ๋ฌธ์์ด์ ์์ฑํ๋ค.
@Bean
public ItemWriter<? super Member> itemWriter() {
return new FlatFileItemWriterBuilder<>()
.name("flatFileWriter")
.resource(new FileSystemResource("/Users/a1101720/IdeaProjects/TIL/Spring/SpringBatch/src/main/resources/memberOut.csv"))
.append(true)
.delimited()
.delimiter("|")
.names(new String[] {"name", "id"})
.build();
}@Bean
public ItemWriter<? super Member> itemWriter() {
return new FlatFileItemWriterBuilder<>()
.name("flatFileWriter")
.resource(new FileSystemResource("/Users/a1101720/IdeaProjects/TIL/Spring/SpringBatch/src/main/resources/memberOut.csv"))
.append(true)
.formatted()
.format("%-5s|%-2s")
.names(new String[] {"name", "id"})
.build();
}์ฝ์ ๋์ ๋์ผํ๊ฒ Resource, marshaller, rootTagName(์กฐ๊ฐ๋จ์์ ๋ฃจํธ๊ฐ ๋ ์ด๋ฆ) ์ด ํ์ํ๋ค.
Marshaller์ ์ํด ๊ฐ์ฒด๊ฐ XML ์์๋ก ๋ณํ๋๋ค.
@Bean
public ItemWriter<? super Member> itemWriter() {
return new StaxEventItemWriterBuilder<>()
.name("staxItemWriter")
.resource(new FileSystemResource("/Users/a1101720/IdeaProjects/TIL/Spring/SpringBatch/src/main/resources/memberout.xml"))
.rootTagName("member")
.marshaller(itemMarshaller())
.overwriteOutput(true) // ๋ฎ์ด ์์ฐ๊ธฐ.
.build();
}
@Bean
public Marshaller itemMarshaller() {
Map<String, Class<?>> aliases = new HashMap<>();
aliases.put("member", Member.class);
aliases.put("name", String.class);
aliases.put("id", String.class);
XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
xStreamMarshaller.setAliases(aliases);
return xStreamMarshaller;
}Marshaller ์ ์ค์ ์ ์ด์ ์ Reader์์์ ๋์ผํ๋ค.
jsonObjectMarshaller ์ ์ํด ๊ฐ์ฒด๊ฐ Json ํ์์ผ๋ก ๋ณํ๋๋ค. jsonObjectMarshaller ๋ ๋ด๋ถ์ ์ผ๋ก ObjectMapper๋ฅผ ๊ฐ์ง๊ณ ๋ฐ์ดํฐ๋ฅผ ๋งคํํ๋ค.
@Bean
public ItemWriter<? super Member> itemWriter() {
return new JsonFileItemWriterBuilder<>()
.name("jsonFileItemWriter")
.resource(new FileSystemResource("/Users/a1101720/IdeaProjects/TIL/Spring/SpringBatch/src/main/resources/memberout.json"))
.jsonObjectMarshaller(new JacksonJsonObjectMarshaller<>())
.append(true)
.build();
}JDBC์ Batch ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ bulk Insert, update, delete ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ ์ด์ ์ ๊ฐ์ง๋ค.
- .dataSource(dataSource)
- .sql("insert into ")
- .assertUpdates(true)
- ํธ๋์ญ์ ์ดํ ์ ์ด๋ ํ๋์ ํ์ด ๋ณ๊ฒฝ๋์ง ์์ ๊ฒฝ์ฐ ์์ธ ๋ฐ์.
- .beanMapped()
- Pojo ๊ธฐ๋ฐ์ผ๋ก Insert SQL์ Values ๋ฅผ ๋งคํ
- BeanPropertyItemSqlParameterSourceProvider ๊ฐ ์ฌ์ฉ๋๋ค.
- .columnMapped()
- Key, Value ๊ธฐ๋ฐ์ผ๋ก Insert SQL์ values๋ฅผ ๋งคํ
- ColumnMapItemPreparedStatementSetter ๊ฐ ์ฌ์ฉ๋๋ค.
์ธ ๋์์ ํ์ ์ด Pojo ํ์ ์ ๊ฐ์ฒด๋ผ๋ฉด beanMapped()๋ฅผ, Map ๊ณผ ๊ฐ์ด key, value์ ์์ด๋ผ๋ฉด columnMapped()๋ฅผ ์ฌ์ฉํ๋ค.
@Bean
public ItemWriter<? super Member> itemWriter() {
return new JdbcBatchItemWriterBuilder<>()
.dataSource(dataSource)
.sql("insert into member2(name, id) values(:name, :id)")
.assertUpdates(true)
.beanMapped()
.build();
}JPA ์ํฐํฐ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค. ์ํฐํฐ๋ฅผ chunk ํฌ๊ธฐ๋งํผ insert, merge ํ ๋ค์ flush ํ๋ค.
- .userPersist(false)
- ์ํฐํฐ๋ฅผ persiste ํ ๊ฒ์ธ์ง ์ฌ๋ถ. false ์ด๋ฉด merge ์ฒ๋ฆฌ ํ๋ค.
- EntityManageFactory()
@Bean
public ItemWriter<? super Member2> itemWriter() {
return new JpaItemWriterBuilder<>()
.usePersist(true) // default
.entityManagerFactory(entityManagerFactory)
.build();
}๋ฐฐ์น Job ์์์ ์ด๋ฏธ ์๋ ์๋น์ค๋ฅผ ItemWriter ์์ธ์ ์ฌ์ฉํ ๋ ์ด๋ฅผ ์์ํ๋ค.
@Bean
public ItemWriter<? super Member> itemWriter() {
ItemWriterAdapter<Member> writerAdapter = new ItemWriterAdapter<>();
writerAdapter.setTargetObject(MemberService());
writerAdapter.setTargetMethod("writeMember");
return new CustomItemWriter();
}ํ๊ฒ ํด๋์ค์ ๋ฉ์๋๋ฅผ ์ง์ ํด์ค๋ค. ๋ฆฌํ๋ ์ ์ ์ด์ฉํด ์คํธ๋๋ค.
public class MemberService {
public void writeMember(Member member) {
System.out.println(member);
}
}๊ธฐ์กด์ ItemWriter ์ ๊ตฌํ์ฒด์ ๋ฌ๋ผ ์์ดํ ์ ํ๋์ฉ ๋๊ฒจ๋ฐ์ ์ฒ๋ฆฌํ๋ค.
ItemProcessor ๋ค์ ์ฐ๊ฒฐํด์ ์์ํ๋ฉด ๊ฐ ItemProcessor๋ฅผ ์คํ์ํจ๋ค.
์ด์ ItemProcessor ๋ฐํ ๊ฐ์ ๋ค์ ItemProcessor ๊ฐ์ผ๋ก ์ฐ๊ฒฐ๋๋ค.
@Bean
public ItemProcessor<? super Member, Member> itemProcessor() {
List itemProcessors = new ArrayList<>();
itemProcessors.add(new CustomItemProcessor());
itemProcessors.add(new CustomItemProcessor2());
return new CompositeItemProcessorBuilder<>()
.delegates(itemProcessors)
.build();
}List ๋ก ์ ๋ฌํ ์๋ ์๊ณ , ํ๋์ฉ ์ฒด์ด๋์ผ๋ก ์ ๋ฌํ ์ ๋ ์๋ค.
Classifier๋ก ๋ผ์ฐํ ํจํด์ ๊ตฌํํด์ ๋ถ๋ฅ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ฌ๋ฌ๊ฐ์ ItemProcessor ์ค ํ๋๋ฅผ ํธ์ถํ๋ค.
@Bean
public ItemProcessor<? super ProcessorInfo, ProcessorInfo> itemProcessor() {
ClassifierCompositeItemProcessor processor = new ClassifierCompositeItemProcessor();
ProcessorClassifier<ItemProcessor, ItemProcessor<?, ? extends ProcessorInfo>> processorClassifier = new ProcessorClassifier();
Map<Integer, ItemProcessor<ProcessorInfo, ProcessorInfo>> processorMap = new HashMap<>();
processorMap.put(1, new ClassifiableItemProcessor());
processorMap.put(2, new ClassifiableItemProcessor2());
processorMap.put(3, new ClassifiableItemProcessor3());
processorClassifier.setProcessorMap(processorMap);
processor.setClassifier(processorClassifier);
return processor;
}์์ ์ด๊ธฐ ๋๋ฌธ์ ๊ตฌํ๋ ์ฝ๋์ ์ง์คํ์ง ์์๋ ๋๋ค.
ClassifierCompositeItemProcessor ๋ฅผ ์์ฑํ๊ณ , ์ปค์คํ
ํ๊ฒ ์ ์ ํ Classifier๋ฅผ ์ค์ ํด ์ค ๋ค ๋ฐํํ๋ค ์ ๋๋ก ๋ณด๋ฉด ๋๊ฒ ๋ค.
Classifier ์์๋ Classify() ๋ฉ์๋์์ ์คํํ ํ๋ก์ธ์ค๋ฅผ ๊ฒฐ์ ํ๋ค.



























