1. What Is Scheduling?
Scheduling means executing a task automatically at specific intervals or times — without manual intervention.
For example:
- Sending daily email reports at 9 AM.
- Cleaning up expired sessions every midnight.
- Syncing data with a remote API every 15 minutes.
Analogy:
Like setting a reminder/alarm in your phone — once set, it just runs at the given time.
2. When To Use Scheduling (And When Not)
Use scheduling when:
-
You need periodic background jobs, like:
- Cache cleanup
- Sending notifications
- Data synchronization
- Analytics aggregation
You can tolerate small delays (non-real-time).
Avoid scheduling when:
- You need instant reactions to user input → use events or message queues instead.
- The execution needs high reliability and persistence → better use a job queue system (e.g., Redis Queue, Kafka, etc.).
- You have distributed systems where the same job might run in multiple nodes simultaneously (unless you use Quartz’s clustering feature).
3. Simple Scheduling in Spring Boot
Spring Boot has built-in scheduling support using @Scheduled
.
Step 1 — Enable Scheduling
@SpringBootApplication
@EnableScheduling
public class SchedulerApp {
public static void main(String[] args) {
SpringApplication.run(SchedulerApp.class, args);
}
}
Step 2 — Create a Scheduled Task
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SimpleScheduler {
// Runs every 5 seconds
@Scheduled(fixedRate = 5000)
public void taskFixedRate() {
System.out.println("Running every 5 seconds: " + System.currentTimeMillis());
}
// Runs at specific cron expression (every minute)
@Scheduled(cron = "0 * * * * *")
public void taskCron() {
System.out.println("Cron task every minute");
}
}
Step 3 — Common Parameters
-
fixedRate
: runs repeatedly at fixed intervals (starts new task after interval, even if previous not done). -
fixedDelay
: waits until the previous task completes, then waits the delay. -
cron
: full cron syntax for complex schedules.
4. Limitations of Simple Spring Scheduling
While @Scheduled
is simple, it’s static and limited:
Limitation | Description |
---|---|
Static schedule | You must hardcode the schedule in code (@Scheduled annotation). |
No persistence | If the app restarts, jobs are lost — no history. |
No control | You cannot pause/resume/modify job runtime. |
No clustering | Multiple app instances will run the same job. |
No job metadata | You can’t track last execution, duration, or failures easily. |
That’s where Quartz Scheduler comes in.
5. Introducing Quartz Scheduler
What is Quartz?
Quartz is a powerful, open-source job scheduling library designed for enterprise-grade use cases.
Key Features:
- Persistent jobs (backed by a database)
- Dynamic scheduling (add/edit/delete jobs at runtime)
- Cron-based triggers
- Clustering for distributed systems
- Job history, monitoring, pausing, resuming
- Supports concurrency control
Real-world uses:
- Payment retry jobs
- Batch data processing
- Generating scheduled reports
6. Using Quartz with Spring Boot
Step 1 — Add Dependency
In pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
Step 2 — Configure Quartz
In application.yml
:
spring:
quartz:
job-store-type: jdbc # or memory
jdbc:
initialize-schema: always
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
threadPool:
threadCount: 5
Use
job-store-type: jdbc
to persist jobs in DB (Spring Boot will auto-create Quartz tables).
7. Example: Static Job with Quartz
Step 1 — Define a Job
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;
@Component
public class EmailJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("Sending email at " + System.currentTimeMillis());
}
}
Step 2 — Register Job and Trigger
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
@Bean
public JobDetail emailJobDetail() {
return JobBuilder.newJob(EmailJob.class)
.withIdentity("emailJob")
.storeDurably()
.build();
}
@Bean
public Trigger emailJobTrigger() {
CronScheduleBuilder schedule = CronScheduleBuilder.cronSchedule("0 0/1 * * * ?"); // every 1 min
return TriggerBuilder.newTrigger()
.forJob(emailJobDetail())
.withIdentity("emailTrigger")
.withSchedule(schedule)
.build();
}
}
That’s it — Quartz will automatically start and execute the job.
8. Dynamic Jobs and Triggers (Runtime Creation)
This is where Quartz shines.
You can add, update, or remove jobs dynamically at runtime — for example, from a REST API.
Example — Create Job Dynamically
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DynamicJobService {
@Autowired
private Scheduler scheduler;
public void scheduleJob(String jobName, String group, String cron) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(EmailJob.class)
.withIdentity(jobName, group)
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger", group)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
scheduler.scheduleJob(jobDetail, trigger);
}
public void deleteJob(String jobName, String group) throws SchedulerException {
scheduler.deleteJob(new JobKey(jobName, group));
}
public void pauseJob(String jobName, String group) throws SchedulerException {
scheduler.pauseJob(new JobKey(jobName, group));
}
public void resumeJob(String jobName, String group) throws SchedulerException {
scheduler.resumeJob(new JobKey(jobName, group));
}
}
Now, you can expose this service via REST to manage jobs dynamically:
@RestController
@RequestMapping("/api/jobs")
public class JobController {
@Autowired private DynamicJobService service;
@PostMapping("/create")
public String create(@RequestParam String name, @RequestParam String cron) throws Exception {
service.scheduleJob(name, "default", cron);
return "Job scheduled!";
}
@DeleteMapping("/{name}")
public String delete(@PathVariable String name) throws Exception {
service.deleteJob(name, "default");
return "Job deleted!";
}
}
9. Comparing @Scheduled
vs Quartz
Feature | @Scheduled |
Quartz |
---|---|---|
Simplicity | ✅ Very easy | ❌ More setup |
Dynamic runtime scheduling | ❌ No | ✅ Yes |
Persistent jobs | ❌ No | ✅ Yes |
Clustering | ❌ No | ✅ Yes |
Cron syntax | ✅ Yes | ✅ Yes |
Runtime control (pause/resume) | ❌ No | ✅ Yes |
Distributed safe | ❌ No | ✅ Yes |
10. Pro Tips
- Use Quartz JDBC store for production.
- Use Spring’s
SchedulerFactoryBean
for fine-grained control. - Wrap Quartz jobs in transactional service layers when interacting with DB.
- Use UUIDs or composite keys for job identity.
- Combine with Spring Events or Redis Pub/Sub to trigger jobs remotely.
Summary
Concept | Tool |
---|---|
Basic periodic tasks | @Scheduled |
Dynamic, persistent, cluster-safe jobs | Quartz |
Want both? | Start with simple scheduling → move to Quartz as you grow |
Top comments (0)