Heads-up: Java cron ≠ Unix cron
Both Spring and Quartz use a 6-field cron expression that starts with seconds:
# Unix cron (5 fields) min hour dom mon dow 0 9 * * 1-5 # Spring / Quartz (6 fields — seconds first!) sec min hour dom mon dow 0 0 9 * * MON-FRI
- Quartz day-of-week is 1-7 (1 = Sunday), and exactly one of day-of-month / day-of-week must be
?. - Spring day-of-week is 0-7 (0 or 7 = Sunday) or
MON-SUN, and supportsL,W,#.
Spring @Scheduled
@Component
public class Jobs {
// every weekday at 09:00 (sec min hour dom mon dow)
@Scheduled(cron = "0 0 9 * * MON-FRI", zone = "UTC")
public void report() {
// ...
}
// every 15 minutes
@Scheduled(cron = "0 */15 * * * *")
public void poll() { }
}Enable it with @EnableScheduling on a configuration class.
Quartz Scheduler
JobDetail job = JobBuilder.newJob(ReportJob.class).build();
Trigger trigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 9 ? * MON-FRI") // note the ?
.inTimeZone(TimeZone.getTimeZone("UTC")))
.build();
scheduler.scheduleJob(job, trigger);ScheduledExecutorService (fixed intervals)
For simple fixed-rate work with no cron semantics, the JDK has you covered:
ScheduledExecutorService ex = Executors.newScheduledThreadPool(1); ex.scheduleAtFixedRate(this::poll, 0, 15, TimeUnit.MINUTES);
Monitor it free with cron.watch
A scheduled bean that throws on startup, or a JVM that crashes, takes its schedule down with it — quietly. A schedule is only a promise — cron.watch tells you if the job actually ran, alerts you when it silently fails, skips, or runs long, and keeps a history of every run.
// after the job body, ping cron.watch
var client = HttpClient.newHttpClient();
client.send(
HttpRequest.newBuilder(URI.create("https://cron.watch/s/your-id")).build(),
HttpResponse.BodyHandlers.discarding());