SpringBoot整合Quartz定時任務(持久化到數據庫)


背景

最近在做項目,項目中有個需求:需要使用定時任務,這個定時任務需要即時生效。
查看Quartz官網之后發現:Quartz提供兩種基本作業存儲類型:

  • RAMJobStore :RAM也就是內存,默認情況下Quartz會將任務調度存在內存中,這種方式性能是最好的,因為內存的速度是最快的。不好的地方就是數據缺乏持久性,但程序崩潰或者重新發布的時候,所有運行信息都會丟失
  • JDBC作業存儲:存到數據庫之后,可以做單點也可以做集群,當任務多了之后,可以統一進行管理。關閉或者重啟服務器,運行的信息都不會丟失。缺點就是運行速度快慢取決於連接數據庫的快慢。

SpringBoot集成Quartz

我們也可以自己去將quartz和springBoot整合在一起,其實說是springBoot還不如說是sping,因為我們沒有用到spirngboot的相關的快捷方式。
如果童鞋們想快速集成Quartz,立刻看到效果的話,可以直接往下翻,直接看SpirngBoot自帶的Quartz插件。但我建議大家還是從spring整合Quartz開始,懂的原理,方有收獲。

Quartz初始化表

如果需要做持久化的話,數據肯定是要存在數據庫的,那么到底存在哪些表呢?其實官網文檔也跟我們講過了,地址如下:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-09.html
其中有句話:

JDBCJobStore works with nearly any database, it has been used widely with Oracle, PostgreSQL, MySQL, MS SQLServer, HSQLDB, and DB2. To use JDBCJobStore, you must first create a set of database tables for Quartz to use. You can find table-creation SQL scripts in the “docs/dbTables” directory of the Quartz distribution.
大概就是支持這么多的數據庫類型。如果你要使用JDBCJoBStore的話,你先要創建一些表,這些表在 “doc/dbTables”里面。“doc/dbTables” 在哪兒呢?其實都在源碼里面,直接到官網下下來就行了。

Spring整合Quartz

pom文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--quartz -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <!-- <version>2.3.0</version> -->
        </dependency>
        <!--定時任務需要依賴context模塊-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!--&lt;!&ndash; druid數據庫連接池 &ndash;&gt;-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
    </dependencies>

對應的properties 文件

#使用自己的配置文件
org.quartz.jobStore.useProperties:true

#默認或是自己改名字都行
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
#如果使用集群,instanceId必須唯一,設置成AUTO
org.quartz.scheduler.instanceId = AUTO


org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true


#存儲方式使用JobStoreTX,也就是數據庫
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#是否使用集群(如果項目只部署到 一台服務器,就不用了)
org.quartz.jobStore.isClustered = false
org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.jobStore.tablePrefix = qrtz_
org.quartz.jobStore.dataSource = myDS

#配置數據源
#數據庫中quartz表的表名前綴
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/aipyun?serverTimezone=GMT&characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root123
org.quartz.dataSource.myDS.maxConnections = 5

核心QuartzConfiguration類:

ackage com.cj.config;

import org.quartz.Scheduler;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 描述 : quartz 配置信息
 *
 * @author caojing
 * @create 2018-12-24-16:47
 */
@Configuration
public class QuartzConfiguration {
    @Autowired
    private JobFactory jobFactory;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory);
        // 用於quartz集群,QuartzScheduler 啟動時更新己存在的Job
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //延長啟動
        schedulerFactoryBean.setStartupDelay(1);
        //設置加載的配置文件
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
        return schedulerFactoryBean;
    }

    @Bean
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

這其中我們把2個類的初始化移到了IOC中,因為之前Quartz的實例化是自己去控制的,為什么要這么做后面會有講到。
一個是SchedulerFactoryBean類,這個類其實就是之前xml配置中的SchedulerFactoryBean。附上之前xml配置如下(這里不需要配置,springboot建議我們少用xml配置)

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="oceanStatusCronTrigger"/>
        </list>
    </property>
</bean>

這個類我相信只要用過xml配置的人一定很熟悉,這是Quartz入口。同時也是spring 和Scheduler 關系的橋梁。以便在Spring容器啟動后,Scheduler自動開始工作,而在Spring容器關閉前,自動關閉Scheduler。

JobFactory類

package com.cj.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;

/**
 * 描述:
 *
 * @author caojing
 * @create 2018-12-26-14:03
 */
@Component
public  class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory  capableBeanFactory;


    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
        // 調用父類的方法
        Object jobInstance = super.createJobInstance(bundle);
        // 進行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

這個類的作用就是講Job的實例化交給IOC去進行。
其實問題在於:

Job對象的實例化過程是在Quartz中進行的,注入的實體類是在Spring容器當中的 所以在job中無法注入Srping容器的實體類。

如何納入:Job的創建都是通過JobFactory創建的。JobFactory 有2個實現類:AdaptableJobFactory 和 SimpleJobFactory:

  • 自定義的工廠類 JobFactory 繼承 AdaptableJobFactory 。
  • 通過調用父類 AdaptableJobFactory 的方法createJobInstance來實現對Job的實例化。
  • 在Job實例化完以后,再調用自身方法為創建好的Job實例進行屬性自動裝配並將其納入到Spring容器的管理之中。(通過AutowireCapableBeanFactory納入)。

UploadTask 類:

package com.cj.quartzdemo;
import com.cj.controller.IndexController;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 描述:
 *
 * @author caojing
 * @create 2018-12-25-11:38
 */
@Component
@DisallowConcurrentExecution
public class UploadTask extends QuartzJobBean {
    @Autowired
    private IndexController indexController;
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(new Date() + "任務開始------------------------------------");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(new Date() + "任務結束------------------------------------");
    }
}

繼承QuartzJobBean類,重寫executeInternal方法。
附:DisallowConcurrentExecution 比如job執行10秒,任務是每隔5秒執行,加上這個注解,程序就會等10秒結束后再執行下一個任務。

indexController類:

package com.cj.controller;

import com.cj.quartzdemo.UploadTask;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * 描述:
 *
 * @author caojing
 * @create 2018-12-26-14:11
 */
@Controller
public class IndexController {
    @Autowired
    private Scheduler scheduler;

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public void index() throws SchedulerException {
      //cron表達式
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/8 * * * * ?");
        //根據name 和group獲取當前trgger 的身份
        TriggerKey triggerKey = TriggerKey.triggerKey("cj", "123");
        CronTrigger triggerOld = null;
        try {
            //獲取 觸發器的信息
            triggerOld = (CronTrigger) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        if (triggerOld == null) {
            //將job加入到jobDetail中
            JobDetail jobDetail = JobBuilder.newJob(UploadTask.class).withIdentity("cj", "123").build();
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity("cj","123").withSchedule(cronScheduleBuilder).build();
            //執行任務
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            System.out.println("當前job已存在--------------------------------------------");
        }
    }
}

瀏覽器輸入 http://localhost:8080/index 就可以看到數據庫已經存儲了我們寫的cron表達式和相應的類。
查看數據庫表(qrtz_cron_triggers)附上截圖:

至此,job 已經被我們成功持久化到數據庫。我們來回顧下整體的一個流程。

pom文件添加對應的依賴。
mysql數據庫對應表的初始化。
配置對應的properties
將原來quartz控制的類的實例化交給spirng IOC控制。(對應的是核心QuartzConfiguration類和JobFactory類)
業務邏輯層對job進行控制。


免責聲明!

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



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