(一)、封装工具类
上一篇说明了一下Quartz的下载和安装,接下来说具体怎么使用,为了能尽快在项目中使用,下面贴出一个封装好的工具类,以后可以根据情况自己进行封装。QuartzUtils文件内容:
package com.common.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Component;
/**
* Quqrtz任务管理类
* @version 1.0
*/
public class QuartzUtils {
/** 默认任务组名称 */
public static final String JOB_GROUP_NAME = "DEFAULT_JOB_GROUP_NAME";
/** 默认触发器组名称 */
public static final String TRIGGER_GROUP_NAME = "DEFAULT_TRIGGER_GROUP_NAME";
//spring bean中配置的任务调度器实例 这里直接使用
private static Scheduler scheduler;
static{
// 此种方式是从Spring中获取bean
scheduler = SpringContextHolder.getBean("quartzScheduler");
// 下面这种方式是让Quartz根据quartz.properties配置文件手动初始化调度器,quartz.properties文件放在classpath目录下
/*
StdSchedulerFactory factory = new StdSchedulerFactory();
try {
factory.initialize("quartz.properties");
scheduler = factory.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
throw new RuntimeException("scheduler init fail.");
}
*/
}
/**
* 检查任务是否启动
* @param jobName 任务名
* @param jobGroupName 任务组名称
*/
public static boolean checkJobIsExists(String jobName, String jobGroupName) throws SchedulerException {
String[] jobNames = scheduler.getJobNames(jobGroupName);
List<String> jobNameList = Arrays.asList(jobNames);
return jobNameList.contains(jobName);
}
/**
* 添加一个定时任务(标准)
* @param jobName 任务名
* @param cls 任务执行类
* @param time 时间设置
* @return 任务启动结果
*/
public static void addJob(String jobName, Class<? extends Job> cls, String time) throws RuntimeException{
try {
// 任务名,任务组,任务执行类
JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);
jobDetail.setRequestsRecovery(true);
// 创建触发器
CronTrigger trigger = new CronTrigger(jobDetail.getName(), TRIGGER_GROUP_NAME); // 触发器名,触发器组
trigger.setCronExpression(time); // 触发器时间设定
// 调度任务
if(!scheduler.isShutdown()) {
scheduler.scheduleJob(jobDetail, trigger);
}
// 启动调度器
if (!scheduler.isStarted()) {
scheduler.start();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("添加一个定时任务失败: 方法(addJob) 失败原因:" + e.getMessage());
}
}
/**
* 添加一个定时任务 可以将任何对象保存到此Job中(将对象放到jobDataMap中,map中的key为jobName)
* @param jobName 任务名
* @param cls 任务执行类
* @param time 时间设置
* @param obj 需要保存的对象
* @return 启动结果
*/
public static void addJobAndData(String jobName, Class<? extends Job> cls, String time, Object obj) throws RuntimeException {
try {
// 任务名,任务组,任务执行类
JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);
jobDetail.setRequestsRecovery(true);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put(jobName, obj);
jobDetail.setJobDataMap(jobDataMap);
// 创建触发器
CronTrigger trigger = new CronTrigger(jobDetail.getName(), TRIGGER_GROUP_NAME); // 触发器名,触发器组
trigger.setCronExpression(time); // 触发器时间设定
// 调度任务
if(!scheduler.isShutdown()) {
scheduler.scheduleJob(jobDetail, trigger);
}
// 启动调度器
if (!scheduler.isStarted()) {
scheduler.start();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("添加一个定时任务失败: 方法(addJobAndData) 失败原因:" + e.getMessage());
}
}
/**
* 暂停一个任务
* @param jobName 任务名称
* @return 暂停结果
*/
public static void pauseJob(String jobName) throws RuntimeException {
try {
scheduler.pauseTrigger(jobName, TRIGGER_GROUP_NAME); // 停止触发器
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("暂停一个定时任务失败: 方法(pauseJob) 失败原因:" + e.getMessage());
}
}
/**
* 恢复一个任务
* @param jobName 任务名称
* @return 恢复结果
*/
public static void resumeJob(String jobName) throws RuntimeException {
try {
scheduler.resumeTrigger(jobName, TRIGGER_GROUP_NAME); // 恢复触发器
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("恢复一个定时任务失败: 方法(resumeJob) 失败原因:" + e.getMessage());
}
}
/**
* 删除一个任务
* @param jobName 任务名称
* @return 删除结果
*/
public static void removeJob(String jobName) throws RuntimeException {
try {
scheduler.pauseTrigger(jobName, TRIGGER_GROUP_NAME); // 停止触发器
scheduler.unscheduleJob(jobName, TRIGGER_GROUP_NAME); // 移除触发器
scheduler.deleteJob(jobName, JOB_GROUP_NAME); // 删除任务
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("删除一个定时任务失败: 方法(removeJob) 失败原因:" + e.getMessage());
}
}
/**
* 启动所有定时任务(启动调度器)
* @return 启动结果
*/
public static void startJobs() throws RuntimeException{
try {
scheduler.start();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("启动所有定时任务失败: 方法(startJobs) 失败原因:" + e.getMessage());
}
}
/**
* 关闭所有定时任务(关闭调度器)
* @return 启动结果
*/
public static void shutdownJobs() throws RuntimeException{
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException("关闭所有定时任务失败: 方法(shutdownJobs) 失败原因:" + e.getMessage());
}
}
/** 当前时间常量标识 */
public static final String CURR_TIME = "CURR_TIME";
/**将指定时间加上N天 然后计算出Quartz定时的时间表达式(eg: "0 22 14 12 8 ? 2015") */
public static String countQuartzTimeAndDays(String dateTime, int days) throws RuntimeException{
//将字符串转化为日期对象
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
if(CURR_TIME.equals(dateTime)){
date = new Date();
}else{
try {
date = format.parse(dateTime);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("计算时间表达式失败: 方法(countQuartzTimeAndDays) 失败原因:" + e.getMessage());
}
}
Calendar currCalendar = Calendar.getInstance(); //当前的时间
currCalendar.setTime(date);
currCalendar.add(Calendar.DAY_OF_MONTH, days);
return countQuartzTime(format.format(currCalendar.getTime()));
}
/**将指定时间加上N秒 然后计算出Quartz定时的时间表达式(eg: "0 22 14 12 8 ? 2015") */
public static String countQuartzTimeAndSeconds(String dateTime, int seconds) throws RuntimeException{
//将字符串转化为日期对象
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
if(CURR_TIME.equals(dateTime)){
date = new Date();
}else{
try {
date = format.parse(dateTime);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("计算时间表达式失败: 方法(countQuartzTimeAndSeconds) 失败原因:" + e.getMessage());
}
}
Calendar currCalendar = Calendar.getInstance(); //当前的时间
currCalendar.setTime(date);
currCalendar.add(Calendar.SECOND, seconds);
return countQuartzTime(format.format(currCalendar.getTime()));
}
/**
* 根据时间 计算出Quartz定时的时间表达式(eg: "0 22 14 12 8 ? 2015")
* @param time 定时时间字符串 精确到时分秒 注意: 不能为过去时
* @return 返回Quartz指定的时间表达式 如果转换失败 返回null
*/
public static String countQuartzTime(String time) throws RuntimeException{
Calendar sendCalendar = Calendar.getInstance(); //定时时间
Calendar currCalendar = Calendar.getInstance(); //当前时间
//将字符串转化为日期对象
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date sendDate = null;
try {
sendDate = format.parse(time);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("计算时间表达式失败: 方法(countQuartzTime) 失败原因:" + e.getMessage());
}
sendCalendar.setTime(sendDate);
currCalendar.setTime(new Date());
if(sendCalendar.after(currCalendar)){
int seconds = sendCalendar.get(Calendar.SECOND);
int minutes = sendCalendar.get(Calendar.MINUTE);
int hours = sendCalendar.get(Calendar.HOUR_OF_DAY);
int day = sendCalendar.get(Calendar.DAY_OF_MONTH);
int month = sendCalendar.get(Calendar.MONTH) + 1;
int year = sendCalendar.get(Calendar.YEAR);
//拼接表达式
StringBuffer sb = new StringBuffer();
sb.append(seconds).append(" ").append(minutes).append(" ").append(hours).append(" ").append(day)
.append(" ").append(month).append(" ").append("?").append(" ").append(year);
return sb.toString();
}
throw new RuntimeException("计算时间表达式失败: 方法(countQuartzTime) 失败原因: 参数时间已是过时时间,必须是未来时间!");
}
}
后面的篇章会讲到Quartz具体的内部实现细节,现在先从最简单添加一个Job任务开始。
(二)、添加一个简单的job任务
1、第一步创建一个java类,实现org.quartz.Job接口,重写execute()方法,示例:
package com.modules.monitor.timer;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class DemoJob implements Job{
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("demo 演示");
}
}
org.quartz.Job接口是一个无状态的任务接口,实现此接口的任务每当触发器触发时都会准时执行,调度器并不关心此任务是否有其他实例正在运行,如果任务存在阻塞,可能会导致大量的任务实例并行执行。如果业务中存在不允许并行执行的任务,此时就用到有状态的org.quartz.StatefulJob接口,此接口的任务实现类不能并行执行,必须等上一次触发器触发执行完成后 才可以进行下一次触发执行。
2、第二步调用QuartzUtils.java中addJob方法
public static void addJob(String jobName, Class<? extends Job> cls, String time)
参数:
- String jobName是任务的名称,建议使用"Job类名+uuid"作为名称,保证可快速识别和保证唯一;
- Class cls是需要执行的job类的class对象,例如上面的DemoJob.class;
- time是时间表达式,可以使用QuartzUtils中的countQuartzTime方法计算出表达式
大家也可以通过@在线Cron表达式生成器网站生成
调用示例:
String timeCron = QuartzUtils.countQuartzTime("2019-1-18 10:45:57");
QuartzUtils.addJob("DemoJob-uuid",DemoJob.class, timeCron);
调用完成!就可以等触发器触发执行任务了。
(三)、添加一个带参数的job任务
1、第一步创建一个java类,实现org.quartz.Job接口,重写execute()方法,示例:
package com.modules.monitor.timer;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class DemoJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
// 任务详情类
JobDetail jobDetail = context.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
// get方法中的key名称使用jobDetail.getName()是因为addJobAndData方法封装的
// 自己可以定义其他的key名称
String msg = (String) jobDataMap.get(jobDetail.getName());
// 打印传递过来的参数
System.out.println(msg);
}
}
2、第二步调用QuartzUtils.java中addJobAndData方法
public static void addJobAndData(String jobName, Class<? extends Job> cls, String time, Object obj)
参数:
- 其他参数在上面已讲解到
- Object obj是需要传递的参数
调用示例:
String timeCron = QuartzUtils.countQuartzTime("2019-1-18 10:45:57");
QuartzUtils.addJob("DemoJob-uuid",DemoJob.class, timeCron, "我是一段任务执行时打印的文本");
调用完成!这里传递参数的核心载体是org.quartz.JobDataMap类,将此类当做java中的Map来使用就行。