spring

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 a void 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, and SimpleScheduleBuilder.
  • org.quartz.SchedulerFactory interface – DirectSchedulerFactory and StdSchedulerFactory 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:

  1. Go to https://start.spring.io/.
  2. Select Maven Project with Java and Spring Boot version.
  3. Enter the group name: jcg.zheng.demo and artifact:quartz-spring-demo.
  4. 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.Jobinterface 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.

Download
You can download the full source code of this example here: Spring @Scheduled vs Quartz Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button