實際項目應用中經常會用到定時任務,可以通過quartz和spring的簡單配置即可完成,但如果要改變任務的執行時間、頻率,廢棄任務等就需要改變配置甚至代碼需要重啓服務器,這裏介紹一下如何通過quartz與spring的組合實現動態的改變定時任務的狀態的一個實現。
參考文章:http://www.meiriyouke.net/?p=82
本文章適合對quartz和spring有一定了解的讀者。
spring版本爲3.2 quartz版本爲2.2.1 如果使用了quartz2.2.1 則spring版本需3.1以上
1.
spring中引入註冊bean
1
2
|
< bean id = "schedulerFactoryBean" class = "org.springframework.scheduling.quartz.SchedulerFactoryBean" /> |
爲什麼要與spring結合?
與spring結合可以使用spring統一管理quartz中任務的生命週期,使得web容器關閉時所有的任務一同關閉。如果不用spring管理可能會出現web容器關閉而任務仍在繼續運行的情況,不與spring結合的話要自己控制任務在容器關閉時一起關閉。
2.創建保存計劃任務信息的實體類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
/** * * @Description: 計劃任務信息 * @author snailxr * @date 2014年4月24日 下午10:49:43 */ public class ScheduleJob { public static final String STATUS_RUNNING = "1" ; public static final String STATUS_NOT_RUNNING = "0" ; public static final String CONCURRENT_IS = "1" ; public static final String CONCURRENT_NOT = "0" ; private Long jobId; private Date createTime; private Date updateTime; /** * 任務名稱 */ private String jobName; /** * 任務分組 */ private String jobGroup; /** * 任務狀態 是否啓動任務 */ private String jobStatus; /** * cron表達式 */ private String cronExpression; /** * 描述 */ private String description; /** * 任務執行時調用哪個類的方法 包名+類名 */ private String beanClass; /** * 任務是否有狀態 */ private String isConcurrent; /** * spring bean */ private String springId; /** * 任務調用的方法名 */ private String methodName; //get set....... } |
該實體類與數據庫中的表對應,在數據庫中存儲多個計劃任務。
注意:jobName 跟 groupName的組合應該是唯一的,beanClass springId至少有一個
在項目啓動時運行以下代碼:
1
2
3
4
5
6
7
8
9
10
11
|
public void init() throws Exception { Scheduler scheduler = schedulerFactoryBean.getScheduler(); // 這裏從數據庫中獲取任務信息數據 List<ScheduleJob> jobList = scheduleJobMapper.getAll(); for (ScheduleJob job : jobList) { addJob(job); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/** * 添加任務 * * @param scheduleJob * @throws SchedulerException */ public void addJob(ScheduleJob job) throws SchedulerException { if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) { return ; } Scheduler scheduler = schedulerFactoryBean.getScheduler(); log.debug(scheduler + ".......................................................................................add" ); TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 不存在,創建一個 if ( null == trigger) { Class clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory. class : QuartzJobFactoryDisallowConcurrentExecution. class ; JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build(); jobDetail.getJobDataMap().put( "scheduleJob" , job); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build(); scheduler.scheduleJob(jobDetail, trigger); } else { // Trigger已存在,那麼更新相應的定時設置 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); // 按新的cronExpression表達式重新構建trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); // 按新的trigger重新設置job執行 scheduler.rescheduleJob(triggerKey, trigger); } } |
看到代碼第20行根據scheduleJob類中CONCURRENT_IS來判斷任務是否有狀態。來給出不同的Job實現類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * * @Description: 若一個方法一次執行不完下次輪轉時則等待改方法執行完後才執行下一次操作 * @author snailxr * @date 2014年4月24日 下午5:05:47 */ @DisallowConcurrentExecution public class QuartzJobFactoryDisallowConcurrentExecution implements Job { public final Logger log = Logger.getLogger( this .getClass()); @Override public void execute(JobExecutionContext context) throws JobExecutionException { ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get( "scheduleJob" ); TaskUtils.invokMethod(scheduleJob); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/** * * @Description: 計劃任務執行處 無狀態 * @author snailxr * @date 2014年4月24日 下午5:05:47 */ public class QuartzJobFactory implements Job { public final Logger log = Logger.getLogger( this .getClass()); @Override public void execute(JobExecutionContext context) throws JobExecutionException { ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get( "scheduleJob" ); TaskUtils.invokMethod(scheduleJob); } } |
真正執行計劃任務的代碼就在TaskUtils.invokMethod(scheduleJob)裏面
通過scheduleJob的beanClass或springId通過反射或spring來獲得需要執行的類,通過methodName來確定執行哪個方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class TaskUtils { public final static Logger log = Logger.getLogger(TaskUtils. class ); /** * 通過反射調用scheduleJob中定義的方法 * * @param scheduleJob */ public static void invokMethod(ScheduleJob scheduleJob) { Object object = null ; Class clazz = null ; //springId不爲空先按springId查找bean if (StringUtils.isNotBlank(scheduleJob.getSpringId())) { object = SpringUtils.getBean(scheduleJob.getSpringId()); } else if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) { try { clazz = Class.forName(scheduleJob.getBeanClass()); object = clazz.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (object == null ) { log.error( "任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啓動成功,請檢查是否配置正確!!!" ); return ; } clazz = object.getClass(); Method method = null ; try { method = clazz.getDeclaredMethod(scheduleJob.getMethodName()); } catch (NoSuchMethodException e) { log.error( "任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啓動成功,方法名設置錯誤!!!" ); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (method != null ) { try { method.invoke(object); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } |
對任務的暫停,刪除,修改等操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
** * 獲取所有計劃中的任務列表 * * @return * @throws SchedulerException */ public List< ScheduleJob > getAllJob() throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); GroupMatcher< JobKey > matcher = GroupMatcher.anyJobGroup(); Set< JobKey > jobKeys = scheduler.getJobKeys(matcher); List< ScheduleJob > jobList = new ArrayList< ScheduleJob >(); for (JobKey jobKey : jobKeys) { List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { ScheduleJob job = new ScheduleJob(); job.setJobName(jobKey.getName()); job.setJobGroup(jobKey.getGroup()); job.setDescription("觸發器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } jobList.add(job); } } return jobList; } /** * 所有正在運行的job * * @return * @throws SchedulerException */ public List< ScheduleJob > getRunningJob() throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); List< JobExecutionContext > executingJobs = scheduler.getCurrentlyExecutingJobs(); List< ScheduleJob > jobList = new ArrayList< ScheduleJob >(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { ScheduleJob job = new ScheduleJob(); JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); job.setJobName(jobKey.getName()); job.setJobGroup(jobKey.getGroup()); job.setDescription("觸發器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } jobList.add(job); } return jobList; } /** * 暫停一個job * * @param scheduleJob * @throws SchedulerException */ public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup()); scheduler.pauseJob(jobKey); } /** * 恢復一個job * * @param scheduleJob * @throws SchedulerException */ public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup()); scheduler.resumeJob(jobKey); } /** * 刪除一個job * * @param scheduleJob * @throws SchedulerException */ public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup()); scheduler.deleteJob(jobKey); } /** * 立即執行job * * @param scheduleJob * @throws SchedulerException */ public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup()); scheduler.triggerJob(jobKey); } /** * 更新job時間表達式 * * @param scheduleJob * @throws SchedulerException */ public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); scheduler.rescheduleJob(triggerKey, trigger); } |
小提示
更新表達式,判斷表達式是否正確可用一下代碼
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("xxxxx");
拋出異常則表達式不正確
或者可以到github上獲取
https://github.com/snailxr/quartz-spring_demo
來源:http://chuhanzhi.com/?p=45