项目中需要用到定时任务,考虑了下java方面定时任务无非就三种:
- 用Java自带的timer类。稍微看了一下,可以实现大部分的指定频率的任务的调度(timer.schedule()),也可以实现关闭和开启(timer.cancle)。但是用其来实现某天的某个时间或者某月的某一天调度任务有点不方便。
- 采用Quartz 调度器实现。这是一个功能很强大的开源的专门用于定时任务调度的框架,也很好的和springboot整合,缺点:配置复杂,需要花费一定的时间去了解和研究。
- spring3.0以后自带的scheduletask任务调度,可以实现quartz的大部分功能,不需要额外引用jar,也不需要另外配置。而且支持注解和配置文件两种。
鉴于项目有些地方要考虑动态管理定时任务的,所以考虑吧quartz也集成进去,方便调用。
一、首先引入依赖(必需)
<!--任务调度相关依赖--> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.3</version> </dependency>
二、创建job 实例工厂,解决spring注入问题,如果使用默认会导致spring的@Autowired 无法注入问题
1 package com.qunyi.jifenzhi_zx.core.quartz.taskjobfactory; 2 3 import org.quartz.spi.TriggerFiredBundle; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 6 import org.springframework.scheduling.quartz.AdaptableJobFactory; 7 import org.springframework.stereotype.Component; 8 9 /** 10 * @author xujingyang 11 * @Description: 解决quartz无法注入spring bean问题 12 * @date 2018/5/30. 13 */ 14 @Component 15 public class TaskJobFactory extends AdaptableJobFactory { 16 @Autowired 17 private AutowireCapableBeanFactory capableBeanFactory; 18 19 @Override 20 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { 21 //调用父类的方法 22 Object jobInstance = super.createJobInstance(bundle); 23 //进行注入 24 capableBeanFactory.autowireBean(jobInstance); 25 return jobInstance; 26 } 27 }
三、创建job工具类,对job进行增加,暂停,恢复,更新,删除等操作
1 package com.qunyi.jifenzhi_zx.core.quartz.taskjobfactory; 2 3 import com.qunyi.jifenzhi_zx.core.quartz.BaseTaskJob; 4 import org.quartz.*; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Component; 9 10 /** 11 * @author xujingyang 12 * @Description: task任务创建工具 13 * @date 2018/5/30. 14 */ 15 @Component 16 public class TaskJobUtil { 17 private static final Logger logger = LoggerFactory.getLogger(TaskJobUtil.class); 18 19 private static TaskJobUtil jobUtil; 20 21 @Autowired 22 private Scheduler scheduler; 23 24 public TaskJobUtil() { 25 logger.info("init jobUtil"); 26 jobUtil = this; 27 } 28 29 public static TaskJobUtil getInstance() { 30 logger.info("retun JobCreateUtil"); 31 return TaskJobUtil.jobUtil; 32 } 33 34 /** 35 * 创建job 36 * 37 * @param clazz 任务类 38 * @param jobGroupName 任务所在组名称 39 * @param cronExpression cron表达式 40 * @throws Exception 41 */ 42 public void addJob(Class clazz, String jobGroupName, String cronExpression) throws Exception { 43 44 // 启动调度器 45 scheduler.start(); 46 47 //构建job信息 48 JobDetail jobDetail = JobBuilder.newJob(((BaseTaskJob)clazz.newInstance()).getClass()).withIdentity(clazz.getSimpleName(), jobGroupName).build(); 49 50 //表达式调度构建器(即任务执行的时间) 51 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression); 52 53 //按新的cronExpression表达式构建一个新的trigger 54 CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(clazz.getSimpleName(), jobGroupName) 55 .withSchedule(scheduleBuilder).build(); 56 scheduler.scheduleJob(jobDetail, trigger); 57 } 58 59 /** 60 * 暂停job 61 * 62 * @param jobClassName 任务类名称 63 * @param jobGroupName 任务所在组名称 64 * @throws SchedulerException 65 */ 66 public void pauseJob(String jobClassName, String jobGroupName) throws SchedulerException { 67 scheduler.pauseJob(JobKey.jobKey(jobClassName, jobGroupName)); 68 } 69 70 /** 71 * 恢复job 72 * 73 * @param jobClassName 任务类名称 74 * @param jobGroupName 任务所在组名称 75 * @throws SchedulerException 76 */ 77 public void resumeJob(String jobClassName, String jobGroupName) throws SchedulerException { 78 79 scheduler.resumeJob(JobKey.jobKey(jobClassName, jobGroupName)); 80 } 81 82 83 /** 84 * job 更新 85 * 86 * @param jobClassName 任务类名称 87 * @param jobGroupName 任务所在组名称 88 * @param cronExpression cron表达式 89 * @throws Exception 90 */ 91 public void jobreschedule(String jobClassName, String jobGroupName, String cronExpression) throws Exception { 92 TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName); 93 // 表达式调度构建器 94 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression); 95 96 CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); 97 98 // 按新的cronExpression表达式重新构建trigger 99 trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); 100 101 // 按新的trigger重新设置job执行 102 scheduler.rescheduleJob(triggerKey, trigger); 103 104 } 105 106 /** 107 * job 删除 108 * 109 * @param jobClassName 任务类名称 110 * @param jobGroupName 任务所在组名称 111 * @throws Exception 112 */ 113 public void jobdelete(String jobClassName, String jobGroupName) throws Exception { 114 scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName)); 115 scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName)); 116 scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName)); 117 } 118 119 }
四、增加quartz 属性配置文件 quartz.properties,放置resource目录下,此文件主要提供schedule自动注入提供属性
# 固定前缀org.quartz org.quartz.scheduler.instanceName = DefaultQuartzScheduler org.quartz.scheduler.instanceId = AUTO org.quartz.scheduler.rmi.export = false org.quartz.scheduler.rmi.proxy = false org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
五、创建quartz的配置文件QuartzrConfiguration,进行属性配置
1 package com.qunyi.jifenzhi_zx.core.config; 2 3 import com.qunyi.jifenzhi_zx.core.quartz.taskjobfactory.TaskJobFactory; 4 import org.quartz.Scheduler; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.beans.factory.config.PropertiesFactoryBean; 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Configuration; 9 import org.springframework.core.io.ClassPathResource; 10 import org.springframework.scheduling.quartz.SchedulerFactoryBean; 11 12 import javax.sql.DataSource; 13 import java.io.IOException; 14 15 /** 16 * @author xujingyang 17 * @Description: 任务调度配置 18 * @date 2018/5/30. 19 */ 20 @Configuration 21 public class QuartzConfiguration { 22 @Autowired 23 private DataSource dataSource; 24 @Autowired 25 private TaskJobFactory jobFactory; 26 27 @Bean(name = "SchedulerFactory") 28 public SchedulerFactoryBean schedulerFactoryBean() throws IOException { 29 //获取配置属性 30 PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); 31 propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); 32 //在quartz.properties中的属性被读取并注入后再初始化对象 33 propertiesFactoryBean.afterPropertiesSet(); 34 //创建SchedulerFactoryBean 35 SchedulerFactoryBean factory = new SchedulerFactoryBean(); 36 factory.setQuartzProperties(propertiesFactoryBean.getObject()); 37 factory.setJobFactory(jobFactory); 38 return factory; 39 } 40 41 /* 42 * 通过SchedulerFactoryBean获取Scheduler的实例 43 */ 44 @Bean(name = "scheduler") 45 public Scheduler scheduler() throws IOException { 46 return schedulerFactoryBean().getScheduler(); 47 } 48 }
六、创建基础任务调度接口
1 package com.qunyi.jifenzhi_zx.core.quartz; 2 3 import org.quartz.Job; 4 import org.quartz.JobExecutionContext; 5 import org.quartz.JobExecutionException; 6 7 /** 8 * @author xujingyang 9 * @Description: 基础任务调度taskJob接口 10 * @date 2018/5/30 11 * <p> 12 */ 13 public interface BaseTaskJob extends Job { 14 void execute(JobExecutionContext context) 15 throws JobExecutionException; 16 }
七、创建任务实现类
1 package com.qunyi.jifenzhi_zx.module.taskJob; 2 3 import com.qunyi.jifenzhi_zx.core.quartz.BaseTaskJob; 4 import org.quartz.JobExecutionContext; 5 import org.quartz.JobExecutionException; 6 import org.slf4j.Logger; 7 import org.slf4j.LoggerFactory; 8 import org.springframework.stereotype.Component; 9 10 @Component 11 public class TestQuartz implements BaseTaskJob { 12 13 Logger logger = LoggerFactory.getLogger(this.getClass()); 14 15 public int i = 0; 16 17 @Override 18 public void execute(JobExecutionContext context) throws JobExecutionException { 19 i++; 20 logger.error("task2>>>>>>> " + i); 21 22 try { 23 // TaskJobUtil.getInstance().jobdelete(this.getClass().getSimpleName(),"ah");//执行完此任务就删除自己 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 } 28 }
八、创建controller测试动态添加任务
1 @PostMapping("/task") 2 public void task(HttpServletRequest request) throws Exception { 3 String name = PrimaryKeyUtil.nextId(); 4 TaskJobUtil.getInstance().addJob(TestQuartz.class, name, "*/1 * * * * ?"); 5 }
九、访问测试结果
总结及注意:
这种任务执行时是整个类都要初始化一遍的,不像spring的schedule只初始执行方法,这种每次执行类中的变量都会初始化。
添加新任务调度时,只需要新建类然后继承任务接口实现方法即可,这样在添加任务的时候只需要传新建类的class就可以了。
传同一个class的时候,是同一个任务方法,只不过新建了开了一个线程来执行而已。