1. 程式人生 > 程式設計 >定時任務:Quartz 整合到Spring boot

定時任務:Quartz 整合到Spring boot

Quartz關鍵介面

  1. Scheduler - 與排程程式互動的主要API。(控制Job與trigger)
  2. Job - 由希望排程程式執行的元件實現的介面。
  3. JobDetail - 用於定義作業的例項。(自定義實現)
  4. Trigger(即觸發器) - 定義執行給定作業的計劃的元件。(定義觸發時間與方式)
  5. JobBuilder - 用於定義/構建JobDetail例項,用於定義作業的例項。
  6. TriggerBuilder - 用於定義/構建觸發器例項。
  7. 監聽器listener
    1. TriggerListeners和JobListeners用於觸發器觸發,觸發失靈,與觸發完成的情況。(大多數情況下不使用,但當需要建立時間通知時,不需要Job本身就必須明確地使用listener通知程式)。
    2. SchedulerListeners用於新增job/觸發器,刪除job/觸發器,排程程式中的嚴重錯誤,關閉排程程式的通知時。
為什麼會同時存在Job,與trigger
  1. 很多工排程器並不區分Job和Trigger。有些排程器只是簡單地通過一個執行時間和一些job識別符號來定義一個Job;其它的一些排程器將Quartz的Job和Trigger物件合二為一。在開發Quartz的時候,我們認為將排程和要排程的任務分離是合理的。在我們看來,這可以帶來很多好處。
  2. 例如,Job被建立後,可以儲存在Scheduler中,與Trigger是獨立的,同一個Job可以有多個Trigger;這種鬆耦合的另一個好處是,當與Scheduler中的Job關聯的trigger都過期時,可以配置Job稍後被重新排程,而不用重新定義Job;還有,可以修改或者替換Trigger,而不用重新定義與之關聯的Job。
例子
//實現Job介面,自定義實現execute方法
  public class DumbJob implements Job {

    public DumbJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      JobKey key = context.getJobDetail().getKey();

      JobDataMap dataMap = context.getJobDetail().getJobDataMap();

      String jobSays = dataMap.getString("jobSays"
); float myFloatValue = dataMap.getFloat("myFloatValue"); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ",and val is: " + myFloatValue); } } // 測試 // define the job and tie it to our DumbJob class JobDetail job = newJob(DumbJob.class) .withIdentity("myJob","group1") // name "myJob",group "group1" .usingJobData("jobSays","Hello World!") .usingJobData("myFloatValue",3.141f) .build(); // Trigger the job to run now,and then every 40 seconds Trigger trigger = newTrigger() .withIdentity("myTrigger","group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(40) .repeatForever()) .build(); // Tell quartz to schedule the job using our trigger sched.scheduleJob(job,trigger); 複製程式碼

Job狀態與併發

  1. 關於job的狀態資料(即JobDataMap)和併發性,還有一些地方需要注意。在job類上可以加入一些註解,這些註解會影響job的狀態和併發性。
  2. @DisallowConcurrentExecution:將該註解加到job類上,告訴Quartz不要併發地執行同一個job定義(這裡指特定的job類)的多個例項。請注意這裡的用詞。該限制是針對JobDetail的,而不是job類的。但是我們認為(在設計Quartz的時候)應該將該註解放在job類上,因為job類的改變經常會導致其行為發生變化。
  3. @PersistJobDataAfterExecution:將該註解加在job類上,告訴Quartz在成功執行了job類的execute方法後(沒有發生任何異常),更新JobDetail中JobDataMap的資料,使得該job(即JobDetail)在下一次執行的時候,JobDataMap中是更新後的資料,而不是更新前的舊資料。和 @DisallowConcurrentExecution註解一樣,儘管註解是加在job類上的,但其限制作用是針對job例項的,而不是job類的。由job類來承載註解,是因為job類的內容經常會影響其行為狀態(比如,job類的execute方法需要顯式地“理解”其”狀態“)。
  4. 如果你使用了@PersistJobDataAfterExecution註解,我們強烈建議你同時使用@DisallowConcurrentExecution註解,因為當同一個job(JobDetail)的兩個例項被併發執行時,由於競爭,JobDataMap中儲存的資料很可能是不確定的。

將Quartz整合到Spring boot中

  1. 在專案的配置檔案中加上Quartz配置,然後再讀取給Quartz。這裡使用的是spring boot的configuration-processor包。
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
複製程式碼
  1. 在專案中新增配置類,這裡說一下@ConfigurationProperties註解,它可以注入在application.yaml配置檔案中的屬性,和@Bean 或者 @Component 能生成spring bean 的註解結合起來使用
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;

@ConfigurationProperties("zongw.TestQuartz")
public class TestQuartzProperties {
    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    /**
     * SchedulerFactoryBean這個類的真正作用提供了對org.quartz.Scheduler的建立與配置,並且會管理它的生命週期與Spring同步。
     * org.quartz.Scheduler: 排程器。所有的排程都是由它控制。
     * 
     * @param jobFactory 為SchedulerFactory配置JobFactory
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource,JobFactory jobFactory) throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        //可選,QuartzScheduler啟動時更新己存在的Job,這樣就不用每次修改targetObject後刪除qrtz_job_details表對應記錄
        factory.setOverwriteExistingJobs(true);
        factory.setAutoStartup(true);
        factory.setDataSource(dataSource);
        factory.setJobFactory(jobFactory);
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }

    //從quartz.properties檔案中讀取Quartz配置屬性
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    //配置JobFactory,為quartz作業新增自動連線支援
    public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
            ApplicationContextAware {
        private AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }
}
複製程式碼

參考:

  1. 《W3C Quartz官方檔案》