1. 程式人生 > >初識quartz 並分析 項目中spring整合quartz的配置【原創+轉載】

初識quartz 並分析 項目中spring整合quartz的配置【原創+轉載】

text lca 目的 並發 目錄結構 public 項目 功能 基於

初識quartz 並分析 項目中spring整合quartz的配置【原創+轉載】
2018年01月29日 12:08:07 守望dfdfdf 閱讀數:114 標簽: quartz 更多
個人分類: 工具 軟件
編輯
版權聲明:本文為博主原創文章,轉載請註明文章鏈接。 https://blog.csdn.net/xiaoanzi123/article/details/79173113
之前接觸一個小demo項目,當時忙於需求開發而只關註功能,沒有註意配置,今天就拾起來分析一下,順便接觸新內容。
首先,關於quartz,之前沒有接觸過,近期在不同的項目模塊中均有所看到這個quartz,那就必須學習了解下了。
Quartz是一個任務調度框架。比如你遇到這樣的問題,想每月6號,自動給老媽發一封郵件,想每隔1分鐘查詢下有沒有新的短消息等等。這些問題可以概括為在某個有規律的時間點幹指定的事情。這個時間點需要一個觸發動作,觸發的條件由你來 配置定義,Quartz只負責到點了就開幹。

創建你自己的類,實現org.quartz.Job接口。Job接口包含唯一的方法:
public voidexecute(JobExecutionContext context)throws JobExecutionException;
添加一些邏輯到execute()方法。一旦你配置好Job實現類並設定好調度時間表,Quartz將密切註意剩余時間。當調度程序確定該是通知你的作業的時候,Quartz框架將調用你Job實現類(作業類)上的execute()方法並允許做它該做的事情。無需報告任
何東西給調度器或調用任何特定的東西。僅僅執行任務和結束任務即可。如果配置你的作業在隨後再次被調用,Quartz框架將在

恰當的時間再次調用它。

------------------以下內容摘錄轉載自-- https://www.cnblogs.com/yqw1994/p/6599562.html --------------------
------------------部分轉載,更詳細的quarz知識體系內容請 參照原文 ------------------------------
一個簡單的示例
這裏面的所有例子都是基於Quartz 2.2.1

package com.test.quartz;
import static org.quartz.DateBuilder.newDate;
import static
org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import java.util.GregorianCalendar; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.calendar.AnnualCalendar; public class QuartzTest { public static void main(String[] args) { try { //創建scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //定義一個Trigger Trigger trigger = newTrigger().withIdentity("trigger1", "group1") //定義name/group .startNow()//一旦加入scheduler,立即生效 .withSchedule(simpleSchedule() //使用SimpleTrigger .withIntervalInSeconds(1) //每隔一秒執行一次 .repeatForever()) //一直執行,奔騰到老不停歇 .build(); //定義一個JobDetail JobDetail job = newJob(HelloQuartz.class) //定義Job類為HelloQuartz類,這是真正的執行邏輯所在 .withIdentity("job1", "group1") //定義name/group .usingJobData("name", "quartz") //定義屬性 .build(); //加入這個調度 scheduler.scheduleJob(job, trigger); //啟動之 scheduler.start(); //運行一段時間後關閉 Thread.sleep(10000); scheduler.shutdown(true); } catch (Exception e) { e.printStackTrace(); } } } package com.test.quartz; import java.util.Date; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloQuartz implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); String name = detail.getJobDataMap().getString("name"); System.out.println("say hello to " + name + " at " + new Date()); } } JobDetail & Job JobDetail是任務的定義,而Job是任務的執行邏輯。在JobDetail裏會引用一個Job Class定義。一個最簡單的例子 public class JobTest { public static void main(String[] args) throws SchedulerException, IOException { JobDetail job=newJob() .ofType(DoNothingJob.class) //引用Job Class .withIdentity("job1", "group1") //設置name/group .withDescription("this is a test job") //設置描述 .usingJobData("age", 18) //加入屬性到ageJobDataMap .build(); job.getJobDataMap().put("name", "quertz"); //加入屬性name到JobDataMap //定義一個每秒執行一次的SimpleTrigger Trigger trigger=newTrigger() .startNow() .withIdentity("trigger1") .withSchedule(simpleSchedule() .withIntervalInSeconds(1) .repeatForever()) .build(); Scheduler sche=StdSchedulerFactory.getDefaultScheduler(); sche.scheduleJob(job, trigger); sche.start(); System.in.read(); sche.shutdown(); } } public class DoNothingJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("do nothing"); } }


這個例子很好的覆蓋了Quartz最重要的3個基本要素:【這裏是最重要的三點!!!】
Scheduler:調度器。所有的調度都是由它控制。
Trigger: 定義觸發的條件。例子中,它的類型是SimpleTrigger,每隔1秒中執行一次(什麽是SimpleTrigger下面會有詳述)。
JobDetail&Job: JobDetail 定義的是任務數據,而真正的執行邏輯是在Job中,例子中是HelloQuartz。為什麽設計成JobDetail + Job,不直接使用Job?這是因為任務是有可能並發執行,如果Scheduler直接使用Job,就會存在對同一個Job實例並發訪問的問題。而JobDetail & Job 方式,sheduler每次執行,都會根據JobDetail創建一個新的Job實例,這樣就可以規避並發訪問的問題。
從上例我們可以看出出,要定義一個任務,需要幹幾件事:
創建一個org.quartz.Job的實現類,並實現實現自己的業務邏輯。比如上面的DoNothingJob。
定義一個JobDetail,引用這個實現類
加入scheduleJob進行調度
Quartz調度一次任務,會幹如下的事:
JobClass jobClass=JobDetail.getJobClass()
Job jobInstance=jobClass.newInstance()。

所以Job實現類,必須有一個public的無參構建方法。
jobInstance.execute(JobExecutionContext context)。JobExecutionContext是Job運行的上下文,可以獲得Trigger、Scheduler、JobDetail的信息。
也就是說,每次調度都會創建一個新的Job實例,這樣的好處是有些任務並發執行的時候,不存在對臨界資源的訪問問題——當然,如果需要共享JobDataMap的時候,還是存在臨界資源的並發訪問的問題。
JobDataMap
Job都次都是newInstance的實例,那我怎麽傳值給它? 比如我現在有兩個發送郵件的任務,一個是發給"liLei",一個發給"hanmeimei",不能說我要寫兩個Job實現類LiLeiSendEmailJob和HanMeiMeiSendEmailJob。實現的辦法是通過JobDataMap。
每一個JobDetail都會有一個JobDataMap。JobDataMap本質就是一個Map的擴展類,只是提供了一些更便捷的方法,比如getString()之類的。
我們可以在定義JobDetail,加入屬性值,方式有二:
newJob().usingJobData("age", 18) //加入屬性到ageJobDataMap

or

job.getJobDataMap().put("name", "quertz"); //加入屬性name到JobDataMap

然後在Job中可以獲取這個JobDataMap的值,方式同樣有二:

public class HelloQuartz implements Job {
    private String name;

    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail detail = context.getJobDetail();
        JobDataMap map = detail.getJobDataMap(); //方法一:獲得JobDataMap
        System.out.println("say hello to " + name + "[" + map.getInt("age") + "]" + " at "
                           + new Date());
    }

    //方法二:屬性的setter方法,會將JobDataMap的屬性自動註入
    public void setName(String name) { 
        this.name = name;
    }
}

對於同一個JobDetail實例,執行的多個Job實例,是共享同樣的JobDataMap,也就是說,如果你在任務裏修改了裏面的值,會對其他Job實例(並發的或者後續的)造成影響。
除了JobDetail,Trigger同樣有一個JobDataMap,共享範圍是所有使用這個Trigger的Job實例。
Scheduler
Scheduler就是Quartz的大腦,所有任務都是由它來設施。
Schduelr包含一個兩個重要組件: JobStore和ThreadPool。
JobStore是會來存儲運行時信息的,包括Trigger,Schduler,JobDetail,業務鎖等。它有多種實現RAMJob(內存實現),JobStoreTX(JDBC,事務由Quartz管理),JobStoreCMT(JDBC,使用容器事務),ClusteredJobStore(集群實現)、TerracottaJobStore(什麽是Terractta)。
ThreadPool就是線程池,Quartz有自己的線程池實現。所有任務的都會由線程池執行。
SchedulerFactory
SchdulerFactory,顧名思義就是來用創建Schduler了,有兩個實現:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用來在代碼裏定制你自己的Schduler參數。後者是直接讀取classpath下的quartz.properties(不存在就都使用默認值)配置來實例化Schduler。通常來講,我們使用StdSchdulerFactory也就足夠了。
SchdulerFactory本身是支持創建RMI stub的,可以用來管理遠程的Scheduler,功能與本地一樣,可以遠程提交個Job什麽的。

----------------------------------------------------------------------- 轉載引用結束線- --------------------------- -----------------------------------------------

記住三個步驟: 參考文章spring和quartz結合配置:【很好的文章,條理清晰:http://blog.csdn.net/u010648555/article/details/54891264】
1:定義工作任務的Job
2:定義觸發器Trigger,並將觸發器與工作任務綁定
3:定義調度器,並將Trigger註冊到Scheduler

通過上述原文作者的詳細描述,對quarz有了較為直觀的認識,接下來就算去分析下目前的這個項目demo的配置文件。
目錄結構如圖:主要目的就是分析配置文件的配置執行流程。

技術分享圖片


現在看來這個demo是quartz和spring進行結合通過配置文件來實現上述總結的三個步驟,這就好理解了。
先看demoJob.xml文件:

<beans>
 
    <!-- quartz與spring整合  第二步  定義觸發器的bean,定義一個Cron的Trigger,一個觸發器只能和一個任務進行綁定 --> 
    <bean id="demoJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
         <!-- 指定Tirgger綁定的Job -->
        <property name="jobDetail" ref="demoJob_jobDetail" />
         <!-- 指定Cron 的表達式 ,每隔多久運行多少次 -->
        <property name="cronExpression" value="10/59 * * * * ?" />
    </bean>
    
    
    <!--quartz與spring整合  第一步  : 定義任務的bean(工作任務的job) -->
    <bean id="demoJob_jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject">
             <bean class="com.xxxx.zhejiang.hangzhou.xxxx.tasks.DemoJob"></bean>
        </property>
        <property name="targetMethod" value="doJob" />
        <property name="concurrent" value="false" />
    </bean>
 
</beans>

可以看出前兩步在這裏都已經完成了,定義job,然後job連接到觸發器trigger裏面。然後走到下面的trigger.xml

<beans>
    <bean id="jobList" class="java.util.ArrayList">
        <constructor-arg>
            <list>
                <ref bean="demoJobTrigger" />
            </list>
        </constructor-arg>
    </bean>
</beans>

然後接下來就是第三步,把已經連接好job的trigger 連接到 調度器schedule裏面,接下來quartz.xml

<beans>
 

    <!-- quartz與spring整合  第三步.定義調度器,並將Trigger註冊到調度器中 --> -->
    <!-- 總管理類 如果將lazy-init=‘false‘那麽容器啟動就會執行調度程序 -->
    <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers" ref="jobList" />
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
                <prop key="org.quartz.threadPool.threadCount">50</prop>
                <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
                <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
            </props>
        </property> 
        <property name="globalJobListeners">
            <list>
                <ref bean="jobListeners" />
            </list>
        </property>
    </bean>
    <bean id="jobListeners" class="com.xxxx.job.TaskListerner" />
 
</beans>

三個步驟之間的連接過程我在代碼中已經用顏色來進行區分。以後 com.xxxx.zhejiang.hangzhou.xxxx.tasks.DemoJob 中的doJob方法就會按照設定好的定時任務有規律的執行。

初識quartz 並分析 項目中spring整合quartz的配置【原創+轉載】