1. 程式人生 > >6. springboot整合quartz實現定時任務

6. springboot整合quartz實現定時任務

轉載:http://www.cnblogs.com/lic309/p/4089633.html

在做專案時有時候會有定時器任務的功能,比如某某時間應該做什麼,多少秒應該怎麼樣之類的。

spring支援多種定時任務的實現。我們來介紹下使用spring的定時器和使用quartz定時器

  1.我們使用spring-boot作為基礎框架,其理念為零配置檔案,所有的配置都是基於註解和暴露bean的方式。

  2.使用spring的定時器:

    spring自帶支援定時器的任務實現。其可通過簡單配置來使用到簡單的定時任務。

複製程式碼
@Component
@Configurable
@EnableScheduling
public class ScheduledTasks{ @Scheduled(fixedRate = 1000 * 30) public void reportCurrentTime(){ System.out.println ("Scheduling Tasks Examples: The time is now " + dateFormat ().format (new Date ())); } //每1分鐘執行一次 @Scheduled(cron = "0 */1 * * * * ") public void reportCurrentByCron(){ System.out.println (
"Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ())); } private SimpleDateFormat dateFormat(){ return new SimpleDateFormat ("HH:mm:ss"); } }
複製程式碼

          沒了,沒錯,使用spring的定時任務就這麼簡單,其中有幾個比較重要的註解:

   @EnableScheduling:標註啟動定時任務。

          @Scheduled(fixedRate = 1000 * 30)  定義某個定時任務。

  3.使用quartz實現定時任務。
    Quartz設計者做了一個設計選擇來從排程分離開作業。Quartz中的觸發器用來告訴排程程式作業什麼時候觸發。框架提供了一把觸發器型別,但兩個最常用的是SimpleTrigger和CronTrigger。SimpleTrigger為需要簡單打火排程而設計。典型地,如果你需要在給定的時間和重複次數或者兩次打火之間等待的秒數打火一個作業,那麼SimpleTrigger適合你。另一方面,如果你有許多複雜的作業排程,那麼或許需要CronTrigger。
    CronTrigger是基於Calendar-like排程的。當你需要在除星期六和星期天外的每天上午10點半執行作業時,那麼應該使用CronTrigger。正如它的名字所暗示的那樣,CronTrigger是基於Unix克隆表示式的。

    使用quartz說使用的maven依賴。

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>1.8.4</version>
</dependency>

    由於我們使用的是spring-boot框架,其目的是做到零配置檔案,所以我們不使用xml檔案的配置檔案來定義一個定時器,而是使用向spring容器暴露bean的方式。

    向spring容器暴露所必須的bean

複製程式碼
@Configuration
public class SchedledConfiguration {

    // 配置中設定了
    // ① targetMethod: 指定需要定時執行scheduleInfoAction中的simpleJobTest()方法
    // ② concurrent:對於相同的JobDetail,當指定多個Trigger時, 很可能第一個job完成之前,
    // 第二個job就開始了。指定concurrent設為false,多個job不會併發執行,第二個job將不會在第一個job完成之前開始。
    // ③ cronExpression:0/10 * * * * ?表示每10秒執行一次,具體可參考附表。
    // ④ triggers:通過再新增其他的ref元素可在list中放置多個觸發器。 scheduleInfoAction中的simpleJobTest()方法
    @Bean(name = "detailFactoryBean")
    public MethodInvokingJobDetailFactoryBean detailFactoryBean(ScheduledTasks scheduledTasks){
        MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean ();
        bean.setTargetObject (scheduledTasks);
        bean.setTargetMethod ("reportCurrentByCron");
        bean.setConcurrent (false);
        return bean;
    }

    @Bean(name = "cronTriggerBean")
    public CronTriggerBean cronTriggerBean(MethodInvokingJobDetailFactoryBean detailFactoryBean){
        CronTriggerBean tigger = new CronTriggerBean ();
        tigger.setJobDetail (detailFactoryBean.getObject ());
        try {
            tigger.setCronExpression ("0/5 * * * * ? ");//每5秒執行一次
        } catch (ParseException e) {
            e.printStackTrace ();
        }
        return tigger;

    }

    @Bean
    public SchedulerFactoryBean schedulerFactory(CronTriggerBean[] cronTriggerBean){
        SchedulerFactoryBean bean = new SchedulerFactoryBean ();
        System.err.println (cronTriggerBean[0]);
        bean.setTriggers (cronTriggerBean);
        return bean;
    }
}
複製程式碼

    MethodInvokingJobDetailFactoryBean:此工廠主要用來製作一個jobDetail,即製作一個任務。由於我們所做的定時任務根本上講其實就是執行一個方法。所以用這個工廠比較方便。

      注意:其setTargetObject所設定的是一個物件而不是一個類。

    CronTriggerBean:定義一個觸發器。

      注意:setCronExpression:是一個表示式,如果此表示式不合規範,即會丟擲異常。

    SchedulerFactoryBean:主要的管理的工廠,這是最主要的一個bean。quartz通過這個工廠來進行對各觸發器的管理。

  4.對quartz的封裝

    由上面程式碼可以看出來,此處我們設定的是一個固定的cronExpression,那麼,做為專案中使用的話,我們一般是需要其動態設定比如從資料庫中取出來。

    其實做法也很簡單,我們只需要定義一個Trigger來繼承CronTriggerBean。頂用其setCronExpression方法即可。

    那麼另外一個問題,如果我們要定義兩個定時任務則會比較麻煩,需要先注入一個任務工廠,在注入一個觸發器。

       為了減少這樣的配置,我們定義了一個抽象的超類來繼承CronTriggerBean

    具體程式碼如下:

複製程式碼
public abstract class BaseCronTrigger extends CronTriggerBean implements Serializable {

    private static final long serialVersionUID = 1L;

    public void init(){
        // 得到任務
        JobDetail jobdetail = new JobDetail (this.getClass ().getSimpleName (),this.getMyTargetObject ().getClass ());
        this.setJobDetail (jobdetail);
        this.setJobName (jobdetail.getName ());
        this.setName (this.getClass ().getSimpleName ());
        try {
            this.setCronExpression (this.getMyCronExpression ());
        } catch (java.text.ParseException e) {
            e.printStackTrace ();
        }

    }

    public abstract String getMyCronExpression();

    public abstract Job getMyTargetObject();

}
複製程式碼

    其init()方法,來為這個觸發器繫結任務。其任務為一個Job型別的,也就是說其執行的任務為實現了Job介面的類,這個任務會有一個execute()方法,來執行任務題。

複製程式碼
public class ScheduledTasks implements Job {
   
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException{
        System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ()));
    }

   private SimpleDateFormat dateFormat(){
        return new SimpleDateFormat ("HH:mm:ss");
    }
}
複製程式碼

    為了給觸發器新增任務,我們需要在子類中呼叫init()方法,由於spring容器注入時是使用的空參的建構函式,所以我們在此建構函式中呼叫init()方法。

複製程式碼
@Component
public class InitializingCronTrigger extends BaseCronTrigger implements Serializable {

    private static final long    serialVersionUID = 1L;

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    public InitializingCronTrigger() {
        init ();
    }

    @Override
    public String getMyCronExpression(){
        return "0/5 * * * * ?";
    }

    @Override
    public Job getMyTargetObject(){
        return new ScheduledTasks ();
    }

    public void parse(){
        try {
            schedulerFactoryBean.getObject ().pauseAll ();
        } catch (SchedulerException e) {
            e.printStackTrace ();
        }
    }

}
複製程式碼

    此時我們只需要在配置類中加入一個配置就可以了。

複製程式碼
  @Bean
    public SchedulerFactoryBean schedulerFactory(CronTriggerBean[] cronTriggerBean){
        SchedulerFactoryBean bean = new SchedulerFactoryBean ();
        System.err.println (cronTriggerBean[0]);
        bean.setTriggers (cronTriggerBean);

        return bean;
    }
複製程式碼

  4.介紹一個cronExpression表示式。

    這一部分是摘抄的:

欄位 允許值 允許的特殊字元
0-59 , - * /
0-59 , - * /
小時 0-23 , - * /
日期 1-31 , - *   / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - *   / L C #
年(可選) 留空, 1970-2099 , - * /

  
如上面的表示式所示: 

“*”字元被用來指定所有的值。如:”*“在分鐘的欄位域裡表示“每分鐘”。 

“-”字元被用來指定一個範圍。如:“10-12”在小時域意味著“10點、11點、12點”。
 
“,”字元被用來指定另外的值。如:“MON,WED,FRI”在星期域裡表示”星期一、星期三、星期五”. 

“?”字元只在日期域和星期域中使用。它被用來指定“非明確的值”。當你需要通過在這兩個域中的一個來指定一些東西的時候,它是有用的。看下面的例子你就會明白。 


“L”字元指定在月或者星期中的某天(最後一天)。即“Last ”的縮寫。但是在星期和月中“L”表示不同的意思,如:在月子段中“L”指月份的最後一天-1月31日,2月28日,如果在星期欄位中則簡單的表示為“7”或者“SAT”。如果在星期欄位中在某個value值得後面,則表示“某月的最後一個星期value”,如“6L”表示某月的最後一個星期五。

“W”字元只能用在月份欄位中,該欄位指定了離指定日期最近的那個星期日。

“#”字元只能用在星期欄位,該欄位指定了第幾個星期value在某月中


每一個元素都可以顯式地規定一個值(如6),一個區間(如9-12),一個列表(如9,11,13)或一個萬用字元(如*)。“月份中的日期”和“星期中的日期”這兩個元素是互斥的,因此應該通過設定一個問號(?)來表明你不想設定的那個欄位。表7.1中顯示了一些cron表示式的例子和它們的意義:

表示式

意義
"0 0 12 * * ?" 每天中午12點觸發
"0 15 10 ? * *" 每天上午10:15觸發
"0 15 10 * * ?" 每天上午10:15觸發
"0 15 10 * * ? *" 每天上午10:15觸發
"0 15 10 * * ? 2005" 2005年的每天上午10:15觸發
"0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發
"0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發
"0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
"0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發
"0 15 10 ? * MON-FRI" 週一至週五的上午10:15觸發
"0 15 10 15 * ?" 每月15日上午10:15觸發
"0 15 10 L * ?" 每月最後一日的上午10:15觸發
"0 15 10 ? * 6L" 每月的最後一個星期五上午10:15觸發 
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最後一個星期五上午10:15觸發
"0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發

    每天早上6點 0 6 * * * 

     每兩個小時 0 */2 * * *

     晚上11點到早上8點之間每兩個小時,早上八點 0 23-7/2,8 * * *

        每個月的4號和每個禮拜的禮拜一到禮拜三的早上11點 0 11 4 * 1-3

     1月1日早上4點 0 4 1 1 *