du.study기록공간

Spring Scheduling @EnableScheduling, @Scheduled 본문

스프링

Spring Scheduling @EnableScheduling, @Scheduled

du.study 2020. 5. 27. 22:19
728x90

이번엔 스프링에서 사용하고 있는 Scheduling에 대해서 기록해보려합니다.

 

현재 저희 프로젝트에서는 일정시간단위로 반복되는 작업 처리 방법 중 하나로 해당 Scheduled를 사용하고 있습니다.

 

오늘 갑자기 코드를 보면서 해당 @Scheduled 가 어떻게 실행되고 set되는지가 궁금하여 조금 간략하게나마 여기에 기입해보려합니다.

 

먼저 Spring에서 Scheduling을 사용하려면 @Configuration을 선언한 설정파일과 같이 @EnableScheduling를 선언해야합니다.

@Configuration
@EnableScheduling
public class ScheduleConfiguration implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }
    
    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }
}

 

@EnableScheduling 내부를 보면 다음과 같이 ScheduledAnnotationBeanPostProcessor Bean을 등록하게 됩니다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}


@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
		return new ScheduledAnnotationBeanPostProcessor();
	}

}

Bean으로 생성하는 ScheduledAnnotationBeanPostProcessor에서 핵심적인 역할을 하게됩니다.

 

postProcessAfterInitialization() 메소드에서 @Scheduled을 선언한 메소드를 수집,해당 스케쥴들을 processScheduled을통해 등록을 하게됩니다. ( 간단하게 ScheduledAnnotationBeanPostProcessor 가 스케쥴 테스크등록의 핵심 클레스입니다. ) 

 

해당 클레스에서 스케쥴러 쓰레드 등록하는 코드또한 지원합니다.

@Configuration
@EnableScheduling
public class ScheduleConfiguration implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }
}

private void finishRegistration() {
    .....
    
    Map<String, SchedulingConfigurer> beans =
        ((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
    List<SchedulingConfigurer> configurers = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(configurers);
    for (SchedulingConfigurer configurer : configurers) {
      configurer.configureTasks(this.registrar);
    }
    .....


@EnableScheduling 와는 별도로 SchedulingConfigurer를 implements해줍니다. 해당 선언을 통해 해당 클레스는 SchedulingConfigurer.class 조회에 포함되며, 해당 선언을 세팅할 수 있게됩니다.

 

public void setScheduler(@Nullable Object scheduler) {
  if (scheduler == null) {
    this.taskScheduler = null;
  }
  else if (scheduler instanceof TaskScheduler) {
    this.taskScheduler = (TaskScheduler) scheduler;
  }
  else if (scheduler instanceof ScheduledExecutorService) {
    this.taskScheduler = new ConcurrentTaskScheduler(((ScheduledExecutorService) scheduler));
  }
  else {
    throw new IllegalArgumentException("Unsupported scheduler type: " + scheduler.getClass());
  }
}

아래 해당 setScheduler 코드를 보면 하부 ScheduledExecutorService를 등록하여 쓰레드를 할당해줍니다.

 

 

해당과정을 통해서 선언한 Schedule 등록, 스레드 세팅 까지 완료가됩니다. ( 너무간단하게 적은거 같긴한데..)

 

마지막으로 해당 스케쥴링을 어디서 실행해 주는가는 ThreadPoolExecutor에 있는 runWorker 에 의하여 while문 루프를 돌면서 스케쥴을 주기적으로 실행하게 됩니다..

 

음 ... 정리하면서도 너무 간단하게 정리한것같아 조금씩 해당포스팅은 업데이트를 해야할 것 같습니다.

 

 

728x90
Comments