JAVA基礎--Quartz定時任務調度的簡單應用(1)

      上次學習了Timer,我們知道了在項目中使用Timer有很多不足之處,那麼對那些複雜的定時調度任務,我們應該怎麼處理呢? 這個時候Quartz就閃亮登場啦,它會幫我們解決這些難題;

    1,  首先我們來說一下 Quartz 與 Timer 的區別:

        (1), 出身不同, Timer 爲JDK自帶; Quartz 爲開源項目;
        (2), 能力區別, Timer 遠沒有 Quartz 功能強大和完善, Timer 不能搞定的交給 Quartz ;
        (3), 底層機制不同, Timer 利用後臺子線程實現, Quartz 可以用於線程池中;

    2, Quartz簡介:

        OpenSymphony提供的強大的開源任務調度框架;

        官網: http://www.quartz-scheduler.org/

        Maven倉庫:

<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

3, 好了,先來個HelloWorld:

    (1),引入Jar包;(不用Maven的話需要引入: quartz-2.3.0.jar, slf4j-api-1.7.25.jar)

    (2),創建任務:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloJob implements Job{

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
        // TODO 具體的業務邏輯:
        System.out.println("Hellow Quartz! 執行時間:"+sf.format(calender.getTime()));
    }

}

    (3),調度任務:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
        
        // 創建一個 JobDetail 實例,將該實例與 HelloJob Class 綁定;
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob").build();
        // 創建一個Trigger實例,定義該job立即執行,並且每隔1秒鐘重複執行一次;
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1).repeatForever())
                .build();
        // 創建 Scheduler 實例;
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        System.out.println("當前時間:"+sf.format(calender.getTime()));
        
        scheduler.start();
        
    }
}

4, 現在我們再來分析一下上面的例子:

    (1), 首先是我們要創建一個Job: HelloJob; Job的應用及生命週期:

/**
 * (1),Job接口非常容易實現,只有一個execute方法,類似TimerTask的run方法,在裏面編寫業務邏輯;
 * (2),Job實例在Quartz中的生命週期: 
 *      每次調度器執行Job時,它在調用execute方法前會創建一個新的Job實例.當調用完成後,關聯的Job對象實例會被釋放,之後會被垃圾回收;
 **/
public class HelloJob implements Job{

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        // TODO 具體的業務邏輯:
        
    }

}

 (2), 之後需要調用我們的HelloJob,那麼就有了JobDetail:

 public class Test{
     public static void main(String[] args) throws SchedulerException {
       /**
         * 創建一個 JobDetail 實例,將該實例與 HelloJob Class 綁定;
         * 
         * JobDetail爲Job實例提供了許多設置屬性,以及JobDataMap成員變量屬性,
         * 它用來儲存特定Job實例的狀態信息,調度器需要藉助JobDetail對象來添加Job實例.
         * 重要屬性: name, group, jobClass, jobDataMap
         * */
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob","group1").build();
        //我們可以通過 jobDetail.getKey() 來獲取這些屬性,方便以後調度;
        System.out.println("jobDetail name is "+jobDetail.getKey().getName());
        System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
        System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
    }
}

5, 通過 JobExecutionContext 來獲取向 Job 中傳入的參數:

(1), 傳入參數:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
        
        /**
         * 創建一個 JobDetail 實例,將該實例與 HelloJob綁定;
         * 
         * JobDetail爲Job實例提供了許多設置屬性,以及JobDataMap成員變量屬性,它用來儲存特定Job實例的狀態信息,調度器需要藉助JobDetail對象來添加Job實例.
         * 重要屬性: name, group, jobClass, jobDataMap
         * */    
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                //傳入JobDetail的屬性及參數
                .withIdentity("myJob","group1")
                .usingJobData("message", "HelloJobDetail")
                .usingJobData("FloatMessage", 2.2F)
                .build();
        
        //通過 getKey()獲取相關屬性;
        System.out.println("jobDetail name is "+jobDetail.getKey().getName());
        System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
        System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
        
        // 創建一個Trigger實例,定義該job立即執行,並且每隔1秒鐘重複執行一次;
        Trigger trigger = TriggerBuilder.newTrigger()

                //傳入Trigger(觸發器)的屬性及參數
                .withIdentity("myTrigger")
                .usingJobData("message", "HelloTrigger")
                .usingJobData("DoubleMessage", 2.4D)

                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1).repeatForever())
                .build();
        
        // 創建 Scheduler 實例;
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        System.out.println("當前時間:"+sf.format(calender.getTime()));
        
        scheduler.start();
        
    }
}

(2), 獲取參數:

package quartz;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;

public class HelloJob implements Job{
    /**
     * JobExecutionContext :
     * (1), 當Scheduler(調度器) 調用一個Job,就會將JobExecutionContext傳遞給Job的execute()方法;
     * (2), Job能通過JobExecutionContext對象訪問到Quartz運行時候的環境及Job本身的明細數據;
     **/
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException { 
        //獲取傳入的任務屬性信息;
        JobKey jobKey = context.getJobDetail().getKey();
        System.out.println("Job(任務) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
        //獲取傳入的觸發器屬性信息;
        TriggerKey triggerKey = context.getTrigger().getKey();
        System.out.println("Trigger(觸發器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());


        /**
         * JobDataMap: 
         *  (1), 在進行任務調度時JobDataMap存儲,在JobExecutionContext中,非常方便獲取.
         *  (2), JobDataMap可以用來裝載任何可序列化的數據對象,當Job實例對象被執行時,這些參數對象會傳遞給它.
         *  (3), JobDateMap實現了JDK的Map接口,並且添加了一些非常方便的方法用來存取基本數據類型.
         *  獲取方試:
         * */
        //獲取傳入的Job參數信息;
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
        //獲取傳入的Trigger參數信息;
        JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
        System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));

        
        /* --------------獲取JobDataMap方法二: 合併獲取--------------- */
        JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
        System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage")+"\n");
        /**
         * 測試證明: 當Trigger與JobDetail中傳入的Key值相同,使用mergedJobDataMap獲取,則優先獲取Trigger中的Key值;JobDetail中的Key值被覆蓋掉;
         * */
        
    }
}

(3), 通過set,get方法獲取參數:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;

/**
 * (1),Job接口非常容易實現,只有一個execute方法,類似TimerTask的run方法,在裏面編寫業務邏輯;
 * (2),Job實例在Quartz中的生命週期: 
 *      每次調度器執行Job時,它在調用execute方法前會創建一個新的Job實例.當調用完成後,關聯的Job對象實例會被釋放,之後會被垃圾回收;
 **/
public class HelloJob implements Job{
    
    private String message;
    private float FloatMessage;
    private double DoubleMessage;
    
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public float getFloatMessage() {
        return FloatMessage;
    }
    public void setFloatMessage(float floatMessage) {
        FloatMessage = floatMessage;
    }
    public double getDoubleMessage() {
        return DoubleMessage;
    }
    public void setDoubleMessage(double doubleMessage) {
        DoubleMessage = doubleMessage;
    }


    /**
     * JobExecutionContext是什麼:
     * (1), 當Scheduler(調度器) 調用一個Job,就會將JobExecutionContext傳遞給Job的execute()方法;
     * (2), Job能通過JobExecutionContext對象訪問到Quartz運行時候的環境及Job本身的明細數據;
     **/
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
        // TODO 具體的業務邏輯:
        System.out.println("Hellow Quartz! 執行時間:"+sf.format(calender.getTime()));
        
        //獲取傳入的任務屬性信息;
        JobKey jobKey = context.getJobDetail().getKey();
        System.out.println("Job(任務) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
        //獲取傳入的觸發器屬性信息;
        TriggerKey triggerKey = context.getTrigger().getKey();
        System.out.println("Trigger(觸發器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());
        
        /**
         * JobDataMap: 
         *  (1), 在進行任務調度時JobDataMap存儲,在JobExecutionContext中,非常方便獲取.
         *  (2), JobDataMap可以用來裝載任何可序列化的數據對象,當Job實例對象被執行時,這些參數對象會傳遞給它.
         *  (3), JobDateMap實現了JDK的Map接口,並且添加了一些非常方便的方法用來存取基本數據類型.
         *  獲取方試:
         * */
        //獲取傳入的Job參數信息;
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
        //獲取傳入的Trigger參數信息;
        JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
        System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));
        
        /* --------------獲取JobDataMap方法二: 合併獲取--------------- */
        JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
        System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage"));
        /**
         * 測試證明: 當Trigger與JobDetail中傳入的Key值相同,使用mergedJobDataMap獲取,則優先獲取Trigger中的Key值;JobDetail中的Key值被覆蓋掉;
         * */
        
        /* --------------獲取JobDataMap方法三: 屬性 get,set--------------- */
        System.out.println("ALL get param: "+message+", "+FloatMessage+", "+DoubleMessage+"\n");
        /**
         * Job實現類中創建setter方法對應JobDataMap的鍵值(Quartz框架默認的JobFactory實現類在初始化Job實例對象時會自動調用這些setter方法);
         * */
        
        
    }
}

6, Trigger 設置開始/結束 觸發時間:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
        
        /**
         * 創建一個 JobDetail 實例,將該實例與 HelloJob綁定;
         * 
         * JobDetail爲Job實例提供了許多設置屬性,以及JobDataMap成員變量屬性,它用來儲存特定Job實例的狀態信息,調度器需要藉助JobDetail對象來添加Job實例.
         * 重要屬性: name, group, jobClass, jobDataMap
         * */
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("myJob","group1")
                .usingJobData("message", "HelloJobDetail")
                .usingJobData("FloatMessage", 2.2F)
                .build();
        
        // 創建一個Trigger實例,定義該job立即執行,並且每隔1秒鐘重複執行一次;
        
        /**
         * Trigger是Quartz中的出觸發器,用來告訴調度任務作業什麼時候觸發.即Trigger對象是用來觸發Job的;
         * 通用屬性:
         *  JobKey: 表示Job實例的標識,觸發器被觸發時,該指定的Job實例會被執行;
         *  StartTime: 表示觸發器的時間表 首次被觸發的時間.值爲java.util.Date;
         *  EndTime: 指定觸發器的不在被觸發的時間.值的類型是java.util.Date;
         * */
        //開始執行時間爲當前時間的3秒後
        Date startTime = new Date();
        startTime.setTime(startTime.getTime()+3000);
        //結束執行時間爲當前時間的6秒後
        Date endTime = new Date();
        endTime.setTime(endTime.getTime()+6000);
        
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger")
                .usingJobData("DoubleMessage", 2.4D)
                
                //設置開始/結束時間
                .startAt(startTime)
                .endAt(endTime)
                
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
                .build();
        
        // 創建 Scheduler 實例;
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        System.out.println("當前時間:"+sf.format(calender.getTime()));
        
        scheduler.start();
        
    }
}

Job中也可以獲取 開始/結束 時間:

public class HelloJob implements Job{
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
        // TODO 具體的業務邏輯:
        System.out.println("Hellow Quartz! 執行時間:"+sf.format(calender.getTime()));
        
        /* --------------------------獲取開始/結束時間--------------------- */
        Trigger trigger = context.getTrigger();
        Date startTime = trigger.getStartTime();
        Date endTime = trigger.getEndTime();
        System.out.println("StartTime is "+sf.format(startTime));
        System.out.println("EndTime is "+sf.format(endTime)+"\n");        
    }
}

7, SimpleTrigger設置執行條件:


        /**
         * SimpleTrigger: 在一個指定時間段內執行一次任務,或在指定時間間隔內多次執行任務;
         * 注意: (1)重複次數可以爲0,正整數或是 SimpleTrigger.REPEAT_INDEFINITELY 常量值.
         *      (2)重複執行時間間隔必須爲0或長整數;
         *      (3)一擔被指定了endTime參數,那麼它會覆蓋重複次數參數的效果;
         * */
        SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
                .withIdentity("myTrigger")
                .usingJobData("DoubleMessage", 2.4D)
                
                //設置開始/結束時間
                .startAt(startTime)
                .endAt(endTime)
                
                //設置多次執行條件
                .withSchedule(
                        SimpleScheduleBuilder
                        .simpleSchedule()
                        //時間間隔,單位有: 秒, 毫秒, 分鐘, 小時
                        .withIntervalInSeconds(1)
                        //循環執行
//                        .repeatForever()
                        //執行次數, (參數+1)次, 參數爲0執行1次,參數爲1執行2次,...; 參數爲-1,則執行無窮
                        .withRepeatCount(-1)
                        )
                
                .build();

到這我們已經對Quartz有了一定的認識,那麼下次再來分享更復雜的任務調度方式;看看怎麼在某個節日來調度的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章