Spring @Scheduled vs Quartz Scheduler Example
1. Introduction
Spring is a Java application framework that provides many useful services for building applications. It provides two annotations to enable scheduling tasks:
@EnableScheduling
– to enable Spring’s scheduled task execution capability. It’s used on a class with@Configuration
annotation.@Scheduled
– to mark a method to be scheduled. The annotated method must have no arguments and avoid
return type.
Quartz is an open source library designed to schedule a job for enterprises. It provides several interfaces and classes to schedule a job:
org.quartz.Job
interface – any class which implements it can be used to schedule a job.org.quartz.ScheduleBuilder
– abstract class to construct scheduling-related entities. There are four sub-classes:CalendarIntervalScheduleBuilder
,CronScheduleBuilder
,DailyTimeIntervalScheduleBuilder
, andSimpleScheduleBuilder
.org.quartz.SchedulerFactory
interface –DirectSchedulerFactory
andStdSchedulerFactory
implement it to schedule a job.
In this example, I will build two scheduler applications: one with Spring, the other with Quartz.
2. Technologies used
The example code in this article was built and run using:
- Java 1.8.101
- Maven 3.3.9
- Quartz 2.3.0
- Spring Boot 2.1.2.RELEASE
- Eclipse Oxygen
- Logback 1.2.3
3. Maven Project
Spring Boot Starters provides more than 30 starters to ease the dependency management for your projects. I will use it to generate a Spring Boot application:
- Go to
https://start.spring.io/
. - Select
Maven Project
withJava
and Spring Boot version. - Enter the group name:
jcg.zheng.demo
and artifact:quartz-spring-demo
. - Click the
Generate Project
button.
A maven project will be generated and downloaded to your workstation. Import it into your Eclipse workspace.
3.1 Pom.xml
Add dependencies to the pom.xml
.
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jcg.zheng.demo</groupId> <artifactId>quartz-spring-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>quartz-spring-demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.2 Business Task
In this step, I will create a BusinessTask.java
class whose method takes 90 seconds to complete and throw a RuntimeException
when the current thread’s name ends with “2”.
BusinessTask.java
package jcg.zheng.demo.scheduler.spring.service; import org.springframework.stereotype.Service; @Service public class BusinessTask { private static final long NITY_SECONDS = 90000; public void doit() { if (Thread.currentThread().getName().endsWith("2")) { throw new RuntimeException("Opps! unexpected Error!"); } try { Thread.sleep(NITY_SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
3.3 Application Properties
In this step, I will create an application.properties
to hold the cron
expression string and fixed rate value in milliseconds.
application.properties
scheduler.cron.expression=0 * * * * ? scheduler.fixedrate.ms=15000
3.4 Logback.xml
To illustrate the scheduled job, I will configure a logback.xml
file for each scheduler.
logback.xml
<configuration debug="true" scan="true" scanPeriod="150 seconds"> <property name="LOG_DIR" value="logs" /> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" target="System.out"> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="SPRING_SCHEDULER_FILE_FIXEDRATE" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/spring_scheduler_fixedRate.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="SPRING_SCHEDULER_FILE_FIXEDRATE_ASYNC" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/spring_scheduler_fixedRate_async.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="SPRING_SCHEDULER_FILE_FIXEDDELAY" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/spring_scheduler_fixedDelay.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="SPRING_SCHEDULER_FILE_FIXEDDELAY_ASYNC" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/spring_scheduler_fixedDelay_async.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="SPRING_SCHEDULER_FILE_CRON" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/spring_scheduler_cron.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="SPRING_SCHEDULER_FILE_CRON_ASYNC" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/spring_scheduler_cron_async.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <appender name="QUARTZ_SCHEDULER_FILE" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/quartz_scheduler.log</file> <encoder> <charset>UTF-8</charset> <Pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n </Pattern> </encoder> </appender> <logger name="jcg.zheng.demo.scheduler.quartz" level="info" additivity="false"> <appender-ref ref="QUARTZ_SCHEDULER_FILE" /> </logger> <logger name="jcg.zheng.demo.scheduler.spring.scheduler.FixedRateScheduler_HandleException" level="info" additivity="false"> <appender-ref ref="SPRING_SCHEDULER_FILE_FIXEDRATE" /> </logger> <logger name="jcg.zheng.demo.scheduler.spring.scheduler.AsyncFixedRateScheduler" level="info" additivity="false"> <appender-ref ref="SPRING_SCHEDULER_FILE_FIXEDRATE_ASYNC" /> </logger> <logger name="jcg.zheng.demo.scheduler.spring.scheduler.FixedDelayScheduler" level="info" additivity="false"> <appender-ref ref="SPRING_SCHEDULER_FILE_FIXEDDELAY" /> </logger> <logger name="jcg.zheng.demo.scheduler.spring.scheduler.AsyncFixedDelayScheduler" level="info" additivity="false"> <appender-ref ref="SPRING_SCHEDULER_FILE_FIXEDDELAY_ASYNC" /> </logger> <logger name="jcg.zheng.demo.scheduler.spring.scheduler.CronScheduler" level="info" additivity="false"> <appender-ref ref="SPRING_SCHEDULER_FILE_CRON" /> </logger> <logger name="jcg.zheng.demo.scheduler.spring.scheduler.AsyncCronScheduler" level="info" additivity="false"> <appender-ref ref="SPRING_SCHEDULER_FILE_CRON_ASYNC" /> </logger> <root level="info"> <appender-ref ref="CONSOLE" /> </root> </configuration>
4. Quartz Scheduler
In this step, I will create a Quartz scheduler application. It will schedule two jobs:
- One runs every minute triggered by a
cron
expression. - Another runs with one minute intervals for a total of 30 times.
4.1 QuartzJob
Quartz job must implement org.quartz.Job
interface. In this step, I will create a QuartzJob
class which implements org.quartz.Job
interface and overrides the execute
method.
QuartzJob.java
package jcg.zheng.demo.scheduler.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; public class QuartzJob implements Job { Logger logger = LoggerFactory.getLogger(QuartzJob.class); private BusinessTask service = new BusinessTask(); @Override public void execute(JobExecutionContext context) throws JobExecutionException { logger.info(" started "); service.doit(); logger.info(" completed."); } }
I will create QuartzJob_HandleException.java
which does same as above class with additional exception handling logic.
QuartzJob_HandleException.java
package jcg.zheng.demo.scheduler.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; public class QuartzJob_HandleException implements Job { Logger logger = LoggerFactory.getLogger(QuartzJob_HandleException.class); private BusinessTask service = new BusinessTask(); @Override public void execute(JobExecutionContext context) throws JobExecutionException { logger.info(" started "); boolean isCompleted = false; try { service.doit(); isCompleted = true; } catch (Exception e) { logger.error("Failed. due to " + e.getMessage()); } finally { logger.info(" completed with status=" + isCompleted); } } }
4.2 Quartz Scheduler
Quartz library provides org.quartz.impl.StdSchedulerFactory
. I will use it to create a scheduler instance and schedule two jobs: one for QuartzJob_HandleException
with CronScheduleBuilder
, the other for QuartzJob
with SimpleScheduleBuilder
.
QuartzScheduler.java
package jcg.zheng.demo.scheduler.quartz; import org.quartz.CronScheduleBuilder; import org.quartz.Job; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuartzScheduler { static Logger logger = LoggerFactory.getLogger(QuartzScheduler.class); private static final String TRIGGER_NAME = "MyTriggerName"; private static final String JOB_NAME = "someJob"; private static final String CRON_EXPRESSION_EVERY_SECOND_1 = "1 * * * * ?"; private Scheduler scheduler; public void start() throws Exception { logger.info("QuartzScheduler started. "); scheduler = new StdSchedulerFactory().getScheduler(); scheduleJob(buildCronSchedulerTrigger("cronGroup"), QuartzJob_HandleException.class, "cronGroup"); scheduleJob(buildSimpleSchedulerTrigger("simpleGroup"), QuartzJob.class, "simpleGroup"); scheduler.start(); } private void scheduleJob(Trigger trigger, Class jobClass, String groupName) throws Exception { JobDetail someJobDetail = JobBuilder.newJob(jobClass).withIdentity(JOB_NAME, groupName).build(); scheduler.scheduleJob(someJobDetail, trigger); } private Trigger buildCronSchedulerTrigger(String groupName) { return TriggerBuilder .newTrigger().withIdentity(TRIGGER_NAME, groupName).withSchedule(CronScheduleBuilder .cronSchedule(CRON_EXPRESSION_EVERY_SECOND_1).withMisfireHandlingInstructionDoNothing()) .build(); } private Trigger buildSimpleSchedulerTrigger(String groupName) { return TriggerBuilder .newTrigger().withIdentity(TRIGGER_NAME, groupName).withSchedule(SimpleScheduleBuilder.simpleSchedule() .withRepeatCount(30).withIntervalInMinutes(1).withMisfireHandlingInstructionIgnoreMisfires()) .build(); } }
4.3 Demo
In this step, I will create a java application to start the Quartz scheduler application.
Note: Quartz provides ten threads as the default thread pool count.
QuartzSchedulerApplication.java
package jcg.zheng.demo.scheduler.quartz; public class QuartzSchedulerApplication { public static void main(String[] args) throws Exception { QuartzScheduler scheduler = new QuartzScheduler(); scheduler.start(); } }
Quartz log
2019-01-23 14:00:06,336 234 [main] INFO j.z.d.s.quartz.QuartzScheduler - QuartzScheduler started. 2019-01-23 14:00:06,431 329 [DefaultQuartzScheduler_Worker-1] INFO j.z.demo.scheduler.quartz.QuartzJob - started 2019-01-23 14:01:01,011 54909 [DefaultQuartzScheduler_Worker-2] INFO j.z.d.s.q.QuartzJob_HandleException - started 2019-01-23 14:01:01,011 54909 [DefaultQuartzScheduler_Worker-2] ERROR j.z.d.s.q.QuartzJob_HandleException - Failed. due to Opps! unexpected Error! 2019-01-23 14:01:01,011 54909 [DefaultQuartzScheduler_Worker-2] INFO j.z.d.s.q.QuartzJob_HandleException - completed with status=false 2019-01-23 14:01:06,436 60334 [DefaultQuartzScheduler_Worker-3] INFO j.z.demo.scheduler.quartz.QuartzJob - started 2019-01-23 14:01:36,427 90325 [DefaultQuartzScheduler_Worker-1] INFO j.z.demo.scheduler.quartz.QuartzJob - completed. 2019-01-23 14:02:01,004 114902 [DefaultQuartzScheduler_Worker-4] INFO j.z.d.s.q.QuartzJob_HandleException - started 2019-01-23 14:02:06,428 120326 [DefaultQuartzScheduler_Worker-5] INFO j.z.demo.scheduler.quartz.QuartzJob - started 2019-01-23 14:02:36,477 150375 [DefaultQuartzScheduler_Worker-3] INFO j.z.demo.scheduler.quartz.QuartzJob - completed. 2019-01-23 14:03:01,007 174905 [DefaultQuartzScheduler_Worker-6] INFO j.z.d.s.q.QuartzJob_HandleException - started 2019-01-23 14:03:06,473 180371 [DefaultQuartzScheduler_Worker-7] INFO j.z.demo.scheduler.quartz.QuartzJob - started 2019-01-23 14:03:31,009 204907 [DefaultQuartzScheduler_Worker-4] INFO j.z.d.s.q.QuartzJob_HandleException - completed with status=true 2019-01-23 14:03:36,433 210331 [DefaultQuartzScheduler_Worker-5] INFO j.z.demo.scheduler.quartz.QuartzJob - completed. 2019-01-23 14:04:01,029 234927 [DefaultQuartzScheduler_Worker-8] INFO j.z.d.s.q.QuartzJob_HandleException - started 2019-01-23 14:04:06,430 240328 [DefaultQuartzScheduler_Worker-9] INFO j.z.demo.scheduler.quartz.QuartzJob - started 2019-01-23 14:04:31,013 264911 [DefaultQuartzScheduler_Worker-6] INFO j.z.d.s.q.QuartzJob_HandleException - completed with status=true 2019-01-23 14:04:36,444 270342 [DefaultQuartzScheduler_Worker-7] INFO j.z.demo.scheduler.quartz.QuartzJob - completed. 2019-01-23 14:05:01,010 294908 [DefaultQuartzScheduler_Worker-10] INFO j.z.d.s.q.QuartzJob_HandleException - started 2019-01-23 14:05:06,434 300332 [DefaultQuartzScheduler_Worker-2] INFO j.z.demo.scheduler.quartz.QuartzJob - started
5. Spring Scheduler
In this step, I will create a Spring boot application which enables the scheduling and has several schedulers.
5.1 Scheduler Configuration
Spring provides a single thread as the default setting for all the tasks marked with @Scheduled
annotation. You don’t need this step if a single thread scheduler meets your requirements.
Spring framework provides TaskScheduler
interface and its own implementation, such as ThreadPoolTaskScheduler
. It also provides a SchedulingConfigurer
functional interface which sets up a specified task scheduler.
In this step, I will configure a four-thread pool with ThreadPoolTaskScheduler
and mark it with @EnableAsync
.
SchedulerConfig.java
package jcg.zheng.demo.scheduler.spring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; @Configuration @EnableAsync public class SchedulerConfig implements SchedulingConfigurer { Logger logger = LoggerFactory.getLogger(SchedulerConfig.class); @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegister) { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setErrorHandler(t-> logger.error("Caught exception on the @Scheduled task. " + t.getMessage())); taskScheduler.setPoolSize(4); taskScheduler.setThreadNamePrefix("Spring-scheduler-task-pool-"); taskScheduler.initialize(); scheduledTaskRegister.setTaskScheduler(taskScheduler); } }
5.2 Spring boot application
I will add @EnableScheduling
annotation to the generated SpringSchedulerApplication.java
.
SpringSchedulerApplication.java
package jcg.zheng.demo.scheduler.spring; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class SpringSchedulerApplication { public static void main(String[] args) { SpringApplication.run(SpringSchedulerApplication.class, args); } }
5.3 Fixed Rate Scheduler
Spring @Scheduled
annotation has fixedRate
and fixedRateString
attributes. Tasks with this attribute are executed in a fixed interval in milliseconds. fixedRate
requires an integer value and fixedRateString
requires a String value.
In this step, I will create two schedulers for the fixedRate
attributes: one with an exception handling, the other with @Async
annotation.
FixedRateScheduler_HandleException.java
package jcg.zheng.demo.scheduler.spring.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; @Component public class FixedRateScheduler_HandleException { @Autowired private BusinessTask service; Logger logger = LoggerFactory.getLogger(FixedRateScheduler_HandleException.class); // will wait for previous execution because it's not marked as Async @Scheduled(fixedRateString = "${scheduler.fixedrate.ms}") public void scheduledTask() { logger.info(" started."); boolean isCompleted = false; try { service.doit(); isCompleted = true; } catch (Exception e) { logger.error("Caught Exception" + e.getMessage()); } finally { logger.info(" completed with status=" + isCompleted); } } }
AsyncFixedRateScheduler.java
package jcg.zheng.demo.scheduler.spring.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; @Component public class AsyncFixedRateScheduler { @Autowired private BusinessTask service; Logger logger = LoggerFactory.getLogger(AsyncFixedRateScheduler.class); // trigger with every 15 seconds, will not wait for the previous execution @Scheduled(fixedRate = 15000) @Async public void scheduledTask() { logger.info(" started."); service.doit(); logger.info(" completed."); } }
5.4 Fixed Delay Scheduler
Spring @Scheduled
annotation has fixedDelay
and fixedDelayString
attributes. Tasks with this attribute are executed sequentially and the next execution is waited for the previous execution with a fixed delay in milliseconds. fixedDelay
requires an Integer
value and fixedDelayString
requires a String
value.
FixedDelayScheduler.java
package jcg.zheng.demo.scheduler.spring.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; @Component public class FixedDelayScheduler { @Autowired private BusinessTask service; Logger logger = LoggerFactory.getLogger(FixedDelayScheduler.class); // trigger by delay 15 seconds after previous execution, the intial execution is // delayed by 30 seconds @Scheduled(fixedDelay = 15000, initialDelay = 30000) public void scheduledTask() { logger.info(" started."); service.doit(); logger.info(" completed."); } }
AsyncFixedDelayScheduler.java
package jcg.zheng.demo.scheduler.spring.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; @Component public class AsyncFixedDelayScheduler { @Autowired private BusinessTask service; Logger logger = LoggerFactory.getLogger(AsyncFixedDelayScheduler.class); // trigger by delay 15 seconds after previous execution, the intial execution is // delayed by 30 seconds @Scheduled(fixedDelay = 15000, initialDelay = 30000) @Async public void scheduleFixedDelayTask() { logger.info(" started."); service.doit(); logger.info(" completed."); } }
5.5 Cron Scheduler
Spring @Scheduled
annotation has a cron
attribute. Tasks with this attribute are executed based on a cron
expression.
Note: it always waits for the previous execution.
CronScheduler.java
package jcg.zheng.demo.scheduler.spring.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; @Component public class CronScheduler { @Autowired private BusinessTask service; Logger logger = LoggerFactory.getLogger(CronScheduler.class); @Scheduled(cron = "${scheduler.cron.expression}") public void schedulesTask() { logger.info(" started."); service.doit(); logger.info(" completed."); } }
AsyncCronScheduler.java
package jcg.zheng.demo.scheduler.spring.scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import jcg.zheng.demo.scheduler.spring.service.BusinessTask; @Component public class CronScheduler { @Autowired private BusinessTask service; Logger logger = LoggerFactory.getLogger(CronScheduler.class); @Scheduled(cron = "${scheduler.cron.expression}") public void schedulesTask() { logger.info(" started."); service.doit(); logger.info(" completed."); } }
5.6 Demo
Execute it as a Spring boot application and check the log files. I will only list the fixed_rate_scheduler log as an example.
Fixed_Rate_scheduler log
2019-01-23 14:12:38,601 1666 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:12:38,602 1667 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:12:38,602 1667 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:12:53,603 16668 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:12:53,604 16669 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:12:53,604 16669 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:13:08,632 31697 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:13:08,632 31697 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:13:08,632 31697 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:13:23,629 46694 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:13:23,629 46694 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:13:23,629 46694 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:13:38,637 61702 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:13:38,640 61705 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:13:38,640 61705 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:13:53,640 76705 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:13:53,640 76705 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:13:53,640 76705 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:14:08,642 91707 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:14:08,642 91707 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:14:08,642 91707 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:14:23,636 106701 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:14:23,636 106701 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:14:23,636 106701 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:14:38,643 121708 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:14:38,643 121708 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 14:14:38,643 121708 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 14:14:53,607 136672 [Spring-scheduler-task-pool-1] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:16:23,616 226681 [Spring-scheduler-task-pool-1] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=true 2019-01-23 14:16:23,616 226681 [Spring-scheduler-task-pool-1] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 14:17:53,654 316719 [Spring-scheduler-task-pool-1] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=true 2019-01-23 14:17:53,654 316719 [Spring-scheduler-task-pool-1] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 19:12:37,885 1673 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 19:12:37,885 1673 [Spring-scheduler-task-pool-2] ERROR j.z.d.s.s.s.FixedRateScheduler_HandleException - Caught ExceptionOpps! unexpected Error! 2019-01-23 19:12:37,886 1674 [Spring-scheduler-task-pool-2] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=false 2019-01-23 19:12:52,886 16674 [Spring-scheduler-task-pool-4] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 19:14:22,890 106678 [Spring-scheduler-task-pool-4] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=true 2019-01-23 19:14:22,890 106678 [Spring-scheduler-task-pool-4] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started. 2019-01-23 19:15:52,894 196682 [Spring-scheduler-task-pool-4] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - completed with status=true 2019-01-23 19:15:52,894 196682 [Spring-scheduler-task-pool-4] INFO j.z.d.s.s.s.FixedRateScheduler_HandleException - started.
6. Spring @Scheduled vs Quartz Scheduler – Summary
There are three things we must do to use Quartz scheduler:
- Create a task class which implements the
org.quartz.Job
interface - Create a trigger with
SimpleScheduleBuilder
,CronScheduleBuilder,
etc - Create a quartz scheduler instance from the Quartz factory class and schedule a job with a corresponding trigger.
There are only two things we must do to use Spring scheduler:
- Add
@EnableScheduling
annotation on a class with@Configuration
annotation - Add
@Scheduled
annotation for the scheduled method.
Spring scheduling annotations make Spring scheduler easier to use.
7. Download the Source Code
This example builds two java scheduler applications: one utilizes Quartz and the other uses spring built-in scheduler library.
You can download the full source code of this example here: Spring @Scheduled vs Quartz Example