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
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 적용

한 번만 실행시키는 Trigger

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("예외"); } }

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

  1. JobDetail을 만들고 해당 JobDetail에서 JobDataMap 얻어서 값 넣기
    1. JobDetail jobDetail = JobBuilder.newJob(TownHallCreateJob.class) .withIdentity(reservationId.toString()).build(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); jobDataMap.put("test", 27);
  1. Job을 구현한 class에서 JobExecutionContext에서 JobDetail 얻고, JobDataMap 얻고 값 얻기
    1. @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); System.out.printf("Townhall Create Test : %s\n", jobDataMap.get("test")); }
 

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.