Integrating Quartz Scheduler with Spring Boot for Custom Task Scheduling
This article provides a comprehensive guide on using the Quartz job‑scheduling library in Java, covering core concepts such as Job, Trigger, and Scheduler, showing Maven setup, sample job and trigger code, Spring Boot configuration, utility classes for creating and managing jobs, handling concurrency, misfire policies, and REST‑based job management.
The article starts by describing a common requirement to let customers define custom task start times and explains why the widely used Quartz library—an OpenSymphony open‑source project for job scheduling—should be modularized instead of hard‑coding timers.
It introduces the three core components of Quartz: Job (the executable task class implementing execute() ), Trigger (including SimpleTrigger and CronTrigger to define when a job fires), and Scheduler (which coordinates triggers and jobs). The relationship among them is illustrated with a diagram.
Basic usage is demonstrated with Maven dependencies: <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.3.0</version> </dependency> A sample job class implements org.quartz.Job and prints a message in its execute() method.
The main method creates a SchedulerFactory , builds a JobDetail for the sample job, configures a SimpleTrigger to fire every 30 seconds, schedules the job, prints timestamps, starts the scheduler, sleeps for one minute, and then shuts down the scheduler: public static void main(String[] args) throws Exception { SchedulerFactory factory = new StdSchedulerFactory(); Scheduler scheduler = factory.getScheduler(); JobDetail job = JobBuilder.newJob(MyJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(30) .repeatForever()) .build(); scheduler.scheduleJob(job, trigger); scheduler.start(); TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); }
The article then explains JobDetail as the holder of job metadata and why Quartz creates a new job instance for each execution to avoid concurrency issues. It shows how to access the JobExecutionContext inside the job to retrieve trigger start/end times or custom data maps.
For Spring Boot integration, a @Configuration class ScheduleConfig defines a SchedulerFactoryBean with properties such as instance name, thread pool size, job store class, clustering options, and startup delay. The configuration also sets overwriteExistingJobs to true so that changes to target objects are reflected without manual DB cleanup.
The utility class ScheduleUtils contains methods to generate JobKey and TriggerKey , create a schedule job, and handle misfire policies based on the misfirePolicy field (default, ignore, fire‑and‑proceed, do‑nothing). It builds a CronScheduleBuilder , attaches the job data map, and pauses the job if its status is set to PAUSE.
An abstract base class AbstractQuartzJob implements Job and defines a template method pattern with before() , doExecute() , and after() hooks. Two concrete subclasses— QuartzJobExecution (concurrent) and QuartzDisallowConcurrentExecution (annotated with @DisallowConcurrentExecution to prevent overlapping executions)—override doExecute() to invoke the target method via JobInvokeUtil .
JobInvokeUtil parses the invokeTarget string (e.g., mysqlJob.execute('got it!!!') ), extracts the bean name, method name, and parameters, determines whether the target is a Spring bean or a fully‑qualified class, and uses reflection to call the method with appropriate argument types (String, Boolean, Long, Double, Integer).
The article also shows how to add a new scheduled job via a JSON payload (specifying fields like concurrent , cronExpression , invokeTarget , jobGroup , etc.), insert it into the database with status PAUSE, and then create the Quartz job using ScheduleUtils.createScheduleJob . It provides service methods to change job status, resume or pause jobs, and an @PostConstruct init() method that clears the scheduler on startup and re‑creates all jobs from the database to keep the scheduler and persistence layer in sync.
Finally, the article discusses concurrency control: by default Quartz allows concurrent execution of the same job definition; adding @DisallowConcurrentExecution prevents overlapping runs, and @PersistJobDataAfterExecution can be used to keep job data between executions, but should be combined with the disallow‑concurrent annotation to avoid data races.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.