์ง๋ ๊ธ๋ค์์ Job์ ๊ฐ๋ ๊ณผ ๊ธฐ๋ณธ ๊ตฌ์ฑ, Job์ ์์ฑ์๋ฆฌ์ ๋์์๋ฆฌ์ ๋ํด ํ์ ํ๋ค.
๊ฐ์ ์์๋ก, ์ด๋ฒ์ Step์ ๋ํด ์์๋ณด๊ณ ์ ํ๋ค. Step์ด ๋ฌด์์ด๊ณ , ๊ด๋ จ๋ ๊ฐ๋
์๋ ์ด๋ค ๊ฒ๋ค์ด ์๋์ง ์ดํด๋ณธ๋ค.
๋ ๋์๊ฐ Step์ ์ข
๋ฅ์ ์์ฑ์๋ฆฌ, ๋์์๋ฆฌ์ ๋ํด ๊น๊ฒ ํ์
ํด๋ณด๊ณ ์ ํ๋ค.
Job์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ์ค๋ช
์ ํ๋ฉด์, Job์ "์ ์ฒด ๋ฐฐ์น ํ๋ก์ธ์ค๋ฅผ ์บก์ํํ๋ ์ํฐํฐ"๋ผ๊ณ ํํํ์๋ค.
์ด์ ๋น์ทํ๊ฒ Step์ "Job์ ๋
๋ฆฝ์ ์ธ ์์ฐจ์ ๋จ๊ณ๋ฅผ ์บก์ํํ๋ ์ํฐํฐ"๋ผ๊ณ ๊ณต์๋ฌธ์์์ ์ ์ํ๋ค.
์ฌ์ค, ๊ฐ์ธ์ ์ผ๋ก๋ Step์ "Job์ ์ธ๋ถ ์์
๋จ์๋ฅผ ์๋ฏธํ๋ฉฐ, ์ธ๋ถ์ ์ผ๋ก ๋๋ ์์
์ ์คํํ๋ ๋
ผ๋ฆฌ์ ๋จ๊ณ"๋ผ๊ณ ํํํ๋ ๊ฒ์ ์ข์ํ๋ค.
์ฆ, Job์ ํ๋ ์ด์์ Step์ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ฉฐ, ์ด Step์๋ ๋ฐฐ์น์์
์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํ๋ก์ธ์ค๋ฅผ ๊ฐ์ง๋ค.
์ ๋ด์ฉ์ ํ ๋๋ก ํ์ฌ Job๊ณผ Step์ ๊ด๊ณ๋ฅผ ๊ทธ๋ฆผ์ผ๋ก ํํํ๋ฉด ์๋์ ๊ฐ๋ค.
Step์ ์ฌ๋ฌ๊ฐ์ง ์ญํ ์ ๊ฐ์ง ์ ์์ผ๋ฉฐ, ๋ง์ฝ Step์ด ์ญํ ๋ณ(๋ฐ์ดํฐ ์กฐํ ์ํ, ์ค์ ๋ฐฐ์น ๋ก์ง ์ํ, ๋ฐ์ดํฐ ์ ์ฅ ์ํ ๋ฑ)๋ก ๋ถ๋ฆฌ๋๋ค๋ฉด, ๊ฐ ์ญํ ๋ณ Step์ด ํ๋์ Job ์์ ๋ฌถ์ด๋ ์ ์ด๋ค.
๊ฒฐ๊ตญ, Step์ ๋ฐฐ์น์์
์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ต์ํ์ ๋จ๊ณ์ด๊ณ , ๋ด๋ถ์ ์ผ๋ก ์ค์ ๋ฐฐ์น ์์
์ ์ํํ๋ ์ฃผ์ฒด์ด๋ค. ๊ทธ๋ ๋ค๋ฉด Step์ ์คํ ์ํ๋ ๊ด๋ฆฌ๋์ด์ผ ํ์ง ์์๊น?(Job์ ์คํ์ํ๋ฅผ ๊ด๋ฆฌํ๋ JobExecution์ฒ๋ผ)
๊ทธ๋์, JobExecution๊ณผ ๊ฐ์ ๊ฐ๋
์ผ๋ก Step์๋ StepExecution์ด ์กด์ฌํ๋ค. ์ฆ, Step์ ์คํ์ ์๋ฏธํ๋ StepExecution ๊ฐ์ฒด๊ฐ ์กด์ฌํ๋ค.
๊ฒฐ๊ตญ, Step์๋ ์คํ์ ์๋ฏธํ๋ ๊ฐ์ฒด์ธ StepExecution์ด ์๊ณ , ์ด ๋
์์ด Step์ ์คํ์ํ๋ฅผ ๊ด๋ฆฌํ๋ค. JobExecution์ด BATCH_JOB_EXECUTION ๋ฉํ๋ฐ์ดํฐ ํ
์ด๋ธ๋ก ๊ด๋ฆฌ๋๋ ๊ฒ์ฒ๋ผ, StepExecution๋ ๋ฉํ๋ฐ์ดํฐ ํ
์ด๋ธ์ด ์ง์๋๋ค.
BATCH_STEP_EXECUTION๋ผ๋ ํ
์ด๋ธ๋ก ๊ด๋ฆฌ๋๋ค. ์คํค๋ง๋ฅผ ํตํด ์ค์ํ ํฌ์ธํธ ๋ช ๊ฐ์ง๋ง ํ์ธํด๋ณด์
STEP_NAME: Step์ ์ ์ํ ๋, ์ง์ ํ Step ์ด๋ฆ์ด๋ค.JOB_EXECUTION_ID: JobExecution๊ณผ StepExecution์ 1:N ๊ด๊ณ๋ค.
StepExecution์ด ๋ชจ๋ ์ฑ๊ณตํด์ผ๋ง JobExecution๋ ์ฑ๊ณตํ๋ค. ์ฆ, StepExecution๋ค ์ค์ ํ๋๋ผ๋ ์คํจํ๋ฉด JobExecution์ ์คํจ์ฒ๋ฆฌ๊ฐ ๋๋ค.
๋ํ, Job์ด ์ฌ์์ ๋์ด๋ ์คํจํ Step์ ํํด์๋ง ์ฌ์คํ๋๋ค.COMMIT_COUNT,READ_COUNT,WRITE_COUNT๋ฑ: Step์ ์คํ ๊ณผ์ ์ ๋ชจ๋ํฐ๋งํ๋ ์ญํ ์ ํ๋ค. ๊ฐ๊ฐ ํธ๋์ญ์ ์ปค๋ฐ ํ์, ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ /์ด ํ์๋ฅผ ์๋ฏธํ๋ค. ๋ฟ๋ง ์๋๋ผFILTER_COUNT๋READ_SKIP_COUNT๋ฑ ๋ค์ํ countํ๋๊ฐ ์๋๋ฐ, ์ด๋ค ๋ชจ๋ step์ด ์คํ๋๋ฉด์, ๋ฐ์ดํฐ๋ฅผ ์ด๋ค ์์ผ๋ก ์ฌ์ฉํ๋์ง ์ถ์ ํ๋๋ฐ ์ฉ์ดํ ํ๋๋ก ํ์ฉ๋๋ค.
๊ณต์๋ฌธ์์์ ์ ๊ณตํ๋ Job-Step์ ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ๋์ผ๋ก ๊ธฐ๋ณธ๊ฐ๋
์ ๋ํ ์ค๋ช
์ ๋ง์น๋ค.

Job๊ณผ Step์ ๊ด๊ณ์ ๋ํด ์์์ ์์๋ดค๋ค. ์ด๋ฒ์๋ Job ํ์ ๊ฐ๋
์ผ๋ก ์ํ๋ Step์ ์ด๋ค ์์ผ๋ก ๊ตฌ์ฑ๋์ง ์์๋ณด๊ณ , ๊ตฌ์กฐ์ ๋ํด ์์๋ณด์.
์ฐ์ , ์์๋ฅผ ํตํด Step ๊ตฌ์ฑ์ ํ์
ํด๋ณด์.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TransferNewUserJobConfiguration {
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
private static final String JOB_NAME = "TRANSFER_NEW_USER_JOB";
private static final String STEP_1_NAME = "TRANSFER_NEW_USER_STEP";
private final UserService userService;
@Bean
public Job transferNewUserJob() {
return new JobBuilder(JOB_NAME, jobRepository)
.start(transferNewUserStep(null))
.build();
}
@Bean
@JobScope
public Step transferNewUserStep(
@Value("#{jobParameters['targetDate']}") LocalDate targetDate
) {
return new StepBuilder(STEP_1_NAME, jobRepository) // StepBuilder ํธ์ถ
.tasklet((contribution, chunkContext) -> { // TaskletStep์ ๋ง๋ค๊ธฐ ์ํ ํธ์ถ
final List<User> users = userService.findByRegisteredDate(targetDate);
log.info("{} ๋ช
์ ์ ์ ์ ๋ณด๋ฅผ AML ๋ฑ์ ์๋น์ค๋ก ์ ์ก", users.size());
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build(); // TaskletStep ์์ฑ
}
}์ ์ฝ๋์์ ํ์
ํด๋ณผ ๋ถ๋ถ์ transferNewUserStep()๋ฉ์๋๋ค. ์ด ๋ฉ์๋๋ ํ๋์ Step์ ์ ์ํด์, returnํ๊ณ ์๋ค.
์ด๋ป๊ฒ Step์ ๊ตฌ์ฑํ๋์ง ํด๋น ๋ฉ์๋๋ง ์ดํด๋ณด์.
Step๋ Job๊ณผ ๊ต์ฅํ ๋น์ทํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค. ๊ณตํต ๋ถ๋ถ์ AbstractStep์ผ๋ก ๋ฌถ์ฌ ์๊ณ , ๊ฐ ์ข ๋ฅ๋ณ๋ก ์๋ก๋ค๋ฅธ ์คํ๋ฐฉ์์ ๊ฐ์ง ๊ฐ Step๋ค์ ์ด๋ฅผ ์์๋ฐ์ ๊ตฌํํ๊ณ ์๋ ํํ๋ค.
๊ฐ Step์ ๊ฐ๋ตํ ํน์ง์ ์๋์ ๊ฐ๋ค. ์์ธํ ์ค๋ช ์ ๋ณ๋์ ์ฅ์์ ์ค๋ช ํ๋ค.
TaskletStep:Tasklet์ ํธ์ถํด, ์์ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ Step(Tasklet์ ๋ํ ์์ธํ ์ค๋ช ์ 4.3.4 ์ฐธ๊ณ )FlowStep:Flow์๊ฒ ์์ ์ ์์ํ๋ ๋ฐฉ์์ StepJobStep: ๋ณ๋์Job์๊ฒ ์์ ์ ์์ํ๋ ๋ฐฉ์์ StepPartitionStep: ๋ฉํฐ ์ค๋ ๋ฉ ๋ฐฉ์์ผ๋ก ์์ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ Step
Step์ ์ข ๋ฅ๋ ์์ ๊ฐ์ด ๋ค ์ข ๋ฅ๊ฐ ์๊ณ , ์ฐ์ ๊ฐ Step์ ํน์ง์ ์ ์ ๋๋ง ์ดํดํด๋์. Tasklet์ด ๋ญ๊ณ , Flow๊ฐ ๋ฌด์์ธ์ง๋ ๋ค์ด์ด์ ์ ๋ฆฌํ๋ค.
๊ฐ Step์ ์ด๋ค ๊ตฌ์กฐ๋ก ์คํ๋๊ณ , ์์์ ์์ฑํ ์์์์์ transferNewUserStep()์ ์ด๋ค Step์ด ์คํ๋๋์ง ๋ฑ์ ์คํ ์๋ฆฌ๋ฅผ ์์๋ด์ผ ํ๋ค.
ํ์ง๋ง, Job์ ์์ฑ ์๋ฆฌ๋ฅผ ์ดํดํ๊ณ ์คํ ์๋ฆฌ๋ฅผ ์ดํดํ ๊ฒ์ฒ๋ผ, ์ญ์๋ Step๋ ์ด๋ป๊ฒ Step์ด ์์ฑ๋๋์ง ์์ฑ ์๋ฆฌ๋ฅผ ๋จผ์ ์ดํด๋ณด์.
์๋ Step ๊ตฌ์ฑ์ ๊ธฐ๋ฐ์ผ๋ก ์ค๋ช ํ๋ค.
@Bean
@JobScope
public Step transferNewUserStep(
@Value("#{jobParameters['targetDate']}") LocalDate targetDate
) {
return new StepBuilder(STEP_1_NAME, jobRepository) // StepBuilder ํธ์ถ
.tasklet((contribution, chunkContext) -> { // TaskletStep์ ๋ง๋ค๊ธฐ ์ํ ํธ์ถ
final List<User> users = userService.findByRegisteredDate(targetDate);
log.info("{} ๋ช
์ ์ ์ ์ ๋ณด๋ฅผ AML ๋ฑ์ ์๋น์ค๋ก ์ ์ก", users.size());
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build(); // TaskletStep ์์ฑ
}new StepBuilder()๋ฅผ ํตํด StepBuilder ์์ฑ์๋ฅผ ํธ์ถํ๊ณ , ๋ด๋ถ์ ์ผ๋ก StepBuilderHelper๋ฅผ ํธ์ถํ๊ณ ์๋ค.
CommonJobProperties๋ StepBuilderHelper ํด๋์ค์ ๋ด๋ถ ํด๋์ค๋ก ์ ์๋์ด ์์ผ๋ฉฐ, Step ์คํ์ ํ์ํ ๋ค์ํ ์์ฑ ๊ฐ๋ค์ ๋ด๋ถ ํ๋๋ก ๊ฐ์ง๊ณ ์๋ค.
์ 1,2 ๊ณผ์ (new StepBuilder())์ ํตํด์ StepBuilder ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
๊ทธ ๋ค์์ผ๋ก ํธ์ถํ ๋ฉ์๋์ ๋ฐ๋ผ ์ด๋ค ์ข
๋ฅ์ step์ด ์์ฑ๋ ์ง ๋ฌ๋ผ์ง๋๋ฐ, ์ด๋ค ์๋ฏธ์ธ์ง StepBuilder ํด๋์ค๋ฅผ ์ดํด๋ณด์.
ํ์ํ ๋ถ๋ถ๋ง ๊ฐ์ ธ์ค๊ธฐ ์ํด ์์ค์ฝ๋๋ฅผ ๊ทธ๋๋ก ๋ถ๋ถ ๋ฐ์ทํ๋ค.
public class StepBuilder extends StepBuilderHelper<StepBuilder> {
public TaskletStepBuilder tasklet(Tasklet tasklet, PlatformTransactionManager transactionManager) {
return (new TaskletStepBuilder(this)).tasklet(tasklet, transactionManager);
}
public <I, O> SimpleStepBuilder<I, O> chunk(int chunkSize, PlatformTransactionManager transactionManager) {
return ((SimpleStepBuilder)(new SimpleStepBuilder(this)).transactionManager(transactionManager)).chunk(chunkSize);
}
public PartitionStepBuilder partitioner(String stepName, Partitioner partitioner) {
return (new PartitionStepBuilder(this)).partitioner(stepName, partitioner);
}
public JobStepBuilder job(Job job) {
return (new JobStepBuilder(this)).job(job);
}
public FlowStepBuilder flow(Flow flow) {
return (new FlowStepBuilder(this)).flow(flow);
}
// ...
}4.3.2์์ ์ดํด๋ดค๋ Step์ ๊ตฌํ์ฒด๋ค์ด ์์๋ค. ๊ฐ ๊ตฌํ์ฒด๋ฅผ ๋ง๋๋ ๋น๋ ํด๋์ค๋ค์ด ์กด์ฌํ๋ค. ์ฆ, StepBuilder๋ Step์ ๊ฐ ๊ตฌํ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํด ๊ฐ StepBuilder์๊ฒ ๊ทธ ์ญํ ์ ์์ํ๋ ๊ฒ์ด๋ค. ์๋ฅผ ๋ค์ด, StepBuilder๊ฐ TaskletStep์ ์์ฑํ๊ณ ์ถ๋ค๋ฉด, TaskletStepBuilder์๊ฒ ๊ทธ ํ์๋ฅผ ๋๊ธด๋ค.
์ฌ๊ธฐ๊น์ง Step ๊ตฌํ์ฒด๋ฅผ ์์ฑํ๋ ํ๋ฆ์ ๋๋ฒ๊น
์ ํตํด ์์๋ดค๋ค.
๊ฐ ๊ตฌํ์ฒด์ ๋ฐ๋ผ ๋ฐ๋ ํ๋ผ๋ฏธํฐ๋ ๋ค๋ฅด๋ฏ๋ก, ๋ณ๋์ ์ฑํฐ์์ ์ค๋ช
ํ ์์ ์ด๋ค.
๋ค์ํ๋ฒ ์ฒ์ ์์ ์ฝ๋๋ก ๋์๊ฐ์ ๋ค์ํ๋ฒ ์ดํด๋ณด์.
@Bean
@JobScope
public Step transferNewUserStep(
@Value("#{jobParameters['targetDate']}") LocalDate targetDate
) {
return new StepBuilder(STEP_1_NAME, jobRepository) // StepBuilder ํธ์ถ
.tasklet((contribution, chunkContext) -> { // TaskletStep์ ๋ง๋ค๊ธฐ ์ํ ํธ์ถ
final List<User> users = userService.findByRegisteredDate(targetDate);
log.info("{} ๋ช
์ ์ ์ ์ ๋ณด๋ฅผ AML ๋ฑ์ ์๋น์ค๋ก ์ ์ก", users.size());
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build(); // TaskletStep ์์ฑ
}new StepBuilder(): StepBuilderHelper๋ฅผ ํธ์ถํด, Step์ ํ์ํ ๋ค์ํ ์์ฑ๊ฐ์ ์ค์ ํ๋ค.tasklet(): ๋ค์ํ ๋ฉ์๋ ์ค์tasklet()์ ํธ์ถํ์ฌ,TaskletStepBuilder์๊ฒ Step ์์ฑ์ ์์ํ๋ค. ์ฌ๊ธฐ์ Step์ ๊ตฌ์ฒด์ ์ผ๋ก TaskletStep์ด๋ค.build(): TaskletStepBuilder::build()๋ฅผ ํธ์ถํ์ฌ,TaskletStep์ ์์ฑํ๋ค. (์ ํํ๋ TaskletStepBuilder์๋ build()๊ฐ ์๊ณ , ๊ทธ ์์ ํด๋์ค์ธ AbstractTaskletStepBuilder ํด๋์ค์ ๊ตฌํ๋์ด์๋ค.)
Step์ ๊ตฌํ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํ ์ํฐํฐ ๋ค์ด์ด๊ทธ๋จ์ ๊ทธ๋ฆฌ๋ฉด ์๋์ ๊ฐ๋ค.
4.3์ ์์๋ Step ๊ฐ์ฒด ์์ฑ์ ์ํ ๊ตฌ์กฐ์ ์๋ฆฌ์ ๋ํด ์์๋ดค๋ค. ์ด์ ๋ ์์ฑ๋ Step์ด ์ด๋ป๊ฒ ์คํ๋๋์ง. ๊ทธ ์คํ ์๋ฆฌ์ ๋ํด ์์๋ณผ ๊ฒ์ด๋ค.
Step์ ์คํ์๋ฆฌ๋ฅผ ์ค๋ช ํ๊ธฐ์ ์์, Step์ ๊ธฐ๋ณธ์ ์ผ๋ก Job์ด ์คํํ๋ ์ฃผ์ฒด์ด๋ฏ๋ก Job์ ์ข ์์ ์ธ ๊ด๊ณ์ผ ์ ๋ฐ์ ์๋ค. ๋๋ฌธ์, Job์ ์คํ์์๋ถํฐ ์์ํด์ Step์ ์คํ๊น์ง ํ๋ฌ๊ฐ๋ ํ๋ก์ฐ์ด๋ค. ๋ณธ ๊ธ์์ Job์ ์คํ์๋ฆฌ๋ฅผ ์ค๋ช ํ๊ธฐ์ ์์ด ๋ฐฉ๋ํ๊ธฐ ๋๋ฌธ์, ์ด์ ๊ธ์ ๋ํ ์ปจํ ์คํธ๊ฐ ์์ด์ผ๋ง ํ๋ค.
์ด์ ๊ธ์ 3.1.2.7์ ์์ SimpleJob::doExecute() ์ฝ๋์ ๋ํ ์ค๋ช ์ ๋์ด๊ฐ๋ค. ์ด์ ๋ step์ ์คํํ๋ ๋ก์ง์ด ๋๋ถ๋ถ์ด์๊ธฐ ๋๋ฌธ์ธ๋ฐ, ์ฌ๊ธฐ์๋ถํฐ ๋ณธ ๊ธ์์ ์ค๋ช ํ๊ณ ์ ํ๋ค.
์ด์ ๊ธ์ ์ด์ง ์์ฝํ๋ฉด ์๋์ ๊ฐ๋ค.
๊ฐ์ ์์ ๋ก SimpleJob์ด ์์ฑ๋๊ณ , ์คํ๋๋ ๊ฒ๊น์ง ๋๋ฒ๊น
์ ํตํด ํ์ธํ๋ค. ๊ทธ ๊ณผ์ ์์ AbstractJob::execute()๊ฐ ์คํ ๋์๊ณ , ๋ด๋ถ์ ์ผ๋ก SimpleJob::doExecute()๊ฐ ํธ์ถ๋์๋ค.
์ถ๊ฐ๋ก, ์ด๊ฑด ์ด๋๊น์ง๋ SimpleJob์ ๊ธฐ๋ฐ์ผ๋ก ์ค๋ช ํ๋ ๊ฒ์ด๋ค. ๊ฐ Job๋ง๋ค doExecute() ๊ตฌํ ๋ฐฉ๋ฒ๋ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์, ๋ค๋ฅผ ์ ์๋ค๋ ๊ฒ์ ์ผ๋์ ๋ฌ์ผ ํ๋ค.
public class SimpleJob extends AbstractJob {
private final List<Step> steps;
protected void doExecute(JobExecution execution)
throws JobInterruptedException, JobRestartException, StartLimitExceededException {
StepExecution stepExecution = null;
Iterator var3 = this.steps.iterator(); // 1. steps์ ์ ์ฅ๋ step๋ค์ ๋ฐ๋ณตํ์ฌ ์คํํ๊ธฐ ์ํจ
while (var3.hasNext()) { // 1. steps์ ์ ์ฅ๋ step๋ค์ ๋ฐ๋ณตํ์ฌ ์คํํ๋ค.
Step step = (Step) var3.next();
stepExecution = this.handleStep(step, execution); // 2. ๊ฐ Step์ ํ๋์ฉ ์ฒ๋ฆฌํ๋ค. ๊ทธ๋ฆฌ๊ณ , ์คํ ์ํ๋ฅผ ์
๋ฐ์ดํธ ํ๋ค.
if (stepExecution.getStatus() != BatchStatus.COMPLETED) {
break;
}
}
if (stepExecution != null) {
if (logger.isDebugEnabled()) {
logger.debug("Upgrading JobExecution status: " + stepExecution);
}
execution.upgradeStatus(stepExecution.getStatus()); // 3. BatchStatus๋ฅผ JobExecution์ ๋ฐ์
execution.setExitStatus(stepExecution.getExitStatus()); // 4. ExitStatus๋ฅผ JobExecution์ ๋ฐ์
}
}
}SimpleJob์ ๋ฉค๋ฒ๋ก List ํ์
์ steps๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ์ด์ ๊ธ์์ ์ค๋ช
ํ๋ฏ์ด Job ์์ฑ ์, SimpleJobBuilder::build()์์ ์ ์๋ step ๋ฆฌ์คํธ๋ฅผ SimpleJob ๋ฉค๋ฒ์ ์ ์ฅํ๋ค.
๊ทธ๋ ๊ฒ ์๋์ ๊ฐ์ด SimpleJob::doExecute()๋ ์๋ ์์์ ๊ฐ์ด ์ํ๋๋ค.
- ์ ์ฅ๋ Step ๋ฆฌ์คํธ๋ฅผ ํ๋์ฉ ์ํํ๋ฉด์ ์์ ์ ์ํ
- ๊ฐ Step์ ์ํํ๊ณ ,
stepExecution๋ณ์์ ์คํ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋ค. ๊ทธ๋ฆฌ๊ณ , Step์ ์คํ ๊ฒฐ๊ณผ๊ฐCOMPLETED๊ฐ ์๋๋ผ๋ฉด ์์ ์ ์ค์งํ๋ค. Step ์์ ์ ์ํํ๋handleStep()๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์๋๋ฐ, ์ด ๋ฉ์๋๋ ๋ค์ ์ ์์ ์์๋ณด๊ฒ ๋ค. - StepExecution์๋
status์exitStatusํ๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ๊ฐ๊ฐ BatchStatus๋ผ๋ enum ํ์ , ExitStatus๋ผ๋ ํด๋์ค ํ์ ์ด๋ค. Job์ ํ์ฌ ์คํ ์ํ์ ์ข ๋ฃ ์ํ๋ฅผ ์๋ฏธํ๋ค.
3๋ฒ์์๋, ํ์ฌ StepExecution์ ์ํ๋ฅผ JobExecution ์ํ์ ๋ฐ์ํ๋ค. - ํ์ฌ StepExecution์ ์ข ๋ฃ ์ํ๋ฅผ JobExecution์ ์ข ๋ฃ ์ํ์ ๋ฐ์ํ๋ค.
2๋ฒ์์ handleStep()์ ํตํด Step์ ์ฒ๋ฆฌํ๋ค๊ณ ํ๋ค. ๋ด๋ถ์ ์ผ๋ก ์ด๋ป๊ฒ ์ฒ๋ฆฌ๋๋์ง ์์๋ณด์.
public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, InitializingBean {
private StepHandler stepHandler;
// ...
protected final StepExecution handleStep(Step step, JobExecution execution)
throws JobInterruptedException, JobRestartException, StartLimitExceededException {
return this.stepHandler.handleStep(step, execution);
}
// ...
}handleStep() ๋ฉ์๋๋ AbstractJobํด๋์ค์ ๊ตฌํ๋์ด ์๋ ๋ฉ์๋๋ค. ๋ด๋ถ์ ์ผ๋ก StepHandler::handleStep()์ ํธ์ถํ๋ ์ญํ ๋ง ํ๋ค.
StepHandler๋ Step ์ฒ๋ฆฌ๋ฅผ ์ํ ์ธํฐํ์ด์ค๋ก SimpleStepHandler ๊ตฌํ์ฒด๋ง ์กด์ฌํ๋ค. (5.1 ๊ธฐ์ค)
๊ทธ๋์, ๋ค์์ผ๋ก๋ SimpleStepHandler์ handleStep() ๋ฉ์๋๋ฅผ ์ดํด๋ณผ ๊ฒ์ด๋ค.
public StepExecution handleStep(Step step, JobExecution execution) throws JobInterruptedException, JobRestartException, StartLimitExceededException {
if (execution.isStopping()) {
throw new JobInterruptedException("JobExecution interrupted.");
} else {
JobInstance jobInstance = execution.getJobInstance(); // 1. ํ์ฌ JobExecution์ JobInstance๋ฅผ ๊ฐ์ง๊ณ ์จ๋ค.
StepExecution lastStepExecution = this.jobRepository.getLastStepExecution(jobInstance, step.getName()); // 2. JobInstance์ ๊ฐ์ฅ ๋ง์ง๋ง ์คํ Step์ ๊ฐ์ง๊ณ ์จ๋ค.
if (this.stepExecutionPartOfExistingJobExecution(execution, lastStepExecution)) {
if (logger.isInfoEnabled()) {
logger.info("ํ์ฌ Job์์ ์ค๋ณต๋ Step ์คํ์ด ๊ฐ์ง๋์๋ค๋ ๋ด์ฉ");
}
lastStepExecution = null;
}
StepExecution currentStepExecution = lastStepExecution;
if (this.shouldStart(lastStepExecution, execution, step)) { // 3. ๊ฐ์ฅ ๋ง์ง๋ง์ผ๋ก ๊ฐ์ ธ์จ Step์ด ์คํ๋์ด์ผ ํ๋์ง ๊ฒ์ฆ
currentStepExecution = execution.createStepExecution(step.getName()); // 4. JobExecution์ ์๋ก์ด StepExecution์ ์ถ๊ฐํ๋ค.
boolean isRestart = lastStepExecution != null && !lastStepExecution.getStatus().equals(BatchStatus.COMPLETED);
if (isRestart) { // 5-1. ์ฌ์คํ๋ Step์ด๋ฉด, ExecutionContext๋ฅผ ๊ทธ๋๋ก ์ค์ ํ๋ค.
currentStepExecution.setExecutionContext(lastStepExecution.getExecutionContext());
if (lastStepExecution.getExecutionContext().containsKey("batch.executed")) {
currentStepExecution.getExecutionContext().remove("batch.executed");
}
} else { // 5-2. ์๋ก ์คํ๋ Step์ด๋ผ๋ฉด, ์๋ก์ด ExecutionContext๋ฅผ ์์ฑํด์ ์ค์ ํ๋ค.
currentStepExecution.setExecutionContext(new ExecutionContext(this.executionContext));
}
this.jobRepository.add(currentStepExecution); // 6. StepExecution ์ ์ฅ
try {
step.execute(currentStepExecution); // 7. Step ์คํ
currentStepExecution.getExecutionContext().put("batch.executed", true);
} catch (JobInterruptedException var8) {
JobInterruptedException e = var8;
execution.setStatus(BatchStatus.STOPPING);
throw e;
}
this.jobRepository.updateExecutionContext(execution); // 8. ์๋ก์ด StepExecution์ด ์ถ๊ฐ๋ JobExecution์ ์ ์ฅํ๋ค.
if (currentStepExecution.getStatus() == BatchStatus.STOPPING || currentStepExecution.getStatus() == BatchStatus.STOPPED) {
execution.setStatus(BatchStatus.STOPPING);
throw new JobInterruptedException("Job interrupted by step execution");
}
}
return currentStepExecution;
}
}์ ์ฝ๋ ์์ผ๋ก 18 ์ฃผ์์ ์์ฑํด๋จ๋ค. ๋ค์ด์ด์ 4.3.1์์ ์์ฑํ ์์์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ด์ด๊ทธ๋จ์ ๊ทธ๋ฆฌ๋ฉฐ ์ดํดํด๋ณผ ๊ฒ์ด๋ค.8 ๊ณผ์ ์ค์ ํ๋ ์ธ์งํด์ผํ ๋ถ๋ถ์ด ์๋ค. ๋ฐ๋ก 7๋ฒ ์ฆ, Step์ ์คํํ๋ ๋ถ๋ถ์ด๋ค. SimpleStepHandler์์ Step::execute()๋ฅผ ํธ์ถํ๋ฉด,
๊ทธ ์ ์, 1AbstractStep::execute()๊ฐ ํธ์ถ๋๋ค.
AbstractStep์ execute() ๋ฉ์๋๋ ์์ ๋ค๋ฃฌ AbstractJob์ execute() ๋ฉ์๋์ ๋งค์ฐ ์ ์ฌํ ์ญํ ์ ๋ด๋นํ๋ค.
์คํ ์ /ํ๋ก StepExecution์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , Step์ ์คํํ ๋๋ doExecute() ๋ฉ์๋๋ฅผ ํธ์ถ์์ผ ๊ตฌํ ํด๋์ค์ ์ ์ํ ์์
์ ํธ์ถํ๋ค.
์ด์ ์ ๋ด์ฉ์ ๋ฐํ์ผ๋ก 4.3.1 ์์์ฝ๋๋ฅผ ์คํํ๋ฉด, Step์ด ์ด๋ป๊ฒ ์์ฑ๋๊ณ ์คํ๋๋์ง ๋ค์ด์ด๊ทธ๋จ์ ๋์ผ๋ก Step์ ๋ํ ์ ๋ฆฌ๋ฅผ ๋ง์น๋ค.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TransferNewUserJobConfiguration {
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
private static final String JOB_NAME = "TRANSFER_NEW_USER_JOB";
private static final String STEP_1_NAME = "TRANSFER_NEW_USER_STEP";
private final UserService userService;
@Bean
public Job transferNewUserJob() {
return new JobBuilder(JOB_NAME, jobRepository)
.start(transferNewUserStep(null))
.build();
}
@Bean
@JobScope
public Step transferNewUserStep(
@Value("#{jobParameters['targetDate']}") LocalDate targetDate
) {
return new StepBuilder(STEP_1_NAME, jobRepository)
.tasklet((contribution, chunkContext) -> {
final List<User> users = userService.findByRegisteredDate(targetDate);
log.info("{} ๋ช
์ ์ ์ ์ ๋ณด๋ฅผ AML ๋ฑ์ ์๋น์ค๋ก ์ ์ก", users.size());
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
}






