HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
Spring
Spring
/
Spring Cron (Spring Quartz, @Scheduler)

Spring Cron (Spring Quartz, @Scheduler)

[ Baeldung ] Scheduling in Spring with Quartz
Running one-time jobs with Quartz and Spring Boot
Quartz vs Spring SchedulerShedlock — Spring Scheduler 중복 실행 제어하기동작원리Cron ExpressionJob 클래스에 스프링의 의존성 추가하기한 번만 실행시키는 TriggerJob 클래스에 파라미터 넘기기 → JobDataMapTroubleShooting

Quartz vs Spring Scheduler

Spring Scheduler
spring에서 built-in으로 제공하고, 프로젝트가 복잡하지 않고 단순히 scheduling과 bean 만 필요하다면 quartz 보다 spring scheduler가 나음
메인 메소드에 @EnableScheduling 을 달고, 원하는 메서드에 @Scheduler 를 달아서 사용
메서드는 void의 return 이어야 함. 파라미터 가질 수 없음
 
quartz에서 제공해주는 기능
1. scheduler간의 clustering기능
2. shceduler 실패에 대한 후처리 기능
3. JVM  종료 이벤트를 캐치하여 스케줄러에게 종료를 알려주는기능
4. 여러가지 plugin
5. 이벤트 처리 (Job, Trigger)
 
즉 복잡한 설정이 필요할때는 quartz를 사용하면 된다.

Shedlock — Spring Scheduler 중복 실행 제어하기

Spring Scheduler 에서는 여러 대의 인스턴스에 대한 클러스터링 기능을 제공하지 않는다. 이 때문에 서버 인스턴스가 여러 대로 늘어나는 Scale Out 상황에서 중복으로 실행되어 문제를 일으킬 수 있다. 이를 위해 Lukas Krecan 님께서 만들어주신 Shedlock 이라는 오픈소스 솔루션을 적용하면 문제를 해결할 수 있다.

동작원리

[ Baeldung ] Scheduling in Spring with Quartz
#Job, #JobDetail, #Trigger, #Scheduler
  • 매번 Job이 실행될 때마다 JobDetail instance를 만듦(Job 클래스의 instance를 만들지 않음). JobDetail 객체는 Job의 상세한 속성들을 갖고 있음

Cron Expression

CronMaker
Spring Cron Expressions
Cron 표현식으로는 한 번만 실행시킬 수가 없음. 정기적인 작업에 대한 스케줄링
 

Job 클래스에 스프링의 의존성 추가하기

How to Inject Spring Beans into Quartz Jobs

한 번만 실행시키는 Trigger

Job 클래스에 파라미터 넘기기 → JobDataMap

  1. JobDetail을 만들고 해당 JobDetail에서 JobDataMap 얻어서 값 넣기
  1. Job을 구현한 class에서 JobExecutionContext에서 JobDetail 얻고, JobDataMap 얻고 값 얻기
 

TroubleShooting

  • Job 인터페이스를 구현한 Job 클래스는 Job이 실행될 때 마다 만들어지는 방식으로 동작함. 그래서 Job 사이에 데이터를 공유하기 위해서는 static 필드를 가져야 데이터 공유가 가능함
    • While the job is the workhorse, Quartz doesn’t store an actual instance of the job class. Instead, we can define an instance of the Job using the JobDetail class. 
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } }
SpringBeanJobFactory 를 이용해서 자동으로 quartz object를 autowire시킬 수 있도록
@Bean public SchedulerFactoryBean quartzScheduler() { SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean(); ... AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); quartzScheduler.setJobFactory(jobFactory); ... return quartzScheduler; }
위의 AutowiringSpringBeanJobFactory를 SchedulerFactoryBean에 넣어주기
@Component public class TownHallCreateJob implements Job { @Autowired private GameServerService gameServerService; @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("Townhall Create Test"); } }
그 후 Job에 @Component 달고 @Autowired 적용
public void scheduleTownHallCreate(LocalDateTime startTime, Integer reservationId) { try { JobDetail jobDetail = JobBuilder.newJob(TownHallCreateJob.class) .withIdentity(reservationId.toString()).build(); Instant instant = startTime.atZone(ZoneId.systemDefault()).toInstant(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity(reservationId.toString()) .startAt(Date.from(instant)) .build(); scheduler.scheduleJob(jobDetail, trigger); if (!scheduler.isStarted()) { scheduler.start(); } } catch (SchedulerException e) { throw new RuntimeException("예외"); } }
JobDetail jobDetail = JobBuilder.newJob(TownHallCreateJob.class) .withIdentity(reservationId.toString()).build(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); jobDataMap.put("test", 27);
@Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); System.out.printf("Townhall Create Test : %s\n", jobDataMap.get("test")); }