一:Quartz 百度百科
quartz
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。Quartz可以用來創建簡單或為運行十個,百個,甚至是好幾萬個Jobs這樣復雜的程序。Jobs可以做成標准的Java組件或 EJBs。Quartz的最新版本為Quartz 2.3.2。
調度簡單
Quartz是一個完全由java編寫的開源作業調度框架。不要讓作業調度這個術語嚇着你。盡管Quartz框架整合了許多額外功能, 但就其簡易形式看,你會發現它易用得簡直讓人受不了!簡單地創建一個實現org.quartz.Job接口的java類。Job接口包含唯一的方法:
public void execute(JobExecutionContext context)
throws JobExecutionException;
在你的Job接口實現類里面,添加一些邏輯到execute()方法。一旦你配置好Job實現類並設定好調度時間表,Quartz將密切注意剩余時間。當調度程序確定該是通知你的作業的時候,Quartz框架將調用你Job實現類(作業類)上的execute()方法並允許做它該做的事情。無需報告任何東西給調度器或調用任何特定的東西。僅僅執行任務和結束任務即可。如果配置你的作業在隨后再次被調用,Quartz框架將在恰當的時間再次調用它。
內部架構
在規模方面,Quartz跟大多數開源框架類似。大約有300個java類和接口,並被組織到12個包中。這可以和Apache Struts把大約325個類和接口以及組織到11個包中相比。盡管規模幾乎不會用來作為衡量框架質量的一個特性,但這里的關鍵是quartz內含很多功能,這些功能和特性集是否成為、或者應該成為評判一個開源或非開源框架質量的因素。
調度器
Quartz框架的核心是調度器。調度器負責管理Quartz應用運行時環境。調度器不是靠自己做所有的工作,而是依賴框架內一些非常重要的部件。Quartz不僅僅是線程和線程管理。為確保可伸縮性,Quartz采用了基於多線程的架構。啟動時,框架初始化一套worker線程,這套線程被調度器用來執行預定的作業。這就是Quartz怎樣能並發運行多個作業的原理。Quartz依賴一套松耦合的線程池管理部件來管理線程環境。本篇文章中,我們會多次提到線程池管理,但Quartz里面的每個對象是可配置的或者是可定制的。所以,例如,如果你想要插進自己線程池管理設施,我猜你一定能!
框架特征
Quartz框架有一個豐富的特征集。事實上,Quartz有太多特性以致不能在一種情況中全部領會,下面列出了一些有意思的特征,但沒時間在此詳細討論。
監聽器和插件
每個人都喜歡監聽和插件。今天,幾乎下載任何開源框架,你必定會發現支持這兩個概念。監聽是你創建的java類,當關鍵事件發生時會收到框架的回調。例如,當一個作業被調度、沒有調度或觸發器終止和不再觸發時,這些都可以通過設置來通知你的監聽器。Quartz框架包含了調度器監聽、作業和觸發器監聽。你可以配置作業和觸發器監聽為全局監聽或者是特定於作業和觸發器的監聽。
一旦你的一個具體監聽被調用,你就能使用這個技術來做一些你想要在監聽類里面做的事情。例如,你如果想要在每次作業完成時發送一個電子郵件,你可以將這個邏輯寫進作業里面,也可以寫進JobListener里面。寫進JobListener的方式強制使用松耦合有利於設計上做到更好。
Quartz插件是一個新的功能特性,無須修改Quartz源碼便可被創建和添加進Quartz框架。他為想要擴展Quartz框架又沒有時間提交改變給Quartz開發團隊和等待新版本的開發人員而設計。如果你熟悉Struts插件的話,那么完全可以理解Quartz插件的使用。
與其Quartz提供一個不能滿足你需要的有限擴展點,還不如通過使用插件來擁有可修整的擴展點。
集群Quartz應用
Quartz應用能被集群,是水平集群還是垂直集群取決於你自己的需要。集群提供以下好處:
·伸縮性
·高可用性
·負載均衡
Quartz可以借助關系數據庫和JDBC作業存儲支持集群。
Terracotta擴展quartz提供集群功能而不需要數據庫支持
相關工具
Quartz經常會用到cron表達式,可以使用國外網站cronmaker輔助生成cron表達式。
二: Quartz 的使用思路
- Job-任務: 你要做什么事情
- Trigger-觸發器:你什么時候去做
- Scheduler-任務調度器:你什么時候需要去做什么事
什么是Quartz?
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,完全由Java開發,可以用來執行定時任務,類似於java.util.Timer。但是相較於Timer, Quartz增加了很多功能:
持久性作業 - 就是保持調度定時的狀態;
作業管理 - 對調度作業進行有效的管理;
大部分公司都會用到定時任務這個功能。
拿火車票購票來說,當你下單后,后台就會插入一條待支付的task(job),一般是30分鍾,超過30min后就會執行這個job,去判斷你是否支付,未支付就會取消此次訂單;當你支付完成之后,后台拿到支付回調后就會再插入一條待消費的task(job),Job觸發日期為火車票上的出發日期,超過這個時間就會執行這個job,判斷是否使用等。在我們實際的項目中,當Job過多的時候,肯定不能人工去操作,這時候就需要一個任務調度框架,幫我們自動去執行這些程序。那么該如何實現這個功能呢?
(1)首先我們需要定義實現一個定時功能的接口,我們可以稱之為Task(或Job),如定時發送郵件的task(Job),重啟機器的task(Job),優惠券到期發送短信提醒的task(Job),實現接口如下:
(2)有了任務之后,還需要一個能夠實現觸發任務去執行的觸發器,觸發器Trigger最基本的功能是指定Job的執行時間,執行間隔,運行次數等。
(3)有了Job和Trigger后,怎么樣將兩者結合起來呢?即怎樣指定Trigger去執行指定的Job呢?這時需要一個Schedule,來負責這個功能的實現
上面三個部分就是Quartz的基本組成部分:
- 調度器:Scheduler
- 任務:JobDetail
- 觸發器:Trigger,包括SimpleTrigger和CronTrigger
Quartz的三大核心組件
調度器:Scheduler。
任務:JobDetail。
觸發器:Trigger,包括 SimpleTrigger 和 CronTrigger。
(1)Job(任務):是一個接口,有一個方法 void execute(JobExecutionContext context) ,可以通過實現該接口來定義需要執行的任務(具體的邏輯代碼)。JobDetail:Quartz每次執行Job時,都重新創建一個Job實例,會接收一個Job實現類,以便運行的時候通過newInstance()的反射調用機制去實例化Job。JobDetail是用來描述Job實現類以及相關靜態信息,比如任務在scheduler中的組名等信息。
(2)Trigger(觸發器):描述觸發Job執行的時間觸發規則實現類SimpleTrigger和CronTrigger可以通過crom表達式定義出各種復雜的調度方案。
Calendar:是一些日歷特定時間的集合。一個Trigger可以和多個 calendar關聯,比如每周一早上10:00執行任務,法定假日不執行,則可以通過calendar進行定點排除。
(3)Scheduler(調度器):代表一個Quartz的獨立運行容器。Trigger和JobDetail可以注冊到Scheduler中。Scheduler可以將Trigger綁定到某一JobDetail上,這樣當Trigger被觸發時,對應的Job就會執行。一個Job可以對應多個Trigger,但一個Trigger只能對應一個Job
CronTrigger配置格式
格式:[秒] [分] [小時] [日] [月] [周] [年]
序號 說明 是否必填 允許填寫的值 允許的通配符
1 秒 是 0-59 , - * /
2 分 是 0-59 , - * /
3 小時 是 0-23 , - * /
4 日 是 1-31 , - * ? / L W
5 月 是 1-12 or JAN-DEC , - * /
6 周 是 1-7 or SUN-SAT , - * ? / L #
7 年 否 empty 或 1970-2099 , - * /通配符說明:
* 表示所有值. 例如:在分的字段上設置 "*",表示每一分鍾都會觸發。
? 表示不指定值。使用的場景為不需要關心當前設置這個字段的值。例如:要在每月的10號觸發一個操作,但不關心是周幾,所以需要周位置的那個字段設置為"?" 具體設置為 0 0 0 10 * ?
- 表示區間。例如 在小時上設置 "10-12",表示 10,11,12點都會觸發。
, 表示指定多個值,例如在周字段上設置 "MON,WED,FRI" 表示周一,周三和周五觸發
/ 用於遞增觸發。如在秒上面設置"5/15" 表示從5秒開始,每增15秒觸發(5,20,35,50)。在月字段上設置'1/3'所示每月1號開始,每隔三天觸發一次。
L 表示最后的意思。在日字段設置上,表示當月的最后一天(依據當前月份,如果是二月還會依據是否是潤年[leap]), 在周字段上表示星期六,相當於"7"或"SAT"。如果在"L"前加上數字,則表示該數據的最后一個。例如在周字段上設置"6L"這樣的格式,則表示“本月最后一個星期五"
W 表示離指定日期的最近那個工作日(周一至周五). 例如在日字段上設置"15W",表示離每月15號最近的那個工作日觸發。如果15號正好是周六,則找最近的周五(14號)觸發, 如果15號是周未,則找最近的下周一(16號)觸發.如果15號正好在工作日(周一至周五),則就在該天觸發。如果指定格式為 "1W",它則表示每月1號往后最近的工作日觸發。如果1號正是周六,則將在3號下周一觸發。(注,"W"前只能設置具體的數字,不允許區間"-").
小提示:'L'和 'W'可以一組合使用。如果在日字段上設置"LW",則表示在本月的最后一個工作日觸發(一般指發工資) 。
# 序號(表示每月的第幾個周幾),例如在周字段上設置"6#3"表示在每月的第三個周六.注意如果指定"#5",正好第五周沒有周六,則不會觸發該配置(用在母親節和父親節再合適不過了)
小提示:周字段的設置,若使用英文字母是不區分大小寫的 MON 與mon相同。可通過在線生成Cron表達式的工具:http://cron.qqe2.com/ 來生成自己想要的表達式。
常用示例:
0 0 12 * * ? 每天12點觸發
0 15 10 ? * * 每天10點15分觸發
0 15 10 * * ? 每天10點15分觸發
0 15 10 * * ? * 每天10點15分觸發
0 15 10 * * ? 2005 2005年每天10點15分觸發
0 * 14 * * ? 每天下午的 2點到2點59分每分觸發
0 0/5 14 * * ? 每天下午的 2點到2點59分(整點開始,每隔5分觸發)
0 0/5 14,18 * * ? 每天下午的 2點到2點59分(整點開始,每隔5分觸發)每天下午的 18點到18點59分(整點開始,每隔5分觸發)
0 0-5 14 * * ? 每天下午的 2點到2點05分每分觸發
0 10,44 14 ? 3 WED 3月分每周三下午的 2點10分和2點44分觸發
0 15 10 ? * MON-FRI 從周一到周五每天上午的10點15分觸發
0 15 10 15 * ? 每月15號上午10點15分觸發
0 15 10 L * ? 每月最后一天的10點15分觸發
0 15 10 ? * 6L 每月最后一周的星期五的10點15分觸發
0 15 10 ? * 6L 2002-2005 從2002年到2005年每月最后一周的星期五的10點15分觸發
0 15 10 ? * 6#3 每月的第三周的星期五開始觸發
0 0 12 1/5 * ? 每月的第一個中午開始每隔5天觸發一次
0 11 11 11 11 ? 每年的11月11號 11點11分觸發(光棍節)
Quartz學習資料: 《Quartz官方文檔》
三:SpringBoot 整合 Quartz
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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.alan</groupId> <artifactId>SpingbootQuartz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringBootQuartz</name> <description>SpringBootQuartz</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot 整合 Quartz 定時任務 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> <exclusions> <exclusion> <!--去掉自帶的日志記錄的jar包--> <groupId>slf4j-api</groupId> <artifactId>org.slf4j</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加Scheduled 坐標--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!--Spring tx 坐標 Spring 事務的jar包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
SpringBootQuartzApplication
package com.alan.SpringBootQuartz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication //修改啟動類 增加定時任務 注解 @EnableScheduling public class SpringBootQuartzApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuartzApplication.class, args); } }
SpringBootQuartzApplicationTests
package com.alan.SpringBootQuartz; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringBootQuartzApplicationTests { @Test void contextLoads() { } }
QuartzDemo
package com.alan.SpringBootQuartz.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; /* * Job 類 :定義任務類 * 在 com.alan.SpringBootQuartz.quartz 包下創建同步用戶信息Job類(QuartzDemo.java), * 並繼承 Job 類, * 重寫 public void execute(JobExecutionContext context) throws JobExecutionException 方法。 * @author: Alan_liu * @date 2021/6/5 12:47 */ public class QuartzDemo implements Job { /** * * 任務被觸發時被執行的方法 * @user Alan_liu * @date 2021/6/5 12:50 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("execute .............>>>>>>.........."+new Date()); } }
QuartzMain
package com.alan.SpringBootQuartz.config; import com.alan.SpringBootQuartz.SpringBootQuartzApplication; import com.alan.SpringBootQuartz.quartz.QuartzDemo; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; /* * @author: Alan_liu * @date 2021/6/5 12:52 */ public class QuartzMain { public static void main(String[] args) { QuartzSimpleScheduleBuilderTest(); } public static void QuartzSimpleScheduleBuilderTest( ) { try { //1:創建job 對象,你要做什么事情 JobDetail job= JobBuilder.newJob(QuartzDemo.class).build(); //2:創建trigger對象,在什么時間做 /* * 簡單的 trigger 觸發時間,通過quartz 提供的 一個方法完成簡單的重復任務 * cron trigger :按照Cron的表達式來給定觸發時間 * */ Trigger trigger = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever()).build(); //3:創建Schedule對象:在什么時間做什么事情 Scheduler scheduled= StdSchedulerFactory.getDefaultScheduler(); scheduled.scheduleJob(job,trigger); //啟動 scheduled.start(); } catch (Exception e) { e.printStackTrace(); } } public static void QuartzCronScheduleBuilderTest( ) { try { //1:創建job 對象,你要做什么事情 JobDetail job= JobBuilder.newJob(QuartzDemo.class).build(); //2:創建trigger對象,在什么時間做 Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build(); //3:創建Schedule對象:在什么時間做什么事情 Scheduler scheduled= StdSchedulerFactory.getDefaultScheduler(); scheduled.scheduleJob(job,trigger); //啟動 scheduled.start(); } catch (Exception e) { e.printStackTrace(); } } }
QuartzConfigDemo
package com.alan.SpringBootQuartz.quartz;/* * Todo * * @author: Alan_liu * @date 2021/6/5 21:26 */ import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; public class QuartzConfigDemo implements Job { /** * * 任務被觸發時被執行的方法 * @user Alan_liu * @date 2021/6/5 12:50 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("QuartzConfigDemo execute .............>>>>>>.........."+new Date()); } }
QuartzConfig
package com.alan.SpringBootQuartz.config; import com.alan.SpringBootQuartz.quartz.QuartzConfigDemo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; import javax.xml.crypto.dsig.keyinfo.RetrievalMethod; import java.nio.channels.Pipe; /* * Quartz配置類 * @author: Alan_liu * @date 2021/6/5 21:12 */ @Configuration public class QuartzConfig { /*** * 1:創建job對象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public JobDetailFactoryBean jobDetailFactoryBean(){ JobDetailFactoryBean factory=new JobDetailFactoryBean(); //關聯自己創建的job類 factory.setJobClass(QuartzConfigDemo.class); return factory; } /*** * 2: 創建trigger對象 * @user Alan_liu * @date 2021/6/5 21:13 */ /* @Bean public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ SimpleTriggerFactoryBean factory =new SimpleTriggerFactoryBean(); //關聯jobDetail對象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //該入參表示一個執行的毫秒數 factory.setRepeatInterval(2000); //設置啟動后重復執行次數:5次 factory.setRepeatCount(5); return factory; }*/ /** * * Cron Trigger */ @Bean public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ CronTriggerFactoryBean factory=new CronTriggerFactoryBean(); //關聯jobDetail對象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //設置觸發時間 factory.setCronExpression("0/2 * * * * ?"); return factory; } /*** * 3:創建Scheduler 對象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public SchedulerFactoryBean schedulerFactoryBean(/*SimpleTriggerFactoryBean simpleTriggerFactoryBean*/CronTriggerFactoryBean cronTriggerFactoryBean ){ SchedulerFactoryBean factory=new SchedulerFactoryBean(); //關聯trigger factory.setTriggers(/*simpleTriggerFactoryBean.getObject()*/ cronTriggerFactoryBean.getObject()); return factory; } }
Job 類中注入業務邏輯層對象
Pom.xml
略。。。。。。。。。!
項目啟動類:SpringBootQuartzApplication
package com.alan.SpringBootQuartz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication //修改啟動類 增加定時任務 注解 @EnableScheduling public class SpringBootQuartzApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuartzApplication.class, args); } }
業務邏輯層對象:UsersService
package com.alan.SpringBootQuartz.Service; /* * 業務類 注入到job 類中 的示例 * @author: Alan_liu * @date 2021/6/5 21:45 */ import org.springframework.stereotype.Service; @Service public class UsersService { public void addUsers(){ System.out.println("Add Users..............>>>>>>........"); } }
將業務層對象添加到定時任務對象:QuartzConfigDemo:
package com.alan.SpringBootQuartz.quartz;/* * Todo * * @author: Alan_liu * @date 2021/6/5 21:26 */ import com.alan.SpringBootQuartz.Service.UsersService; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; public class QuartzConfigDemo implements Job { @Autowired private UsersService usersService; /** * * 任務被觸發時被執行的方法 * @user Alan_liu * @date 2021/6/5 12:50 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("QuartzConfigDemo execute .............>>>>>>.........."+new Date()); /** * * 將數據邏輯處理的業務層對象注入到 定時任務器里后。 * 在啟動springboot 項目后,進行實例化QuartzConfigDemo對象的時候 * 並沒有通過spring ioc 容器進行完成注入 * 解決方案: * 創建MyAdaptableJobFactory 對象 對 AdaptableJobFactory 對象進而對JobFactory對象 * 的createJobInstance 方法進行重寫,通過 調用AutowireCapableBeanFactory * 進行手動把 ReflectionUtils.accessibleConstructor(jobClass).newInstance();的對象注入到spring ioc容器中 * * ****/ this.usersService.addUsers(); } }
重新實例化 jobfactory :MyAdaptableJobFactory
重寫實例化jobfactory 的業務代碼:
package com.alan.SpringBootQuartz.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; /* * 用於解決: *org.quartz.SchedulerException: Job threw an unhandled exception. * at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.2.jar:na] * at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.2.jar:na] *Caused by: java.lang.NullPointerException: null * at com.alan.SpringBootQuartz.quartz.QuartzConfigDemo.execute(QuartzConfigDemo.java:28) ~[classes/:na] * at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na] * ... 1 common frames omitted * * @author: Alan_liu * @date 2021/6/5 21:52 */ //進行實例化 MyAdaptableJobFactory 對象 @Component("myAdaptableJobFactory") public class MyAdaptableJobFactory extends AdaptableJobFactory { /*** *AutowireCapableBeanFactory 可以將一個對象添加到SPringIOC容器中,並且完成該對象的屬性注入 */ @Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory; /*** * 該方法需要將實例化的任務對象手動添加到springIOC容器中並且完成對象注入 * @user Alan_liu * @date 2021/6/5 21:55 */ @Override protected Object createJobInstance(TriggerFiredBundle bundle)throws Exception{ Object obj=super.createJobInstance(bundle); //將Obj對象添加到Spring IOC容器中,並完成注入 this.autowireCapableBeanFactory.autowireBean(obj); return obj; } }
框架代碼:被用於繼承的對象:
/* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.scheduling.quartz; import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; import org.springframework.util.ReflectionUtils; /** * {@link JobFactory} implementation that supports {@link java.lang.Runnable} * objects as well as standard Quartz {@link org.quartz.Job} instances. * * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 2.0 * @see DelegatingJob * @see #adaptJob(Object) */ public class AdaptableJobFactory implements JobFactory { @Override public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException { try { Object jobObject = createJobInstance(bundle); return adaptJob(jobObject); } catch (Throwable ex) { throw new SchedulerException("Job instantiation failed", ex); } } /** * Create an instance of the specified job class. * <p>Can be overridden to post-process the job instance. * @param bundle the TriggerFiredBundle from which the JobDetail * and other info relating to the trigger firing can be obtained * @return the job instance * @throws Exception if job instantiation failed */ protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Class<?> jobClass = bundle.getJobDetail().getJobClass(); return ReflectionUtils.accessibleConstructor(jobClass).newInstance(); } /** * Adapt the given job object to the Quartz Job interface. * <p>The default implementation supports straight Quartz Jobs * as well as Runnables, which get wrapped in a DelegatingJob. * @param jobObject the original instance of the specified job class * @return the adapted Quartz Job instance * @throws Exception if the given job could not be adapted * @see DelegatingJob */ protected Job adaptJob(Object jobObject) throws Exception { if (jobObject instanceof Job) { return (Job) jobObject; } else if (jobObject instanceof Runnable) { return new DelegatingJob((Runnable) jobObject); } else { throw new IllegalArgumentException( "Unable to execute job class [" + jobObject.getClass().getName() + "]: only [org.quartz.Job] and [java.lang.Runnable] supported."); } } }
/* * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.quartz.spi; import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; /** * <p> * A JobFactory is responsible for producing instances of <code>Job</code> * classes. * </p> * * <p> * This interface may be of use to those wishing to have their application * produce <code>Job</code> instances via some special mechanism, such as to * give the opertunity for dependency injection. * </p> * * @see org.quartz.Scheduler#setJobFactory(JobFactory) * @see org.quartz.simpl.SimpleJobFactory * @see org.quartz.simpl.PropertySettingJobFactory * * @author James House */ public interface JobFactory { /** * Called by the scheduler at the time of the trigger firing, in order to * produce a <code>Job</code> instance on which to call execute. * * <p> * It should be extremely rare for this method to throw an exception - * basically only the the case where there is no way at all to instantiate * and prepare the Job for execution. When the exception is thrown, the * Scheduler will move all triggers associated with the Job into the * <code>Trigger.STATE_ERROR</code> state, which will require human * intervention (e.g. an application restart after fixing whatever * configuration problem led to the issue wih instantiating the Job. * </p> * * @param bundle * The TriggerFiredBundle from which the <code>JobDetail</code> * and other info relating to the trigger firing can be obtained. * @param scheduler a handle to the scheduler that is about to execute the job. * @throws SchedulerException if there is a problem instantiating the Job. * @return the newly instantiated Job */ Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException; }
重新修改 quartz 配置對象:QuartzConfig
package com.alan.SpringBootQuartz.config; import com.alan.SpringBootQuartz.quartz.QuartzConfigDemo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; /* * Quartz配置類 * @author: Alan_liu * @date 2021/6/5 21:12 */ @Configuration public class QuartzConfig { /*** * 1:創建job對象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public JobDetailFactoryBean jobDetailFactoryBean(){ JobDetailFactoryBean factory=new JobDetailFactoryBean(); //關聯自己創建的job類 factory.setJobClass(QuartzConfigDemo.class); return factory; } /*** * 2: 創建trigger對象 * @user Alan_liu * @date 2021/6/5 21:13 */ /* @Bean public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ SimpleTriggerFactoryBean factory =new SimpleTriggerFactoryBean(); //關聯jobDetail對象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //該入參表示一個執行的毫秒數 factory.setRepeatInterval(2000); //設置啟動后重復執行次數:5次 factory.setRepeatCount(5); return factory; }*/ /** * * Cron Trigger */ @Bean public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ CronTriggerFactoryBean factory=new CronTriggerFactoryBean(); //關聯jobDetail對象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //設置觸發時間 factory.setCronExpression("0/2 * * * * ?"); return factory; } /*** * 3:創建Scheduler 對象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public SchedulerFactoryBean schedulerFactoryBean(/*SimpleTriggerFactoryBean simpleTriggerFactoryBean*/CronTriggerFactoryBean cronTriggerFactoryBean,MyAdaptableJobFactory myAdaptableJobFactory ){ SchedulerFactoryBean factory=new SchedulerFactoryBean(); //關聯trigger factory.setTriggers(/*simpleTriggerFactoryBean.getObject()*/ cronTriggerFactoryBean.getObject()); //修改該類:完成對象注入spring ioc 容器 factory.setJobFactory(myAdaptableJobFactory); return factory; } }