二、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里面的代碼進行其他功能實現,如果有說明不準確的地方,請指正,謝謝!
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容