spring Quartz 源碼分析--觸發器類CronTriggerBean源碼剖析

    前面我們講到了Quartz框架在項目中的實現,在Quartz中的重要API有兩個重要的觸發器類:CronTrigger 和SimpleTrigger 
在Quartz框架中這兩個觸發器都繼承了一個抽象基類Trigger,這個類有觸發器共有的屬性name,jobName,group,jobGroup以及description;

但是在spring框架中使用的觸發器類是org.springframework.scheduling.quartz.CronTriggerBean ,該類的源碼如下:
package org.springframework.scheduling.quartz;

import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Constants;

public class CronTriggerBean extends CronTrigger
  implements JobDetailAwareTrigger, BeanNameAware, InitializingBean
{
  private static final Constants constants = new Constants(CronTrigger.class);
  private JobDetail jobDetail;
  private String beanName;

  public void setJobDataAsMap(Map jobDataAsMap)
  {
    getJobDataMap().putAll(jobDataAsMap);
  }

  public void setMisfireInstructionName(String constantName)
  {
    setMisfireInstruction(constants.asNumber(constantName).intValue());
  }

  public void setTriggerListenerNames(String[] names)
  {
    for (int i = 0; i < names.length; i++)
      addTriggerListener(names[i]);
  }

  public void setJobDetail(JobDetail jobDetail)
  {
    this.jobDetail = jobDetail;
  }

  public JobDetail getJobDetail() {
    return this.jobDetail;
  }

  public void setBeanName(String beanName) {
    this.beanName = beanName;
  }

  public void afterPropertiesSet() throws ParseException
  {
    if (getName() == null) {
      setName(this.beanName);
    }
    if (getGroup() == null) {
      setGroup("DEFAULT");
    }
    if (getStartTime() == null) {
      setStartTime(new Date());
    }
    if (getTimeZone() == null) {
      setTimeZone(TimeZone.getDefault());
    }
    if (this.jobDetail != null) {
      setJobName(this.jobDetail.getName());
      setJobGroup(this.jobDetail.getGroup());
    }
  }
}


 由此類源碼可知,該觸發器的核心代碼還是在CronTrigger 類中。
但是在這個類中,可以在框架配置文件中設置屬性jobDetail,至於另外一個屬性cronExpression的設置方法是在類CronTrigger 中的。
public class CronTrigger extends Trigger
{
	......
	private CronExpression cronEx = null;
	......
	//由此可知在spring配置文件中,屬性名是和set函數後面的字符串一樣的,但首字母小寫
	public void setCronExpression(String cronExpression) throws ParseException {
		TimeZone origTz = getTimeZone();
		this.cronEx = new CronExpression(cronExpression);
		this.cronEx.setTimeZone(origTz);
	......
}



我們都知道spring框架IOC的容器管理爲核心模塊,配置文件的一些bean的設置都是爲bean對象設置一些屬性值來初始化,在此的觸發器類分析,我們只關注觸發器如何進行實踐週期設置,時間字符串是如何解析的。
我們在spring配置文件中是隻配置觸發時間字符串的,而在CronTrigger類的public void setCronExpression(String cronExpression)內會根據這字符串來 CronExpression
對象,其中觸發時間的解析就發生在這個類裏面;
CronExpression類中解析後的結果存放在這些屬性中:
   
  protected transient TreeSet seconds;//秒 
  protected transient TreeSet minutes;//分
  protected transient TreeSet hours;//時
  protected transient TreeSet daysOfMonth;//月中第幾天
  protected transient TreeSet months;//月
  protected transient TreeSet daysOfWeek;//星期中第幾天
  protected transient TreeSet years;//年
  protected transient boolean lastdayOfWeek = false;//是否爲星期最後一天
  protected transient int nthdayOfWeek = 0;//
  protected transient boolean lastdayOfMonth = false;//月的最後一天,默認爲false	
  protected transient boolean nearestWeekday = false;
  protected transient boolean expressionParsed = false;
這些存儲時間信息的屬性會在類CronExpression中的下面函數進行初始化
  
protected void buildExpression(String expression)
    throws ParseException
  {
	//剛開始觸發時間是否解析設置爲false,開始解析則設置爲true
    this.expressionParsed = true;
	//初始化存儲解析結果的屬性
    try
    {
      if (this.seconds == null) {
        this.seconds = new TreeSet();
      }
      if (this.minutes == null) {
        this.minutes = new TreeSet();
      }
      if (this.hours == null) {
        this.hours = new TreeSet();
      }
      if (this.daysOfMonth == null) {
        this.daysOfMonth = new TreeSet();
      }
      if (this.months == null) {
        this.months = new TreeSet();
      }
      if (this.daysOfWeek == null) {
        this.daysOfWeek = new TreeSet();
      }
      if (this.years == null) {
        this.years = new TreeSet();
      }

      int exprOn = 0;
	//將觸發時間字符串分割成6個字符串
      StringTokenizer exprsTok = new StringTokenizer(expression, " \t", false);

	//遍歷這6個字符串,並分別處理,exprOn 從0-5依次增加,作爲辨別字符串不同意義的識別碼
      while ((exprsTok.hasMoreTokens()) && (exprOn <= 6)) {
        String expr = exprsTok.nextToken().trim();

	//對每個字符串進行格式判斷,如果不正確就中斷報錯。
        if ((exprOn == 3) && (expr.indexOf('L') != -1) && (expr.length() > 1) && (expr.indexOf(",") >= 0)) {
          throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);
        }

        if ((exprOn == 5) && (expr.indexOf('L') != -1) && (expr.length() > 1) && (expr.indexOf(",") >= 0)) {
          throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);
        }

	//對每個字符串進行按“,”分割,並對每個分割後的字符串進行按識別碼exprOn解析。
        StringTokenizer vTok = new StringTokenizer(expr, ",");
        while (vTok.hasMoreTokens()) {
          String v = vTok.nextToken();
          storeExpressionVals(0, v, exprOn);
        }

        exprOn++;
      }

	//如果出發時間字符串分割後的字符串數組大小少於5則中斷報錯,不符合語法
      if (exprOn <= 5) {
        throw new ParseException("Unexpected end of expression.", expression.length());
      }
	//因爲出發時間格式可以爲6個字符串,所以當處於這種情況的時候,就將第7個時間數據年設置成“*”,即每年的意義
      if (exprOn <= 6) {
        storeExpressionVals(0, "*", 6);
      }
	//get函數的方法體下面已經給出,指的是獲取該類對象中存儲觸發器時間的存儲結果。
      TreeSet dow = getSet(5);
      TreeSet dom = getSet(3);  

      boolean dayOfMSpec = !dom.contains(NO_SPEC);
      boolean dayOfWSpec = !dow.contains(NO_SPEC);

      if ((!dayOfMSpec) || (dayOfWSpec))
      {
        if ((!dayOfWSpec) || (dayOfMSpec))
        {
          throw new ParseException("Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);
        }
      }
    } catch (ParseException pe) {
      throw pe;
    } catch (Exception e) {
      throw new ParseException("Illegal cron expression format (" + e.toString() + ")", 0);
    }
  }

由這個方法確定,最後的每個字符的意義解析由方法storeExpressionVals完成。
get方法:
/*
*	觸發器時間解析後會存放在TreeSet屬性中,這個函數就是獲取這些屬性的引用。
*/
protected TreeSet getSet(int type)
  {
    switch (type) {
    case 0:
      return this.seconds;
    case 1:
      return this.minutes;
    case 2:
      return this.hours;
    case 3:
      return this.daysOfMonth;
    case 4:
      return this.months;
    case 5:
      return this.daysOfWeek;
    case 6:
      return this.years;
    }
    return null;
  }




 
protected int storeExpressionVals(int pos, String s, int type)
    throws ParseException
  {
    int incr = 0;
	//由字符串開始位置遍歷,跳過空字符,空字符包括空格,tab鍵,回車鍵等。
    int i = skipWhiteSpace(pos, s);
	//如果排除掉所有空字符後,只剩下一個字符或是沒有字符,則方法返回,解析結束。
    if (i >= s.length()) {
      return i;
    }
	//獲取第一個不爲空的字符
    char c = s.charAt(i);
	//判斷字符是否爲字母,觸發時間語法中,只有type爲4,5的兩項爲字母,至於L(當月或這周最後一天) LW(當月最後一個工作日)情況在後面考慮
    if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW"))) {
	//前面提到,只有type爲4,5的兩項爲字母,且這兩項設置的值爲字符個數爲3的單詞,即月份和周幾單詞的3個字母的縮寫
      String sub = s.substring(i, i + 3);
      int sval = -1;
      int eval = -1;
	//當這項爲月份時,通過getMonthNumber函數獲取月份數據,在此類屬性中,有關於月份單詞與數字的映射,星期的映射也是一樣。
      if (type == 4) {
        sval = getMonthNumber(sub) + 1;
	//月份數字不大於0報錯,接下來的類似情況就不贅述了
        if (sval <= 0) {
          throw new ParseException("Invalid Month value: '" + sub + "'", i);
        }
	//如果該項字符數量不只爲3,則該項形式就不是JAN(月份項)或者是SUN(星期項),就是JAN-DEC或者是SUN-SAT,下面就是解析第二種情況
        if (s.length() > i + 3) {
          c = s.charAt(i + 3);
          if (c == '-') {
            i += 4;
	//同樣是截取3個字符月份單詞,然後通過getMonthNumber函數獲取月份數字數據
            sub = s.substring(i, i + 3);
            eval = getMonthNumber(sub) + 1;
            if (eval <= 0)
              throw new ParseException("Invalid Month value: '" + sub + "'", i);
          }
        }
      }
	//這個是星期項的數據解析,該項的解析和月份項的解析類似,讀者可以自己分析
      else if (type == 5) {
        sval = getDayOfWeekNumber(sub);
        if (sval < 0) {
          throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i);
        }

        if (s.length() > i + 3) {
          c = s.charAt(i + 3);
          if (c == '-') {
            i += 4;
            sub = s.substring(i, i + 3);
            eval = getDayOfWeekNumber(sub);
            if (eval < 0) {
              throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i);
            }

          }
	//對於星期項,這個字符爲#的時候,後面跟着的一個數字表示這個月的第幾個星期。
          else if (c == '#') {
            try {
              i += 4;
              this.nthdayOfWeek = Integer.parseInt(s.substring(i));
		//因爲一個月最多橫跨5個星期,不可能橫跨6個以上的星期。
              if ((this.nthdayOfWeek < 1) || (this.nthdayOfWeek > 5))
                throw new Exception();
            }
            catch (Exception e) {
              throw new ParseException("A numeric value between 1 and 5 must follow the '#' option", i);
            }

          }
	//如果該項這個字符爲L時,表示是該月的最後一個星期,前面加數字或單詞表示最後一個星期的星期幾
          else if (c == 'L') {
            this.lastdayOfWeek = true;
            i++;
          }
        }
      }
      else {
        throw new ParseException("Illegal characters for this position: '" + sub + "'", i);
      }

      if (eval != -1) {
	//這個是將當每一項未設置遞增項時候,默認爲1,如月份項爲0-8,那麼此時會按1從0到8遞增將數字0,1,2,3,4....8放入月份TreeSet數據集合中。
        incr = 1;
      }
      addToSet(sval, eval, incr, type);
      return i + 3;
    }
	//如果首個字符爲?
    if (c == '?') {
      i++;
	//語法規定,?後面不會再有其他字符或數字,所以此處解析中斷跳出處理異常。
      if ((i + 1 < s.length()) && (s.charAt(i) != ' ') && (s.charAt(i + 1) != '\t'))
      {
        throw new ParseException("Illegal character after '?': " + s.charAt(i), i);
      }
	//?字符只能用於dayofmonth和dayofweek這兩項,即type爲3和5的兩項
      if ((type != 5) && (type != 3)) {
        throw new ParseException("'?' can only be specfied for Day-of-Month or Day-of-Week.", i);
      }
	//因爲在這兩項中,如果dayofmonth項已經有了?,那麼在dayofweek就不應該有?了。
      if ((type == 5) && (!this.lastdayOfMonth)) {
        int val = ((Integer)this.daysOfMonth.last()).intValue();
        if (val == 98) {
          throw new ParseException("'?' can only be specfied for Day-of-Month -OR- Day-of-Week.", i);
        }

      }
	//98表示字符爲?,99表示字符爲*;姑且是猜測。
      addToSet(98, -1, 0, type);
      return i;
    }

	//如果該項第一個字符爲*或者是/的時候
    if ((c == '*') || (c == '/')) {
	//此情況是該項只有*,後面就沒有了其他字符或數字
      if ((c == '*') && (i + 1 >= s.length())) {
	//由此能證實99就是表示*字符的。
        addToSet(99, -1, incr, type);
        return i + 1;
      }
	//這種情況是字符爲“/”的情況下,如果後面沒有了字符或者後面的字符爲空,解析就中斷報錯,拋出異常。
      if ((c == '/') && ((i + 1 >= s.length()) || (s.charAt(i + 1) == ' ') || (s.charAt(i + 1) == '\t')))
      {
        throw new ParseException("'/' must be followed by an integer.", i);
      }
	//字符爲*,且後面還有不爲空的字符
      if (c == '*') {
        i++;
      }
      c = s.charAt(i);
	//*字符後面的字符爲/
      if (c == '/') {
        i++;
	//如果“/”字符後面沒有了字符,就拋出異常。
        if (i >= s.length()) {
          throw new ParseException("Unexpected end of string.", i);
        }
	//得到“/”後面的數字,這個數字爲遞增量
        incr = getNumericValue(s, i);

        i++;
        if (incr > 10) {
          i++;
        }
	//下面是在不同type下對遞增量incr進行判斷,每一項都會有最大的值,因此incr的值不能超過這些值
        if ((incr > 59) && ((type == 0) || (type == 1)))
          throw new ParseException("Increment > 60 : " + incr, i);
        if ((incr > 23) && (type == 2))
          throw new ParseException("Increment > 24 : " + incr, i);
        if ((incr > 31) && (type == 3))
          throw new ParseException("Increment > 31 : " + incr, i);
        if ((incr > 7) && (type == 5))
          throw new ParseException("Increment > 7 : " + incr, i);
        if ((incr > 12) && (type == 4))
          throw new ParseException("Increment > 12 : " + incr, i);
      }
	//首字符爲*字符的後面沒有“/”的話,遞增量incr設置爲1.
      else {
        incr = 1;
      }
	//99表示的是*
      addToSet(99, -1, incr, type);
      return i;
    }
	//如果首字符爲L,表示本月最後一天或是一個星期的最後一天
    if (c == 'L') {
      i++;
      if (type == 3) {
        this.lastdayOfMonth = true;
      }
      if (type == 5) {
        addToSet(7, 7, 0, type);
      }
	//在dayofmonth項,L和W可以合着一起用,表示獲取離設置時間最近的工作日
      if ((type == 3) && (s.length() > i)) {
        c = s.charAt(i);
        if (c == 'W') {
          this.nearestWeekday = true;
          i++;
        }
      }
      return i;
    }
	//如果該項首字符爲數字,則該項形式有0,0-4, 10, 10-14,0/2等等
      if ((c >= '0') && (c <= '9')) {
      int val = Integer.parseInt(String.valueOf(c));
      i++;
	//如果只是0這種形式,就直接將這個放到TreeSet數據集合中去
      if (i >= s.length()) {
        addToSet(val, -1, -1, type);
      } else {
	//如果是其他形式,就獲取多位數字,並調用checkNext函數,即checkNext函數是解析數字形式的函數。
        c = s.charAt(i);
        if ((c >= '0') && (c <= '9')) {
          ValueSet vs = getValue(val, s, i);
          val = vs.value;
          i = vs.pos;
        }
        i = checkNext(i, s, val, type);
        return i;
      }
    } else {
      throw new ParseException("Unexpected character: " + c, i);
    }

    return i;
  }
對於這個函數都是如何解析觸發時間字符串的,我將在函數中的每段代碼這個做詳細的解釋。下面給出的是觸發時間的語法:
Cron表達式的格式:秒 分 時 日 月 周 年(可選)。
字段名              允許的值                    允許的特殊字符 
秒                     0-59                           , - * / 
分                     0-59                           , - * / 
小時                  0-23                           , - * / 
日                     1-31                            , - * ? / L W C
月                     1-12 or JAN-DEC        , - * / 
周幾                  1-7 or SUN-SAT         , - * ? / L C # 
年(可選字段)     empty                         1970-2099 , - * /
爲了獲取更加詳細的語法解釋,請讀者朋友可以在這個鏈接的文章學習http://blog.csdn.net/eacter/article/details/44308459,這篇文章解釋的非常詳細;
checkNext函數的源代碼:


protected int checkNext(int pos, String s, int val, int type)
    throws ParseException
  {
    int end = -1;
    int i = pos;

	//在之前已經處理了數爲一位的情況,這個if內代碼處理的是數爲兩位的情況
    if (i >= s.length()) {
      addToSet(val, end, -1, type);
      return i;
    }

    char c = s.charAt(pos);

	//繼續解析數字後面的字符,如果爲L的話
    if (c == 'L') {
	//如果是在dayofweek項,則設置最後一個星期屬性爲true
      if (type == 5) {
        if ((val < 1) || (val > 7))
          throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
        this.lastdayOfWeek = true;
      } else {
        throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i);
      }
	//將數放入dayofweek項的TreeSet數據集合中去
      TreeSet set = getSet(type);
      set.add(new Integer(val));
      i++;
      return i;
    }

	
	//繼續解析數字後面的字符,如果爲w的話,w只能用在dayofmonth項,則設置靠近設置時間的工作日(nearestWeekday = true)爲true
 if (c == 'W') { if (type == 3) this.nearestWeekday = true; else { throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i); } TreeSet set = getSet(type); set.add(new Integer(val)); i++; return i; }
	
	//繼續解析數字後面的字符,如果爲#的話,#只能用於dayofweek項,後面的數表示一個月的第幾個星期
 if (c == '#') { if (type != 5) { throw new ParseException("'#' option is not valid here. (pos=" + i + ")", i); } i++; try {
	//設置第幾個星期值
        this.nthdayOfWeek = Integer.parseInt(s.substring(i));
        if ((this.nthdayOfWeek < 1) || (this.nthdayOfWeek > 5))
          throw new Exception();
      }
      catch (Exception e) {
        throw new ParseException("A numeric value between 1 and 5 must follow the '#' option", i);
      }

      TreeSet set = getSet(type);
      set.add(new Integer(val));
      i++;
      return i;
    }
	//-用於兩個數之間,表示兩個數的範圍內,如果第二個數後面沒有/的話,遞增量默認爲1
    if (c == '-') {
      i++;
      c = s.charAt(i);
      int v = Integer.parseInt(String.valueOf(c));
      end = v;
      i++;
      if (i >= s.length()) {
        addToSet(val, end, 1, type);
        return i;
      }
      c = s.charAt(i);
      if ((c >= '0') && (c <= '9')) {
	//獲取-右邊的數
        ValueSet vs = getValue(v, s, i);
        int v1 = vs.value;
        end = v1;
        i = vs.pos;
      }
	//第二個數後面跟了/的情況
      if ((i < s.length()) && ((c = s.charAt(i)) == '/')) {
        i++;
        c = s.charAt(i);
	//獲取遞增量,遞增量爲一位的數情況
        int v2 = Integer.parseInt(String.valueOf(c));
        i++;
        if (i >= s.length()) {
          addToSet(val, end, v2, type);
          return i;
        }
        c = s.charAt(i);
        if ((c >= '0') && (c <= '9')) {
	//獲取遞增量,遞增量爲兩位的數情況
 ValueSet vs = getValue(v2, s, i); int v3 = vs.value; addToSet(val, end, v3, type); i = vs.pos; return i; }
//當數爲1位數時,但是後面字符爲空的情況
 addToSet(val, end, v2, type); return i; }//將默認遞增量爲1插入TreeSet中 addToSet(val, end, 1, type); return i; }//數字之後字符爲/,並獲取遞增量 if (c == '/') { i++; c = s.charAt(i); int v2 = Integer.parseInt(String.valueOf(c)); i++;
	
      if (i >= s.length()) {
        addToSet(val, end, v2, type);
        return i;
      }
      c = s.charAt(i);
      if ((c >= '0') && (c <= '9')) {
        ValueSet vs = getValue(v2, s, i);
        int v3 = vs.value;
        addToSet(val, end, v3, type);
        i = vs.pos;
        return i;
      }
      throw new ParseException("Unexpected character '" + c + "' after '/'", i);
    }
	//當數爲1位數時,但是後面字符爲空的情況
    addToSet(val, end, 0, type);
    i++;
    return i;
  }
這些都語法解析的關鍵代碼,至於addToSet函數,它的功能是將解析後的結果放到各項TreeSet數據集合中去。addToSet函數源碼如下
protected void addToSet(int val, int end, int incr, int type)
    throws ParseException
  {
	//根據type值獲取TreeSet數據集合
    TreeSet set = getSet(type);

	//根據type值的不同分別判斷val的值是否在各項的值範圍內
	//當時間項是秒和分項的時候,最小值爲0 ,最大值爲59
    if ((type == 0) || (type == 1)) {
      if (((val < 0) || (val > 59) || (end > 59)) && (val != 99)) {
        throw new ParseException("Minute and Second values must be between 0 and 59", -1);
      }

    }
	//如果時間項爲時,最小值爲0,最大值爲23
   else if (type == 2) {
      if (((val < 0) || (val > 23) || (end > 23)) && (val != 99)) {
        throw new ParseException("Hour values must be between 0 and 23", -1);
      }
    }
	//時間項爲dayofmonth,最小值爲1,最大爲31
    else if (type == 3) {
      if (((val < 1) || (val > 31) || (end > 31)) && (val != 99) && (val != 98))
      {
        throw new ParseException("Day of month values must be between 1 and 31", -1);
      }
    }
	//時間項爲月份時,最小值爲1,最大值爲12
    else if (type == 4) {
      if (((val < 1) || (val > 12) || (end > 12)) && (val != 99)) {
        throw new ParseException("Month values must be between 1 and 12", -1);
      }
    }
	//dayofweek項,範圍是1-7
    else if ((type == 5) && 
      ((val == 0) || (val > 7) || (end > 7)) && (val != 99) && (val != 98))
    {
      throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
    }

    if (((incr == 0) || (incr == -1)) && (val != 99)) {
      if (val != -1)
        set.add(new Integer(val));
      else {
        set.add(NO_SPEC);
      }

      return;
    }

    int startAt = val;
    int stopAt = end;

	//如果所有項沒有設置最終值,則將最終值都默認設置爲最大值,最小值如果沒有設置,則將都默認設置爲最小值
	//且在“*”的情況下,如果增量沒有設置的話,遞增量默認設置爲1
    if ((val == 99) && (incr <= 0)) {
      incr = 1;
      set.add(ALL_SPEC);
    }

    if ((type == 0) || (type == 1)) {
      if (stopAt == -1) {
        stopAt = 59;
      }
      if ((startAt == -1) || (startAt == 99))
        startAt = 0;
    }
    else if (type == 2) {
      if (stopAt == -1) {
        stopAt = 23;
      }
      if ((startAt == -1) || (startAt == 99))
        startAt = 0;
    }
    else if (type == 3) {
      if (stopAt == -1) {
        stopAt = 31;
      }
      if ((startAt == -1) || (startAt == 99))
        startAt = 1;
    }
    else if (type == 4) {
      if (stopAt == -1) {
        stopAt = 12;
      }
      if ((startAt == -1) || (startAt == 99))
        startAt = 1;
    }
    else if (type == 5) {
      if (stopAt == -1) {
        stopAt = 7;
      }
      if ((startAt == -1) || (startAt == 99))
        startAt = 1;
    }
    else if (type == 6) {
      if (stopAt == -1) {
        stopAt = 2299;
      }
      if ((startAt == -1) || (startAt == 99)) {
        startAt = 1970;
      }

    }

    int max = -1;
    if (stopAt < startAt) {
      switch (type) { case 0:
        max = 60; break;
      case 1:
        max = 60; break;
      case 2:
        max = 24; break;
      case 4:
        max = 12; break;
      case 5:
        max = 7; break;
      case 3:
        max = 31; break;
      case 6:
        throw new IllegalArgumentException("Start year must be less than stop year");
      default:
        throw new IllegalArgumentException("Unexpected type encountered");
      }
      stopAt += max;
    }

    for (int i = startAt; i <= stopAt; i += incr)
      if (max == -1)
      {
        set.add(new Integer(i));
      }
      else {
        int i2 = i % max;

	//此處代碼type只有4,5,3滿足條件,因爲max的值,在這三種情況下都能取的得到,其他type下是無法取到的。
        if ((i2 == 0) && ((type == 4) || (type == 5) || (type == 3))) {
          i2 = max;
        }

        set.add(new Integer(i2));
      }
  }

  這個函數根據傳進來的範圍值和遞增量以及type值,將各個時間項 的所有值都放到TreeSet集合找那個去,當任務調度器實時地將時間與這些TreeSet集合內的時間信息做對比,
如果時間對上後,就會觸發跑批任務。
	總結:CronTrigger類是時間點跑批任務設置的類,本問終點分析的是哪一個類對時間進行解析,即CronTrigger類繼承的類Trigger中的屬性CronExpression類。時間的語法
解析都是在這個類中完成,得到的結果是7個TreeSet數據集合屬性,分別存放的是seconds ,minutes,hours,dayOfMonth,months,dayOfWeek,years等屬性中,這些集合存放的數據
都是整數數據,即跑批任務觸發時間點的各項(年月日時分秒)數據,scheduler對象在每秒都會比對當前時間和存放的觸發時間信息,如果吻合就觸發跑批任務對象。

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