Quartz定時任務


一、Quartz簡介
二、Quartz使用
三、Trigger(重 點)
四、Job並發(重點)
五、Spring整合Quartz (重點)
六、持久化
七、springboot整合 自帶的quartz

版本:

Quartz 2.2.3

官網鏈接

一、Quartz簡介

1.1、簡介

Quartz是一個任務調度框架。比如你遇到這樣的問題
想每月29號,信用卡自動還款
想每年4月1日自己給當年暗戀女神發一封匿名賀卡
想每隔1小時,備份一下自己的學習筆記
這些問題總結起來就是:在某一個有規律的時間點干某件事。並且時間的觸發的條件可以非常復雜(比如每月最后一個工作日的17:50),復雜到需要一個專門的框架來干這個事。
Quartz就是來干這樣的事,你給它一個觸發條件的定義,它負責到了時間點,觸發相應的Job起來干活
如果應用程序需要在給定時間執行任務,或者如果系統有連續維護作業,那么Quartz是理想的解決方案。

1.2、特點

1.2.1、作業調度

作業被安排在-個給定的觸發時運行。觸發器可以使用以下指令的組合來創建:

在一天中的某個時間(到毫秒)
在一周的某幾天
在每月的某一天
在一年中的某些日期
不在注冊的日歷中列出的特定日期(如商業節假日除外)
重復特定次數
重復進行,直到一個特定的時間/日期
無限重復
重復的延遲時間間隔

1.2.2、作業持久性

Quartz的設計包括一個作業存儲接口,有多種實現。
通過使用包含的JDBCJobStore,所有的作業和觸發器配置為“非揮發性"都存儲在通過JDBC關系數據庫。
通過使用包含的RAMJobStore,所有的作業和觸發器存儲在RAM,因此不計划執行仍然存在 - 但這是無需使用外部數據庫的優勢。

二、Quartz使用

2.1、導入依賴

 <!--Quartz任務調度-->
    <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.2.3</version>
    </dependency>

2.2、定義Job

@DisallowConcurrentExecution
public class MyJob implements Job{
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //創建工作詳情
        JobDetail jobDetail=context.getJobDetail();
        //獲取工作的名稱
        String name = jobDetail.getKey().getName();//任務名
        String group = jobDetail.getKey().getGroup();//任務group
        String job=jobDetail.getJobDataMap().getString("data04");//任務中的數據
        System.out.println("job執行,job名:"+name+" group:"+group+new Date()+job);//這里是我們最終要定時的任務
    }
}

2.3、API測試

package com.xibei.test;


import com.xibei.job.MyJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.DateBuilder.*;

public class TestQuartz {
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        testSimpleTrigger();
    }


    public static void testSimpleTrigger() throws SchedulerException, InterruptedException {
        // 1. 創建scheduler,調度器  核心組件
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 2. 定義一個Trigger,創建觸發器:Trigger
        /*Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1") //定義觸發器name/group
                .startNow()//一旦加入scheduler,立即生效,即開始時間
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2)//每隔2秒執行一次
                        .withRepeatCount(2))//一共執行幾次
                .build();*/
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1") //定義name/group
                .startNow()//一旦加入scheduler,立即生效,即開始時間
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2)
                        .repeatForever())//一直執行下去
                //.endAt(new GregorianCalendar(2019,9,19,9,59,10).getTime())//設置最終停止時間
                .build();

        // 3. 創建JobDetail,JobBuilder(任務)
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                .withIdentity("job04", "group04")//任務的名字和組
                .usingJobData("data04", "hello world~~")//任務中的數據和值
                .build();
        // 4. 注冊 JobDetail 和 Trigger
        scheduler.scheduleJob(jobDetail, trigger);//將任務和觸發器注冊到調度器
        // 5. 啟動調度器, 內部注冊的所有觸發器開始計時
        scheduler.start();
        // 6.關閉調度器
        Thread.sleep(10000);//用線程睡眠來操作調度器運行的時間
        scheduler.shutdown();
    }
}

2.4、配置

# 指定調度器名稱,非實現類
org.quartz.scheduler.instanceName = DefaultQuartzScheduler04
# 指定線程池實現類
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 線程池線程數量
org.quartz.threadPool.threadCount = 15
# 優先級,默認5
org.quartz.threadPool.threadPriority = 5
# 非持久化job
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

2.5、核心類說明

Scheduler:調度器。所有的調度都是由它控制
	Scheduler就是Quartz的大腦,所有任務都是由它來設施
	Schduelr包含一個兩個重要組件: JobStore和ThreadPool
		JobStore是會來存儲運行時信息的,包括Trigger ,Schduler , JobDetail,業務鎖等
		ThreadPool就是線程池,Quartz有 自己的線程池實現。所有任務的都會由線程池執行
SchdulerFactory,顧名思義就是來用創建Schduler了,有兩個實現: DirectSchedulerFactory和StdSchdulerFactory。 前者可以用來在代碼里定制你自己的Schduler參數。后者是直接讀取classpath下的quartz .properties (不存 在就都使用默認值)配置來實例化Schduler。通常來講,我們使用StdSchdulerF actory也就足夠了。
SchdulerFactory本身是支持創建RMI stub的, 可以用來管理遠程的Scheduler,功能與本地一樣

三、Trigger(重點)

3.1 、SimpleTrigger

指定從某一個時間開始,以一定的時間間隔(單位是毫秒)執行的任務。
它適合的任務類似於: 9:00開始,每隔1小時,執行一次。
它的屬性有:
	repeatInterval重復間隔
	repeatCount重復次數。實際執行次數是repeatCount+1。 因為在startTime的時候一定會執行一次。

示例:

SimpleScheduleBuilder.simpleSchedule().
					withIntervalInSeconds(10).//每隔10秒執行-次
					repeatForever().//永遠執行
					build();
SimpleScheduleBuilder.simpleSchedule().
					withIntervalInMinutes(3).//每隔3分鍾執行一-次
					withRepeatCount(3).//執行3次
					build();

3.2、CalendarlntervalTrigger

類似於SimpleTrigger,指定從某一個時間開始, 以一定的時間間隔執行的任務。但是不同的是SimpleT rigger指定的時間間隔為毫秒,沒辦法指定每隔一個月執行一次(每月的時間間隔不是固定值), 而CalendarIntervalTrigger支持的間隔單 位有秒,分鍾,小時,天,月,年,星期。

示例:

CalendarIntervalScheduleBuilder.calendarIntervalSchedule()
					.withIntervalInDays(2) //每2天執行一次
					.build();
CalendarIntervalScheduleBuilder.calendarIntervalSchedule()
					.withIntervalInWeeks(1) //每周執行一次
					.build();

3.3、DailyTimelntervalTrigger

指定每天的某個時間段內,以一定的時間間隔執行任務。並且它可以支持指定星期。
它適合的任務類似於:指定每天9:00至18:00 ,每隔70秒執行一次,並且只要周一至周五執行。
它的屬性有:
startTime0fDay每天開始時間
endTime0fDay每天結束時間
daysOfWeek需要執行的星期
interval執行間隔
intervalUnit執行間隔的單位(秒, 分鍾,小時,天,月,年,星期)
repeatCount重復次數
示例:
DailyTimeIntervalScheduleBuilder.dailyTime IntervalSchedule()
					.start ingDailyAt (TimeOfDay.hourAndMinute0fDay(9, 0)) //每天9:00開始
					.endingDailyAt (TimeOfDay.hour AndMinute0fDay(18, 0)) //18:00結束
					.onDaysOfTheWeek( MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五執行
					.withIntervalInHours(1) //每間隔1小時執行一次
					.withRepeatCount(100) //最多重復100次(實際執行100+1次)
					.build();
DailyTimeIntervalScheduleBuilder.dailyTime IntervalSchedule()
							.start ingDailyAt(Time0fDay.hourAndMinuteOfDay(10,0)) //每天10:00開始
							.endingDailyAfterCount(10)//每天執行10次, 這個方法實際上根據startTimeOfDaytinterval*count算出endTimeOfDay
							.onDaysOfTheWeek(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY)//周一至周五執行
							.withIntervalInHours(1)//每間隔1小時執行一次
							.build();

3.4 CronTrigger (重點)

適合於更復雜的任務,它支持類型於Linux Cron的語法(並且更強大)。基本上它覆蓋了以上三個Trigger的絕大部分能力(但不是全部) -- 當然,也更難理解。
它適合的任務類似於:每天0:00,9:00,18:00各執行-次。
它的屬性只有:
Cron表達式。但這個表示式本身就夠復雜了

示例:

CronScheduleBuilder.cronSchedule("0 0/2 10-12 * * ?") //每天10:00-12:00, 每隔2分鍾執行一次
.build();
cronSchedule("0 30 9 ? * MON") //每周一,9:30執行一次
.build();
CronScheduleBuilder.week1yOnDayAndHourAndMinute(MONDAY,9,30) //等同於 0 30 9 ? * MON
. build();

3.4.1、Cron表達式

[1] [2] [3] ? [5] 3#5

星號(*): 可用在所有字段中,表示對應時間域的每一個時刻,例如,在分鍾字段時, 表示“每分鍾”;

問號(?): 該字符只在日期和星期字段中使用,它通常指定為“不確定值”

減號(-): 表達一一個范圍,如在小時字段中使用“10-12",則表示從10到12點,即10, 11,12;

逗號(,): 表達一個列表值,如在星期字段中使用“MON, WED, FRI",則表示星期一,星期三和星期五;

斜杠(/): x/y表達一個等步長序列, x為起始值,y為增量步長值。如在分鍾字段中使用0/15,則表示為日,15,30和45秒,而5/15在分鍾字段中表示5, 20, 35, 50,你也可以使用*/y,它等同於0/y;

L: 該字符只在日期和星期字段中使用,代表"Last"的意思,但它在兩個字段中意思不同。L在日期字段中,表示這個月份的最后一天,如一月的31號,非閏年二月的28號;如果L用在星期中,則表示星期六,等同於7。但是,如果L出現在星期字段里,而且在前面有-個數值X,則表示"這個月的最后一個周x",例如,6L表示該月的最后星期五;

W: 該字符只能出現在日期字段里,是對前導日期的修飾,表示離該日期最近的工作日。例如1 5W表示離該月15號最近的工作日,如果該月15號是星期六,則匹配14號星期五;如果15日是星期日,則匹配16號星期一;如果15號是星期二,那結果就是15號星期二。但必須注意關聯的匹配日期不能夠跨月,如你指定1W,如果1號是星期六,結果匹配的是3號星期一,而非上個月最后的那天。W字符串只能指定單一日期,而不能指定日期范圍;

LW組合: 在日期字段可以組合使用LW,它的意思是當月的最后一個工作日;

表達式示例:

Calendar不是jdk的java.util.Calendar,不是為了計算日期的。它的作用是在於補充Trigger的時間。可以排除或加入某一些特定的時間點。
以”每月29日零點自動還信用卡“為例,我們想排除掉每年的2月29號零點這個時間點(因為平年和潤年2月不一樣)。這個時間,就可以用Calendar來實現。

Quartz提供以下幾種Calendar,注意,所有的Calendar既可以是排除, 也可以是包含,取決於:
HolidayCalendar。指定特定的日期,比如20140613。 精度到天。
DailyCalendar.指定每天的時間段(rangeStartingTime, rangeEndingTime),格式是HH:M:SS[:mmm]]。 也就是最大精度可以到毫秒。
WeeklyCalendar。指定每星期的星期幾,可選值比如為java.util.Calendar.SUNDAY.精度是天。
MonthlyCalendar。指定每月的幾號。可選值為1-31。精度是天
AnnualCalendar。指定每年的哪一天。使用方式如上例。精度是天。
CronCalendar。指定Cron表達式。精度取決於Cron表達式,也就是最大精度可以到秒。

當scheduler比較繁忙的時候,可能在同一個時刻,有多個Trigger被觸發了,但資源不足(比如線程池不足)。那么這個時候比剪刀石頭布更好的方式,就是設置優先級。優先級高的先執行。
需要注意的是,優先級只有在同一時刻執行的Trigger之間才會起作用,如果一個Trigger是9:00, 另-個Trigger是9:30。那么無論后一個優先級多高, 前一個都是先執行。
優先級的值默認是5,當為負數時使用默認值。最大值似乎沒有指定,但建議遵循Java的標准,使用1-10,不然鬼才知道看到[優先級為10]是時,上頭 還有沒有更大的值。

四、Job並發(重點)

job是有可能並發執行的,比如一個任務要執行10秒中,而調度算法是每秒中觸發1次,那么就有可能多個任務被並發執行。

有時候我們並不想任務並發執行,比如這個任務要去"獲得數據庫中所有未發送郵件的名單”,如果是並發執行,就需要一個數據庫 鎖去避免一個數據被多次處理。這個時候一個@DisallowConcurrentExecution解決這個問題
@DisallowConcur rentExecution
public class DoNothingJob implements Job {
	public void execute (JobExecutionContext context) throws JobExecut ionE xception {
		System.out.println( "操作"):
	}
}
注意,@DisallowConcurrentExecution是對 JobDetail實例生效,也就是如果你定義兩個JobDetail,引用同一個Job類,是可以並發執行的

代碼示例:

@DisallowConcurrentExecution //會不允許並發執行,(如果每1s觸發 -次,但每個j ob要執行3秒)
public class MyJob implements Job{
	@Override
	public void execute (JobExecutionContext context) throws JobExecutionException {
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e. printStackTrace();
        }
		System.out.println("任務調度:組:"+group+",工作名:" +name+" "+data+new Date());
    }
}

五、Spring整合Quartz (重點)

5.1、依賴

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <springframework.version>4.3.6.RELEASE</springframework.version>
    <quartz.version>2.2.3</quartz.version>
    <druid>1.1.12</druid>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!--Quartz任務調度-->
    <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.2.3</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${springframework.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${springframework.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${springframework.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${springframework.version}</version>
    </dependency>

    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>${quartz.version}</version>
    </dependency>

    <!-- druid依賴 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid}</version>
    </dependency>
    <!-- mysql驅動 依賴 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.25</version>
      <scope>runtime</scope>
    </dependency>

    <!-- lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

5.2、配置

調度器   SchedulerFactoryBean
觸發器   CronTriggerFactoryBean
JobDetail   JobDetailFactoryBean

1、applicationContext_quartz.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        Spring整合Quartz進行配置遵循下面的步驟:
        1:定義工作任務的Job
        2:定義觸發器Trigger,並將觸發器與工作任務綁定
        3:定義調度器,並將Trigger注冊到Scheduler
     -->

    <!-- 1:定義任務的bean ,這里使用JobDetailFactoryBean,也可以使用MethodInvokingJobDetailFactoryBean ,配置類似-->
    <bean name="lxJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <!-- 指定job的名稱 -->
        <property name="name" value="job1"/>
        <!-- 指定job的分組 -->
        <property name="group" value="group1"/>
        <!-- 指定具體的job類 -->
        <property name="jobClass" value="com.xibei.job.MyJob"/>
        <!-- 如果為false,當沒有活動的觸發器與之關聯時會在調度器中會刪除該任務 (可選) -->
        <property name="durability" value="true"/>
        <!-- (可選)
             指定spring容器的key,如果不設定在job中的jobmap中是獲取不到spring容器的
             其實現了ApplicationContextWare,則其中的setApplicationContext方法會得到
             當前的工廠對象,且將工廠對象存在了類中的一個屬性“applicationContext”中,源碼如下

             getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
             則在Job的jobmap中可以獲得工廠對象,如果需要可以使用
			 (ApplicationContext) jobDataMap.get("applicationContext04");
			 jobDataMap.get("data04");

			 .usingJobData("data04", "hello world~~")
			 .usingJobData("applicationContext04",spring工廠對象)
        -->
        <!--<property name="applicationContextJobDataKey" value="applicationContext04"/>-->
    </bean>


    <!-- 2.2:定義觸發器的bean,定義一個Cron的Trigger,一個觸發器只能和一個任務進行綁定 -->
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!-- 指定Trigger的名稱 -->
        <property name="name" value="hw_trigger"/>
        <!-- 指定Trigger的名稱 -->
        <property name="group" value="hw_trigger_group"/>
        <!-- 指定Tirgger綁定的Job -->
        <property name="jobDetail" ref="lxJob"/>
        <!-- 指定Cron 的表達式 ,當前是每隔5s運行一次 -->
        <property name="cronExpression" value="* * * * * ?" />
    </bean>

    <!-- 3.定義調度器,並將Trigger注冊到調度器中 -->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="cronTrigger"/>
            </list>
        </property>
        <!-- 添加 quartz 配置,如下兩種方式均可 -->
        <!--<property name="configLocation" value="classpath:quartz.properties"></property>-->
        <property name="quartzProperties">
            <value>
                # 指定調度器名稱,實際類型為:QuartzScheduler
                org.quartz.scheduler.instanceName = MyScheduler
                # 指定連接池
                org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
                # 連接池線程數量
                org.quartz.threadPool.threadCount = 11
                # 優先級
                org.quartz.threadPool.threadPriority = 5
                # 不持久化job
                org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
            </value>
        </property>
    </bean>
</beans>

web.xml項目的全局配置


5.3、代碼

MyJob自定義任務類

TestQuartzSpring

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_quartz.xml")
public class TestQuartzSpring {

    @Autowired
    private StdScheduler scheduler;

    @Test
    public void test1() throws InterruptedException, SchedulerException {
        System.out.println("hello");
        Thread.sleep(5000);

        // 刪除 job
        /*scheduler.pauseTrigger(TriggerKey.triggerKey("hw_trigger","hw_trigger_group"));//暫停觸發器的計時
        scheduler.unscheduleJob(TriggerKey.triggerKey("hw_trigger", "hw_trigger_group"));// 移除觸發器中的任務
        scheduler.deleteJob(JobKey.jobKey("job1","group1"));//移除trigger后,刪除工作*/

        // job 暫停 和 恢復
       /* scheduler.pauseJob(JobKey.jobKey("job1","group1"));
        Thread.sleep(30000);
        scheduler.resumeJob(JobKey.jobKey("job1","group1"));*/

        GroupMatcher<JobKey> groups = GroupMatcher.groupEquals("group1");//名字等於group1
        scheduler.pauseJobs(groups);// 暫停組內所有的job
        Thread.sleep(5000);
        scheduler.resumeJobs(groups);
        Thread.sleep(5000);
    }
}

六、持久化

6.1、建表

quartz官方提供了完整的持久化job的支持,並給出了一套庫表

CREATE TABLE QRTZ_JOB_DETAILS(  
SCHED_NAME VARCHAR(120) NOT NULL,  
JOB_NAME VARCHAR(200) NOT NULL,  
JOB_GROUP VARCHAR(200) NOT NULL,  
DESCRIPTION VARCHAR(250) NULL,  
JOB_CLASS_NAME VARCHAR(250) NOT NULL,  
IS_DURABLE VARCHAR(1) NOT NULL,  
IS_NONCONCURRENT VARCHAR(1) NOT NULL,  
IS_UPDATE_DATA VARCHAR(1) NOT NULL,  
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,  
JOB_DATA BLOB NULL,  
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_TRIGGERS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
TRIGGER_NAME VARCHAR(200) NOT NULL,  
TRIGGER_GROUP VARCHAR(200) NOT NULL,  
JOB_NAME VARCHAR(200) NOT NULL,  
JOB_GROUP VARCHAR(200) NOT NULL,  
DESCRIPTION VARCHAR(250) NULL,  
NEXT_FIRE_TIME BIGINT(13) NULL,  
PREV_FIRE_TIME BIGINT(13) NULL,  
PRIORITY INTEGER NULL,  
TRIGGER_STATE VARCHAR(16) NOT NULL,  
TRIGGER_TYPE VARCHAR(8) NOT NULL,  
START_TIME BIGINT(13) NOT NULL,  
END_TIME BIGINT(13) NULL,  
CALENDAR_NAME VARCHAR(200) NULL,  
MISFIRE_INSTR SMALLINT(2) NULL,  
JOB_DATA BLOB NULL,  
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)  
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
TRIGGER_NAME VARCHAR(200) NOT NULL,  
TRIGGER_GROUP VARCHAR(200) NOT NULL,  
REPEAT_COUNT BIGINT(7) NOT NULL,  
REPEAT_INTERVAL BIGINT(12) NOT NULL,  
TIMES_TRIGGERED BIGINT(10) NOT NULL,  
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_CRON_TRIGGERS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
TRIGGER_NAME VARCHAR(200) NOT NULL,  
TRIGGER_GROUP VARCHAR(200) NOT NULL,  
CRON_EXPRESSION VARCHAR(120) NOT NULL,  
TIME_ZONE_ID VARCHAR(80),  
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_SIMPROP_TRIGGERS  
  (            
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    STR_PROP_1 VARCHAR(512) NULL,  
    STR_PROP_2 VARCHAR(512) NULL,  
    STR_PROP_3 VARCHAR(512) NULL,  
    INT_PROP_1 INT NULL,  
    INT_PROP_2 INT NULL,  
    LONG_PROP_1 BIGINT NULL,  
    LONG_PROP_2 BIGINT NULL,  
    DEC_PROP_1 NUMERIC(13,4) NULL,  
    DEC_PROP_2 NUMERIC(13,4) NULL,  
    BOOL_PROP_1 VARCHAR(1) NULL,  
    BOOL_PROP_2 VARCHAR(1) NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)   
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_BLOB_TRIGGERS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
TRIGGER_NAME VARCHAR(200) NOT NULL,  
TRIGGER_GROUP VARCHAR(200) NOT NULL,  
BLOB_DATA BLOB NULL,  
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),  
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_CALENDARS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
CALENDAR_NAME VARCHAR(200) NOT NULL,  
CALENDAR BLOB NOT NULL,  
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
TRIGGER_GROUP VARCHAR(200) NOT NULL,  
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_FIRED_TRIGGERS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
ENTRY_ID VARCHAR(95) NOT NULL,  
TRIGGER_NAME VARCHAR(200) NOT NULL,  
TRIGGER_GROUP VARCHAR(200) NOT NULL,  
INSTANCE_NAME VARCHAR(200) NOT NULL,  
FIRED_TIME BIGINT(13) NOT NULL,  
SCHED_TIME BIGINT(13) NOT NULL,  
PRIORITY INTEGER NOT NULL,  
STATE VARCHAR(16) NOT NULL,  
JOB_NAME VARCHAR(200) NULL,  
JOB_GROUP VARCHAR(200) NULL,  
IS_NONCONCURRENT VARCHAR(1) NULL,  
REQUESTS_RECOVERY VARCHAR(1) NULL,  
PRIMARY KEY (SCHED_NAME,ENTRY_ID))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_SCHEDULER_STATE (  
SCHED_NAME VARCHAR(120) NOT NULL,  
INSTANCE_NAME VARCHAR(200) NOT NULL,  
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,  
CHECKIN_INTERVAL BIGINT(13) NOT NULL,  
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))  
ENGINE=InnoDB;  
  
CREATE TABLE QRTZ_LOCKS (  
SCHED_NAME VARCHAR(120) NOT NULL,  
LOCK_NAME VARCHAR(40) NOT NULL,  
PRIMARY KEY (SCHED_NAME,LOCK_NAME))  
ENGINE=InnoDB;  

6.2、配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 定義調度器,並將Trigger注冊到調度器中 -->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- 添加 quartz 配置,如下兩種方式均可 -->
        <!--<property name="configLocation" value="classpath:quartz.properties"></property>-->
        <property name="quartzProperties">
            <value>
                # 指定調度器名稱,實際類型為:QuartzScheduler
                org.quartz.scheduler.instanceName = MyScheduler
                # 指定連接池
                org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
                # 連接池線程數量
                org.quartz.threadPool.threadCount = 11
                # 優先級
                org.quartz.threadPool.threadPriority = 5
                # 不持久化job
                # org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
                #持久化
                org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
                #quartz表的前綴
                org.quartz.jobStore.tablePrefix = QRTZ_
            </value>
        </property>
        <property name="dataSource" ref="druid"/>
    </bean>

    <!-- 導入外部參數文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 連接池:druid -->
    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close">
        <!-- 基本屬性 url、user、password -->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1"/>
        <property name="minIdle" value="1"/>
        <property name="maxActive" value="${jdbc.maxPoolSize}"/>

        <!-- 配置獲取連接等待超時的時間 -->
        <property name="maxWait" value="3000"/>

        <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>

        <!-- 配置一個連接在池中最小空閑的時間,單位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>

        <property name="validationQuery" value="SELECT 'x'"/>
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
    </bean>
</beans>

mvc.xml

<beans 	xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.xsd
							http://www.springframework.org/schema/context
							http://www.springframework.org/schema/context/spring-context.xsd
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd
                            http://www.springframework.org/schema/aop
							http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 告知springmvc  哪些包中 存在 被注解的類
         use-default-filters="false"  遇到到 @Controller  @Service  @Repository  @Component類,都會忽略
    -->
    <context:component-scan base-package="com.xibei" use-default-filters="false">
        <!-- 只掃描  有@Controller注解的類 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 注冊注解開發驅動 -->
    <mvc:annotation-driven/>

    <!-- 視圖解析器
	     作用:1.捕獲后端控制器的返回值="index"
	          2.解析: 在返回值的前后 拼接 ==> "/index.jsp"
	 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前綴 -->
        <property name="prefix" value="/"></property>
        <!-- 后綴 -->
        <property name="suffix" value=".jsp"></property>
    </bean>
    <!-- 在項目中 自動添加一個 映射{"/**" : DefaultServletHttpRequestHandler}
         請求進入前端后,會先匹配自定義的Handler,如果沒有匹配的則進入DefaultServletHttpRequestHandler。
         DefaultServletHttpRequestHandler會將請求轉發給Tomcat中名為"default"的servlet。
         最終實現了靜態資源的訪問
    -->
    <mvc:default-servlet-handler/>


</beans>

web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- Post請求中文參數亂碼解決 -->
    <filter>
        <filter-name>encode</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encode</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext_quartz2.xml</param-value>
    </context-param>
</web-app>

6.3、增加任務

定義一個springMVC的Handler

JobAndTrigger

public class JobAndTrigger {
    private String jobName;
    private String jobGroup;
    private String jobClassName;
    private String triggerName;
    private String triggerGroup;
    private BigInteger repeatInterval;
    private BigInteger timesTriggered;
    private String cronExpression;
    private String timeZoneId;
}

QuartzController

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

    @Autowired //注入了工廠中 調度器
    private Scheduler scheduler;

    @RequestMapping("/add")
    public String addJob(JobAndTrigger jt) throws ClassNotFoundException, SchedulerException {
        // 創建JobDetail
        JobDetail jobDetail=null;
        jobDetail = JobBuilder.newJob((Class<? extends Job>)Class.forName(jt.getJobClassName()))
                .withIdentity(jt.getJobName(), jt.getJobGroup()).storeDurably(true).build();
        CronTrigger cronTrigger = null;
        cronTrigger = TriggerBuilder.newTrigger().withIdentity(jt.getJobName(),jt.getJobGroup())
                .withSchedule(CronScheduleBuilder.cronSchedule(jt.getCronExpression()))
                .build();
        scheduler.scheduleJob(jobDetail,cronTrigger);
        return "index";
    }
}

addjob.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/quartz/add" method="post">
    jobName: <input type="text" name="jobName"> <br>
    jobGroup: <input type="text" name="jobGroup"> <br>
    cronExp: <input type="text" name="cronExpression"> <br>
    jobClass: <input type="text" name="jobClassName"> <br>
    <input type="submit" value="增加">
</form>
</body>
</html>

啟動Tomcat測試

七、springboot整合自帶的quartz

7.1、簡單任務

1、SimpleJob

@Configuration
public class SimpleJob {
    @Scheduled(cron = "0/1 * * * * ?")
    public void run() {
        //任務
        System.err.println(LocalDateTime.now()+"我是簡單的");//輸出紅色

    }

2、SpringbootQuartzApplication

@EnableScheduling//開啟任務調度支持
@SpringBootApplication
public class SpringbootQuartzApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootQuartzApplication.class, args);
    }
}

7.2、復雜任務

1、pom.xml

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.oracle.ojdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2、連接Oracle數據庫

數據表

cron_id cron
1 0/1 * * * * ?
2 0/5 * * * * ?

查詢語句

select "cron" from "cron" where "cron_id"=1

application.properties

spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url= jdbc:oracle:thin:@localhost:1521:MLDN
spring.datasource.password=zhu
spring.datasource.username=zhu

3、ComplexJob

@Configuration
public class ComplexJob implements SchedulingConfigurer {
    @Mapper
    public interface CronMapper {
        @Select("select \"cron\" from \"cron\" where \"cron_id\"=1")
        String getCron();//返回查詢結果
    }

    //將cronMapper注入給ComplexJob
    @Autowired
    CronMapper cronMapper;


    //任務,觸發器
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(
                () -> System.out.println(LocalDateTime.now()+"我是復雜的"),//任務
                triggerContext -> {
                    String cron = cronMapper.getCron();
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);//觸發器
                });


        /*scheduledTaskRegistrar.addTriggerTask(new Runnable() { //任務
            @Override
            public void run() {
                System.out.println(LocalDateTime.now()+"我是復雜的");
            }
        }, new Trigger() {//觸發器
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                String cron = cronMapper.getCron();
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        });*/


    }
}

4、SpringbootQuartzApplication

@EnableScheduling//開啟任務調度支持
@SpringBootApplication
public class SpringbootQuartzApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootQuartzApplication.class, args);
    }

}

5、測試

調度器作用:調度安排一些工作,

觸發器作用:每個任務在某個時間觸發執行,觸發誰

一個job 對應一個觸發器

jobDetail封裝了job

調度器啟動起來,啟動調度器中所有觸發器+任務(job)

http://www.bejson.com/othertools/cronvalidate/


免責聲明!

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



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