Comprehensive Guide to Quartz Scheduler in Java: Basics, Advanced Usage, and Persistence
This article provides a detailed overview of the Quartz scheduling framework for Java, covering its core concepts, basic and advanced usage with Spring Boot, code examples for interval and Cron‑based jobs, multi‑trigger configurations, bean injection techniques, and database persistence setup.
Quartz is a powerful Java scheduling framework that separates jobs, triggers, and the scheduler, making it suitable for scenarios without distributed requirements but needing dynamic task management such as start, pause, resume, stop, and time modification.
The article begins with an introduction to Quartz's three main components—Job, Trigger, and Scheduler—explaining their relationships and providing a factory‑style diagram to illustrate how a JobDetail can be bound to multiple triggers while a trigger binds to a single JobDetail .
Basic Usage
It demonstrates how to add the spring-boot-starter-quartz dependency, implement a simple job class that prints the thread name and current time, and configure a SimpleTrigger to execute the job every 2 seconds for 30 seconds. The test code creates a SchedulerFactory , builds a JobDetail , defines a Trigger with SimpleScheduleBuilder , schedules the job, starts the scheduler, and finally shuts it down after a sleep period.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
public class SimpleJob implements Job {
@Override
public void execute(JobExecutionContext ctx) {
System.out.println(Thread.currentThread().getName() + "--" +
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
}
}
public class SimpleQuartzTest {
@Test
public void simpleTest() throws SchedulerException, InterruptedException {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
JobDetail job = JobBuilder.newJob(SimpleJob.class)
.withIdentity("job1", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger-1", "trigger-group")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2).repeatForever())
.build();
scheduler.scheduleJob(job, trigger);
scheduler.start();
TimeUnit.SECONDS.sleep(30);
scheduler.shutdown();
}
}It also shows a Cron‑based example using CronTrigger with a sample expression.
public class SimpleQuartzTest {
@Test
public void cronTest() throws SchedulerException, InterruptedException {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
JobDetail job = JobBuilder.newJob(SimpleJob.class)
.withIdentity("job-1", "job-group").build();
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger-1", "trigger-group")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 *"))
.build();
scheduler.scheduleJob(job, cronTrigger);
scheduler.start();
TimeUnit.SECONDS.sleep(30);
scheduler.shutdown();
}
}Advanced Usage
The article explains how a single JobDetail can be associated with multiple triggers, requiring the job to be stored durably using storeDurably() and added to the scheduler without an initial trigger. It provides code for creating two triggers with different intervals and binding them to the same job.
JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
.withIdentity("job1", "job-group")
.storeDurably()
.build();
Trigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "trigger-group")
.startNow()
.forJob(jobDetail)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2).repeatForever())
.build();
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "trigger-group")
.startNow()
.forJob(jobDetail)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3).repeatForever())
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.addJob(jobDetail, false);
scheduler.scheduleJob(trigger1);
scheduler.scheduleJob(trigger2);
scheduler.start();
TimeUnit.SECONDS.sleep(20);
scheduler.shutdown();For injecting beans such as a MyBatis mapper into a Quartz job, two solutions are presented: using JobDataMap to pass the mapper instance (noting serialization issues) and a static utility class implementing ApplicationContextAware to retrieve beans at runtime.
@Component
public class SpringContextJobUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext ctx) { this.context = ctx; }
public static Object getBean(String name) { return context.getBean(name); }
}
public class MajorJob implements Job {
@Override
public void execute(JobExecutionContext ctx) {
JobDataMap map = ctx.getJobDetail().getJobDataMap();
String jobName = map.getString("jobName");
PersonMapper mapper = (PersonMapper) SpringContextJobUtil.getBean("personMapper");
List
list = mapper.queryList();
System.out.println(Thread.currentThread().getName() + "--" + jobName + "--" + list);
}
}The article also covers Quartz persistence, describing the two job stores ( RAMJobStore and JDBCJobStore ), adding the C3P0 dependency, configuring SchedulerFactoryBean , and providing a full quartz.properties file and the complete set of SQL statements to create the required Quartz tables in MySQL.
Finally, it includes a disclaimer and promotional material unrelated to the technical content.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.