首先定时任务实现方式有无数种,不过最终还是要基于 1、JDK 的Timer类 2、Quartz 3、SpringTask 。这三种方式。但是使用过程中用的最多的便是xml配置的方式,这种方式最简单,无代码侵入,也比较好理解。
但是却有个致命的缺点,比如你要改某个任务的触发时间,亦或是你要新增一个任务,暂停一个任务,那就比较麻烦了,需要停应用!改XML配置!重新启动!
这种方式特别不方便,而且还难受,最近重新研究了下Quartz的基本配置,可以实现定时任务的暂停,添加,删除,立即触发。是不是感觉非常的方便呢。下面看一下Quartz的用法吧!!
<!-- 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>
实现步骤
启动项目,启动task监听
读取数据库,将开启的任务job和trigger加载到scheduler调度器
根据任务调度运行job类
每次运行利用AdaptableJobFactory实例化job类,以便注入要运行的service
听着是不是很简单,直接上代码就明白了
1.Job接口(任务)
Job是定时任务用来实现具体功能的主要接口,可以在里面写想要实现的具体功能,Job接口提供了一个方法叫execute,想要实现Job接口必须实现execute方法,然后把具体的功能代码写入execute方法中,具体代码如下!!!
package com.xb.test.controller.quartz;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.annotation.Resource;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
/**
* @author XWJ
*2019年9月19日
*/
@DisallowConcurrentExecution//定时任务不并发
public class TestJob implements Job{
public void execute(JobExecutionContext context) {
try {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowTime=df.format(new Date());
System.out.println("定时任务测试"+nowTime);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2.scheduler(调度器)简介
scheduler是Quartz的核心,主要由StdScheduler类、Scheduler接口、StdSchedulerFactory类、SchedulerFactory接口、QuartzScheduler类实现,至于里面具体的源码我们就不看了,有兴趣的可以自己研究一下,scheduler主要的功能是用来对定时任务进行控制的,可以对Job进行添加,删除,修改,暂停,恢复等一系列的控制操作,
3.trigger(触发器)简介
顾名思义,trigger就是用来设置定时任务的触发时间的,里面的具体方法如下表
属性名 | 属性类型 | 属性简介 |
---|---|---|
name | trigger通用 | trigger名称 |
group | trigger通用 | trigger所属的组名 |
description | trigger通用 | trigger描述 |
calendarName | trigger通用 | 日历名称,指定使用哪个Calendar类,经常用来从trigger的调度计划中排除某些时间段 |
misfireInstruction | trigger通用 | 错过job(未在指定时间执行的job)的处理策略,默认为MISFIRE_INSTRUCTION_SMART_POLICY。详见这篇文章 |
priority | trigger通用 | 优先级,默认为5。当多个trigger同时触发job时,线程池可能不够用,此时根据优先级来决定谁先触发 |
jobDataMap | trigger通用 | 同job的jobDataMap。假如job和trigger的jobDataMap有同名key,通过getMergedJobDataMap()获取的jobDataMap,将以trigger的为准 |
startTime | trigger通用 | 触发开始时间,默认为当前时间。决定什么时间开始触发job |
endTime | trigger通用 | 触发结束时间。决定什么时间停止触发job |
nextFireTime | SimpleTrigger私有 | 下一次触发job的时间 |
previousFireTime | SimpleTrigger私有 | 上一次触发job的时间 |
repeatCount | SimpleTrigger私有 | 需触发的总次数 |
timesTriggered | SimpleTrigger私有 | 已经触发过的次数 |
repeatInterval | SimpleTrigger私有 | 触发间隔时间 |
4.Quartz主要工具类
这个工具类不需要我们做任何操作,直接使用即可,所有方法的参数都是来自于一个实体类,实体类代码看下面!!
package com.xb.test.controller.quartz;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zt.jingcai.entity.TaskDO;
/**
* @author XWJ
*2019年9月17日
*/
@Service
public class QuartzManager {
@Autowired
private Scheduler scheduler;
/**
* 添加任务
*
* @param scheduleJob
* @throws SchedulerException
*/
@SuppressWarnings("unchecked")
public void addJob(TaskDO task) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(task.getBeanClass()).newInstance()
.getClass());
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobName(), task.getJobGroup())// 任务名称和组构成任务key
.build();
// 定义调度触发规则
// 使用cornTrigger规则
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobName(), task.getJobGroup())// 触发器key
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取所有计划中的任务列表
*
* @return
* @throws SchedulerException
*/
public List<TaskDO> getAllJob() throws SchedulerException {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
List<TaskDO> jobList = new ArrayList<TaskDO>();
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
TaskDO job = new TaskDO();
job.setJobName(jobKey.getName());
job.setJobGroup(jobKey.getGroup());
job.setDescription("触发器:" + trigger.getKey());
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
job.setJobStatus(triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
job.setCronExpression(cronExpression);
}
jobList.add(job);
}
}
return jobList;
}
/**
* 所有正在运行的job
*
* @return
* @throws SchedulerException
*/
public List<TaskDO> getRunningJob() throws SchedulerException {
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
List<TaskDO> jobList = new ArrayList<TaskDO>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
TaskDO job = new TaskDO();
JobDetail jobDetail = executingJob.getJobDetail();
JobKey jobKey = jobDetail.getKey();
Trigger trigger = executingJob.getTrigger();
job.setJobName(jobKey.getName());
job.setJobGroup(jobKey.getGroup());
job.setDescription("触发器:" + trigger.getKey());
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
job.setJobStatus(triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
job.setCronExpression(cronExpression);
}
jobList.add(job);
}
return jobList;
}
/**
* 暂停一个job
*
* @param task
* @throws SchedulerException
*/
public void pauseJob(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
scheduler.pauseJob(jobKey);
}
/**
* 恢复一个job
*
* @param task
* @throws SchedulerException
*/
public void resumeJob(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
scheduler.resumeJob(jobKey);
}
/**
* 删除一个job
*
* @param task
* @throws SchedulerException
*/
public void deleteJob(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
scheduler.deleteJob(jobKey);
}
/**
* 立即执行job
*
* @param task
* @throws SchedulerException
*/
public void runJobNow(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
scheduler.triggerJob(jobKey);
}
/**
* 更新job时间表达式
*
* @param task
* @throws SchedulerException
*/
public void updateJobCron(TaskDO task) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
}
}
5.Job service注入问题
光上面这些还是不够,Job在实例化过程中是在Quartz中进行的,这时候我们将spring的东西注入进来,肯定是行不通的,所以需要这个类,如果想在Job里面进行注入操作就必须添加下面这个工具类!!
/**
*
*/
package com.xb.test.controller.quartz;
import org.quartz.spi.TriggerFiredBundle;
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 XWJ
*2019年9月17日
*/
@Component
public class JobFactory extends AdaptableJobFactory {
//这个对象Spring会帮我们自动注入进来,.
//为什么需要这个类呢,在我写的这个demo中,大家可以将此类删掉,发现程序也可以政策运营,可是我为什么还是加上呢。
//大家可以看下我们的任务类,大家可以看到Job对象的实例化过程是在Quartz中进行的,这时候我们将spring的东西注入进来,肯定是行不通的,所以需要这个类
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
6.实体类代码
package com.zt.jingcai.entity;
import java.io.Serializable;
import java.util.Date;
public class TaskDO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Long id;
// 任务名
private String jobName;
// 任务描述
private String description;
// cron表达式(不会写,百度可以搜到)
private String cronExpression;
// 任务执行时调用哪个类的方法 包名+类名
private String beanClass;
// 任务状态
private String jobStatus;
// 任务分组
private String jobGroup;
private String createUser;
private Date createTime;
private String updateUser;
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName == null ? null : jobName.trim();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description == null ? null : description.trim();
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression == null ? null : cronExpression.trim();
}
public String getBeanClass() {
return beanClass;
}
public void setBeanClass(String beanClass) {
this.beanClass = beanClass == null ? null : beanClass.trim();
}
public String getJobStatus() {
return jobStatus;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus == null ? null : jobStatus.trim();
}
public String getJobGroup() {
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup == null ? null : jobGroup.trim();
}
public String getCreateUser() {
return createUser;
}
public void setCreateUser(String createUser) {
this.createUser = createUser == null ? null : createUser.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateUser() {
return updateUser;
}
public void setUpdateUser(String updateUser) {
this.updateUser = updateUser == null ? null : updateUser.trim();
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
7.任务调度的具体代码(我这个是测试的你们可以参考)
/**
*
*/
package com.xb.test.controller.quartz;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.zt.jingcai.entity.TaskDO;
/**
* @author XWJ
*2019年9月17日
*/
@Component
public class JobTaskScheduling {
@Autowired
private QuartzManager quartzManager;
@PostConstruct
void UserClockRemindedTask() {
try {
TaskDO task = new TaskDO();
task.setJobName("userClockRemindedJob");
task.setDescription("用户每日打卡提醒");
task.setCronExpression("0 0/9 * * * ?");
task.setBeanClass("com.xb.test.controller.quartz.TestJob");
quartzManager.addJob(task);
} catch (Exception e) {
e.printStackTrace();
}
}
}