Job塌衰、Trigger诉稍、Schedule 使用
第一步:編寫一個(gè) job 類,需要實(shí)現(xiàn) org.quartz.Job 接口
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
public void execute(final JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(System.currentTimeMillis() + " - helloJob 任務(wù)執(zhí)行");
}
}
第二步:使用 job最疆、trigger杯巨、schedule 調(diào)用定時(shí)任務(wù)
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzTest {
public static void main(String[] args) {
try {
// 獲取一個(gè)調(diào)度程序的實(shí)例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
System.out.println(scheduler.getSchedulerName() + " - " + scheduler.getSchedulerInstanceId());
// 定義一個(gè) job,并綁定到 HelloJob.class
// 這里并不會(huì)馬上創(chuàng)建一個(gè) HelloJob 實(shí)例努酸,實(shí)例創(chuàng)建是在 scheduler 安排任務(wù)觸發(fā)執(zhí)行時(shí)創(chuàng)建的
JobDetail job = newJob(HelloJob.class) .withIdentity("job1", "group1") .build();
// 聲明一個(gè)觸發(fā)器
// schedule.start() 方法開始調(diào)用的時(shí)候執(zhí)行服爷,每間隔2秒執(zhí)行一次
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(
simpleSchedule().withIntervalInSeconds(3).repeatForever()
).build();
// 安排執(zhí)行任務(wù)
scheduler.scheduleJob(job, trigger);
// 啟動(dòng)任務(wù)調(diào)度程序(內(nèi)部機(jī)制是線程的啟動(dòng))
scheduler.start();
Thread.sleep(30000);
// 關(guān)閉任務(wù)調(diào)度程序
scheduler.shutdown();
} catch (SchedulerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第三步:執(zhí)行程序
DefaultQuartzScheduler - NON_CLUSTERED
1560771694931 - helloJob 任務(wù)執(zhí)行
1560771697879 - helloJob 任務(wù)執(zhí)行
1560771700876 - helloJob 任務(wù)執(zhí)行
1560771703880 - helloJob 任務(wù)執(zhí)行
1560771706876 - helloJob 任務(wù)執(zhí)行
1560771709881 - helloJob 任務(wù)執(zhí)行
...
深入理解 Scheduler、Job、Trigger仍源、JobDetail
Scheduler 調(diào)度程序心褐,只有安排進(jìn)執(zhí)行計(jì)劃的 Job(通過 scheduler.scheduleJob 方法安排),當(dāng)生命的執(zhí)行時(shí)間(Trigger)到了的時(shí)候笼踩,該任務(wù)才會(huì)執(zhí)行逗爹。
Quartz API 幾個(gè)重要接口:
Scheduler,用于與調(diào)度程序交互的主程序接口戳表。
Job桶至,被調(diào)度程序執(zhí)行的任務(wù)類。
JobDetail匾旭,使用 JobDetail 來(lái)定義定時(shí)任務(wù)的實(shí)例镣屹。
Trigger,觸發(fā)器价涝,表明任務(wù)在什么時(shí)候會(huì)執(zhí)行女蜈。
JobBuilder,用于聲明一個(gè)任務(wù)實(shí)例色瘩,也可以定義關(guān)于該任務(wù)的詳情比如任務(wù)名伪窖、組名等。
TriggerBuilder居兆,觸發(fā)器創(chuàng)建器覆山,用于創(chuàng)建觸發(fā)器 trigger。
Scheduler 調(diào)度程序
org.quartz.Scheduler
是 Quartz 調(diào)度程序的主要接口泥栖。Scheduler 維護(hù)了一個(gè) JobDetails 和 Triggers 的注冊(cè)表簇宽。一旦在 Scheduler 注冊(cè)過了,當(dāng)定時(shí)任務(wù)觸發(fā)時(shí)間一到吧享,調(diào)度程序就會(huì)負(fù)責(zé)執(zhí)行預(yù)先定義的 Job魏割。
調(diào)度程序創(chuàng)建之后,處于“待機(jī)”狀態(tài)钢颂,必須調(diào)用 scheduler 的 start()
方法啟用調(diào)度程序钞它。可以使用 shutdown()
方法關(guān)閉調(diào)度程序殊鞭,使用 isShutdown()
方法判斷該調(diào)度程序是否已經(jīng)處于關(guān)閉狀態(tài)遭垛。通過 Scheduler.scheduleJob(…)
方法將任務(wù)納入調(diào)度程序中,當(dāng)任務(wù)觸發(fā)時(shí)間到了的時(shí)候钱豁,該任務(wù)將被執(zhí)行耻卡。
SchedulerFactory 調(diào)度程序工廠
調(diào)度程序 Scheduler 實(shí)例是通過 SchedulerFactory 工廠來(lái)創(chuàng)建的。SchedulerFactory 有兩個(gè)默認(rèn)的實(shí)現(xiàn)類:DirectSchedulerFactory 和 StdSchedulerFactory牲尺。
DirectSchedulerFactory
DirectSchedulerFactory 是一個(gè) org.quartz.SchedulerFactory 的單例實(shí)現(xiàn)卵酪。
示例1:使用 createVolatileScheduler
方法去創(chuàng)建一個(gè)不需要寫入數(shù)據(jù)庫(kù)的調(diào)度程序?qū)嵗?/p>
// 創(chuàng)建一個(gè)擁有10個(gè)線程的調(diào)度程序
DirectSchedulerFactory.getInstance().createVolatileScheduler(10);
DirectSchedulerFactory.getInstance().getScheduler().start();
示例2:使用 createScheduler
方法創(chuàng)建
public void createScheduler(String schedulerName,
String schedulerInstanceId,
ThreadPool threadPool,
JobStore jobStore,
String rmiRegistryHost,
int rmiRegistryPort)
// 創(chuàng)建線程池
SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY);
threadPool.initialize();
// 創(chuàng)建 JobStore
JobStore jobStore = new RAMJobStore();
// 創(chuàng)建調(diào)度程序
DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099);
// 啟動(dòng)調(diào)度程序
DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();
也可使用 JDBCJobStore:
DBConnectionManager.getInstance().addConnectionProvider("someDatasource",
new JNDIConnectionProvider("someDatasourceJNDIName"));
JobStoreTX jdbcJobStore = new JobStoreTX();
jdbcJobStore.setDataSource("someDatasource");
jdbcJobStore.setPostgresStyleBlobs(true);
jdbcJobStore.setTablePrefix("QRTZ_");
jdbcJobStore.setInstanceId("My Instance");
StdSchedulerFactory
StdSchedulerFactory 是 org.quartz.SchedulerFactory
的實(shí)現(xiàn)類幌蚊,基于 Quartz 屬性文件(quartz.properties
)創(chuàng)建 Quartz Scheduler 調(diào)度程序的。
默認(rèn)情況下是加載當(dāng)前工作目錄下的 quartz.properties
文件溃卡。如果加載失敗溢豆,會(huì)去加載 org/quartz 包下的 quartz.properties
屬性文件∪诚郏可以在 org/quartz 包下找到其默認(rèn)的屬性文件的配置信息:
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
如果不想使用默認(rèn)的文件名漩仙,可以指定 org.quartz.properties
屬性指向?qū)傩耘渲梦募犹赖;蛘呖梢栽谡{(diào)用 getScheduler()
方法之前調(diào)用 initialize(xxx)
方法初始化工廠配置队他。
屬性配置文件中,還可以引用其他配置文件的信息峻村,可以使用 $@
來(lái)引用:
quartz1.properties
org.quartz.scheduler.instanceName=HelloScheduler
quartz2.properties
org.quartz.scheduler.instanceName=$@org.quartz.scheduler.instanceName
參照以下 StdSchedulerFactory 的屬性配置麸折,實(shí)際使用中可以指定一些符合需求的參數(shù)。
PROPERTIES_FILE = "org.quartz.properties";
PROP_SCHED_INSTANCE_NAME = "org.quartz.scheduler.instanceName";
PROP_SCHED_INSTANCE_ID = "org.quartz.scheduler.instanceId";
PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS = "org.quartz.scheduler.instanceIdGenerator.class";
PROP_SCHED_THREAD_NAME = "org.quartz.scheduler.threadName";
PROP_SCHED_SKIP_UPDATE_CHECK = "org.quartz.scheduler.skipUpdateCheck";
PROP_SCHED_BATCH_TIME_WINDOW = "org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow";
PROP_SCHED_MAX_BATCH_SIZE = "org.quartz.scheduler.batchTriggerAcquisitionMaxCount";
PROP_SCHED_JMX_EXPORT = "org.quartz.scheduler.jmx.export";
PROP_SCHED_JMX_OBJECT_NAME = "org.quartz.scheduler.jmx.objectName";
PROP_SCHED_JMX_PROXY = "org.quartz.scheduler.jmx.proxy";
PROP_SCHED_JMX_PROXY_CLASS = "org.quartz.scheduler.jmx.proxy.class";
PROP_SCHED_RMI_EXPORT = "org.quartz.scheduler.rmi.export";
PROP_SCHED_RMI_PROXY = "org.quartz.scheduler.rmi.proxy";
PROP_SCHED_RMI_HOST = "org.quartz.scheduler.rmi.registryHost";
PROP_SCHED_RMI_PORT = "org.quartz.scheduler.rmi.registryPort";
PROP_SCHED_RMI_SERVER_PORT = "org.quartz.scheduler.rmi.serverPort";
PROP_SCHED_RMI_CREATE_REGISTRY = "org.quartz.scheduler.rmi.createRegistry";
PROP_SCHED_RMI_BIND_NAME = "org.quartz.scheduler.rmi.bindName";
PROP_SCHED_WRAP_JOB_IN_USER_TX = "org.quartz.scheduler.wrapJobExecutionInUserTransaction";
PROP_SCHED_USER_TX_URL = "org.quartz.scheduler.userTransactionURL";
PROP_SCHED_IDLE_WAIT_TIME = "org.quartz.scheduler.idleWaitTime";
PROP_SCHED_DB_FAILURE_RETRY_INTERVAL = "org.quartz.scheduler.dbFailureRetryInterval";
PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON = "org.quartz.scheduler.makeSchedulerThreadDaemon";
PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD = "org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer";
PROP_SCHED_CLASS_LOAD_HELPER_CLASS = "org.quartz.scheduler.classLoadHelper.class";
PROP_SCHED_JOB_FACTORY_CLASS = "org.quartz.scheduler.jobFactory.class";
PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN = "org.quartz.scheduler.interruptJobsOnShutdown";
PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT = "org.quartz.scheduler.interruptJobsOnShutdownWithWait";
PROP_THREAD_POOL_CLASS = "org.quartz.threadPool.class";
PROP_JOB_STORE_CLASS = "org.quartz.jobStore.class";
PROP_JOB_STORE_USE_PROP = "org.quartz.jobStore.useProperties";
PROP_CONNECTION_PROVIDER_CLASS = "connectionProvider.class";
Job 定時(shí)任務(wù)實(shí)例類
任務(wù)是一個(gè)實(shí)現(xiàn) org.quartz.Job
接口的類粘昨,任務(wù)類必須含有空構(gòu)造器垢啼,只有一個(gè)方法:
void execute(JobExecutionContext context) throws JobExecutionException;
當(dāng)關(guān)聯(lián)這個(gè)任務(wù)實(shí)例的觸發(fā)器表明的執(zhí)行時(shí)間到了的時(shí)候,調(diào)度程序 Scheduler 會(huì)調(diào)用這個(gè)方法來(lái)執(zhí)行任務(wù)张肾,任務(wù)內(nèi)容就可以在這個(gè)方法中執(zhí)行芭析。
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(System.currentTimeMillis() + " - helloJob 任務(wù)執(zhí)行");
}
}
在該方法退出之前,會(huì)設(shè)置一個(gè)結(jié)果對(duì)象到 JobExecutionContext
中吞瞪。盡管這個(gè)結(jié)果對(duì) Quartz 來(lái)說沒什么意義馁启,但是 JobListeners
或者 TriggerListeners
可以監(jiān)聽查看 job 的執(zhí)行情況。芍秆。
JobDataMap
提供了一種“初始化成員屬性數(shù)據(jù)的機(jī)制”进统,在實(shí)現(xiàn)該 Job 接口的時(shí)候可能會(huì)用到。
Job 實(shí)例化的過程
首先創(chuàng)建一個(gè) Job 類浪听,在調(diào)度程序中可以創(chuàng)建很多個(gè) JobDetai,分別設(shè)置不同的 JobDataMap
眉菱。
舉個(gè)例子說明迹栓,創(chuàng)建一個(gè)類 SalesReportJob
實(shí)現(xiàn) Job 接口,用做銷售報(bào)表使用俭缓】艘粒可以通過 JobDataMap
指定銷售員的名稱和銷售報(bào)表的依據(jù)等等。這就會(huì)創(chuàng)建多個(gè) JobDetails
了华坦,例如 SalesReportForJoe
愿吹,SalesReportForMike
分別對(duì)應(yīng)在 JobDataMap
中指定的名字 joe 和 mike。
當(dāng)觸發(fā)器的執(zhí)行時(shí)間到了的時(shí)候惜姐,會(huì)加載與之關(guān)聯(lián)的 JobDetail犁跪,并在調(diào)度程序 Scheduler 中通過 JobFactory 的配置實(shí)例化它引用的 Job椿息。
JobFactory 調(diào)用 newInstance()
創(chuàng)建一個(gè)任務(wù)實(shí)例,然后調(diào)用 setter
方法設(shè)置在 JobDataMap 定義好的名字坷衍∏抻牛可以實(shí)現(xiàn) JobFactory,比如使用 IOC/DI 機(jī)制初始化的任務(wù)實(shí)例枫耳。
Job 的聲明和并發(fā)
以下對(duì)注解使用在 Job 實(shí)現(xiàn)類中乏矾,可以影響 Quartz 的行為:
@DisallowConcurrentExecution
告訴 Quartz 不要執(zhí)行多個(gè)任務(wù)實(shí)例。
在上面的 SalesReportJob 類添加該注解迁杨,將會(huì)只有一個(gè) SalesReportForJoe
實(shí)例在給定的時(shí)間執(zhí)行钻心,但是 SalesReportForMike
是可以執(zhí)行的。這個(gè)約束是基于 JobDetail 的铅协,而不是基于任務(wù)類的捷沸。
@PersistJobDataAfterExecution
告訴 Quartz 在任務(wù)執(zhí)行成功完畢之后(沒有拋出異常),修改 JobDetail 的 JobDataMap 備份警医,以供下一個(gè)任務(wù)使用亿胸。
如果使用了 @PersistJobDataAfterExecution
注解,強(qiáng)烈建議同時(shí)使用 @DisallowConcurrentExecution
注解预皇,以避免當(dāng)兩個(gè)同樣的 Job 并發(fā)執(zhí)行的時(shí)候產(chǎn)生的存儲(chǔ)數(shù)據(jù)混亂侈玄。
Job 的其他屬性
- 持久化,如果一個(gè)任務(wù)不是持久化的吟温,則當(dāng)沒有觸發(fā)器關(guān)聯(lián)它的時(shí)候序仙,Quartz 會(huì)從 scheduler 中刪除它。
- 請(qǐng)求恢復(fù)鲁豪,如果一個(gè)任務(wù)請(qǐng)求恢復(fù)潘悼,一般是該任務(wù)執(zhí)行期間發(fā)生了系統(tǒng)崩潰或者其他關(guān)閉進(jìn)程的操作,當(dāng)服務(wù)再次啟動(dòng)的時(shí)候爬橡,會(huì)再次執(zhí)行該任務(wù)治唤。這種情況下,
JobExecutionContext.isRecovering()
會(huì)返回 true糙申。
JobDetail 定義任務(wù)實(shí)例的一些屬性特征
org.quartz.JobDetail
接口負(fù)責(zé)傳輸給定的任務(wù)實(shí)例的屬性到 Scheduler宾添。JobDetail 是通過 JobBuilder 創(chuàng)建的。
Quartz 不會(huì)存儲(chǔ)一個(gè)真實(shí)的 Job 類實(shí)例柜裸,但是允許通過 JobDetail 定義一個(gè)任務(wù)實(shí)例缕陕。
任務(wù)有一個(gè)名稱 name 和 group 來(lái)關(guān)聯(lián),在一個(gè) Scheduler 中這二者的組合必須是唯一的疙挺。
多個(gè)觸發(fā)器可以指向同一個(gè) Job扛邑,但一個(gè)觸發(fā)器只能指向一個(gè) Job。
JobDataMap 任務(wù)數(shù)據(jù)映射
JobDataMap 用來(lái)保存任務(wù)實(shí)例的狀態(tài)信息铐然。當(dāng)一個(gè) Job 被添加到 scheduler 的時(shí)候蔬崩,JobDataMap 實(shí)例就會(huì)存儲(chǔ)一次關(guān)于該任務(wù)的狀態(tài)信息數(shù)據(jù)恶座。也可以使用 @PersistJobDataAfterExecution
注解標(biāo)明在一個(gè)任務(wù)執(zhí)行完畢之后就存儲(chǔ)一次。
JobDataMap 實(shí)例也可以存儲(chǔ)一個(gè)觸發(fā)器舱殿。這是非常有用的奥裸,特別是當(dāng)任務(wù)被多個(gè)觸發(fā)器引用的時(shí)候,根據(jù)不同的觸發(fā)時(shí)機(jī)沪袭,你可以提供不同的輸入條件湾宙。
JobExecutionContext 也可以再執(zhí)行時(shí)包含一個(gè) JobDataMap ,合并了觸發(fā)器的 JobDataMap (如果有的話)和 Job 的 JobDataMap (如果有的話)冈绊。
在定義 JobDetail 的時(shí)候侠鳄,將一些數(shù)據(jù)放入JobDataMap 中:
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("jobSays", "Hello World!")
.usingJobData("myFloatValue", 3.141f)
.build();
在任務(wù)執(zhí)行的時(shí)候,可以獲取 JobDataMap 中的數(shù)據(jù):
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
// ...
}
在觸發(fā)器也添加數(shù)據(jù):
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("trigger_key", "每2秒執(zhí)行一次")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(2)
.repeatForever())
.build();
任務(wù)執(zhí)行方法可以獲取到合并后的數(shù)據(jù)
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
// 使用歸并的 JobDataMap
JobDataMap dataMap = context.getMergedJobDataMap();
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
String triggerSays = dataMap.getString("trigger_key");
// ...
}
Trigger 觸發(fā)器
觸發(fā)器使用 TriggerBuilder 來(lái)實(shí)例化死宣,有一個(gè) TriggerKey 關(guān)聯(lián)伟恶,在一個(gè) Scheduler 中必須是唯一的。
多個(gè)觸發(fā)器可以指向同一個(gè)工作毅该,但一個(gè)觸發(fā)器只能指向一個(gè)工作博秫。
觸發(fā)器可以傳送數(shù)據(jù)給 job,通過將數(shù)據(jù)放進(jìn)觸發(fā)器的 JobDataMap眶掌。
觸發(fā)器常用屬性
觸發(fā)器也有很多屬性挡育,這些屬性都是在使用 TriggerBuilder 定義觸發(fā)器時(shí)設(shè)置的。
- TriggerKey朴爬,唯一標(biāo)識(shí)即寒,在一個(gè) Scheduler 中必須是唯一的
- startTime,開始時(shí)間召噩,通常使用 startAt(java.util.Date)
- endTime母赵,結(jié)束時(shí)間,設(shè)置了結(jié)束時(shí)間則在這之后具滴,不再觸發(fā)
觸發(fā)器的優(yōu)先級(jí)
有時(shí)候凹嘲,會(huì)安排很多任務(wù),但是 Quartz 并沒有更多的資源去處理它构韵。這種情況下施绎,必須需要很好地控制哪個(gè)任務(wù)先執(zhí)行。這時(shí)候可以設(shè)置 priority 屬性(使用方法 withPriority(int)
)來(lái)控制觸發(fā)器的優(yōu)先級(jí)贞绳。
優(yōu)先級(jí)只有觸發(fā)器出發(fā)時(shí)間一樣的時(shí)候才有意義。
當(dāng)一個(gè)任務(wù)請(qǐng)求恢復(fù)執(zhí)行時(shí)致稀,它的優(yōu)先級(jí)和原始優(yōu)先級(jí)是一樣的冈闭。
JobBuilder 用于創(chuàng)建 JobDetail,TriggerBuilder 用于創(chuàng)建觸發(fā)器 Trigger
JobBuilder 用于創(chuàng)建 JobDetail抖单,如果沒有調(diào)用 withIdentity(..)
指定 job 的名字萎攒,會(huì)自動(dòng)生成一個(gè)遇八。
TriggerBuilder 用于創(chuàng)建 Trigger,如果沒有調(diào)用 withSchedule(..)
方法耍休,會(huì)使用默認(rèn)的 schedule 刃永。如果沒有使用 withIdentity(..)
會(huì)自動(dòng)生成一個(gè)觸發(fā)器名稱。
Quartz 通過一種領(lǐng)域特定語(yǔ)言(DSL)提供了一種自己的 builder 的風(fēng)格API來(lái)創(chuàng)建任務(wù)調(diào)度相關(guān)的實(shí)體羊精。DSL 可以通過對(duì)類的靜態(tài)方法的使用來(lái)調(diào)用:TriggerBuilder斯够、JobBuilder、DateBuilder喧锦、JobKey读规、TriggerKey 以及其它的關(guān)于 Schedule 創(chuàng)建的實(shí)現(xiàn)。
// import static org.quartz.JobBuilder.newJob;
// import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
// import static org.quartz.TriggerBuilder.newTrigger;
JobDetail job = newJob(MyJob.class)
.withIdentity("myJob")
.build();
Trigger trigger = newTrigger()
.withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
.withSchedule(simpleSchedule()
.withIntervalInHours(1)
.repeatForever())
.startAt(futureDate(10, MINUTES))
.build();
scheduler.scheduleJob(job, trigger);
Cron 表達(dá)式的使用
Quartz 提供了多種觸發(fā)器:
最常用的兩種觸發(fā)器:簡(jiǎn)單觸發(fā)器 SimpleTrigger燃少、基于 Cron 表達(dá)式的觸發(fā)器 CronTrigger
簡(jiǎn)單觸發(fā)器 SimpleTrigger
SimpleTrigger 是接口 Trigger 的一個(gè)具體實(shí)現(xiàn)束亏,可以觸發(fā)一個(gè)已經(jīng)安排進(jìn)調(diào)度程序的任務(wù),并可以指定時(shí)間間隔重復(fù)執(zhí)行該任務(wù)阵具。
SimpleTrigger 包含幾個(gè)特點(diǎn):開始時(shí)間碍遍、結(jié)束時(shí)間、重復(fù)次數(shù)以及重復(fù)執(zhí)行的時(shí)間間隔阳液。
重復(fù)的次數(shù)可以是零怕敬,一個(gè)正整數(shù),或常量 SimpleTrigger.REPEAT_INDEFINITELY趁舀。
重復(fù)執(zhí)行的時(shí)間間隔可以是零赖捌,或者 long 類型的數(shù)值表示毫秒。值得注意的是矮烹,零重復(fù)間隔會(huì)造成觸發(fā)器同時(shí)發(fā)生(或接近同時(shí))越庇。
結(jié)束時(shí)間的會(huì)重寫重復(fù)的次數(shù),這可能是有用的奉狈,如果你想創(chuàng)建一個(gè)觸發(fā)器卤唉,如每10秒觸發(fā)一次,直到一個(gè)給定的時(shí)刻仁期,而不是要計(jì)算的次數(shù)桑驱,它會(huì)在開始時(shí)間和結(jié)束時(shí)間重復(fù)執(zhí)行。結(jié)束時(shí)間一到跛蛋,就算你指定了重復(fù)次數(shù)很多次(比如執(zhí)行10W次)熬的,但是時(shí)間一到它將不再執(zhí)行。
SimpleTrigger 實(shí)例創(chuàng)建依賴于 TriggerBuilder 和 SimpleScheduleBuilder 赊级,使用 Quartz 提供的DSL風(fēng)格創(chuàng)建觸發(fā)器實(shí)例押框,
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.DateBuilder.*:
可以創(chuàng)建很多不同形式的觸發(fā)器:
創(chuàng)建一個(gè)指定時(shí)間開始執(zhí)行,但是不重復(fù)的觸發(fā)器理逊,使用 startAt(java.util.Date) 設(shè)置觸發(fā)器的第一次執(zhí)行時(shí)間:
SimpleTrigger trigger = (SimpleTrigger) newTrigger()
.withIdentity("trigger1", "group1")
.startAt(myStartTime)
.forJob("job1", "group1")
.build();
創(chuàng)建一個(gè)指定時(shí)間開始執(zhí)行橡伞,每10s執(zhí)行一次盒揉,共執(zhí)行10次的觸發(fā)器
使用 SimpleScheduleBuilder.withIntervalInSeconds(N)
方法可以指定間隔N秒就執(zhí)行一次;withRepeatCount(M)
可以指定執(zhí)行次數(shù)M兑徘。
SimpleScheduleBuilder 有很多類似的方法刚盈。
Trigger trigger = newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring)
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)
)
.forJob(myJob)
.build();
創(chuàng)建一個(gè)在未來(lái)第五分鐘的時(shí)候執(zhí)行一次的觸發(fā)器
使用 DateBuilder 的 futureDate 方法可以指定在未來(lái)時(shí)間執(zhí)行。
Trigger trigger = (SimpleTrigger) newTrigger()
.withIdentity("trigger5", "group1")
.startAt(futureDate(5, IntervalUnit.MINUTE))
.forJob(myJobKey)
.build();
創(chuàng)建一個(gè)馬上執(zhí)行挂脑,每隔5分鐘執(zhí)行藕漱、直到22:00結(jié)束執(zhí)行的觸發(fā)器
使用 TriggerBuilder 的 startNow() 方法立即觸發(fā)(scheduler 調(diào)用 start 時(shí)算起,視優(yōu)先級(jí)而定)最域;
withIntervalInMinutes(5) 每5分鐘執(zhí)行一次谴分;
repeatForever() 一直重復(fù);
endAt(dateOf(22, 0, 0)) 直到22:00終結(jié)觸發(fā)器:
Trigger trigger = newTrigger()
.withIdentity("trigger7", "group1")
.startNow()
.withSchedule(
simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever()
)
.endAt(dateOf(22, 0, 0))
.build();
創(chuàng)建一個(gè)在偶數(shù)小時(shí)執(zhí)行镀脂、每?jī)蓚€(gè)小時(shí)執(zhí)行一次的觸發(fā)器
Trigger trigger = newTrigger()
.withIdentity("trigger8")
.startAt(evenHourDate(null))
.withSchedule(simpleSchedule().withIntervalInHours(2).repeatForever())
.build();
值得注意的是牺蹄,如果沒有調(diào)用 startAt(..)
方法,默認(rèn)使用 startNow()
薄翅。
關(guān)于簡(jiǎn)單觸發(fā)器“熄火(misfire)”的指令
SimpleTrigger 包含一些指令在“熄火”時(shí)可以告知 Quartz 怎么去處理沙兰。這些指令包含在 SimpleTrigger 的常量中。
REPEAT_INDEFINITELY - 用于表示觸發(fā)器的“重復(fù)計(jì)數(shù)”是不確定的翘魄《μ欤或者換句話說,觸發(fā)應(yīng)該不斷重復(fù)直到觸發(fā)的結(jié)尾時(shí)間戳
MISFIRE_INSTRUCTION_FIRE_NOW - 如果熄火暑竟,該指令會(huì)告訴 Quartz 應(yīng)該馬上再次觸發(fā)
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT - 如果熄火斋射,該指令會(huì)告訴 Quartz 馬上執(zhí)行并計(jì)數(shù)累計(jì)到已經(jīng)執(zhí)行的次數(shù)當(dāng)中去,如果結(jié)束時(shí)間已經(jīng)過了但荤,則不會(huì)再執(zhí)行罗岖。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT - 如果熄火,會(huì)告訴 Quartz 想要現(xiàn)在就執(zhí)行一次(即使現(xiàn)在不是它原本計(jì)劃的觸發(fā)時(shí)間)
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT - 如果熄火腹躁,會(huì)告訴 Quartz 在下一次執(zhí)行時(shí)間再次開始執(zhí)行
一個(gè)使用“熄火”策略的觸發(fā)器示例:
Trigger trigger = newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(
simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever()
.withMisfireHandlingInstructionNextWithExistingCount()
)
.build();
基于 Cron 表達(dá)式的觸發(fā)器 CronTrigger
CronTrigger 通常使用得比 SimpleTrigger 多一些桑包。特別是基于日歷的概念,而不是對(duì)具體間隔的行為纺非。
通過 CronTrigger哑了,可以指定“每個(gè)星期五的中午”、“每個(gè)工作日上午9:30”烧颖,“一月的每星期一的上午9點(diǎn)至10點(diǎn)之間的每5分鐘弱左,星期三和星期五” 執(zhí)行。
Cron 表達(dá)式
首先了解 Cron 表達(dá)式炕淮,是用于配制 CronTrigger 實(shí)例的科贬。Cron 表達(dá)式,實(shí)際上是由七個(gè)子表達(dá)式組成的字符串,它描述了不同的調(diào)度細(xì)節(jié)榜掌。這些子表達(dá)式是用空格分隔的,并表示:
秒
分
時(shí)
月中的天
月
周中的天
年(可選項(xiàng))
例如: “0 0 12 ? * WED” 表示 “每個(gè)星期三的12點(diǎn)”乘综,單個(gè)子表達(dá)式可以包含范圍和/或列表憎账,例如:
"0 0 7 ? * MON-FRI" 表示 "每個(gè)工作日的7點(diǎn)"
"0 0 19 ? * MON,WED,FRI" 表示 "周一、周三和周五的19點(diǎn)"
"0 0 14 ? * MON-WED,SAT" 表示 "周一到周三以及周六的14點(diǎn)"
Cron 表達(dá)式的規(guī)則說明
所有字段都有一組可以指定的有效值卡辰。
- 數(shù)字 0 到 59 可以表示秒和分
- 0到23可以表示小時(shí)
- 月中的天可以使用1到31的數(shù)值, 但是你要注意該月的天數(shù)!
- 月用0 到 11之間的數(shù)值表示, 或者使用JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 和 DEC來(lái)表示1-12月
- 一周中的天試用1到7表示 (1 表示 周日) 或者使用 SUN, MON, TUE, WED, THU, FRI 和 SAT
創(chuàng)建 CronTrigger
CronTrigger 實(shí)例使用 TriggerBuilder 和 CronScheduleBuilder 創(chuàng)建
創(chuàng)建一個(gè)8到17點(diǎn)間每?jī)煞昼妶?zhí)行一次的 Cron 觸發(fā)器:
Trigger cronTrigger1 = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?"))
.forJob("myJob", "group1")
.build();
創(chuàng)建一個(gè)每天10:42執(zhí)行的Cron觸發(fā)器:
Trigger cronTrigger2 = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(dailyAtHourAndMinute(10, 42))
.forJob(job.getKey())
.build();
Trigger cronTrigger3 = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 * * ?"))
.forJob(job.getKey())
.build();
關(guān)于 Cron 觸發(fā)器“熄火”的指令
CronTrigger 同樣包含一些指令在它“熄火”時(shí)可以告知 Quartz 怎么去處理胞皱。
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW - 如果熄火,該指令會(huì)告訴 Quartz 希望馬上再次觸發(fā)
MISFIRE_INSTRUCTION_DO_NOTHING - 如果熄火九妈,該指令會(huì)告訴 Quartz 下一次執(zhí)行時(shí)間到來(lái)時(shí)再執(zhí)行反砌,并不想馬上執(zhí)行
Trigger cronTrigger4MisfireInstruction = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?")
.withMisfireHandlingInstructionFireAndProceed())
.forJob("myJob", "group1")
.build();
常用的 Cron 表達(dá)式例子:
0 0 12 * * ? 每天12點(diǎn)執(zhí)行
0 15 10 ? * * 每天的10:15執(zhí)行
0 15 10 * * ? 每天的10:15執(zhí)行
0 15 10 * * ? * 每天的10:15執(zhí)行
0 15 10 * * ? 2005 2005年每天的10:15執(zhí)行
0 * 14 * * ? 每天的14:00到14:59期間每分鐘執(zhí)行
0 0/5 14 * * ? 每天的14:00到14:55每隔5分鐘執(zhí)行
0 0/5 14,18 * * ? 每天的14:00到14:55每隔5分鐘執(zhí)行和18:00到18:55每隔5分鐘執(zhí)行
0 0-5 14 * * ? 每天的14:00到14:05執(zhí)行
0 10,44 14 ? 3 WED 三月的每一個(gè)周三的14:10和14:44執(zhí)行
0 15 10 ? * MON-FRI 工作日每天的10:15:00執(zhí)行
0 15 10 15 * ? 每個(gè)月的第15天的10:15:00執(zhí)行
0 15 10 L * ? 每個(gè)月最后一天的10:15:00執(zhí)行
0 15 10 ? * 6L 每個(gè)月最后一個(gè)周五的10:15:00執(zhí)行
0 15 10 ? * 6L 2002-2005 2002, 2003, 2004, 和2005年每個(gè)月最后一個(gè)周五的10:15:00執(zhí)行
0 15 10 ? * 6#3 每個(gè)月的第三個(gè)周五的10:15:00執(zhí)行
0 0 12 1/5 * ? 每個(gè)月的第一天的12:00:00開始執(zhí)行,每隔5天間隔執(zhí)行
0 11 11 11 11 ? 每年的11月11日11:11:00執(zhí)行