Backend Development 21 min read

Comprehensive Guide to Using Quartz Scheduler with Spring Boot for Custom Task Scheduling

This article explains how to integrate the Quartz job‑scheduling library into a Spring Boot application, covering core concepts, Maven dependencies, sample job and configuration code, trigger types, concurrency control, and runtime management of scheduled tasks.

Top Architect
Top Architect
Top Architect
Comprehensive Guide to Using Quartz Scheduler with Spring Boot for Custom Task Scheduling

Introduction – The need for a configurable task start time leads to using Quartz, a Java‑based open‑source job scheduler that offers persistence, job management, and flexible triggers.

Core Concepts

Quartz revolves around three main components:

Job : implement org.quartz.Job and its execute() method.

Trigger : defines when a job runs, e.g., SimpleTrigger or CronTrigger .

Scheduler : orchestrates jobs and triggers.

Demo Maven Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

Sample Job Implementation

public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任务被执行了。。。");
    }
}

Scheduler Configuration (ScheduleConfig)

@Configuration
public class ScheduleConfig {
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "shivaScheduler");
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        prop.put("org.quartz.jobStore.isClustered", "true");
        factory.setQuartzProperties(prop);
        factory.setOverwriteExistingJobs(true);
        return factory;
    }
}

Utility Class (ScheduleUtils)

public class ScheduleUtils {
    public static void createScheduleJob(Scheduler scheduler, QuartzJob job) throws Exception {
        Class
jobClass = getQuartzJobClass(job);
        JobDetail jobDetail = JobBuilder.newJob(jobClass)
            .withIdentity(getJobKey(job.getJobId(), job.getJobGroup()))
            .build();
        CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(job.getCronExpression())
            .withMisfireHandlingInstructionDoNothing();
        CronTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(getTriggerKey(job.getJobId(), job.getJobGroup()))
            .withSchedule(cron)
            .build();
        jobDetail.getJobDataMap().put("TASK_PROPERTIES", job);
        if (scheduler.checkExists(getJobKey(job.getJobId(), job.getJobGroup()))) {
            scheduler.deleteJob(getJobKey(job.getJobId(), job.getJobGroup()));
        }
        scheduler.scheduleJob(jobDetail, trigger);
        if (ScheduleConstants.Status.PAUSE.getValue().equals(job.getStatus())) {
            scheduler.pauseJob(getJobKey(job.getJobId(), job.getJobGroup()));
        }
    }
    // helper methods omitted for brevity
}

Abstract Job and Execution Flow

public abstract class AbstractQuartzJob implements Job {
    private static final ThreadLocal
threadLocal = new ThreadLocal<>();
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        QuartzJob job = new QuartzJob();
        BeanUtils.copyBeanProp(job, context.getMergedJobDataMap().get("TASK_PROPERTIES"));
        try {
            before(context, job);
            if (job != null) {
                doExecute(context, job);
            }
            after(context, job, null);
        } catch (Exception e) {
            after(context, job, e);
        }
    }
    protected void before(JobExecutionContext ctx, QuartzJob job) { threadLocal.set(new Date()); }
    protected void after(JobExecutionContext ctx, QuartzJob job, Exception e) {}
    protected abstract void doExecute(JobExecutionContext ctx, QuartzJob job) throws Exception;
}

Concurrency Control

Two concrete job classes illustrate concurrent vs. non‑concurrent execution:

public class QuartzJobExecution extends AbstractQuartzJob {
    @Override
    protected void doExecute(JobExecutionContext ctx, QuartzJob job) throws Exception {
        JobInvokeUtil.invokeMethod(job);
    }
}

@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
    @Override
    protected void doExecute(JobExecutionContext ctx, QuartzJob job) throws Exception {
        JobInvokeUtil.invokeMethod(job);
    }
}

Job Invocation Utility

public class JobInvokeUtil {
    public static void invokeMethod(QuartzJob job) throws Exception {
        String beanName = getBeanName(job.getInvokeTarget());
        String methodName = getMethodName(job.getInvokeTarget());
        List
params = getMethodParams(job.getInvokeTarget());
        Object bean = isValidClassName(beanName) ? Class.forName(beanName).newInstance() : SpringUtils.getBean(beanName);
        invokeMethod(bean, methodName, params);
    }
    // parsing helpers omitted for brevity
}

Managing Jobs at Runtime

Adding a job via JSON (example shown) inserts a paused record, then ScheduleUtils.createScheduleJob creates the Quartz entry. Status changes invoke scheduler.resumeJob or scheduler.pauseJob accordingly.

Initialization

@PostConstruct
public void init() throws Exception {
    scheduler.clear();
    List
jobs = quartzMapper.selectJobAll();
    for (QuartzJob job : jobs) {
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
}

Additional Topics

Discussion of @DisallowConcurrentExecution vs. default concurrent behavior, @PersistJobDataAfterExecution , and calendar exclusions for specific dates.

BackendJavaconcurrencySpring BootQuartzJob Scheduling
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.