Quartz在Java構建的系統中,是十分常用的定時任務框架。
本文記錄和介紹Quartz的簡單入門的單獨搭建。
注:本文以入門學習Quartz為主,並非基於Spring托管形式。其中有另一篇關於Spring Boot與Quartz集成的博文《Quartz與Spring Boot集成使用》。
版本說明
除了Quartz,還引入logback(為了看詳細的日志嘛!)

<dependencies> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.0</version> </dependency> </dependencies>
簡單的搭建
jar包的引入參考上述的pom文件。
quartz.properties,配置quartz的設置。
注,org.quartz.threadPool.threadCount,配置線程池的容量,即表示同時最多可運行的線程數量。在生產環境,此參數應根據實際情況配置。

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
logback.xml,日志框架logback的配置。這里只簡單地配置了控制台和日志文件的輸出哦(>_<)

<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>D:/logs/quartz_task_application.log</file> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE" /> </root> </configuration>
HelloJob.java,具體執行的任務

package No01簡單的定時任務; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloJob implements Job { Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { // 此任務僅打印日志便於調試、觀察 this.logger.debug(this.getClass().getName() + " trigger..."); } }
那么,在哪里定義“在什么時候執行什么任務呢?”

package No01簡單的定時任務; import java.util.concurrent.TimeUnit; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; 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 Bootstrap { private static Logger logger = LoggerFactory.getLogger(Bootstrap.class); public static void main(String[] args) { try { // 獲取Scheduler實例 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); // 具體任務 JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build(); // 觸發時間點 SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5).repeatForever(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") .startNow().withSchedule(simpleScheduleBuilder).build(); // 交由Scheduler安排觸發 scheduler.scheduleJob(job, trigger); /* 為觀察程序運行,此設置主程序睡眠3分鍾才繼續往下運行(因下一個步驟是“關閉Scheduler”) */ try { TimeUnit.MINUTES.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } // 關閉Scheduler scheduler.shutdown(); } catch (SchedulerException se) { logger.error(se.getMessage(), se); } } }
在Web應用中使用Quartz
Quartz也常用在Web應用中,常見的是交由Spring托管的形式,但這里並非介紹這個。這里介紹Quartz在Web應用中單獨使用。
一般來說,Web應用啟動時,應注冊已經確定的定時任務;一些動態的、未確定觸發時間的定時任務,后續可通過靜態的Scheduler注冊。
這里使用監聽器在應用啟動時注冊,記得在web.xml注冊這個監聽器哦(>_<);在關閉Web應用時,也要相應的注銷定時任務。
其他配置文件、Java類與上例子相同,這里只是注冊定時任務的地方換成此監聽器了。

package No02Web應用使用Quartz; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; 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; import No01簡單的定時任務.HelloJob; /** * Application Lifecycle Listener implementation class AListener * */ public class ApplicationContextListener implements ServletContextListener { private Logger logger = LoggerFactory.getLogger(this.getClass()); public static Scheduler scheduler = null; @Override public void contextInitialized(ServletContextEvent arg0) { this.logger.info("The application start..."); /* 注冊定時任務 */ try { // 獲取Scheduler實例 scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); // 具體任務 JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build(); // 觸發時間點 SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5).repeatForever(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") .startNow().withSchedule(simpleScheduleBuilder).build(); // 交由Scheduler安排觸發 scheduler.scheduleJob(job, trigger); this.logger.info("The scheduler register..."); } catch (SchedulerException se) { logger.error(se.getMessage(), se); } } @Override public void contextDestroyed(ServletContextEvent arg0) { this.logger.info("The application stop..."); /* 注銷定時任務 */ try { // 關閉Scheduler scheduler.shutdown(); this.logger.info("The scheduler shutdown..."); } catch (SchedulerException se) { logger.error(se.getMessage(), se); } } }

<listener> <listener-class>No02Web應用使用Quartz.ApplicationContextListener</listener-class> </listener>
注,如果你在Eclipse中調試,可能發現無法看到contextDestroyed方法的執行,請注意用Stop方式(圖一)關閉應用,而不是Terminate(圖二)。
圖一
圖二
常用的Cron Schedule
相對於其他方式定義定時任務的觸發時間,我們較常用Cron Schedule,小伙伴們也是吧?
Cron Schedule的使用

// 具體任務 JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build(); // 觸發時間點 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 * * * * ? *"); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") .withSchedule(cronScheduleBuilder).build(); // 交由Scheduler安排觸發 scheduler.scheduleJob(job, trigger);
而Cron Expression的學習可參考下列優秀的文章:
Tutorial - Lesson 6: CronTrigger
Spring - Quartz - cronExpression中問號(?)的解釋
參考的優秀資料
Chapter 3: Logback configuration