做项目时遇到一个需求,需要在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的名字不要重复