Scheduling & Batch Processing Dependencies
Learn the built-in @Scheduled annotation, spring-boot-starter-quartz for advanced job scheduling, and spring-boot-starter-batch for large-scale batch processing.
Introduction
Applications frequently need to do things on a schedule — clean up expired sessions at midnight, generate a daily report, or process a nightly batch of orders. Spring offers three tiers for this, each with a different amount of power (and a different amount of setup): built-in @Scheduled, Quartz for advanced clustered scheduling, and Spring Batch for large-scale data processing.
- How to schedule simple recurring tasks with @Scheduled — and why it needs no extra dependency.
- How to use spring-boot-starter-quartz for persistent, clustered, cron-driven jobs.
- How to use spring-boot-starter-batch for large-scale, step-based batch processing.
- How to decide which of the three fits a given job.
Simple Scheduling: @Scheduled (No Extra Dependency)
This is the one exception in this entire course: @Scheduled and @EnableScheduling live in spring-context, which is already part of Spring Boot's core. You do not need to add anything to pom.xml to use them — just enable scheduling on a configuration class.
@Configuration@EnableSchedulingpublic class SchedulingConfig {}@Componentpublic class CleanupTask {
@Scheduled(cron = "0 0 0 * * *") // every day at midnight public void purgeExpiredSessions() { System.out.println("Purging expired sessions..."); }}@Scheduled is ideal for simple, single-instance, in-memory recurring tasks: cache eviction, periodic health checks, small housekeeping jobs. It has no persistence and no clustering awareness — if your app runs on multiple instances, every instance runs the job independently, which is often not what you want.
Advanced Scheduling: spring-boot-starter-quartz
Use case: you need jobs that survive an application restart, that run exactly once across a cluster of instances (not once per instance), or that need dynamic scheduling changed at runtime rather than hardcoded at compile time. Quartz is a mature, persistent job scheduler that solves all three.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId></dependency>Define the job logic, then wire up a JobDetail and a Trigger as beans.
public class ReportJob extends QuartzJobBean {
@Override protected void executeInternal(JobExecutionContext context) { System.out.println("Generating daily report..."); }}@Configurationpublic class QuartzConfig {
@Bean public JobDetail reportJobDetail() { return JobBuilder.newJob(ReportJob.class) .withIdentity("reportJob") .storeDurably() .build(); }
@Bean public Trigger reportJobTrigger(JobDetail reportJobDetail) { return TriggerBuilder.newTrigger() .forJob(reportJobDetail) .withIdentity("reportJobTrigger") .withSchedule(CronScheduleBuilder.cronSchedule("0 0 6 * * *")) // 6 AM daily .build(); }}Click Run to see what this code prints.
Batch Processing: spring-boot-starter-batch
Use case: processing large volumes of data in discrete steps — reading thousands of rows from a database or file, transforming them, and writing the results elsewhere (a classic ETL job). Spring Batch provides structured Job and Step abstractions, chunk-based processing, and restart/retry support for failures partway through.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId></dependency>@Configurationpublic class ExportJobConfig {
@Bean public Step exportStep(JobRepository jobRepository, PlatformTransactionManager txManager, ItemReader<Order> reader, ItemProcessor<Order, OrderSummary> processor, ItemWriter<OrderSummary> writer) { return new StepBuilder("exportStep", jobRepository) .<Order, OrderSummary>chunk(100, txManager) .reader(reader) .processor(processor) .writer(writer) .build(); }
@Bean public Job exportJob(JobRepository jobRepository, Step exportStep) { return new JobBuilder("exportJob", jobRepository) .start(exportStep) .build(); }}Click Run to see what this code prints.
Which One Should You Use?
| Need | Use |
|---|---|
| Simple recurring task, single instance, no persistence needed | @Scheduled (built-in) |
| Cron-like jobs that must survive restarts or run once across a cluster | spring-boot-starter-quartz |
| Large-volume, step-based data processing (ETL, nightly imports/exports) | spring-boot-starter-batch |
Common Mistakes
- Using @Scheduled for jobs that must run exactly once across multiple clustered instances — every instance will run it independently.
- Reaching for Spring Batch for a simple task that @Scheduled could handle in a few lines.
- Forgetting to configure a persistent JobStore (JDBC) for Quartz in production, leaving jobs to reset on every restart.
- Not sizing chunk() appropriately in Spring Batch, causing excessive memory use or too many small transactions.
Best Practices
- Start with @Scheduled and only reach for Quartz or Spring Batch once you hit its real limits.
- Use cron expressions (not fixedRate/fixedDelay) for anything that needs to run at a specific wall-clock time.
- Configure Quartz with a JDBC job store in any multi-instance deployment.
- Log Spring Batch job/step completion status so failures are visible in monitoring, not just in job repository tables.
Frequently Asked Questions
Yes, there is no conflict — use @Scheduled for simple in-process tasks and Quartz only for the specific jobs that need persistence or clustering.
Yes, in production it needs a JobRepository backed by a real database to track job/step execution state; an in-memory repository is available for quick testing only.
Often, yes. Most applications never need it — reach for it specifically when @Scheduled's per-instance, non-persistent behavior becomes a real problem.
Summary
@Scheduled handles simple recurring tasks with zero extra dependencies, spring-boot-starter-quartz adds persistent and clustered job scheduling, and spring-boot-starter-batch provides structured, chunk-based processing for large data volumes.
- You scheduled a simple task with @Scheduled and no extra dependency.
- You configured a persistent job with spring-boot-starter-quartz.
- You built a minimal Job/Step with spring-boot-starter-batch.
- You can decide which of the three fits a given scheduling need.