posts - 7,comments - 5,trackbacks - 0

              概述
              各種企業應用幾乎都會碰到任務調度的需求,就拿論壇來說:每隔半個小時生成精華文章的RSS文件,每天凌晨統計論壇用戶的積分排名,每隔30分鐘執行鎖定用戶解鎖任務。對于一個典型的MIS系統來說,在每月1號凌晨統計上個月各部門的業務數據生成月報表,每半個小時查詢用戶是否已經有快到期的待處理業務…… 

              Quartz 是開源任務調度框架中的翹首,它提供了強大任務調度機制,同時保持了使用的簡單性。Quartz 允許開發人員靈活地定義觸發器的調度時間表,并可以對觸發器和任務進行關聯映射。此外,Quartz提供了調度運行環境的持久化機制,可以保存并恢復調度現場,即使系統因故障關閉,任務調度現場數據并不會丟失。此外,Quartz還提供了組件式的偵聽器、各種插件、線程池等功能。

              Spring為創建Quartz的Scheduler、Trigger和JobDetail提供了便利的FactoryBean類,以便能夠在Spring 容器中享受注入的好處。此外Spring還提供了一些便利工具類直接將Spring中的Bean包裝成合法的任務。Spring進一步降低了使用Quartz的難度,能以更具Spring風格的方式使用Quartz。概括來說它提供了兩方面的支持: 

          1)為Quartz的重要組件類提供更具Bean風格的擴展類; 
          2)提供創建Scheduler的BeanFactory類,方便在Spring環境下創建對應的組件對象,并結合Spring容器生命周期進行啟動和停止的動作。

           
              創建JobDetail 
              可以直接使用Quartz的JobDetail在Spring中配置一個JobDetail Bean,但是JobDetail使用帶參的構造函數,對于習慣通過屬性配置的Spring用戶來說存在使用上的不便。為此Spring通過擴展JobDetail提供了一個更具Bean風格的JobDetailBean。此外,Spring提供了一個MethodInvokingJobDetailFactoryBean,通過這個FactoryBean可以將Spring容器中Bean的方法包裝成Quartz任務,這樣開發者就不必為Job創建對應的類。
              JobDetailBean
              JobDetailBean擴展于Quartz的JobDetail。使用該Bean聲明JobDetail時,Bean的名字即是任務的名字,如果沒有指定所屬組,即使用默認組。除了JobDetail中的屬性外,還定義了以下屬性:
              ● jobClass:類型為Class,實現Job接口的任務類;
              ● beanName:默認為Bean的id名,通過該屬性顯式指定Bean名稱,對應任務的名稱;
              ● jobDataAsMap:類型為Map,為任務所對應的JobDataMap提供值。之所以需要提供這個屬性,是因為除非你手工注冊一 個編輯器,你不能直接配置JobDataMap類型的值,所以Spring通過jobDataAsMap設置JobDataMap的值;
              ●applicationContextJobDataKey:你可以將Spring ApplicationContext的引用保存到JobDataMap中,以便在Job的代碼中訪問ApplicationContext。為了達到這個目的,你需要指定一個鍵,用以在jobDataAsMap中保存ApplicationContext,如果不設置此鍵,JobDetailBean就不將ApplicationContext放入到JobDataMap中;
              ●jobListenerNames:類型為String[],指定注冊在Scheduler中的JobListeners名稱,以便讓這些監聽器對本任務的事件進行監聽。
           下面配置片斷使用JobDetailBean在Spring中配置一個JobDetail:

          <bean name="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
              <property name="jobClass" value="com.baobaotao.quartz.MyJob" />
              <property name="jobDataAsMap">
                  <map>
                      <entry key="size" value="10" />
                  </map>
              </property>
              <property name="applicationContextJobDataKey" value="applicationContext"/>
          </bean>
             JobDetailBean封裝了MyJob任務類,并為Job對應JobDataMap設置了一個size的數據。此外,通過指定applicationContextJobDataKey讓Job的JobDataMap持有Spring ApplicationContext的引用。這樣,MyJob在運行時就可以通過JobDataMap訪問到size和ApplicationContext了。來看一下MyJob的代碼: 
            MyJob
          import org.quartz.Job;
          import org.quartz.JobExecutionContext;
          import org.quartz.JobExecutionException;
          import org.springframework.context.ApplicationContext;
          public class MyJob implements Job {
          public void execute(JobExecutionContext jctx) throws JobExecutionException {
          Map dataMap 
          = jctx.getJobDetail().getJobDataMap();  ①獲取JobDetail關聯的JobDataMap
          String size 
          =(String)dataMap.get("size");  ②
          ApplicationContext ctx 
          = (ApplicationContext)dataMap.get("applicationContext");  ③
          System.out.println(
          "size:"+size);
          dataMap.put(
          "size",size+"0");  ④對JobDataMap所做的更改是否被會持久,取決于任務的類型
          //do sth 
          }
          }

              在②處獲取size值,在③處還可以根據鍵“applicationContext”獲取ApplicationContext,有了ApplicationContext的引用,Job就可以毫無障礙訪問Spring容器中的任何Bean了。MyJob可以在execute()方法中對JobDataMap進行更改,如④所示。如果MyJob實現Job接口,這個更改對于下一次執行是不可見的,如果MyJob實現StatefulJob接口,這種更改對下一次執行是可見的。

          MethodInvokingJobDetailFactoryBean
              通常情況下,任務都定義在一個業務類方法中。這時,為了滿足Quartz Job接口的規定,還需要定義一個引用業務類方法的實現類。為了避免創建這個只包含一行調用代碼的Job實現類,Spring為我們提供了MethodInvokingJobDetailFactoryBean,借由該FactoryBean,我們可以將一個Bean的某個方法封裝成滿足Quartz要求的Job。來看一個具體的例子:

          <bean id="jobDetail_1" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
              <property name="targetObject" ref="myService" />   ① 引用一個Bean
              <property name="targetMethod" value="doJob" />   ② 指定目標Bean的方法
              <property name="concurrent" value="false" />   ③ 指定最終封裝出的任務是否有狀態

          <bean id="myService" class="com.baobaotao.service.MyService"/>

              jobDetail_1將MyService#doJob()封裝成一個任務,同時通過concurrent屬性指定任務的類型,默認情況下封裝為無狀態的任務,如果希望目標封裝為有狀態的任務,僅需要將concurrent設置為false就可以了。Spring通過名為concurrent的屬性指定任務的類型,能夠更直接地描述到任務執行的方式(有狀態的任務不能并發執行,無狀態的任務可并發執行)。
              MyService服務類擁有一個doJob()方法,它的代碼如下所示:

          public class MyService {
              public void doJob(){①被封裝成任務的目標方法
                  System.out.println(
          "in MyService.dojob().");
              }
          }

              doJob()方法即可以是static也可以是非static的,但不能擁有方法入參。通過MethodInvokingJobDetailFactoryBean產生的JobDetail不能被序列化,所以不能被持久化到數據庫中的,如果希望使用持久化任務,則你只能創建正規的Quartz的Job實現類了。

              創建Trigger
              Quartz中另一個重要的組件就是Trigger,Spring按照相似的思路分別為SimpleTrigger和CronTrigger提供了更具Bean風格的SimpleTriggerBeanCronTriggerBean擴展類,通過這兩個擴展類更容易在Spring中以Bean的方式配置Trigger。

              SimpleTriggerBean
              默認情況下,通過SimpleTriggerBean配置的Trigger名字即為Bean的名字,并屬于默認組Trigger組。SimpleTriggerBean在SimpleTrigger的基礎上,新增了以下屬性:
              ● jobDetail:對應的JobDetail;
              ● beanName:默認為Bean的id名,通過該屬性顯式指定Bean名稱,它對應Trigger的名稱;
              ● jobDataAsMap:以Map類型為Trigger關聯的JobDataMap提供值;
              ● startDelay:延遲多少時間開始觸發,單位為毫秒,默認為0;
              ● triggerListenerNames:類型為String[],指定注冊在Scheduler中的TriggerListener名稱,以便讓這些監聽器對本觸發器的事件進行監聽。
              下面的實例使用SimpleTriggerBean定義了一個Trigger,該Trigger和jobDetail相關聯,延遲10秒后啟動,時間間隔為20秒,重復執行100次。此外,我們還為Trigger設置了JobDataMap數據:

          <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
              <property name="jobDetail" ref="jobDetail" />
              <property name="startDelay" value="1000" />
              <property name="repeatInterval" value="2000" />
              <property name="repeatCount" value="100" />
              <property name="jobDataAsMap"> ①
                  <map>
                      <entry key="count" value="10" />
                  </map>
              </property>
          </bean>

             需要特別注意的是,①處配置的JobDataMap是Trigger的JobDataMap,任務執行時必須通過以下方式獲取配置的值:

          public class MyJob implements StatefulJob {
              public void execute(JobExecutionContext jctx) throws JobExecutionException {
                  Map dataMap 
          = jctx.getTrigger().getJobDataMap();  ①獲取Trigger的JobDataMap
                  String count 
          = dataMap.get("count");
                  dataMap.put("count","
          30");   ② 對JobDataMap的更改不會被持久,不影響下次的執行
                  …
              }
          }

              CronTriggerBean
              CronTriggerBean擴展于CronTrigger,觸發器的名字即為Bean的名字,保存在默認組中。在CronTrigger的基礎上,新增的屬性和SimpleTriggerBean大致相同,配置的方法也和SimpleTriggerBean相似,下面給出一個簡單的例子:

          <bean id="checkImagesTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
              <property name="jobDetail" ref="jobDetail "/>
              <property name="cronExpression" value="0/5 * * * * ?"/>
          </bean>
             
               創建Scheduler 
              Quartz的SchedulerFactory是標準的工廠類,不太適合在Spring環境下使用。此外,為了保證Scheduler能夠感知Spring容器的生命周期,完成自動啟動和關閉的操作,必須讓Scheduler和Spring容器的生命周期相關聯。以便在Spring容器啟動后,Scheduler自動開始工作,而在Spring容器關閉前,自動關閉Scheduler。為此,Spring提供SchedulerFactoryBean,這個FactoryBean大致擁有以下的功能: 
          1)以更具Bean風格的方式為Scheduler提供配置信息; 
          2)讓Scheduler和Spring容器的生命周期建立關聯,相生相息; 
          3)通過屬性配置部分或全部代替Quartz自身的配置文件。 
              來看一個SchedulerFactoryBean配置的例子: 
              SchedulerFactoryBean配置
          <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
              <property name="triggers"> ①注冊多個Trigger
                  <list>
                      <ref bean="simpleTrigger" />
                  </list>
              </property>
              <property name="schedulerContextAsMap"②以Map類型設置SchedulerContext數據
                  <map>
                      <entry key="timeout" value="30" />
                  </map>
              </property>
             ③顯式指定Quartz的配置文件地址
             <property name="configLocation" value="classpath:com/baobaotao/quartz/quartz.properties" /> 
          </bean>

              SchedulerFactoryBean的triggers屬性為Trigger[]類型,可以通過該屬性注冊多個Trigger,在①處,我們注冊了一個Trigger。Scheduler擁有一個類似于ServletContext的SchedulerContext。SchedulerFactoryBean允許你以Map的形式設置SchedulerContext的參數值,如②所示。默認情況下,Quartz在類路徑下查詢quartz.properties配置文件,你也可以通過configLocation屬性顯式指定配置文件位置,如③所示。
           
              除了實例中所用的屬性外,SchedulerFactoryBean還擁有一些常見的屬性:
              ●calendars:類型為Map,通過該屬性向Scheduler注冊Calendar;
              ●jobDetails:類型為JobDetail[],通過該屬性向Scheduler注冊JobDetail;
              ●autoStartup:SchedulerFactoryBean在初始化后是否馬上啟動Scheduler,默認為true。如果設置為false,需要手工啟動Scheduler;
              ●startupDelay:在SchedulerFactoryBean初始化完成后,延遲多少秒啟動Scheduler,默認為0,表示馬上啟動。如果并非馬上擁有需要執行的任務,可通過startupDelay屬性讓Scheduler延遲一小段時間后啟動,以便讓Spring能夠更快初始化容器中剩余的Bean;

              SchedulerFactoryBean的一個重要功能是允許你將Quartz配置文件中的信息轉移到Spring配置文件中,帶來的好處是,配置信息的集中化管理,同時我們不必熟悉多種框架的配置文件結構。回憶一個Spring集成JPA、Hibernate框架,就知道這是Spring在集成第三方框架經常采用的招數之一。SchedulerFactoryBean通過以下屬性代替框架的自身配置文件:
              ●dataSource:當需要使用數據庫來持久化任務調度數據時,你可以在Quartz中配置數據源,也可以直接在Spring中通過dataSource指定一個Spring管理的數據源。如果指定了該屬性,即使quartz.properties中已經定義了數據源,也會被此dataSource覆蓋;
              ●transactionManager:可以通過該屬性設置一個Spring事務管理器。在設置dataSource時,Spring強烈推薦你使用一個事務管理器,否則數據表鎖定可能不能正常工作;
              ●nonTransactionalDataSource:在全局事務的情況下,如果你不希望Scheduler執行化數據操作參與到全局事務中,則可以通過該屬性指定數據源。在Spring本地事務的情況下,使用dataSource屬性就足夠了;
              ●quartzProperties:類型為Properties,允許你在Spring中定義Quartz的屬性。其值將覆蓋quartz.properties配置文件中的設置,這些屬性必須是Quartz能夠識別的合法屬性,在配置時,你可以需要查看Quartz的相關文檔。下面是一個配置quartzProperties屬性的例子:

          <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
              …
              <property name="quartzProperties">
                  <props>
                      <prop key="org.quartz.threadPool.class"①Quartz屬性項1
                          org.quartz.simpl.SimpleThreadPool
                      </prop>
                      <prop key="org.quartz.threadPool.threadCount">10</prop①Quartz屬性項2
                  </props>
              </property>
          </bean>

             在實際應用中,我們并不總是在程序部署的時候就可能確定需要哪些任務,往往需要在運行期根據業務數據動態產生觸發器和任務。你完全可以在運行期通過代碼調用SchedulerFactoryBean獲取Scheduler實例,進行動態的任務注冊和調度。

              小結
              Spring為Quartz的JobDetail和Trigger提供了更具Bean風格的支持類,這使我們能夠更地方便地在Spring中通過配置定制這些組件實例。Spring的SchedulerFactoryBean讓我們可以脫離Quartz自身的配置體系,而以更具Spring風格的方式定義Scheduler。此外,還可以享受Scheduler生命周期和Spring 容器生命周期綁定的好處。

           

          posted on 2009-03-25 18:07 心路 閱讀(10684) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 德阳市| 英吉沙县| 汉阴县| 定远县| 黑龙江省| 盐山县| 龙南县| 岗巴县| 武定县| 宁远县| 松阳县| 新安县| 石渠县| 台中市| 年辖:市辖区| 庆元县| 五家渠市| 曲麻莱县| 桑植县| 阿拉善右旗| 榆社县| 南澳县| 呼玛县| 邓州市| 宜君县| 九江市| 南川市| 泾源县| 大同县| 广昌县| 桂平市| 城步| 怀安县| 砀山县| 同江市| 舟山市| 密山市| 宣武区| 涿州市| 康马县| 田东县|