国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Spring 3整合Quartz 2實(shí)現(xiàn)定時(shí)任務(wù)二:動(dòng)態(tài)添加任務(wù)

前面,我們已經(jīng)對(duì)Spring 3和Quartz 2用配置文件的方式進(jìn)行了整合,如果需求比較簡(jiǎn)單的話應(yīng)該已經(jīng)可以滿足了。但是很多時(shí)候,我們常常會(huì)遇到需要?jiǎng)討B(tài)的添加或修改任務(wù),而spring中所提供的定時(shí)任務(wù)組件卻只能夠通過修改xml中trigger的配置才能控制定時(shí)任務(wù)的時(shí)間以及任務(wù)的啟用或停止,這在帶給我們方便的同時(shí)也失去了動(dòng)態(tài)配置任務(wù)的靈活性。我搜索了一些網(wǎng)上的解決方法,都沒有很好的解決這個(gè)問題,而且大多數(shù)提到的解決方案都停留在Quartz 1.x系列版本上,所用到的代碼和API已經(jīng)不能適用于新版本的Spring和Quartz。沒辦法只能靠自己了,花了點(diǎn)時(shí)間好好研究了一下Spring和Quartz中相關(guān)的代碼。

首先我們來回顧一下spring中使用quartz的配置代碼:

  1. <!-- 使用MethodInvokingJobDetailFactoryBean,任務(wù)類可以不實(shí)現(xiàn)Job接口,通過targetMethod指定調(diào)用方法-->  
  2. <bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>  
  3. <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
  4.     <property name="group" value="job_work"/>  
  5.     <property name="name" value="job_work_name"/>  
  6.     <!--false表示等上一個(gè)任務(wù)執(zhí)行完后再開啟新的任務(wù)-->  
  7.     <property name="concurrent" value="false"/>  
  8.     <property name="targetObject">  
  9.         <ref bean="taskJob"/>  
  10.     </property>  
  11.     <property name="targetMethod">  
  12.         <value>execute</value>  
  13.     </property>  
  14. </bean>  
  15. <!--  調(diào)度觸發(fā)器 -->  
  16. <bean id="myTrigger"  
  17.       class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">  
  18.     <property name="name" value="work_default_name"/>  
  19.     <property name="group" value="work_default"/>  
  20.     <property name="jobDetail">  
  21.         <ref bean="jobDetail" />  
  22.     </property>  
  23.     <property name="cronExpression">  
  24.         <value>0/5 * * * * ?</value>  
  25.     </property>  
  26. </bean>  
  27. <!-- 調(diào)度工廠 -->  
  28. <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  29.     <property name="triggers">  
  30.         <list>  
  31.             <ref bean="myTrigger"/>  
  32.         </list>  
  33.     </property>  
  34. </bean>  

所有的配置都在xml中完成,包括cronExpression表達(dá)式,十分的方便。但是如果我的任務(wù)信息是保存在數(shù)據(jù)庫的,想要?jiǎng)討B(tài)的初始化,而且任務(wù)較多的時(shí)候不是得有一大堆的xml配置?或者說我要修改一下trigger的表達(dá)式,使原來5秒運(yùn)行一次的任務(wù)變成10秒運(yùn)行一次,這時(shí)問題就來了,試過在配置文件中不傳入cronExpression等參數(shù),但是啟動(dòng)時(shí)就報(bào)錯(cuò)了,難道我每次都修改xml文件然后重啟應(yīng)用嗎,這顯然不合適的。最理想的是在與spring整合的同時(shí)又能實(shí)現(xiàn)動(dòng)態(tài)任務(wù)的添加、刪除及修改配置。

我們來看一下spring實(shí)現(xiàn)quartz的方式,先看一下上面配置文件中定義的jobDetail。其實(shí)上面生成的jobDetail并不是我們定義的Bean,因?yàn)樵赒uartz 2.x版本中JobDetail已經(jīng)是一個(gè)接口(當(dāng)然以前的版本也并非直接生成JobDetail):

  1. public interface JobDetail extends Serializable, Cloneable {...}  
Spring是通過將其轉(zhuǎn)換為MethodInvokingJob或StatefulMethodInvokingJob類型來實(shí)現(xiàn)的,這兩個(gè)都是靜態(tài)的內(nèi)部類,MethodInvokingJob類繼承于QuartzJobBean,而StatefulMethodInvokingJob則直接繼承于MethodInvokingJob。 這兩個(gè)類的實(shí)現(xiàn)區(qū)別在于有狀態(tài)和無狀態(tài),對(duì)應(yīng)于quartz的Job和StatefulJob,具體可以查看quartz文檔,這里不再贅述。先來看一下它們實(shí)現(xiàn)的QuartzJobBean的主要代碼:
  1. /** 
  2.  * This implementation applies the passed-in job data map as bean property 
  3.  * values, and delegates to <code>executeInternal</code> afterwards. 
  4.  * @see #executeInternal 
  5.  */  
  6. public final void execute(JobExecutionContext context) throws JobExecutionException {  
  7.     try {  
  8.         // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...  
  9.         Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context);  
  10.         Map mergedJobDataMap = (Map) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context);  
  11.         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  12.         MutablePropertyValues pvs = new MutablePropertyValues();  
  13.         pvs.addPropertyValues(scheduler.getContext());  
  14.         pvs.addPropertyValues(mergedJobDataMap);  
  15.         bw.setPropertyValues(pvs, true);  
  16.     }  
  17.     catch (SchedulerException ex) {  
  18.         throw new JobExecutionException(ex);  
  19.     }  
  20.     executeInternal(context);  
  21. }  
  22. /** 
  23.  * Execute the actual job. The job data map will already have been 
  24.  * applied as bean property values by execute. The contract is 
  25.  * exactly the same as for the standard Quartz execute method. 
  26.  * @see #execute 
  27.  */  
  28. protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;  
還有MethodInvokingJobDetailFactoryBean中的代碼:
  1. public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {  
  2.     prepare();  
  3.     // Use specific name if given, else fall back to bean name.  
  4.     String name = (this.name != null ? this.name : this.beanName);  
  5.     // Consider the concurrent flag to choose between stateful and stateless job.  
  6.     Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);  
  7.     // Build JobDetail instance.  
  8.     if (jobDetailImplClass != null) {  
  9.         // Using Quartz 2.0 JobDetailImpl class...  
  10.         this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);  
  11.         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.jobDetail);  
  12.         bw.setPropertyValue("name", name);  
  13.         bw.setPropertyValue("group", this.group);  
  14.         bw.setPropertyValue("jobClass", jobClass);  
  15.         bw.setPropertyValue("durability", true);  
  16.         ((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this);  
  17.     }  
  18.     else {  
  19.         // Using Quartz 1.x JobDetail class...  
  20.         this.jobDetail = new JobDetail(name, this.group, jobClass);  
  21.         this.jobDetail.setVolatility(true);  
  22.         this.jobDetail.setDurability(true);  
  23.         this.jobDetail.getJobDataMap().put("methodInvoker", this);  
  24.     }  
  25.     // Register job listener names.  
  26.     if (this.jobListenerNames != null) {  
  27.         for (String jobListenerName : this.jobListenerNames) {  
  28.             if (jobDetailImplClass != null) {  
  29.                 throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " +  
  30.                         "manually register a Matcher against the Quartz ListenerManager instead");  
  31.             }  
  32.             this.jobDetail.addJobListener(jobListenerName);  
  33.         }  
  34.     }  
  35.     postProcessJobDetail(this.jobDetail);  
  36. }  

上面主要看我們目前用的Quartz 2.0版本的實(shí)現(xiàn)部分,到這里或許你已經(jīng)明白Spring對(duì)Quartz的封裝原理了。Spring就是通過這種方式在最后Job真正執(zhí)行時(shí)反調(diào)用到我們所注入的類和方法。

現(xiàn)在,理解了Spring的實(shí)現(xiàn)原理后,我們就可以來設(shè)計(jì)我們自己的了。在設(shè)計(jì)時(shí)我想到以下幾點(diǎn):

1、減少spring的配置文件,為了實(shí)現(xiàn)一個(gè)定時(shí)任務(wù),spring的配置代碼太多了。

2、用戶可以通過頁面等方式添加、啟用、禁用某個(gè)任務(wù)。

3、用戶可以修改某個(gè)已經(jīng)在運(yùn)行任務(wù)的運(yùn)行時(shí)間表達(dá)式,CronExpression。

4、為方便維護(hù),簡(jiǎn)化任務(wù)的運(yùn)行調(diào)用處理,任務(wù)的運(yùn)行入口即Job實(shí)現(xiàn)類最好只有一個(gè),該Job運(yùn)行類相當(dāng)于工廠類,在實(shí)際調(diào)用時(shí)把任務(wù)的相關(guān)信息通過參數(shù)方式傳入,由該工廠類根據(jù)任務(wù)信息來具體執(zhí)行需要的操作。

在上面的思路下來進(jìn)行我們的開發(fā)吧。

一、spring配置文件

通過研究,發(fā)現(xiàn)要實(shí)現(xiàn)我們的功能,只需要以下配置:

  1. <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />  
二、任務(wù)運(yùn)行入口,即Job實(shí)現(xiàn)類,在這里我把它看作工廠類:
  1. /** 
  2.  * 定時(shí)任務(wù)運(yùn)行工廠類 
  3.  *  
  4.  * User: liyd 
  5.  * Date: 14-1-3 
  6.  * Time: 上午10:11 
  7.  */  
  8. public class QuartzJobFactory implements Job {  
  9.     @Override  
  10.     public void execute(JobExecutionContext context) throws JobExecutionException {  
  11.         System.out.println("任務(wù)成功運(yùn)行");  
  12.         ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");  
  13.         System.out.println("任務(wù)名稱 = [" + scheduleJob.getJobName() + "]");  
  14.     }  
  15. }  
這里我們實(shí)現(xiàn)的是無狀態(tài)的Job,如果要實(shí)現(xiàn)有狀態(tài)的Job在以前是實(shí)現(xiàn)StatefulJob接口,在我使用的quartz 2.2.1中,StatefulJob接口已經(jīng)不推薦使用了,換成了注解的方式,只需要給你實(shí)現(xiàn)的Job類加上注解@DisallowConcurrentExecution即可實(shí)現(xiàn)有狀態(tài):

  1. /** 
  2.  * 定時(shí)任務(wù)運(yùn)行工廠類 
  3.  * <p/> 
  4.  * User: liyd 
  5.  * Date: 14-1-3 
  6.  * Time: 上午10:11 
  7.  */  
  8. @DisallowConcurrentExecution  
  9. public class QuartzJobFactory implements Job {...}  

三、創(chuàng)建任務(wù)

既然要?jiǎng)討B(tài)的創(chuàng)建任務(wù),我們的任務(wù)信息當(dāng)然要保存在某個(gè)地方了,這里我們新建一個(gè)保存任務(wù)信息對(duì)應(yīng)的實(shí)體類:

  1. /** 
  2.  * 計(jì)劃任務(wù)信息 
  3.  *  
  4.  * User: liyd 
  5.  * Date: 14-1-3 
  6.  * Time: 上午10:24 
  7.  */  
  8. public class ScheduleJob {  
  9.     /** 任務(wù)id */  
  10.     private String jobId;  
  11.     /** 任務(wù)名稱 */  
  12.     private String jobName;  
  13.     /** 任務(wù)分組 */  
  14.     private String jobGroup;  
  15.     /** 任務(wù)狀態(tài) 0禁用 1啟用 2刪除*/  
  16.     private String jobStatus;  
  17.     /** 任務(wù)運(yùn)行時(shí)間表達(dá)式 */  
  18.     private String cronExpression;  
  19.     /** 任務(wù)描述 */  
  20.     private String desc;  
  21.     getter and setter ....  
  22. }  

接下來我們創(chuàng)建測(cè)試數(shù)據(jù),實(shí)際應(yīng)用中該數(shù)據(jù)可以保存在數(shù)據(jù)庫等地方,我們把任務(wù)的分組名+任務(wù)名作為任務(wù)的唯一key,和quartz中的實(shí)現(xiàn)方式一致:
  1. /** 計(jì)劃任務(wù)map */  
  2. private static Map<String, ScheduleJob> jobMap = new HashMap<String, ScheduleJob>();  
  3. static {  
  4.     for (int i = 0; i < 5; i++) {  
  5.         ScheduleJob job = new ScheduleJob();  
  6.         job.setJobId("10001" + i);  
  7.         job.setJobName("data_import" + i);  
  8.         job.setJobGroup("dataWork");  
  9.         job.setJobStatus("1");  
  10.         job.setCronExpression("0/5 * * * * ?");  
  11.         job.setDesc("數(shù)據(jù)導(dǎo)入任務(wù)");  
  12.         addJob(job);  
  13.     }  
  14. }  
  15. /** 
  16.  * 添加任務(wù) 
  17.  * @param scheduleJob 
  18.  */  
  19. public static void addJob(ScheduleJob scheduleJob) {  
  20.     jobMap.put(scheduleJob.getJobGroup() + "_" + scheduleJob.getJobName(), scheduleJob);  
  21. }  
有了調(diào)度工廠,有了任務(wù)運(yùn)行入口實(shí)現(xiàn)類,有了任務(wù)信息,接下來就是創(chuàng)建我們的定時(shí)任務(wù)了,在這里我把它設(shè)計(jì)成一個(gè)Job對(duì)應(yīng)一個(gè)trigger,兩者的分組及名稱相同,方便管理,條理也比較清晰,在創(chuàng)建任務(wù)時(shí)如果不存在新建一個(gè),如果已經(jīng)存在則更新任務(wù),主要代碼如下:
  1. //schedulerFactoryBean 由spring創(chuàng)建注入  
  2. Scheduler scheduler = schedulerFactoryBean.getScheduler();  
  3. //這里獲取任務(wù)信息數(shù)據(jù)  
  4. List<ScheduleJob> jobList = DataWorkContext.getAllJob();  
  5. for (ScheduleJob job : jobList) {  
  6.     TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());  
  7.     //獲取trigger,即在spring配置文件中定義的 bean id="myTrigger"  
  8.     CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);  
  9.     //不存在,創(chuàng)建一個(gè)  
  10.     if (null == trigger) {  
  11.         JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)  
  12.             .withIdentity(job.getJobName(), job.getJobGroup()).build();  
  13.         jobDetail.getJobDataMap().put("scheduleJob", job);  
  14.         //表達(dá)式調(diào)度構(gòu)建器  
  15.         CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job  
  16.             .getCronExpression());  
  17.         //按新的cronExpression表達(dá)式構(gòu)建一個(gè)新的trigger  
  18.         trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();  
  19.         scheduler.scheduleJob(jobDetail, trigger);  
  20.     } else {  
  21.         // Trigger已存在,那么更新相應(yīng)的定時(shí)設(shè)置  
  22.         //表達(dá)式調(diào)度構(gòu)建器  
  23.         CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job  
  24.             .getCronExpression());  
  25.         //按新的cronExpression表達(dá)式重新構(gòu)建trigger  
  26.         trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)  
  27.             .withSchedule(scheduleBuilder).build();  
  28.         //按新的trigger重新設(shè)置job執(zhí)行  
  29.         scheduler.rescheduleJob(triggerKey, trigger);  
  30.     }  
  31. }  

如此,可以說已經(jīng)完成了我們的動(dòng)態(tài)任務(wù)創(chuàng)建,大功告成了。有了上面的代碼,添加和修改任務(wù)是不是也會(huì)了,順道解決了?

上面我們創(chuàng)建的5個(gè)測(cè)試任務(wù),都是5秒執(zhí)行一次,都將調(diào)用QuartzJobFactory的execute方法,但是傳入的任務(wù)信息參數(shù)不同,execute方法中的如下代碼就是得到具體的任務(wù)信息,包括任務(wù)分組和任務(wù)名:

  1. ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");  

有了任務(wù)分組和任務(wù)名即確定了該任務(wù)的唯一性,接下來需要什么操作實(shí)現(xiàn)起來是不是就很容易了?

以后需要添加新的定時(shí)任務(wù)只需要在任務(wù)信息列表中加入記錄即可,然后在execute方法中通過判斷任務(wù)分組和任務(wù)名來實(shí)現(xiàn)你具體的操作。

以上已經(jīng)初始實(shí)現(xiàn)了我們需要的功能,增加和修改也已經(jīng)可以通過源代碼舉一反三出來,但是我們?cè)趯?shí)際開發(fā)的時(shí)候需要進(jìn)行測(cè)試,如果一個(gè)任務(wù)是1個(gè)小時(shí)運(yùn)行一次的,測(cè)試起來是不是很不方便?當(dāng)然你可以修改任務(wù)的運(yùn)行時(shí)間表達(dá)式,但相信這不是最好的方法,接下來我們就要實(shí)現(xiàn)在不對(duì)當(dāng)前任務(wù)信息做任何修改的情況下觸發(fā)任務(wù),并且該觸發(fā)只會(huì)運(yùn)行一次作測(cè)試用。待續(xù),,,




本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Quartz實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)
用quartz兌現(xiàn)多任務(wù)動(dòng)態(tài)加載【轉(zhuǎn)】
Quartz的基本使用之入門(2.3.0版本)
Quartz 框架快速入門(一)
quartz中JobExecutionContext的使用
Spring中定時(shí)任務(wù)Quartz集群配置學(xué)習(xí)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服