做項目時遇到一個需求,需要在web后台管理頁面設置開關燈時間,然后實現燈源的自動開關操作
首先想到的是springboot自帶的@Schedule注解,但是有個問題,因為我要在前端設置時間,而注解的實現方法只能是寫一個死的cron表達式在那兒,要更改的話只能改代碼,顯然不符合要求
於是用了quartz,實現的效果是每十秒從數據庫獲取一次cron表達式,如果和當前定時任務的cron不同的話,重新設置定時任務,這樣的話,前端就只用調用數據庫的修改接口,就可以實現設置新的定時任務
pom.xml,springboot需要的就不多說了,這里是quartz需要的
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency>
然后在service里寫任務的具體邏輯,這里是關掉所有燈,注意要加上@EnableScheduling
@Service @EnableScheduling public class QuartzServiceImpl implements QuartzService { @Autowired private QuartzConfigDao quartzConfigDao; @Autowired private LampDao lampDao; @Override public void closeAll() { lampDao.TurnOffAll(); System.out.println("close all"); } }
然后是quartz相關的定時任務注冊代碼
public class QuartzConfig { /* 配置定時任務 */ @Bean(name = "jobDetailAfterWork") public MethodInvokingJobDetailFactoryBean detailFactoryBean(QuartzService task){ //這里的參數就是我們剛才寫的QuartzService的一個實例 MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); /* * 是否並發執行 * 例如每5s執行一次任務,但是當前任務還沒有執行完,就已經過了5s了, * 如果此處為true,則下一個任務會執行,如果此處為false,則下一個任務會等待上一個任務執行完后,再開始執行 */ jobDetail.setConcurrent(false); //設置定時任務的名字 jobDetail.setName("AfterWork"); //設置任務的分組,這些屬性都可以在數據庫中,在多任務的時候使用 jobDetail.setGroup("srd"); //為需要執行的實體類對應的對象 jobDetail.setTargetObject(task); /* * closeAll為需要執行的方法 * 通過這幾個配置,告訴JobDetailFactoryBean我們需要執行定時執行QuartzService類中的closeAll方法 */ jobDetail.setTargetMethod("closeAll"); return jobDetail; } /* 配置定時任務的觸發器,也就是什么時候觸發執行定時任務 */ @Bean(name = "jobTriggerAfterWork") public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetailAfterWork){ CronTriggerFactoryBean trigger = new CronTriggerFactoryBean(); trigger.setJobDetail(jobDetailAfterWork); //初始化的cron表達式(每天14:25:20觸發) trigger.setCronExpression("20 25 14 * * ?"); //trigger的name trigger.setName("AfterWorkTrigger"); return trigger; } /* 定義quartz調度工廠 */ @Bean(name = "scheduler") public SchedulerFactoryBean schedulerFactoryBean(Trigger jobTriggerAfterWork){ SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); //用於quartz集群,QuartzScheduler啟動時更新已存在的job factoryBean.setOverwriteExistingJobs(true); //延時啟動,應用啟動1秒后 factoryBean.setStartupDelay(1); //注冊觸發器 factoryBean.setTriggers(jobTriggerAfterWork); return factoryBean; } }
這些做完之后,再運行項目,這時候已經有一個定時任務在工作中了,它會在每天的14:25:20執行QuartzService的closeAll方法
接下來是設置定時查詢數據庫的cron並與當前定時任務對比,進行刷新
@EnableScheduling @Configuration @Component public class QuartzRefreshConfig { @Autowired private QuartzService quartzService; @Resource(name = "jobTriggerAfterWork") private CronTrigger cronTriggerAfterWork; @Resource(name = "scheduler") private Scheduler scheduler; /* 每隔10s查庫,並根據查詢結果決定是否重新設置定時任務 */ @Scheduled(fixedRate = 10000) public void scheduleUpdateCronTrigger() throws SchedulerException { CronTrigger trigger = (CronTrigger) scheduler.getTrigger(cronTriggerAfterWork.getKey()); //當前Trigger使用的 String currentCron = trigger.getCronExpression(); //從數據庫查詢出來的 String searchCron = quartzService.getTaskById(1); //這里是數據庫的查詢操作,根據自己的情況自己寫 if (currentCron.equals(searchCron)) { // 如果當前使用的cron表達式和從數據庫中查詢出來的cron表達式一致,則不刷新任務 } else { //表達式調度構建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron); //按新的cronExpression表達式重新構建trigger trigger = (CronTrigger) scheduler.getTrigger(cronTriggerAfterWork.getKey()); trigger = trigger.getTriggerBuilder().withIdentity(cronTriggerAfterWork.getKey()).withSchedule(scheduleBuilder).build(); // 按新的trigger重新設置job執行 scheduler.rescheduleJob(cronTriggerAfterWork.getKey(), trigger); currentCron = searchCron; } } }
這樣我們就完成了可以動態更新的定時任務
對於多個任務,只需要在QuartzConfig中定義多個jobDetail和對應的Trigger,然后在調度工廠進行注冊就可以啦,注意多個任務時任務名,即jobdetail的名字和trigger的名字不要重復