spring sheduler를 사용하기 위해서 다음 작업이 필요하다.
@EnableScheduling
등록@Scheduled
와 cron
옵션을 통해 작업 등록1초마다 "test..."를 출력하는 메서드를 등록했다.
현재 위치:
@EnableScheduling
@EnableScheduling
어노테이션에서 시작한다. 어노테이션 내부를 보자.
@Import(SchedulingConfiguration.class)
가 보인다.
@Import
어노테이션은 @Configuration
으로 설정한 클래스를 두 개 이상 사용하는 경우 spring에게 해당 클래스를 인식시키기 위해 사용한다.
그렇다면 SchedulingConfiguration
클래스에는 무언가 Bean
으로 등록하고 있다는 소리다. 살펴보자.
현재 위치:
SchedulingConfiguration
클래스
ScheduledAnnotationBeanPostProcessor
클래스를 Bean
으로 등록하고 있다. 그렇다면 해당 클래스는 무슨 일을 하는 것일까? 더 들어가보자.
현재 위치:
ScheduledAnnotationBeanPostProcessor
클래스
우선 생성자에서 registrar
멤버에 ScheduledTaskRegistrar
인스턴스를 생성하고 있다. 이건 나중에 한 번 살펴보자.
밑에 코드를 보면Bean
초기화 전/후처리 메서드가 존재한다.
후처리 메서드인 postProcessAfterInitialization()
메서드를 한 번 살펴보자.
검정색 박스 부분에서 @Scheduled
가 달려 있는 메서드를 스캔해서 annotatedMethods
map에 저장한다.
후에 빨간색 박스 부분에서 스캔한 메서드들을 이용해 processScheduled()
을 호출한다.
processScheduled()
에서 맨 처음에 입력한 cron
식이나, fixedDelay
에 대해 제대로 입력했는지 유효성을 검사한다.
이후에 cron
식을 사용했다면, registrar.scheduleCronTask()
를 실행해 CronTask
인스턴스와 CronTrigger
인스턴스를 인자로 넣어주게 된다.
정확히는 CronTask
생성자에 CronTrigger
가 등록되고, CronTask
가 registrar.scheduleCronTask()
의 인자로 넘어간다.
registrar
는 아까 우리가 맨 처음에 생성자에서 봤던 바로 그 registrar
멤버다.
따라서 ScheduledTaskRegistrar.scheduleCronTask()
를 실행하면서 CronTask
인스턴스를 인자로 넘겨주게 된다. 한 번 따라가보자.
현재 위치:
ScheduledTaskRegistrar
클래스
ScheduledTask
인스턴스를 생성해 Task에 대한 정보를 등록해서 return하고 있다.
검정색 박스 부분을 보면, ScheduledTask.future
부분에 taskScheduler.schedule()
을 통해CronTask
를 넘겨주고 있다. taskScheduler
로 한 번 넘어가보자.
현재 위치:
TaskScheduler
인터페이스
TaskScheduler
는 인터페이스였다. 설명을 읽어보면 ThreadPoolTaskScheduler
클래스가 이 인터페이스의 Default 구현체라고 한다.
그렇다면 taskScheduler.schedule()
에서 taskScheduler
는 바로 ThreadPoolTaskScheduler
일 것이다. 해당 클래스의 schedule()
메서드로 가보자.
현재 위치:
ThreadPoolTaskScheduler
클래스
ScheduledExecutorService
인터페이스를 생성하고 있다. ScheduledExecutorService
인터페이스에 쓰레드 풀을 이용해 우리가 등록한 작업을 실행시켜줄 구현체가 들어갈 것이다.
이를 new ReschedulingRunnable()
에 넘겨주고 있다. 그리고 schedule()
메서드를 실행한다. ReschedulingRunnable
클래스로 가보자.
현재 위치:
ReschedulingRunnable
클래스
ReschedulingRunnable.schedule()
메서드에서 delay를 계산 후, 해당 시간이 지나면 시작하는 작업을 excutor.schedule()
을 통해 등록한다. executor
는 위에서 언급한 ScheduledExecutorService
의 구현체다.
이후에 executor
는 delay 후 쓰레드를 할당해 run()
메서드를 실행하게 된다. 빨간 박스를 보면 실행이 다 끝난다면 다시 ReschedulingRunnable.schedule()
메서드를 호출해 작업을 executor
에 재등록한다.
이렇게 우리가 지정한 시간대로 작업이 무한히 실행되는 것이다.
ScheduledAnnotationBeanPostProcessor
클래스가 Bean
으로 등록됨.Bean
등록 후 후처리 메서드postProcessAfterInitialization()
가 실행. 이 때 @Scheduled
가 달려있는 메서드 스캔 후 Map에 저장.processScheduled()
메서드 호출하여 해당 Map을 넘겨줌.processScheduled()
에서 cron
, fixedDelay
중 어떤 옵션을 선택했느냐에 따라 각각 알맞은 Task 진행.cron
으로 진행했다고 가정. processScheduled()
에서 ScheduledTaskRegistrar.scheduleCronTask()
호출. 이 때 Task와 Delay에 대한 정보를 넘겨줌.ScheduledExecutorService
의 구현체(이하 excutor
)에 Schedule()
메서드에 Task와 Delay 등록됨.excutor
가 쓰레드풀에서 쓰레드를 할당하여 run()
메서드 실행하여 Task 실행.run()
메서드에는 excutor
에 다시 Task와 Delay를 설정하도록 되어있음. 따라서 다시 해당 Delay 후에 Task 실행.이렇게 진행되겠다.
즉 결국엔 Spring Scheduler는 특별한 것을 이용하고 있는 것이 아니라 자바의 java.util.concurrent
패키지에 있는 Executor
를 사용하고 있던 것이다.