SpringBoot整合quartz動態建立定時任務實現以及踩過的坑
需要了解一下quartz有Job,Scheduler,Trigger等概念,在此就不詳細說明
首先說一下功能需求場景
該提醒可以在頁面上進行配置多個,比如可以配置一個提醒:在每天的12點,傳送郵件給某個人,可以繼續配置另外一個提醒:在每個月的10號,給某個人轉賬,等等,多個動態的提醒,
說一下實現的方式
上面的需求即通過頁面的配置,創建出來對應的定時任務,這些任務是動態創建出來的,不能夠在程式碼中固定,該本章採用了quartz的Scheduler任務排程來實現動態任務的建立
實現功能路上踩過的坑
最開始的時候,對於這個動態建立定時任務我也覺得一臉懵逼,於是就在網上查詢資料
最開始採用
StdScheduler stdScheduler = (StdScheduler) annotationContext.getBean(“mySchedulerFactoryBean”);//獲得上面建立的bean
的時候報錯了,原來是專案啟動的時候,先進行建立定時任務,但是此刻並沒有初始化MyJobFactory類中的bean,導致在建立scheduler時候並沒有在spring上下文中找到這個bean,於是就打算寫一個定時器,然後每天執行一次,執行的時候,呼叫動態建立定時任務的方法,剛好這樣做,可以實現,在頁面上修改任務提醒的配置,後臺不用重新啟動就可以動態更新任務,現在要實現就是對動態創建出來的任務進行修改定時時間,新增定時任務的操作了.
實現
1. InitRemindRuleScheduler
2. JobFactory
3. RemindTask
4. RemindRuleScheduler
5. TimingRemindTask
- InitRemindRuleScheduler 該本來用作建立mySchedulerFactoryBean的bean和呼叫RemindRuleScheduler的initStartJob來在專案啟動的時候建立定時任務,無奈出現上面的問題,就只用來建立mySchedulerFactoryBean這個bean物件了
- JobFactory 用來建立job例項
- RemindTask建立的一個定時任務,每天會執行一遍,代替1中的onApplicationEvent方法
- RemindRuleScheduler從資料庫查詢定時提醒任務,進行更新任務.
- TimingRemindTask 任務的具體操作
說明一下,因為目前我的多個定期任務都是同一個任務邏輯操作,故只需要一個定時任務的業務邏輯實現,如果針對不同的任務,有不同的實現的話,在建立定時任務的時候,要針對生成這個任務的時候使用對應的類:TimingRemindTask(該類需實現Job介面)
JobDetail jobDetail = JobBuilder.newJob(TimingRemindTask.class)
.withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE )
.usingJobData("remindRuleId",remindRuleObjF04SQL01OM01.getId())
.build();
最後,貼上程式碼:
1. InitRemindRuleScheduler.class
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import com.paasit.pai.core.quartz.RemindRuleScheduler;
/**
* @author 和彥鵬
* 2018年9月15日
*/
@Configuration
public class InitRemindRuleScheduler implements ApplicationListener<ContextRefreshedEvent> {
/**
* 日誌
*/
private final Logger log = LoggerFactory.getLogger(InitRemindRuleScheduler.class);
@Autowired
private RemindRuleScheduler remindRuleScheduler;
@Autowired
private JobFactory jobFactory;
@Bean(name ="mySchedulerFactoryBean")
public SchedulerFactoryBean mySchedulerFactory() {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
bean.setOverwriteExistingJobs(true);
bean.setStartupDelay(1);
bean.setJobFactory(jobFactory);
return bean;
}
/**
* 專案初始化的時候啟動quartz
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//log.debug("執行onApplicationEvent..");
//try {
// remindRuleScheduler.initStartJob();
// log.debug("任務已經啟動...");
//} catch (SchedulerException e) {
// log.error("初始化啟動錯誤:{}", e);
//}
}
}
- JobFactory
import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @author 和彥鵬
* 2018年9月16日
*/
@Component
public class JobFactory extends AdaptableJobFactory {
private static final Logger log = LoggerFactory.getLogger(JobFactory.class);
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 呼叫父類的方法
Object jobInstance = super.createJobInstance(bundle);
// 進行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
- RemindTask
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @version: 0_1
* @author: 和彥鵬
* @date: 2018年6月6日11:37:44
*/
@Component
@Configurable
@EnableScheduling
public class RemindTask {
/**
* 日誌
*/
private final Logger log = LoggerFactory.getLogger(RemindTask.class);
/**
* 初始化動態建立定期任務
*/
@Autowired
private RemindRuleScheduler remindRuleScheduler;
/**
* 執行間隔提醒業務邏輯
*/
@Autowired
private IntervalRemindTask intervalRemindTask;
// 每天執行定時任務:測試使用,每分鐘執行
@Scheduled(cron = "5 * * * * ?")
public void testGetDemoData() {
log.debug("RemindTask starting...");
// A:建立或者更新定期的所有動態任務
try {
remindRuleScheduler.initStartJob();
} catch (SchedulerException e) {
log.error("RemindTask 初始化定期提醒任務失敗.. {}", e);
}
log.debug("RemindTask end...");
}
}
- RemindRuleScheduler
import java.text.MessageFormat;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdScheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.paasit.pai.core.dao.QueryDAO;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01IM01;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01OM01;
import com.paasit.pai.core.utils.SpringUtil;
/**
* 描述: 提醒規則任務:
* @author 和彥鵬
* @date 2018年9月15日15:33:02
* @version v0.0.1
*/
@Component
public class RemindRuleScheduler {
/**
* 日誌
*/
private final Logger log = LoggerFactory.getLogger(RemindRuleScheduler.class);
private final String REMINDRULE = "REMINDRULE";
@Autowired
private QueryDAO queryDAO;
/**
* 開始執行所有任務
*
* @throws SchedulerException
*/
public void initStartJob() throws SchedulerException {
// 從資料庫中查詢到所有的提醒
ApplicationContext annotationContext = SpringUtil.getApplicationContext();
StdScheduler stdScheduler = (StdScheduler) annotationContext.getBean("mySchedulerFactoryBean");//獲得上面建立的bean
Scheduler myScheduler = stdScheduler;
RemindRuleObjF04SQL01IM01 remindRuleObjF04SQL01IM01 = new RemindRuleObjF04SQL01IM01();
remindRuleObjF04SQL01IM01.setRemindType(0);// 0表示定期,該方法僅執行定期
List<RemindRuleObjF04SQL01OM01> remindRuleObjF04SQL01OM01List = queryDAO.executeForObjectList("RemindRuleObjF04SQL01", remindRuleObjF04SQL01IM01);
log.debug("RemindTask 資料庫查詢出來的定期任務數量:{}", remindRuleObjF04SQL01OM01List.size());
if (remindRuleObjF04SQL01OM01List != null && remindRuleObjF04SQL01OM01List.size() > 0) {
for (int i = 0; i < remindRuleObjF04SQL01OM01List.size(); i++) {
startJob(myScheduler,remindRuleObjF04SQL01OM01List.get(i));
}
myScheduler.start();
}
}
private void startJob(Scheduler myScheduler, RemindRuleObjF04SQL01OM01 remindRuleObjF04SQL01OM01) throws SchedulerException {
// 建立觸發器表示式{0}:表示小時,{1}:表示日,
String cron = MessageFormat.format("0 0 {0} {1} * ?",
remindRuleObjF04SQL01OM01.getOnHour() == null ? 0 : remindRuleObjF04SQL01OM01.getOnHour(),
remindRuleObjF04SQL01OM01.getOnDay() == null || remindRuleObjF04SQL01OM01.getOnDay() == 0 ? "*" : remindRuleObjF04SQL01OM01.getOnDay());
log.info(MessageFormat.format("RemindTask 提醒任務Id=[{0}]的cron的表示式:[{1}]", remindRuleObjF04SQL01OM01.getId(), cron));
TriggerKey triggerKey = new TriggerKey(remindRuleObjF04SQL01OM01.getId(), REMINDRULE);
CronTrigger cronTrigger = (CronTrigger) myScheduler.getTrigger(triggerKey);
// 不存在這個任務,新增任務
if (cronTrigger == null) {
// 通過JobBuilder構建JobDetail例項,JobDetail規定只能是實現Job介面的例項
JobDetail jobDetail = JobBuilder.newJob(TimingRemindTask.class)
.withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE)
.usingJobData("remindRuleId",remindRuleObjF04SQL01OM01.getId())
.build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// CronTrigger表示式觸發器 繼承於Trigger
cronTrigger = TriggerBuilder.newTrigger().withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE)
.withSchedule(cronScheduleBuilder).build();
myScheduler.scheduleJob(jobDetail, cronTrigger);
log.info("RemindTask 建立定時任務成功");
} else {// 存在這個任務,判斷這個任務的觸發時間是否被修改過,如果修改過則更新任務
String oldCron = cronTrigger.getCronExpression();
// 新配置的cron和之前任務中使用的不一致,則更新
if (!StringUtils.equalsIgnoreCase(cron, oldCron)) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE)
.withSchedule(cronScheduleBuilder).build();
myScheduler.rescheduleJob(triggerKey, trigger);
log.info("RemindTask 更新任務執行時間成功");
} else {
log.info("RemindTask 任務不進行操作");
}
} // TODO 暫沒考慮頁面上刪除定期任務提醒
}
// /**
// * 獲取Job資訊
// */
// public String getJobInfo(String name, String group) throws SchedulerException {
// TriggerKey triggerKey = new TriggerKey(name, group);
// CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
// scheduler.getTriggerState(triggerKey).name());
// }
//
// /**
// * 修改某個任務的執行時間
// */
// public boolean modifyJob(String name, String group, String time) throws SchedulerException {
// Date date = null;
// TriggerKey triggerKey = new TriggerKey(name, group);
// CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// String oldTime = cronTrigger.getCronExpression();
// if (!oldTime.equalsIgnoreCase(time)) {
// CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
// CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
// .withSchedule(cronScheduleBuilder).build();
// date = scheduler.rescheduleJob(triggerKey, trigger);
// }
// return date != null;
// }
//
// /**
// * 暫停所有任務
// */
// public void pauseAllJob() throws SchedulerException {
// scheduler.pauseAll();
// }
//
// /**
// * 暫停某個任務
// */
// public void pauseJob(String name, String group) throws SchedulerException {
// JobKey jobKey = new JobKey(name, group);
// JobDetail jobDetail = scheduler.getJobDetail(jobKey);
// if (jobDetail == null)
// return;
// scheduler.pauseJob(jobKey);
// }
//
// /**
// * 恢復所有任務
// */
// public void resumeAllJob() throws SchedulerException {
// scheduler.resumeAll();
// }
//
// /**
// * 恢復某個任務
// */
// public void resumeJob(String name, String group) throws SchedulerException {
// JobKey jobKey = new JobKey(name, group);
// JobDetail jobDetail = scheduler.getJobDetail(jobKey);
// if (jobDetail == null)
// return;
// scheduler.resumeJob(jobKey);
// }
//
// /**
// * 刪除某個任務
// */
// public void deleteJob(String name, String group) throws SchedulerException {
// JobKey jobKey = new JobKey(name, group);
// JobDetail jobDetail = scheduler.getJobDetail(jobKey);
// if (jobDetail == null)
// return;
// scheduler.deleteJob(jobKey);
// }
}
- TimingRemindTask
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.paasit.pai.core.dao.QueryDAO;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01IM01;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01OM01;
/**
* 定時提醒的任務邏輯:該方法為動態創建出來的定時任務執行的邏輯,精確到remindRuleId,僅執行該條提醒的配置
* @author 和彥鵬
* @date 2018年9月16日15:33:09
* @version v0.1
*/
@Component
public class TimingRemindTask implements Job {
/**
* 日誌
*/
private final Logger log = LoggerFactory.getLogger(TimingRemindTask.class);
@Autowired
private QueryDAO queryDAO;
public void execute(JobExecutionContext arg0) throws JobExecutionException {
try {
String remindRuleId = arg0.getJobDetail().getJobDataMap().getString("remindRuleId");
log.debug("RemindTask 1.1執行定期提醒任務資訊Id:{}", remindRuleId);
RemindRuleObjF04SQL01IM01 remindRuleObjF04SQL01IM01 = new RemindRuleObjF04SQL01IM01();
remindRuleObjF04SQL01IM01.setId(remindRuleId);
RemindRuleObjF04SQL01OM01 remindRuleObjF04SQL01OM01 = queryDAO.executeForObject("RemindRuleObjF04SQL01", remindRuleObjF04SQL01IM01, RemindRuleObjF04SQL01OM01.class);
// 進行業務邏輯處理
} catch (Exception e) {
log.error("RemindTask 1.9執行任務,獲取任務資訊Id出錯,任務資訊:{}", arg0);
}
}
}