二、Quartz集成-项目使用

(一)、封装工具类

  上一篇说明了一下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来使用就行。

Quartz的快速使用说完了,大家可以先参考QuartzUtils.java里面的代码进行其他功能实现,如果有说明不准确的地方,请指正,谢谢!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容