SpringBoot整合Quartz定時

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

RAMJobStore :RAM也就是內存,默認狀況下Quartz會將任務調度存在內存中,這種方式性能是最好的,由於內存的速度是最快的。很差的地方就是數據缺少持久性,但程序崩潰或者從新發布的時候,全部運行信息都會丟失
JDBC做業存儲:存到數據庫以後,能夠作單點也能夠作集羣,當任務多了以後,能夠統一進行管理。關閉或者重啓服務器,運行的信息都不會丟失。缺點就是運行速度快慢取決於鏈接數據庫的快慢。
因此決定採用 JDBC做業存儲的方式。java

爲何須要持久化?
之後能夠作集羣。
任務能夠進行管理,隨時中止、暫停、修改任務。
你應該瞭解的概念
由於以前接觸過quartz這個任務調度框架,因此對quartz有必定的瞭解,quartz三要素:Scheduler、Trigger、JobDetai&Job。
忽然想起來,以前寫過介紹-。- 本身都忘了,貼上地址,須要的童鞋能夠先去了解下:
https://blog.csdn.net/bicheng4769/article/details/81097305mysql

SpringBoot集成Quartz
咱們也能夠本身去將quartz和springBoot整合在一塊兒,其實說是springBoot還不如說是sping,由於咱們沒有用到spirngboot的相關的快捷方式。
若是童鞋們想快速集成Quartz,馬上看到效果的話,能夠直接往下翻,直接看SpirngBoot自帶的Quartz插件。但我建議你們仍是從spring整合Quartz開始,懂的原理,方有收穫。web

Quartz初始化表
若是須要作持久化的話,數據確定是要存在數據庫的,那麼到底存在哪些表呢?其實官網文檔也跟咱們講過了,地址以下:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-09.html
其中有句話:spring

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.sql

榮老夫這個四級的水平給大家翻譯下:
大概就是支持這麼多的數據庫類型。若是你要使用JDBCJoBStore的話,你先要建立一些表,這些表在 「doc/dbTables」裏面。「doc/dbTables」 在哪兒呢?其實都在源碼裏面,直接到官網下下來就好了。數據庫

Spring整合Quartz
pom文件加入相關jar
相關配置文件(不論是properties 仍是yml。採用JDBC存儲)
業務邏輯層中使用。
pom文件
以下所示:api

<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 = AUTOspringboot


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 Bean也歸入到Spring容器的管理之中,Spring容器天然可以爲Job Bean自動裝配好所需的依賴。
如何歸入:Job的建立都是經過JobFactory建立的。官網解釋爲證:
https://www.quartz-scheduler.org/api/2.2.1/org/quartz/spi/JobFactory.html

A JobFactory is responsible for producing instances of Job classes.

翻譯:JobFactory負責生成Job類的實例。
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進行控制。總結其實思路整理一下,咱們發現過程其實仍是挺簡單的,惟一可能有些困難的是對QuartzConfiguration類和JobFactory類的理解。這兩個類也是整合的核心類。可是在springboot2.0以後,我發現了一個很神奇的starter。

相關文章
相關標籤/搜索