需求:
對於quartz定時任務,需要自定義設置cron表達式實現修改定時任務的執行週期。
目前條件:
定時任務是在spring容器啓動完畢通過@EventListener監聽容器的ContextRefreshedEvent事件讀取了quarz.properties文件中cron 表達式更新了定時任務觸發器。
用戶在修改了定時任務的cron表達式的時候,存放在了數據庫中,所以需要從數據庫中獲取cron,然後重置觸發器。這個操作希望在spring容器啓動的時候做。
解決方案:
- 考慮使用@PostConstruct來做,這個操作在@EventListener之前,可以修改定時任務的註解,從而更新定時任務的觸發器cron。
@PostConstruct
public void resetCron(ContextRefreshedEvent event) {
String cron = jmsInfoService.getJmsInfo("jms.cron");
if (cron != null) {
try {
//檢查數據庫裏面有jms.cron就重新定義定時任務,替換觸發器
JmsDataSyncJob jmsDataSyncJob = new JmsDataSyncJob();
Method sysnOldData = jmsDataSyncJob.getClass().getDeclaredMethod("sysnOldData");
QuartzScheduled quartzScheduled = sysnOldData.getAnnotation(QuartzScheduled.class);
InvocationHandler handler = Proxy.getInvocationHandler(quartzScheduled);
Field f = handler.getClass().getDeclaredField("memberValues");
f.setAccessible(true);
Map<String, Object> memberValue = (Map<String, Object>) f.get(handler);
memberValue.put("cron", cron);
LogUtil.info("檢查數據庫裏面有jms.cron設置,重新定義定時任務的cron :" + quartzScheduled.cron());
} catch (Exception e) {
e.printStackTrace();
}
}
}
問題:在啓動的時候如果mapper沒有被完全加載完畢,會導致空指針錯誤,無法查詢數據庫,甚至無法啓動spring。
- 考慮使用 @EventListener的方式,監聽容器的ContextRefreshedEvent事件,讀取數據庫數據,重置定時任務觸發器
@EventListener
public void resetCron(ContextRefreshedEvent event) {
String cron = jmsInfoService.getJmsInfo("jms.cron");
if (cron != null) {
try {
System.out.println("執行的任務 start ");
for (int i = 0; i < quartzManageService.getCurrentlyExecutingJobs().size(); i++) {
System.out.println(quartzManageService.getCurrentlyExecutingJobs().get(i).getTrigger().getKey());
}
System.out.println("執行的任務 end ");
String key = "jmsDataSyncJob" + "." + "sysnOldData";
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(key)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
quartzManageService.rescheduleJob(TriggerKey.triggerKey(key), trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
問題:和quartz一樣都是監聽容器的刷新事件,會導致執行順序問題,如果查數據庫重置的操作在quartz之前就會導致最終定時任務的cron還是讀取的配置文件裏面的。所以考慮監聽其他的事件,保證在後面執行。
@EventListener
public void resetCron(ContextReadyEvent event) {
//和上面一樣,也可以監聽 ContextStartedEvent
}
操作成功。
spring容器啓動事件監聽:
[參考連接] http://zhaoshijie.iteye.com/blog/1974682