Quartz Scheduler Calendar日歷的使用


Quartz Calendar 日歷的使用

quartz引擎為我們提供了日歷的功能,讓我們可以自己定義一個時間段,可以控制觸發器在這個時間段內觸發或者不觸發,比如可以設置節假日,工作時間早8晚5等等。

下面這張表是quartz為我們提供的所有日歷組件,我們可以根據具體需求選擇使用,並且還支持擴展。

 

AnnualCalendar This implementation of the Calendar excludes a set of days of the year.
BaseCalendar This implementation of the Calendar may be used (you don't have to) as a base class for more sophisticated one's.
CronCalendar This implementation of the Calendar excludes the set of times expressed by a given CronExpression.
DailyCalendar This implementation of the Calendar excludes (or includes - see below) a specified time range each day.
HolidayCalendar This implementation of the Calendar stores a list of holidays (full days that are excluded from scheduling).
MonthlyCalendar This implementation of the Calendar excludes a set of days of the month.
WeeklyCalendar This implementation of the Calendar excludes a set of days of the week.

 

一、AnnualCalendar

This implementation of the Calendar excludes a set of days of the year. You may use it to exclude bank holidays which are on the same date every year 

年度日歷,這個日歷可以設置一組日期的集合,當與trigger關聯后,trigger在設定的這些日期之內都不會觸發。

// Add the holiday calendar to the schedule
AnnualCalendar holidays = new AnnualCalendar(); //這里設置日期 年月日 //注意這里的一個坑(研究了大變天才發現坑在這里),就是月份要比實際月份少一個月 //2014,7,21 實際是2014年8月21日
Calendar calendar = new GregorianCalendar(2014, 7, 21); holidays.setDayExcluded(calendar, true); // tell the schedule about our holiday calendar
sched.addCalendar("holidays", holidays, false, false);

觸發器每隔一小時觸發一次,並與"holidays"關聯,當日期到達上面設置的日期的時候,觸發器不觸發。

SimpleTrigger trigger = newTrigger()   .withIdentity("trigger1", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInHours(1) .repeatForever())
  .modifiedByCalendar(
"holidays")  .build();

看完上面例子的同學是不是認為 日歷設置成2014-07-21 只有在2014年才生效,這里也是一個坑(官方汶上上有說明(上面紅色)),一定要注意這里設置的年是沒有意義的。它會在每一年的這個日期都生效

下面是它計算時間的源碼,看到沒,並沒有計算設置的年份。

    public boolean isDayExcluded(java.util.Calendar day) { if (day == null) { throw new IllegalArgumentException( "Parameter day must not be null"); } // Check baseCalendar first
        if (! super.isTimeIncluded(day.getTime().getTime())) { return true; } int dmonth = day.get(java.util.Calendar.MONTH); int dday = day.get(java.util.Calendar.DAY_OF_MONTH); if (dataSorted == false) { Collections.sort(excludeDays, new CalendarComparator()); dataSorted = true; } Iterator<java.util.Calendar> iter = excludeDays.iterator(); while (iter.hasNext()) { java.util.Calendar cl = (java.util.Calendar) iter.next(); // remember, the list is sorted if (dmonth < cl.get(java.util.Calendar.MONTH)) { return false; } if (dday != cl.get(java.util.Calendar.DAY_OF_MONTH)) { continue; } if (dmonth != cl.get(java.util.Calendar.MONTH)) { continue; } return true; } return false; }

 

二、BaseCalendar

This implementation of the Calendar may be used (you don't have to) as a base class for more sophisticated one's.

它是一個基類,為上面表格中的各種日歷提供了一些基礎的方法,如果我們需要自定義日期可以繼承它,正常情況我們使用quartz為我們提供好的日歷就足夠了。

 

三、CronCalendar

This implementation of the Calendar excludes the set of times expressed by a given CronExpression. For example, you could use this calendar to exclude all but business hours (8AM - 5PM) every day using the expression "* * 0-7,18-23 ? * *".

Cron表達式日歷,可以寫一個表達式來排除一個時間范圍,比如可以設置為排除所有的時間,但是工作時間除外,也就是 在早8點-晚5點觸發,其他時間暫停。

CronCalendar calendar = new CronCalendar("* * 0-7,18-23 ? * *"); sched.addCalendar("business", calendar, false, false);

 

四、DailyCalendar

This implementation of the Calendar excludes (or includes - see below) a specified time range each day. For example, you could use this calendar to exclude business hours (8AM - 5PM) every day. Each DailyCalendar only allows a single time range to be specified, and that time range may not cross daily boundaries (i.e. you cannot specify a time range from 8PM - 5AM). If the property invertTimeRange is false (default), the time range defines a range of times in which triggers are not allowed to fire. If invertTimeRange is true, the time range is inverted – that is, all times outside the defined time range are excluded.

Note when using DailyCalendar, it behaves on the same principals as, for example, WeeklyCalendar. WeeklyCalendar defines a set of days that are excluded every week. Likewise, DailyCalendar defines a set of times that are excluded every day.

時間范圍日歷,定義一個時間范圍,可以讓觸發器在這個時間范圍內觸發,或者在這個時間范圍內不觸發,每一個DailyCalendar的實例只能設置一次時間范圍,並且這個時間范圍不能超過一天的邊界,比如你不能定義一個時間范圍是(晚上8點至第二天早上5點),如果invertTimeRange這個屬性等於false(默認),那么定義的時間范圍內觸發器不會觸發,相反如果invertTimeRange=true 那么只有在這個時間范圍內觸發器才會觸發,這個時間范圍以外的時間都被排除。

 

定義一個時間范圍 s=當前時間 e=當前時間+10秒鍾。

然后創建一個觸發器立即觸發,時間間隔3秒,無限重復。

invertTimeRange=true 這個觸發器會在s-e這個時間范圍內觸發,invertTimeRange=false 這個觸發器會在s-e這個時間范圍之外觸發。

DailyCalendar的構造參數也支持String new DailyCalendar("20:57:00", "20:59:00"),還有其他很多方式可以參見官方文檔。

Calendar s = Calendar.getInstance(); s.setTime(new Date()); Calendar e = Calendar.getInstance(); e.setTime(futureDate(10, IntervalUnit.SECOND)); DailyCalendar dailyCalendar = new DailyCalendar(s, e); //DailyCalendar dailyCalendar = new DailyCalendar("20:57:00", "20:59:00");
dailyCalendar.setInvertTimeRange(true); sched.addCalendar("dailyCalendar", dailyCalendar, false, false);
SimpleTrigger trigger = newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(3) .repeatForever()) .modifiedByCalendar("dailyCalendar") .build();

 

五、HolidayCalendar

This implementation of the Calendar stores a list of holidays (full days that are excluded from scheduling).

The implementation DOES take the year into consideration, so if you want to exclude July 4th for the next 10 years, you need to add 10 entries to the exclude list.

該日歷與AnnualCalendar一致,區別就是設置的year是有效的,也就是說如果你希望在未來的10年中 7月4日這天 這個日歷生效,那么你需要添加10個日期,分別是 2014-7-4 ,2015-7-4...... 2024-7-4 這樣才行。

HolidayCalendar holidays = new HolidayCalendar(); Calendar calendar = new GregorianCalendar(2014, 7, 21); holidays.addExcludedDate(calendar.getTime()); sched.addCalendar("holidays", holidays, false, false);

下面是查詢排除日期的算法,getStartOfDayJavaCalendar是把分秒時毫秒都去掉 只留年月日 然后判斷日期是否排除

    @Override
    public boolean isTimeIncluded(long timeStamp) {
        if (super.isTimeIncluded(timeStamp) == false) {
            return false;
        }

        Date lookFor = getStartOfDayJavaCalendar(timeStamp).getTime();

        return !(dates.contains(lookFor));
    }

 

六、MonthlyCalendar

This implementation of the Calendar excludes a set of days of the month. You may use it to exclude every first day of each month for example. But you may define any day of a month.

月日歷,你可以定義一個月當中的若干天,例如你可以設置每個月的第一天觸發器不進行觸發,當然你還可以定義一個月當中的任何一天。

下面例子給出每個月2,3,4號不觸發的日歷

MonthlyCalendar monthlyCalendar = new MonthlyCalendar(); monthlyCalendar.setDayExcluded(2, true); monthlyCalendar.setDayExcluded(3, true); monthlyCalendar.setDayExcluded(4, true); sched.addCalendar("monthlyCalendar", monthlyCalendar, false, false);

 

 

七、WeeklyCalendar

This implementation of the Calendar excludes a set of days of the week. You may use it to exclude weekends for example. But you may define any day of the week. By default it excludes SATURDAY and SUNDAY.

星期日歷,可以定義在一個星期當中的星期幾幾幾 是不觸發的日期,例如你可以定義么每個周末(星期天)觸發器不觸發,你也可以定義一周當中的任何一天或是幾天。默認情況SATURDAY ,SUNDAY 這兩天是沒排除的。

下面的例子設置了每個星期四觸發器不觸發,並且默認情況周六和周天也是不觸發的,這個是默認設置。如果需要周六周日也觸發,那么把它清掉就可以了(weeklyCalendar.setDayExcluded(Calendar.SATURDAY , false)像這樣)。一個需要注意的地方就是傳入參數不能直接寫數字星期幾,因為老外的日子計算的與我們不一樣,需要傳入(java.util.Calendar)的常量字段,這樣才准確。

WeeklyCalendar weeklyCalendar = new WeeklyCalendar(); weeklyCalendar.setDayExcluded(Calendar.THURSDAY, true); sched.addCalendar("weeklyCalendar", weeklyCalendar, false, false);

 

 

八、組合日歷的使用

上面的例子都是每一個觸發器(trigger)關聯一個日歷的例子,我們在構建觸發器的時候通過.modifiedByCalendar("日歷的key")關聯一個注冊到引擎當中的日歷,這種情況已經能夠滿足我們大部分的需求。

但是系統的需求往往是復雜多變的,假設有這樣一種情況,需要一個觸發器在 每周一到周五,早8點-晚晚5點 每隔1小時執行,那么該如何使用日歷呢?

 

其實我們不用日歷,使用一個CronTrigger也是可以搞定的,我們這里只不過是拋磚引玉而已。

那讓我們來寫一個組合日歷使用的例子:

 

DailyCalendar dailyCalendar = new DailyCalendar("8:00:00", "17:00:00"); dailyCalendar.setInvertTimeRange(false); WeeklyCalendar weeklyCalendar = new WeeklyCalendar(dailyCalendar); sched.addCalendar("weeklyCalendar", weeklyCalendar, false, false);

 

 

 

我們寫一個時間間隔的日歷dailyCalendar,將其作為參數傳遞給weeklyCalendar就可以了,這樣引擎在計算日歷日期的時候會先判斷dailyCalendar的時間范圍,然后再判斷weeklyCalendar是時間范圍,當條件都滿足的是否,觸發器才會被觸發,我們分析一下源碼:

    @Override
    public boolean isTimeIncluded(long timeStamp) {
        if (excludeAll == true) {
            return false;
        }

        // Test the base calendar first. Only if the base calendar not already
        // excludes the time/date, continue evaluating this calendar instance.
        if (super.isTimeIncluded(timeStamp) == false) { return false; }

        java.util.Calendar cl = createJavaCalendar(timeStamp);
        int wday = cl.get(java.util.Calendar.DAY_OF_WEEK);

        return !(isDayExcluded(wday));
    }

 

我們發現它首先調用 if (super.isTimeIncluded(timeStamp) == false) { return false; } 奧秘就在這里,我們繼續看。

    public boolean isTimeIncluded(long timeStamp) {

        if (timeStamp <= 0) {
            throw new IllegalArgumentException(
                    "timeStamp must be greater 0");
        }

        if (baseCalendar != null) {
            if (baseCalendar.isTimeIncluded(timeStamp) == false) { return false; }
        }

        return true;
    }

 

這里先判斷了baseCalendar,這個對象就是在構造參數傳遞進去的dailyCalendar , 也就是它先試用dailyCalendar 進行日期計算,然后自己在計算,這樣就完成了日歷的組合使用。

 

最后在補充說明一下往quartz的引擎中注冊日歷的方法。

addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) 

這個方法有四個參數

1、calName 日歷的名字,在構建觸發器時通過modifiedByCalendar("")這里使用。

2、calendar 日歷對象。

3、replace 當日歷已經存在的情況下是否替換,true=替換, false=不替換 如果不替換還出現重復的情況會拋出異常。

4、updateTriggers 這個參數比較重要,它的意思是當一個已經存在與調度引擎中的觸發器,並且已經引用了一個日歷,比如:一個(觸發器A)關聯了一個日歷,這個日歷過濾每個星期日。

現在過了一段時間這個日歷更新了(星期六也過濾),那么這個屬性是用來指示觸發器是否使用新的日歷。不然的話(觸發器A)仍然使用舊版本的日歷,如果在有新添加到引擎中的觸發器才會使用新日歷。

 

我們看一下源碼:RAMJobStore 還有其它的Store原理也都一樣。

 

    public void storeCalendar(String name,
            Calendar calendar, boolean replaceExisting, boolean updateTriggers)
        throws ObjectAlreadyExistsException {

        calendar = (Calendar) calendar.clone();
        
        synchronized (lock) {
    
            Object obj = calendarsByName.get(name);
    
            if (obj != null && replaceExisting == false) {
                throw new ObjectAlreadyExistsException(
                    "Calendar with name '" + name + "' already exists.");
            } else if (obj != null) {
                calendarsByName.remove(name);
            }
    
            calendarsByName.put(name, calendar);
    
            if(obj != null && updateTriggers) {
                Iterator<TriggerWrapper> trigs = getTriggerWrappersForCalendar(name).iterator();
                while (trigs.hasNext()) {
                    TriggerWrapper tw = trigs.next();
                    OperableTrigger trig = tw.getTrigger();
                    boolean removed = timeTriggers.remove(tw);

                    trig.updateWithNewCalendar(calendar, getMisfireThreshold());

                    if(removed) { timeTriggers.add(tw); } } }
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM