需求:
对于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