태스크 스케줄링은 크게 세 부분으로 구성된다.
첫 번째 구성 요소인 태스크는 특정 시간에 또는 정기적으로 실행해야 하는 비즈니스 로직 부분이다.
두 번째 구성 요소인 트리거는 태스크를 실행하는 조건을 지정하는 역할을 한다.
마지막으로 스케줄러는 트리거의 정보를 기반으로 태스크를 실행시킨다.
태스크 스케줄링은 태스크, 스케줄 정의, 태스크 실행과 같이 세 부분으로 구성된다.
스프링 애플리케이션에서 태스크 실행을 트리거하는 방법이 있다.
첫 번째 방법은 애플리케이션 배포 환경 내에 존재하는 별도의 스케줄링 시스템을 통해 외부에서 트리거하는 것이다.
또 다른 방법은 스프링이 제공하는 태스크 스케줄링 기능을 사용하는 것이다.
스프링은 태스크 스케줄링을 수행하는 세 가지 방법을 제공한다.
org.springframework.scheduling.Trigger
인터페이스는 트리거 메커니즘을 정의하는 기능을 제공한다.commonj,timers.TimerManager
인터페이스를 래핑한 클래스이다.java.util.concurrent.ScheduledThreadPoolExecutor
클래스를 래핑한 클래스이다.@Entity
@Table
public class Car {
...
}
@Configuration
@EnableJpaRepositories(basePackages = "...")
@ComponentScan(basPackages = "...")
public class DataServiceConfig {
...
}
@Service
public class DBInitializer {
@Autowired
CarRepository carRepository;
@PostConstruct
public void initDB() {
...
}
}
public interface CarRepository extends CrudRepository<Car, Long> {}
public interface CarService {
...
}
@Service
@Repository
@Transactional
public class CarServiceImple implements CarService {
...
@Override
public void updateCarAgeJob() {
...
}
}
스프링의 TaskScheduler 추상화를 이용해 태스크를 스케줄링하는 또 다른 방법은 애너테이션을 사용하는 방법이다.
스프링은 이를 위해 @Scheduled 애너테이션을 제공한다.
태스크 스케줄링을 위해 애너테이션을 사용하려면 구성 클래스에 @EnableScheduling 애너테이션을 적용해야 한다.
@Configuration
@Import({DataServiceConfig.class})
@EnableScheduling
public class AppConfig {}
@Configuration 애너테이션이 적용된 구성 클래스에서 @Scheduled 애너테이션을 감지할 수 있게 한다.
이때 @Scheduled 애너테이션이 선언된 메서다가 @Configuration 클래스 내에 직접 선언될 수도 있다.
이 애너테이션은 스프링에 관련 스케줄러 정의를 찾게 한다.
대게 컨텍스트 내의 유일한 TaskScheduler 빈이거나, taskScheduler라는 이름의 TaskScheduler 빈 또는 ScheduledExecutorService 빈이다.
아무것도 찾지 못하면 로컨 단일 스레드 기본 스케줄러가 생성되어 등록자 내에서 사용된다.
스프링 빈 내의 특정 메서드를 스케줄링하려면 메서드에 @Scheduled 애너테이션을 선언하고 스케줄링 요구사항을 전달해야 한다.
아래 코드는 @Configuration 클래스 내에 @Scheduled 애너테이션이 선언돼 메서드가 직접 선언된 예시와 메서드에 @Scheduled 애너테이션을 선언하는 예시다.
@Configuration
@Import({DataServiceConfig.class})
@EnableScheduling
public class AppConfig {
@Bean
TaskScheduler carScheduler() {
...
}
}
@Service
@Repository
@Transactional
public class CarServiceImple implements CarService {
...
@Override
@Scheduled(fixedDelay=10000)
public void updateCarAgeJob() {
...
}
}
이 기능을 사용하려면 @Async 애너테이션을 메서드에 적용하면 된다.
@Async 애너테이션은 스프링의 비동기 메서드 실행 기능을 활성화하면 사용할 수 있는데, 자바 구성 클래스에 @EnableAsync 애너테이션을 선언하는 것만으로 수행할 수 있다.
@Configuration
@EnableAsync
@ComponetScan(basePackages = "...")
public class AppConfig {}
스프링 2.0 버전 이후부터 프레임워크는 TaskExecutor 인터페이스를 통해 태스크를 샐행하기 위한 추상화를 제공해왔다.
TaskExecutor는 이름에서 드러내듯이 자바 Runnable 구현체인 태스크를 실행시킨다.
스프링은 TaskExecutor 인터페이스를 바로 사용할 수 있도록 용도별 TaskExecutor 구현체 몇 가지를 제공한다.
@Componet
public class TaskToExecute {
@Autowired
private TaskExecutor taskExecutor;
public void executeTask() {
for (int i=0; i < 10; i++) {
taskExecutor.execute(() ->
logger.info(Thread.currentThread().getName));
}
}
}
TaskToExecute 클래스는 TaskExecutor 의존성을 주입받아야 하는 일반적인 빈이며 executeTask()
메서드를 정의한다.
executeTask()
메서드는 주입받은 TaskExecutor의 execute
메서드를 호출하며, 이 execute
메서드를 호출할 때 이 태스크가 실행하고자 하는 로직이 담긴 새 Runnable 인스턴스를 생성해 넘겨준다.
위의 예제에서는 Runnalbe 인스턴스 생성에 람다식을 사용하였다.
구성에서는 추가로 TaskExecutor 빈에 대해 선언하였다.
@Configuration
@EnableAsync
@ComponetScan(basePackages = "...")
public class AppConfig {
@Bean
TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
taskExecutor라는 이름을 가진 간단한 SimpleAsyncTaskExecutor 타입의 빈이 정의되었다.
스프링 IoC 컨테이너는 TaskToExecute 빈에 taskExecutor 빈을 주입한다.