Clean Code that Works.

보통 Spring에서 스케쥴링 기능을 사용할 때, @Scheduled 애노테이션을 사용한다. 

쉽고 간편하다. <task:annotation-driven /> 설정을 해주거나 3.1 부터 추가된 @EnableScheduling을 설정 클래스에 추가해주면 된다.


그럼 이 annotation은 spring이 어떻게 감지 해서 동작하는 것일까?

ScheduledAnnotationBeanPostProcessor를 살펴보면 답이 나와있다.

이 Bean post-processor가 @Scheduled 애노테이션이 붙어있는 메서드들을 다 찾아서 TaskScheduler에다가 다 등록을 해준다.

주석엔 아래와 같이 설명이..

Bean post-processor that registers methods annotated with @{@link Scheduled}

to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according

to the "fixedRate", "fixedDelay", or "cron" expression provided via the annotation.


사실 갑자기 왜 이걸 보게 되었냐면.....

@Scheduled 옵션을 사용할때 고려되는 문제점이 cron 옵션으로 동작하게 할 수 있는데, 이때 서로 다른 서버에서 같은 동작을 하는 작업을 할 때 cron 식을 달리 하고 싶은 경우가 많다. spring batch 호출 메서드를 각 서버마다 다른 시간으로 호출 하고 싶은경우...

해서, ScheduledAnnotationBeanPostProcessor 살펴보니 여기서 @Scheduled 애노테이션의 cron 식 설정을 불러다가 CronTask를 만드는데 사용을 하고 있는걸 확인할 수 있었다. cron에 cron 식이 들어 오면 그대로 쓰고 설정값('${batch.run.cron}') 형식이 들어오면 프로퍼티 파일에서 읽어다가 쓰는 방식.


그럼 @Scheduled 를 상속 하거나 약간 수정해서 프로퍼티 파일에서 값을 읽는 부분을 수정(동작할 때 서버의 host 명을 가지고 와서 이 host 명 + .batch.run.cron ' 식으로 cron 값을 서버별로 읽어 들이는) 하는 방식으로 말이다.

이렇게 동작 시키기 위해서 ScheduledAnnotationBeanPostProcessor 를 참조 하여 별도의 BeanPostProcessor(별도라고는는 하지만, 설정파일 읽는 부분만 변경 하면된다)를 만들고 이를 bean으로 등록 해주면 간단히 구현이 가능해진다.


ScheduledAnnotationBeanPostProcessor 를 3.0 버전 3.1 버전 3.2 버전 별로 살펴보면 조금씩 리팩토링을 해 나간것을 볼 수 있다. (이런거 살펴보는것도 나름 소소한 재미 인듯, 다른 사람 소스도 살펴보고)  바뀐 부분은 ScheduledTaskRegistrar 에다가 cronTasks, fixedDelayTasks, fixedRateTasks 세팅하는 부분이 좀더 캡슐화 되어 보기 더 편해졌다는 정도?

before

if (!"".equals(cron)) {

processedSchedule = true;

if (embeddedValueResolver != null) {

cron = embeddedValueResolver.resolveStringValue(cron);

}

cronTasks.put(runnable, cron);

}


after

if (!"".equals(cron)) {

processedSchedule = true;

if (embeddedValueResolver != null) {

cron = embeddedValueResolver.resolveStringValue(cron);

}

registrar.addCronTask(new CronTask(runnable, cron));

}