《SpringBoot整合Quartz實現(xiàn)定時任務(wù)》

簡介

Quartz是一個很牛的任務(wù)調(diào)度框架徒爹,通過它我們可以實現(xiàn)諸如:定時活動、延時活動贺奠、訂單狀態(tài)延時檢測霜旧、服務(wù)器狀態(tài)定時檢測等,時間調(diào)度任務(wù)功能儡率。

下面我們介紹一下它在SpringBoot中的使用:

  • 1.配置Quartz
  • 2.靜態(tài)任務(wù)調(diào)度
  • 3.動態(tài)任務(wù)調(diào)度
  • 4.任務(wù)調(diào)度監(jiān)聽器

快速集成

快速集成體驗一下Quartz的功能吧挂据。

  • 1.首先引入maven依賴
<!--Quartz-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
  • 2.配置Quartz

SpringBoot集成Quartz依然沿用了Spring的典型方式,使用工廠Bean生成Bean的方式儿普。在Quartz中崎逃,需要被調(diào)度的任務(wù)叫做Job,而負(fù)責(zé)調(diào)度任務(wù)則是Scheduler箕肃。
我們首先需要配置工廠Bean:JobFactory接口婚脱,自定義一個AutowiringSpringBeanJobFactory類繼承SpringBeanJobFactory(實現(xiàn)了JobFactory接口)

AutowiringSpringBeanJobFactory工廠類將負(fù)責(zé)生成實現(xiàn)了Job接口的類的實例對象Bean。

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

/**
 * 自動注入的jobBean工廠
 */
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory
        implements ApplicationContextAware {

    private transient 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;
    }
}

下面創(chuàng)建一個Quartz的config配置類:SchedulerConfig
SchedulerConfig類通過 @Configuration 自動裝載配置勺像。這里它配置了兩種Bean工廠障贸,一個是JobFactory ,另一個是SchedulerFactoryBean吟宦。JobFactory就是上面我們所說的AutowiringSpringBeanJobFactory篮洁,而SchedulerFactoryBean是Quartz為我們提供的一個生成Scheduler實例Bean的工廠類。

而在這個SchedulerConfig類中還提供了simpleJobTrigger()【表達式任務(wù)觸發(fā)器工廠】和quartzProperties()【Properties屬性基本配置參數(shù)】

  • CronTriggerFactoryBean為我們提供了執(zhí)行任務(wù)的觸發(fā)器殃姓,這里是通過靜態(tài)方式配置了一個調(diào)度任務(wù)袁波。
  • quartzProperties配置參數(shù)可以自由配置Quartz的一些基本參數(shù)屬性瓦阐,比如配置調(diào)度線程數(shù),調(diào)度線程池類型等篷牌,這里只配置了一個基本使用參數(shù)睡蟋,如果需要更多請查閱官網(wǎng)。
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.io.IOException;
import java.util.Properties;

/**
 * 定時配置(可以配置靜態(tài)定時任務(wù))
 */
@Configuration
public class SchedulerConfig {


    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();

        jobFactory.setApplicationContext(applicationContext);

        return jobFactory;
    }

    //SchedulerFactoryBean
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, Trigger simpleJobTrigger)
            throws IOException{
        SchedulerFactoryBean factory = new SchedulerFactoryBean();

        factory.setJobFactory(jobFactory);
        factory.setQuartzProperties(quartzProperties());
        factory.setTriggers(simpleJobTrigger);

        return factory;
    }






    /**
     * 靜態(tài)方式配置定時任務(wù)
     * @param jobDetail
     * @return
     */
    @Bean
    public CronTriggerFactoryBean simpleJobTrigger(@Qualifier("simpleJobDetail") JobDetail jobDetail) {
        CronTriggerFactoryBean  factoryBean = new CronTriggerFactoryBean ();

        factoryBean.setJobDetail(jobDetail);
        factoryBean.setStartDelay(1000L);
        factoryBean.setName("trigger1");
        factoryBean.setGroup("group1");
        //周1至周5枷颊,每天上午8點至下午18點戳杀,每分鐘執(zhí)行一次
        factoryBean.setCronExpression("0 0/1 8-18 ? * 2-6");

        return factoryBean;
    }

    @Bean
    public JobDetailFactoryBean simpleJobDetail() {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();

        factoryBean.setJobClass(ScheduledJob.class);
        factoryBean.setGroup("group1");
        factoryBean.setName("job1");

        return factoryBean;
    }



    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();

        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));

        propertiesFactoryBean.afterPropertiesSet();

        return propertiesFactoryBean.getObject();
    }

}

quartz.properties內(nèi)容:

#------配置調(diào)度器的線程池
#線程池類
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#線程個數(shù)
org.quartz.threadPool.threadCount = 3

#------配置任務(wù)調(diào)度現(xiàn)場數(shù)據(jù)保存機制,默認(rèn)保存在內(nèi)存
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
  • 3.自定義一個任務(wù)Job

創(chuàng)建 ScheduledJob.java 并實現(xiàn)Job接口夭苗,它是我們之后任務(wù)被調(diào)度后執(zhí)行業(yè)務(wù)邏輯的主要類信卡,通過execute()方法執(zhí)行:

import org.jboss.logging.Logger;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * 自定義定時任務(wù)
 */
public class ScheduledJob implements Job {

    private static final Logger logger= Logger.getLogger(ScheduledJob.class);

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        //執(zhí)行任務(wù)邏輯....
        logger.info("執(zhí)行自定義定時任務(wù)");
    }
}
  • 4.自定義開啟關(guān)閉任務(wù)的管理器類
    為了之后項目中使用方便我們抽象出一個任務(wù)調(diào)度管理類 SchedulerManager.java,我們開啟题造、暫停傍菇、恢復(fù)、停止界赔、移除所有任務(wù)額功能都是通過它去執(zhí)行丢习。
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

/*
 * 此處可以注入數(shù)據(jù)庫操作,查詢出所有的任務(wù)配置
 */
@Component
public class SchedulerManager {

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    private JobListener scheduleListener;




    /**
     * 開始定時任務(wù)
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void startJob(String cron,String jobName,String jobGroup,Class<? extends Job> jobClass) throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if(scheduleListener==null){
            scheduleListener=new SchedulerListener();
            scheduler.getListenerManager().addJobListener(scheduleListener);
        }
        JobKey jobKey=new JobKey(jobName,jobGroup);
        if(!scheduler.checkExists(jobKey))
        {
            scheduleJob(cron,scheduler,jobName,jobGroup,jobClass);
        }
    }

    /**
     * 移除定時任務(wù)
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void deleteJob(String jobName,String jobGroup) throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey=new JobKey(jobName,jobGroup);
        scheduler.deleteJob(jobKey);
    }
    /**
     * 暫停定時任務(wù)
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void pauseJob(String jobName,String jobGroup) throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey=new JobKey(jobName,jobGroup);
        scheduler.pauseJob(jobKey);
    }
    /**
     * 恢復(fù)定時任務(wù)
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void resumeJob(String jobName,String jobGroup) throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey triggerKey=new JobKey(jobName,jobGroup);
        scheduler.resumeJob(triggerKey);
    }
    /**
     * 清空所有當(dāng)前scheduler對象下的定時任務(wù)【目前只有全局一個scheduler對象】
     * @throws SchedulerException
     */
    public void clearAll() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        scheduler.clear();
    }



    /**
     * 動態(tài)創(chuàng)建Job
     * 此處的任務(wù)可以配置可以放到properties或者是放到數(shù)據(jù)庫中
     * Trigger:name和group 目前和job的name仔蝌、group一致泛领,之后可以擴展歸類
     * @param scheduler
     * @throws SchedulerException
     */
    private void scheduleJob(String cron,Scheduler scheduler,String jobName,String jobGroup,Class<? extends Job> jobClass) throws SchedulerException{
        /*
         *  此處可以先通過任務(wù)名查詢數(shù)據(jù)庫,如果數(shù)據(jù)庫中存在該任務(wù)敛惊,更新任務(wù)的配置以及觸發(fā)器
         *  如果此時數(shù)據(jù)庫中沒有查詢到該任務(wù)渊鞋,則按照下面的步驟新建一個任務(wù),并配置初始化的參數(shù)瞧挤,并將配置存到數(shù)據(jù)庫中
         */
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
        // 每5s執(zhí)行一次
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();

        scheduler.scheduleJob(jobDetail,cronTrigger);
    }

}

任務(wù)監(jiān)聽器SchedulerListener.java:

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;

public class SchedulerListener implements JobListener {
    public static final String LISTENER_NAME = "QuartSchedulerListener";

    @Override
    public String getName() {
        return LISTENER_NAME; //must return a name
    }

    //任務(wù)被調(diào)度前
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {

        String jobName = context.getJobDetail().getKey().toString();
        System.out.println("jobToBeExecuted");
        System.out.println("Job : " + jobName + " is going to start...");

    }

    //任務(wù)調(diào)度被拒了
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("jobExecutionVetoed");
        //可以做一些日志記錄原因

    }

    //任務(wù)被調(diào)度后
    @Override
    public void jobWasExecuted(JobExecutionContext context,
                               JobExecutionException jobException) {
        System.out.println("jobWasExecuted");

        String jobName = context.getJobDetail().getKey().toString();
        System.out.println("Job : " + jobName + " is finished...");

        if (jobException!=null&&!jobException.getMessage().equals("")) {
            System.out.println("Exception thrown by: " + jobName
                    + " Exception: " + jobException.getMessage());
        }

    }
}

這里涉及到Quartz的一些概念锡宋,有必要在這里簡單說明一下:

  • Job: 被執(zhí)行的任務(wù)

  • Scheduler: 負(fù)責(zé)調(diào)度任務(wù),如:開始/結(jié)束一個調(diào)度

  • Trigger: 負(fù)責(zé)執(zhí)行任務(wù)的觸發(fā)器(這里我用的是CronTrigger可以用cron表達式去執(zhí)行任務(wù)的觸發(fā)器)如:"0/5 * * * * ?" //每五秒執(zhí)行一次特恬,具體使用方法可以查閱一下官網(wǎng)执俩。

  • JobDetail: 封裝Job,Scheduler真正調(diào)度的對象癌刽,包括Job名稱役首,組等信息。

  • JobListener: 任務(wù)執(zhí)行的監(jiān)聽器显拜,可以監(jiān)聽到任務(wù)執(zhí)行前衡奥、后以及未能成功執(zhí)行拋出異常。

  • 5.使用示例:
    這里我寫了一個簡單的Controller去開啟/停止任務(wù)調(diào)度:

import com.yu.scloud.baseframe.frame.utils.quartz.ScheduledJob;
import com.yu.scloud.baseframe.frame.utils.quartz.SchedulerManager;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/quartz")
public class QuartzController{

    @Autowired
    public SchedulerManager myScheduler;

    @RequestMapping(value = "/job2",method = RequestMethod.GET)
    public String scheduleJob2()
    {
        try {
            myScheduler.startJob("0/5 * * * * ?","job2","group2", ScheduledJob.class);//每五秒執(zhí)行一次
            //0 0/5 14 * * ?在每天下午2點到下午2:55期間的每5分鐘觸發(fā)
            //0 50 14 * * ?在每天下午2點50分5秒執(zhí)行一次
//            myScheduler.startJob("5 50 14 * * ?","job2","group2", ScheduledJob.class);
            return "啟動定時器成功";
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return "啟動定時器失敗";
    }
    @RequestMapping(value = "/del_job2",method = RequestMethod.GET)
    public String deleteScheduleJob2()
    {
        try {
            myScheduler.deleteJob("job2","group2");
            return "刪除定時器成功";
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return "刪除定時器失敗";
    }


}

啟動服務(wù)远荠,訪問就會開啟一個定時任務(wù)矮固,可以打開控制臺查看打印信息。OK!關(guān)于在SpringBoot中Quartz定時任務(wù)調(diào)度的使用譬淳,就介紹到這里档址。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盹兢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子守伸,更是在濱河造成了極大的恐慌绎秒,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尼摹,死亡現(xiàn)場離奇詭異替裆,居然都是意外死亡,警方通過查閱死者的電腦和手機窘问,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宜咒,“玉大人惠赫,你說我怎么就攤上這事」屎冢” “怎么了儿咱?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長场晶。 經(jīng)常有香客問我混埠,道長,這世上最難降的妖魔是什么诗轻? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任钳宪,我火速辦了婚禮,結(jié)果婚禮上扳炬,老公的妹妹穿的比我還像新娘吏颖。我一直安慰自己,他們只是感情好恨樟,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布半醉。 她就那樣靜靜地躺著,像睡著了一般劝术。 火紅的嫁衣襯著肌膚如雪缩多。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天养晋,我揣著相機與錄音衬吆,去河邊找鬼。 笑死匙握,一個胖子當(dāng)著我的面吹牛咆槽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播圈纺,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秦忿,長吁一口氣:“原來是場噩夢啊……” “哼麦射!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起灯谣,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤潜秋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胎许,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峻呛,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年辜窑,在試婚紗的時候發(fā)現(xiàn)自己被綠了钩述。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡穆碎,死狀恐怖牙勘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情所禀,我是刑警寧澤方面,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站色徘,受9級特大地震影響恭金,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜褂策,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一横腿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辙培,春花似錦蔑水、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尾抑,卻和暖如春歇父,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背再愈。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工榜苫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翎冲。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓垂睬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子驹饺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容