任務(wù)調(diào)度和異步執(zhí)行器

1.任務(wù)調(diào)度概述

各種企業(yè)應(yīng)用都會(huì)遇到任務(wù)調(diào)度的需求,比如每天凌晨統(tǒng)計(jì)論壇用戶的積分排名等等咸包,在特定的時(shí)間做特定的事情。如果將任務(wù)調(diào)度的范圍稍微擴(kuò)大一點(diǎn),則還應(yīng)該包括資源上的調(diào)度麸锉。如Web Server在接收到請(qǐng)求時(shí),會(huì)立即創(chuàng)建一個(gè)新的線程服務(wù)該請(qǐng)求舆声。但是資源是有限的花沉,無限制的使用必然會(huì)耗盡虧空,大多數(shù)系統(tǒng)都要對(duì)資源使用進(jìn)行控制媳握。首先必須限制服務(wù)線程的最大數(shù)目碱屁;其次可以考慮使用線程池以便共享服務(wù)的線程資源,降低頻繁創(chuàng)建蛾找、銷毀線程的消耗娩脾。

任務(wù)調(diào)度本身設(shè)計(jì)多線程并發(fā),運(yùn)行時(shí)間規(guī)則制定及解析打毛、運(yùn)行線程保持與恢復(fù)柿赊、線程池維護(hù)等諸多方面的工作。如果直接使用自定義線程這種最原始的辦法幻枉,則開發(fā)任務(wù)調(diào)度程序是一項(xiàng)頗具挑戰(zhàn)性的工作碰声。

2.Quartz快速進(jìn)階

Quartz允許開發(fā)人員靈活地定義觸發(fā)器的調(diào)度時(shí)間表,并可對(duì)觸發(fā)器和任務(wù)進(jìn)行關(guān)聯(lián)映射熬甫。此外奥邮,Quartz提供了調(diào)度運(yùn)行環(huán)境的持久化機(jī)制,可以保存并恢復(fù)調(diào)度現(xiàn)場(chǎng)罗珍,即使系統(tǒng)因故障關(guān)閉洽腺,任務(wù)調(diào)度現(xiàn)場(chǎng)數(shù)據(jù)也不會(huì)丟失。

2.1.Quartz基礎(chǔ)結(jié)構(gòu)

Quartz對(duì)任務(wù)調(diào)度的領(lǐng)域問題進(jìn)行了高度的抽象覆旱,提出了調(diào)度器蘸朋、任務(wù)和觸發(fā)器這3個(gè)核心概念,并且在org.quartz中通過接口和類對(duì)核心概念進(jìn)行了描述

  • Job:是一個(gè)接口扣唱,只有一個(gè)方法void execute(JobExecutionContext context),開發(fā)者通
    過實(shí)現(xiàn)該接口來定義需要執(zhí)行的任務(wù)藕坯,JobExecutionContext類提供了調(diào)度上下文的各種信息。Job運(yùn)行時(shí)的信息都保存在JobDataMap實(shí)例中
  • JobDetail:Quartz在每次執(zhí)行Job時(shí)噪沙,都重新創(chuàng)建一個(gè)Job實(shí)例炼彪,所以它不是直接接收一個(gè)Job實(shí)例,而是接收一個(gè)Job實(shí)現(xiàn)類正歼,以便運(yùn)行時(shí)通過newInstance()的反射調(diào)用機(jī)制來實(shí)例化Job辐马。因此需要通過一個(gè)類來描述Job的實(shí)現(xiàn)類及其他相關(guān)的靜態(tài)信息,如Job名稱局义、描述喜爷、關(guān)聯(lián)監(jiān)聽器等信息冗疮,而JobDetail承擔(dān)了這一角色
  • Triger:是一個(gè)類,描述觸發(fā)Job執(zhí)行的時(shí)間觸發(fā)規(guī)則檩帐。主要有SimpleTrigger和CronTrigger這兩個(gè)類术幔。當(dāng)僅需要觸發(fā)一次后者以固定間隔周期性執(zhí)行時(shí),SimpleTigger是最適合的選擇湃密;而CronTrigger則可以通過Cron表達(dá)式定義出各種復(fù)雜的調(diào)度方法诅挑,
  • Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日歷特定時(shí)間點(diǎn)的集合
  • Scheduler:代表一個(gè)Quartz的獨(dú)立運(yùn)行容器泛源,Trigger和JobDetail可以注冊(cè)到Scheduler中拔妥,二者在Scheduler中擁有各自的組及名稱。組及名稱是Scheduler查找定位容器中某一對(duì)象的依據(jù)俩由,Trigger的組及名稱的組合必須唯一毒嫡,JobDetail的組合名稱的組合也必須唯一癌蚁。
  • ThreadPool:Scheduler使用一個(gè)線程池作為任務(wù)運(yùn)行的基礎(chǔ)設(shè)施幻梯,任務(wù)通過共享線程池中的線程來提高運(yùn)行效率
2.2.使用SimpleTrigger

SimpleTrigger擁有多個(gè)重載的構(gòu)造函數(shù),用于在不同場(chǎng)合下構(gòu)造出對(duì)應(yīng)的實(shí)例

  • SimpleTrigger(String name,String group):通過該構(gòu)造函數(shù)指定Trigger所屬組合名稱
  • SimpleTrigger(String name,String group,Date startTime):除指定Trigger所屬組和名
    稱外,還可以指定觸發(fā)的開始時(shí)間
  • SimpleTrigger(String name,String group,Date startTime,Date endTime,int repeatCount,long repeatInterval):除指定以上信息外努释,還可以指定結(jié)束時(shí)間碘梢、重復(fù)執(zhí)行次數(shù)、時(shí)間間隔等參數(shù)
  • SimpleTrigger(String name,String group,String jobName,String jobGroup,Date startTime,Date endTime,int repeatCount,long repeatInterval):這是最復(fù)雜的一個(gè)構(gòu)造
    函數(shù)伐蒂,在指定觸發(fā)參數(shù)的同時(shí)煞躬,通過jobGroup和jobName,使該Trigger和Schedule中的某個(gè)任務(wù)關(guān)聯(lián)起來
public class SimpleJob implements Job {
    public void execute(JobExecutionContext jobCtx) throws JobExecutionException {//實(shí)現(xiàn)Job接口方法
        System.out.println(jobCtx.getTrigger().getName()+" triggered. time is:" + (new Date()));
    }
}

以下是通過SimpleTrigger對(duì)SimpleJob進(jìn)行調(diào)度

public class SimpleTriggerRunner {
    public static void main(String args[]) {
        try {

            //創(chuàng)建一個(gè)JobDetail實(shí)例,指定SimpleJob
            JobDetail jobDetail = new JobDetail("job1_1", "jgroup1",
                    SimpleJob.class);
            //通過SimpleTrigger定義調(diào)度規(guī)則逸邦,馬上啟動(dòng)恩沛,每2秒運(yùn)行一次,共運(yùn)行100次
            SimpleTrigger simpleTrigger = new SimpleTrigger("trigger1_1",
                    "tgroup1");
            simpleTrigger.setStartTime(new Date());
            simpleTrigger.setRepeatInterval(2000);
            simpleTrigger.setRepeatCount(100);
            
            //通過SchedulerFactory獲取一個(gè)調(diào)度器實(shí)例
            SchedulerFactory schedulerFactory = new StdSchedulerFactory();
            //注冊(cè)并進(jìn)行調(diào)度
            Scheduler scheduler = schedulerFactory.getScheduler();
            scheduler.scheduleJob(jobDetail, simpleTrigger);
            //調(diào)度啟動(dòng)
            scheduler.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
2.3.使用CronTrigger

CronTrigger能夠提供比SimpleTrigger更具有實(shí)際意義的調(diào)度方案缕减,調(diào)度規(guī)則基于Cron表達(dá)式雷客。CronTrigger支持日歷相關(guān)的周期時(shí)間間隔(比如每月第一個(gè)周一執(zhí)行),而不是簡(jiǎn)單的周期時(shí)間間隔桥狡。一次相對(duì)于SimpleTrigger而言搅裙,CronTrigger在使用上也要復(fù)雜一些。

2.3.1.Cron表達(dá)式

Quartz使用類似Linux下的Cron表達(dá)式定義時(shí)間規(guī)則裹芝。Cron表達(dá)式由6或7個(gè)空格分隔的時(shí)間間隔字段組成

位置 時(shí)間域名 允許值 允許的特殊字符
1 0-59 部逮,-*/
2 分鐘 0-59 ,-*/
3 小時(shí) 0-23 嫂易,-*/
4 日期 1-31 兄朋,-?/L W C
5 月份 1-12 ,-*/
6 星期 1-7 怜械,-*?/L C #
7 年(可選) 空值 1970-2099 蜈漓,-*./
  • 星號(hào)(*):可用在所有字段中穆桂,表示對(duì)應(yīng)時(shí)間域的每一個(gè)時(shí)刻。例如融虽,*在分鐘字段時(shí)享完,表示“每分鐘”
  • 問號(hào)(?):該字符只在日期和星期字段中使用,它通常指定為“無意義的值”,相當(dāng)于占位符
  • 減號(hào)(-):表達(dá)一個(gè)范圍有额,如在小時(shí)字段中使用“10-12”,則表示從10點(diǎn)到12點(diǎn)般又,即10,11,12
  • 逗號(hào)(,):表示一個(gè)列表值。如在星期字段中使用“MON,WED,FRI”,則表示星期一巍佑、星期三和星期五
  • 斜杠(/):x/y表達(dá)一個(gè)等步長(zhǎng)序列茴迁,x為起始值,y為增量步長(zhǎng)值萤衰。如在分鐘字段中使用0/15堕义,則表示為0,15,30和45秒;而5/15在分鐘字段中表示5,20,35,50。用戶也可以使用*/y脆栋,它等同于0/y
  • L:該字符只在日期和星期字段中使用倦卖,代表“Last”的意思,但它在兩個(gè)字段中的意思不同椿争。如果L用在日期字段中怕膛,則表示這個(gè)月份的最后一天,用在星期中秦踪,則表示星期六褐捻。但是,如果L出現(xiàn)在星期字段里椅邓,而且前面有一個(gè)數(shù)字N柠逞,則表示“這個(gè)月的最后N天”。例如景馁,6L表示該月的最后一個(gè)星期五
  • W:該字符只能出現(xiàn)在在日期字段里板壮,是對(duì)前導(dǎo)日期的修飾,表示離該日期最近的工作日裁僧。例如个束,15W表示離該月15日最近的工作日
  • LW組合:在日期字段中可以組合使用LW,它的意思是當(dāng)月的最后一個(gè)工作日
  • :該字符只能在星期字段中使用聊疲,表示當(dāng)月的某個(gè)工作日茬底,如4#5表示當(dāng)月的第五個(gè)星期三。假設(shè)當(dāng)月沒有第五個(gè)星期三获洲,則忽略不觸發(fā)

  • C:該字符只在日期和星期字段中使用阱表,代表“Calendar”的意思。它的意思是計(jì)劃所關(guān)聯(lián)的日期,如果日期沒有被關(guān)聯(lián)最爬,則相當(dāng)于日歷中的所有日期涉馁。例如,5C在日期字段中相當(dāng)于5日以后的一天爱致,1C在星期字段中相當(dāng)于星期日后的第一天
2.3.2.CronTrigger實(shí)例

下面使用CronTrigger對(duì)SimpleJob進(jìn)行調(diào)度烤送,通過Cron表達(dá)式制定調(diào)度規(guī)則

public class CronTriggerRunner {

    public static void main(String args[]) {
        try {           
            JobDetail jobDetail = new JobDetail("job1_2", "jgroup1",
                    SimpleJob.class);
            CronTrigger cronTrigger = new CronTrigger("trigger1_2", "tgroup1");

            CronExpression cexp = new CronExpression("0/5 * * * * ?");
            cronTrigger.setCronExpression(cexp);
            

            SchedulerFactory schedulerFactory = new StdSchedulerFactory();
            Scheduler scheduler = schedulerFactory.getScheduler();
            scheduler.scheduleJob(jobDetail, cronTrigger);
            scheduler.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.4.使用Calendar

在實(shí)際任務(wù)調(diào)度中,不可能一成不變地按照某個(gè)特定周期調(diào)度任務(wù)糠悯,必須考慮到實(shí)現(xiàn)生活中日歷上的特殊日期

public class CalendarExample {

    public static void main(String[] args) throws Exception {
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler scheduler = sf.getScheduler();

        AnnualCalendar holidays = new AnnualCalendar();
        //五一勞動(dòng)節(jié)
        Calendar laborDay = new GregorianCalendar();
        laborDay.add(Calendar.MONTH,5);
        laborDay.add(Calendar.DATE,1);
        holidays.setDayExcluded(laborDay, true);       
        //國(guó)慶節(jié)
        Calendar nationalDay = new GregorianCalendar();
        nationalDay.add(Calendar.MONTH,10);
        nationalDay.add(Calendar.DATE,1);
        holidays.setDayExcluded(nationalDay, true);


        scheduler.addCalendar("holidays", holidays, false, false);
        
        //從5月1號(hào)10am開始
        Date runDate = TriggerUtils.getDateOf(0,0, 10, 1, 5);
        JobDetail job = new JobDetail("job1", "group1", SimpleJob.class);
        SimpleTrigger trigger = new SimpleTrigger("trigger1", "group1", 
                runDate, 
                null, 
                SimpleTrigger.REPEAT_INDEFINITELY, 
                60L * 60L * 1000L);
        //讓Trigger遵守節(jié)日的規(guī)則(排除節(jié)日)
        trigger.setCalendarName("holidays");
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
        try {
            // wait 30 seconds to show jobs
            Thread.sleep(30L * 1000L); 
            // executing...
        } catch (Exception e) {
        }            
        scheduler.shutdown(true);
    }
}
2.5.任務(wù)調(diào)度信息存儲(chǔ)

在默認(rèn)情況下帮坚,Quartz將任務(wù)調(diào)度的運(yùn)行信息保存在內(nèi)存中。這種方法提供了最佳性能互艾,因?yàn)樵趦?nèi)存中數(shù)據(jù)訪問速度最快试和;不足之處是缺乏數(shù)據(jù)的持久性,當(dāng)程序中途停止或者系統(tǒng)崩潰時(shí)纫普,所有運(yùn)行信息都會(huì)丟失阅悍。

如果確實(shí)需要持久化任務(wù)調(diào)度信息,則Quzrtz允許用戶通過調(diào)整其屬性文件昨稼,將這些信息保存到數(shù)據(jù)庫中节视。在使用數(shù)據(jù)庫保存了任務(wù)調(diào)度信息后,即使系統(tǒng)崩潰后重新啟動(dòng)悦昵,任務(wù)調(diào)度信息仍將得到恢復(fù)肴茄。如前面所說的例子晌畅,執(zhí)行50次系統(tǒng)崩潰后重新運(yùn)行但指,計(jì)數(shù)器將從51開始計(jì)數(shù)。使用數(shù)據(jù)庫保存信息的任務(wù)稱為持久化任務(wù)抗楔。

####### 2.5.1.通過配置文件調(diào)整任務(wù)調(diào)度信息的保存策略

其實(shí)Quartz JAR文件的org.quartz包下就包含了一個(gè)quartz.properties屬性配置文件棋凳,并提供了默認(rèn)的配置。如果需要調(diào)整配置连躏,則可以在類路徑下建立一個(gè)新的quartz.properties屬性剩岳,它將自動(dòng)被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

//配置調(diào)度器的線程池
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
//配置任務(wù)調(diào)度現(xiàn)場(chǎng)保存機(jī)制
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

Quartz的屬性配置文件主要包括三方面的信息:

  • 集群信息
  • 調(diào)度器線程池
  • 任務(wù)調(diào)度現(xiàn)場(chǎng)數(shù)據(jù)的保存

可以通過下面的設(shè)置將任務(wù)調(diào)度現(xiàn)場(chǎng)的數(shù)據(jù)保存到數(shù)據(jù)庫中

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.tablePrefix = QRTZ_ //1入热、數(shù)據(jù)庫表前綴
org.quartz.jobStore.dataSource = qzDS //2拍棕、數(shù)據(jù)源名稱

//3、定義數(shù)據(jù)源的具體屬性
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/sampledb
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = 123456
org.quartz.dataSource.qzDS.maxConnections = 30

要將任務(wù)調(diào)度數(shù)據(jù)保存到數(shù)據(jù)庫中勺良,就必須使用org.quartz.impl.jdbcjobstore.JobStoreTX代替原來的org.quartz.simpl.RAMJobStore绰播,并提供相應(yīng)的數(shù)據(jù)庫配置信息。首先在1處指定了Quartz數(shù)據(jù)庫表的前綴尚困,然后在2處定義了一個(gè)數(shù)據(jù)源蠢箩,然后在3處定義這個(gè)數(shù)據(jù)源的連接信息

用戶必須事先在相應(yīng)的數(shù)據(jù)庫中建立Quartz的數(shù)據(jù)表(共8張),在Quartz的完整發(fā)布包的docs/dbTables目錄下?lián)碛袑?duì)應(yīng)不同數(shù)據(jù)庫的SQL腳本

2.5.2.查詢數(shù)據(jù)庫中的運(yùn)行信息
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.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.tablePrefix = QRTZ_<!--配置數(shù)據(jù)庫表前綴-->

org.quartz.jobStore.dataSource = qzDS<!--定義數(shù)據(jù)源名稱-->
<!--配置持久化的數(shù)據(jù)庫-->
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/sampledb
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = 281926
org.quartz.dataSource.qzDS.maxConnections = 30

3.在Spring中使用Quartz

Spring為創(chuàng)建Quartz的Scheduler、Trigger和JobDetail提供了便利的FactoryBean類谬泌,以便能夠在Spring容器中享受注入的好處滔韵。此外,Spring還提供了一些便利工具類掌实,用于直接將Spring中的Bean包裝成合法的任務(wù)陪蜻。Spring進(jìn)一步降低了使用Quartz的難度,能以更具Spring風(fēng)格的方式使用Quartz贱鼻。

3.1.創(chuàng)建JobDetail

用戶可以直接使用Quartz的JobDetail在Spring中配置一個(gè)JobDetail Bean囱皿,但是JobDetail使用帶參的構(gòu)造函數(shù),對(duì)于習(xí)慣通過屬性配置的Spring用戶來說存在使用上的不便忱嘹。為此嘱腥,Spring通過擴(kuò)展JobDetail提供了一個(gè)更具有Bean風(fēng)格的JobDetailFactoryBean。此外拘悦,Spring還提供了一個(gè)MethodInvokingJobDetailFactoryBean齿兔,通過這個(gè)FactoryBean可以將Spring容器中Bean的方法包裝成Quartz任務(wù),這樣開發(fā)者就不必為Job創(chuàng)建對(duì)應(yīng)的類础米。

3.1.1.JobDetailFactoryBean

JobDetailFactoryBean擴(kuò)展于Quartz的JobDetail分苇。使用該Bean聲明JobDetail時(shí),Bean的名字即任務(wù)的名字屁桑,如果沒有指定所屬組医寿,就是用默認(rèn)組。除了JobDetail中的屬性外蘑斧,還定義了以下屬性

  • jobClass:類型為Class靖秩,實(shí)現(xiàn)Job接口的任務(wù)類
  • beanName:默認(rèn)為Bean的id名,通過該屬性顯示指定Bean名稱竖瘾,它對(duì)應(yīng)任務(wù)的名稱
  • jobDataAsMap:類型為Map沟突,為任務(wù)所對(duì)應(yīng)的JobDataMap提供值。之所以需要提供這個(gè)屬性捕传,是因?yàn)橛脩魺o法在Spring的配置文件中為JobDataMap類型的屬性提供信息惠拭,所以Spring通過jobDataAsMap設(shè)置JobDataMap的值
  • applicationContextJobDataKey:用戶可以將Spring ApplicationContext的引用保存
    JobDataMap中,以便在Job的代碼中訪問ApplicationContext.為了達(dá)到這個(gè)目的庸论,用戶需要指定一個(gè)鍵职辅,用于在jobDataAsMap中保存ApplicationContext。如果不設(shè)置此鍵聂示,JobDetailBean就不會(huì)將ApplicationContext放入JobDataMap中
  • jobListenerNames:類型為String[],指定注冊(cè)在Scheduler中的JobListener名稱域携,以便讓這些監(jiān)聽器對(duì)本任務(wù)的時(shí)間進(jìn)行監(jiān)聽

配置JobDetail

<bean name="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"
        p:jobClass="com.smart.quartz.MyJob"
        p:applicationContextJobDataKey="applicationContext">
        <property name="jobDataAsMap">
            <map>
                <entry key="size" value="10" />
            </map>
        </property>
</bean>

public class MyJob implements StatefulJob {
    public void execute(JobExecutionContext jctx) throws JobExecutionException {
//      Map dataMap = jctx.getJobDetail().getJobDataMap();
        Map dataMap = jctx.getTrigger().getJobDataMap();//獲取JobDetail關(guān)聯(lián)的JobDataMap
        String size =(String)dataMap.get("size");
        ApplicationContext ctx = (ApplicationContext)dataMap.get("applicationContext");
        System.out.println("size:"+size);
        dataMap.put("size",size+"0");//對(duì)JobDataMap所做的更改是否會(huì)被持久化取決于任務(wù)的類型
        
        String count =(String)dataMap.get("count");
        System.out.println("count:"+count);
    }
}


3.1.2.MethodInvokingJobDetailFactoryBean

通常情況下,任務(wù)都定義在一個(gè)業(yè)務(wù)類方法中催什,這時(shí)涵亏,為了滿足Quartz Job接口的規(guī)
定宰睡,還需要定義一個(gè)引用業(yè)務(wù)類方法的實(shí)現(xiàn)類。為了避免創(chuàng)建這個(gè)只包含一行調(diào)用代碼的Job實(shí)現(xiàn)類气筋,Spring提供了MethodInvokingJobDetailFactoryBean拆内,借由該FactoryBean,可以將一個(gè)Bean的某個(gè)方法封裝成滿足Quartz要求的Job

<!-- 通過封裝服務(wù)類方法實(shí)現(xiàn) -->
    <bean id="jobDetail_1"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
        p:targetObject-ref="myService" p:targetMethod="doJob" p:concurrent="false" />

    <bean id="myService" class="com.smart.service.MyService" />
    
    
public class MyService {
      public void doJob(){
       System.out.println("in MyService.dojob().");
    }
}

doJob()方法既可以是static的宠默,也可以非static的麸恍,但不能擁有方法入?yún)ⅰMㄟ^
MethodInvokingJobDetailFactoryBean產(chǎn)生的JobDeatail不能序列化搀矫,所以不能被持久化到數(shù)據(jù)庫中抹沪。如果希望使用持久化任務(wù),則只能創(chuàng)建正規(guī)的Quartz的Job實(shí)現(xiàn)類

3.2.創(chuàng)建Trigger
3.2.1.SimpleTriggerFactoryBean

在默認(rèn)情況下瓤球,通過SimpleTriggerFactoryBean配置的Trigger名稱即為Bean的名稱融欧,屬于默認(rèn)數(shù)組。SimpleTriggerFactoryBean在SimpleTrigger的基礎(chǔ)上新增了以下屬性

  • jobDetail:對(duì)應(yīng)的JobDetail
  • beanName:默認(rèn)為Bean的id名卦羡,通過該屬性顯示指定Bean名稱噪馏,它對(duì)應(yīng)Trigger的名稱
  • jobDataAsMap:以Map類型為Trigger關(guān)聯(lián)的JobDataMap提供值
  • startDelay:延遲多少時(shí)間開始觸發(fā),單位為毫秒绿饵,默認(rèn)值為0
  • triggerListenerNames:類型為String[],指定注冊(cè)在Scheduler中的TriggerListener名稱欠肾,以便讓這些監(jiān)聽器對(duì)本觸發(fā)器的事件進(jìn)行監(jiān)聽
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
        p:jobDetail-ref="jobDetail" p:startDelay="1000" p:repeatInterval="2000"
        p:repeatCount="100">
        <property name="jobDataAsMap">
            <map>
                <entry key="count" value="10" />
            </map>
        </property>
    </bean>
3.2.2.CronTriggerFactoryBean

CronTriggerFactoryBean擴(kuò)展于CronTrigger,觸發(fā)器的名稱即為Bean的名稱拟赊,保存在默認(rèn)組中刺桃。在CronTrigger的基礎(chǔ)上,新增的屬性和SimpleTriggerFactoryBean大致相同吸祟,配置的方法也和SimpleTriggerFactoryBean相似

<bean id="checkImagesTrigger" 
          class="org.springframework.scheduling.quartz.CronTriggerBean"
          p:jobDetail-ref="jobDetail"
          p:cronExpression="0/5 * * * * ?"/>

3.3.創(chuàng)建Scheduler

Quartz的SchedulerFactory是標(biāo)準(zhǔn)的工廠類瑟慈,不太適合在Spring環(huán)境下使用。此外欢搜,為了保證Scheduler能夠感知Spring容器的生命周期封豪,在Spring容器啟動(dòng)后谴轮,Scheduler自動(dòng)開始工作炒瘟,而在Spring容器關(guān)閉前,自動(dòng)關(guān)閉Scheduler第步。為此疮装,Spring提供了SchedulerFactoryBean,這個(gè)FactoryBean大致?lián)碛幸韵鹿δ?/p>

  • 以更具有Bean風(fēng)格的方式為Scheduler提供配置信息
  • 讓Scheduler和Spring容器的生命周期建立關(guān)聯(lián)粘都,相生相息
  • 通過屬性配置的方式代替Quartz自身的配置文件
<bean id="scheduler"
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="simpleTrigger" />
            </list>
        </property>
        <property name="schedulerContextAsMap">
            <map>
                <entry key="timeout" value="30" />
            </map>
        </property>
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.class">
                    org.quartz.simpl.SimpleThreadPool
                </prop>
                <prop key="org.quartz.threadPool.threadCount">10</prop>
            </props>
        </property>
    </bean>

在實(shí)際應(yīng)用中廓推,我們并不總是在程序部署的時(shí)候就確定需要哪些任務(wù),往往需要在運(yùn)行期根據(jù)業(yè)務(wù)數(shù)據(jù)動(dòng)態(tài)產(chǎn)生觸發(fā)器和任務(wù)翩隧。用戶完全可以在運(yùn)行時(shí)通過代碼調(diào)用SchedulerFactoryBean獲取Scheduler實(shí)例樊展,然后動(dòng)態(tài)注冊(cè)觸發(fā)器和任務(wù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市专缠,隨后出現(xiàn)的幾起案子雷酪,更是在濱河造成了極大的恐慌,老刑警劉巖涝婉,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哥力,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡墩弯,警方通過查閱死者的電腦和手機(jī)吩跋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渔工,“玉大人锌钮,你說我怎么就攤上這事∫兀” “怎么了轧粟?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)脓魏。 經(jīng)常有香客問我兰吟,道長(zhǎng),這世上最難降的妖魔是什么茂翔? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任混蔼,我火速辦了婚禮,結(jié)果婚禮上珊燎,老公的妹妹穿的比我還像新娘惭嚣。我一直安慰自己,他們只是感情好悔政,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布晚吞。 她就那樣靜靜地躺著,像睡著了一般谋国。 火紅的嫁衣襯著肌膚如雪槽地。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天芦瘾,我揣著相機(jī)與錄音捌蚊,去河邊找鬼。 笑死近弟,一個(gè)胖子當(dāng)著我的面吹牛缅糟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祷愉,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼窗宦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赦颇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赴涵,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤沐扳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后句占,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沪摄,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年纱烘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杨拐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡擂啥,死狀恐怖哄陶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哺壶,我是刑警寧澤屋吨,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站山宾,受9級(jí)特大地震影響择卦,放射性物質(zhì)發(fā)生泄漏侦镇。R本人自食惡果不足惜霹期,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一寡喝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绷杜,春花似錦直秆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至齿诉,卻和暖如春筝野,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹃两。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工遗座, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俊扳。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像猛遍,于是被迫代替她去往敵國(guó)和親馋记。 傳聞我的和親對(duì)象是個(gè)殘疾皇子号坡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • scheduler定時(shí)調(diào)度系統(tǒng)是大多行業(yè)項(xiàng)目都需要的,傳統(tǒng)的spring-job模式梯醒,個(gè)人感覺已經(jīng)out了宽堆,因?yàn)榇?..
    安琪拉_4b7e閱讀 2,825評(píng)論 4 6
  • 概述 了解Quartz體系結(jié)構(gòu) Quartz對(duì)任務(wù)調(diào)度的領(lǐng)域問題進(jìn)行了高度的抽象,提出了調(diào)度器茸习、任務(wù)和觸發(fā)器這3個(gè)...
    張晨輝Allen閱讀 2,218評(píng)論 2 11
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理畜隶,服務(wù)發(fā)現(xiàn),斷路器号胚,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • Quartz是一個(gè)完全由java編寫的功能豐富的開源作業(yè)調(diào)度庫籽慢,可以集成到幾乎任何Java應(yīng)用程序中,小到獨(dú)立應(yīng)用...
    ProteanBear閱讀 7,008評(píng)論 3 24
  • 就事論事猫胁,就是游泳箱亿,不是特別有信心,現(xiàn)在有點(diǎn)卡在了轉(zhuǎn)肩動(dòng)作弃秆,第四節(jié)上教練課届惋,夾板只用胳膊,動(dòng)作灰常不規(guī)范菠赚,只能說沒...
    瑞恩出本書閱讀 369評(píng)論 0 0