排程器Quartz的簡述與使用總結
Quartz是一款效能強大的定時任務排程器。開發人員可以使用Quartz讓任務在特定時間特定階段進行執行。比如對特定型別新聞或股指期貨指數等內容的爬取,可以編寫爬蟲程式然後使用Quartz在後臺指定特定時間點對任務進行執行,來自動收集資訊。大型系統間資料的按時批量匯入任務也可由Quartz進行排程。Quartz提供兩種型別的任務觸發方式,一種是按指定時間間隔觸發任務,另一種是按指定日曆時間觸發任務。下面將對Quartz進行詳細介紹。
一、Hello Quartz
下面首先實現一個簡單例項。 新建maven專案,在pom.xml匯入Quartz的jar包:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency>
定義HelloJob類,實現Job介面並定義具體的任務邏輯。
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("hello");
}
}
例項化Scheduler、Triggle和Job物件,並執行定時任務。
public class QuartzConsole { public static void main(String[] args) throws SchedulerException { SchedulerFactory factory=new org.quartz.impl.StdSchedulerFactory(); Scheduler scheduler=factory.getScheduler();//通過SchedulerFactory例項化Scheduler物件 scheduler.start(); JobDetail job=newJob(HelloJob.class)//指定Job的執行類 .withIdentity("myJob","group1") .build();// name "myJob", group "group1"兩個變數作為一個job的key Trigger trigger=newTrigger().withIdentity("myTrigger","group1")// name "myTrigger", group "group1" 兩個變數作為一個trigger的key .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(5) .repeatForever())//定義任務觸發方式,每5秒執行一次,一直重複。 .build(); scheduler.scheduleJob(job,trigger); } }
執行程式每5秒執行一次Job。 Quartz通過Job、Triggle和Schedule實現任務的排程。三者關係如圖所示。
Job定義:開發者實現Job介面,重寫execute()方法定義具體Job實現。JobDetail介面定義一個job的相關配置細節。通過JobBuilder構建一個實現JobDetail介面的JobDetailImpl類,傳入Scheduler物件。 **Triggle定義:**Triggle有兩種觸發器實現,SimpleTriggle按指定時間間隔進行觸發,CronTriggle按指定日曆時間進行觸發。Triggle介面同Job類似定義了觸發器的具體配置細節,由TriggleBuilder構建觸發器例項。 **Scheduler定義:**Scheduler排程器由SchedulerFactory產生,start()方法定義schedule的執行,將例項化的Job和Triggle物件作為scheduleJob()的入參,由該方法執行具體任務的觸發執行。
二、SimpleTriggle和CronTriggle觸發器。
SimTriggle觸發器可以指定某一個任務在一個特定時刻執行一次,或者在某一時刻開始執行然後重複若干次。 SimpleTriggle的程式碼實現如下。
public class SimpleTriggerJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("Hello SimpleTriggerJob -- "+df.format(new Date()));
}
}
//SimpleTrigger觸發器按指定時間間隔排程任務
public class SimpleTriggerTester {
public static void main(String[] args) throws SchedulerException {
JobDetail jobDetail=newJob(SimpleTriggerJob.class)
.withIdentity("simpleTriggerJob","group1")
.build();
//未來5秒後執行一次
// SimpleTrigger trigger=(SimpleTrigger) newTrigger()
// .withIdentity("simpleTrigger","group1")
// .startAt(futureDate(5, DateBuilder.IntervalUnit.SECOND))
// .forJob("simpleTriggerJob","group1")
// .build();
//從特定時間開始,然後每隔十秒執行1次,重複3次:
// SimpleTrigger trigger=(SimpleTrigger) newTrigger()
// .withIdentity("simpleTrigger","group1")
// .startAt(dateOf(18,11,40))
// .withSchedule(simpleSchedule()
// .withIntervalInSeconds(10)
// .withRepeatCount(3))
// .forJob("simpleTriggerJob","group1")
// .build();
//在每分鐘開始時執行,每分鐘執行一次,永遠重複,直到指定時間點停止
SimpleTrigger trigger=(SimpleTrigger) newTrigger()
.withIdentity("simpleTrigger","group1")
.startAt(evenMinuteDate(null))
.withSchedule(simpleSchedule()
.withIntervalInMinutes(1)
.repeatForever())
.endAt(dateOf(18,22,0))
.forJob("simpleTriggerJob","group1")
.build();
SchedulerFactory factory=new StdSchedulerFactory();
Scheduler scheduler=factory.getScheduler();
scheduler.start();
scheduler.scheduleJob(jobDetail,trigger);
}
}
CronTriggle觸發器作用範圍更廣,它是基於日曆的概念而不是像SimpleTriggle觸發器基於較短的一段特定時間間隔。 例如:可以使用CronTriggle觸發器,指定任務在每個週五晚上7點執行一次;在每個月的倒數第二天早上7點執行三次;按照時區的變換對任務執行進行動態調整。 通過向cronSchedule()構造方法傳遞特定格式字串配置任務的執行。 字串格式如“Seconds Minutes Hours Day-of-Month Month Day-of-Week Year” 例如: “0 30 10-12 ? * WED,FRI”表示每週三和週五的10:30,11:30,12:30各執行一次 “0 0/30 8-9 5,20 * ?”表示每個月第五天和第二十天的8點、9點每半個小時執行一次。 取值範圍: Seconds:0-60 Minutes :0-60 Hours:0-23 Day-of-Month:1-31 Month:1-12 Day-of-Week:1-7或SUN, MON, TUE, WED, THU, FRI 和SAT. “-”可代表從A到B時間段 “/”代表一個遞增時間,A/B指在當前的時間域,從A開始每B個當前時間單位執行一次,等價於在該時間域的第A,A+B,A+2B…時刻各觸發任務一次。 “?”用於day-of-month和day-of-week時間域,表示沒有特別的設定。 “L”用於day-of-month和day-of-week時間域,指定每個月或每週的倒數第n天。day-of-month的“6L”或者“FRIL”代表每個月的最後一個週五。“L-3”代表從每個月的第三天到最後一天。 “A#B”在day-of-week時間域代表每個月的第B周的星期A。 CronTriggle的程式碼實現如下。 “*”在時間域上代表“每個”或者無限重複的意思。 CronTrigger例項程式碼如下:
//CronTrigger按指定日曆時間執行job
// cronSchedule("_ _ _ _ _ _") 方法的6個引數代表的含義
// Seconds
// Minutes
// Hours
// Day-of-Month
// Month
// Day-of-Week
// Year (optional field)
// 具體引數設定見http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-06.html
public class CronTriggerTester {
public static void main(String[] args) throws SchedulerException {
JobDetail jobDetail=newJob(CronTriggerJob.class)
.withIdentity("CronTriggerJob","group1")
.build();
//每天的指定小時分鐘點執行一次
// Trigger trigger=newTrigger()
// .withIdentity("CronTrigger","group1")
// .forJob("CronTriggerJob","group1")
// .withSchedule(dailyAtHourAndMinute(19,7))
// .build();
//每週日每分鐘的第五秒開始執行,每5秒執行一次
// Trigger trigger=newTrigger()
// .withIdentity("CronTrigger","group1")
// .forJob("CronTriggerJob","group1")
// .withSchedule(cronSchedule("0/5 * * ? * 1"))
// .build();
//按指定時區的時間執行
Trigger trigger=newTrigger()
.withIdentity("CronTrigger","group1")
.forJob("CronTriggerJob","group1")
.withSchedule(dailyAtHourAndMinute(19,23)
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")))
.build();
SchedulerFactory factory=new StdSchedulerFactory();
Scheduler scheduler= factory.getScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
三、Listeners ——TriggerListeners、JobListeners和SchedulerListeners
監聽器用來對Job、Trigger和Schedule執行過程中的所處的執行狀態和執行行為進行監聽。TriggerListeners、JobListeners和SchedulerListeners分別為一組介面。實現介面並重寫介面方法,實現對監聽器的定製化開發。 然後通過ListenerManager對監聽器進行註冊。 關於監聽器的例項程式碼如下: 定製化的JobListner類:
public class MyJobListener implements JobListener{
@Override
public String getName() {
return "jobListener name is MyJobListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
System.out.println("當前任務將要執行。");
}
@Override public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("當前任務執行被否決。");
}
@Override public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
System.out.println("當前任務執行完畢。");
}
}
定製化的TriggerListener類:
public class MyTriggerListener implements TriggerListener {
@Override public String getName() {
return "TriggerListener name is MyTriggerListener";
}
@Override public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println("觸發器正在觸發");
}
@Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
return false;
}
@Override public void triggerMisfired(Trigger trigger) {
System.out.println("觸發器錯過觸發");
}
@Override public void triggerComplete(Trigger trigger, JobExecutionContext context,
Trigger.CompletedExecutionInstruction triggerInstructionCode) {
System.out.println("觸發器觸發完畢");
}
}
定製化的SchedulerListener類:
public class MySchedulerListener implements SchedulerListener {
@Override public void jobScheduled(Trigger trigger) {
System.out.println("jobScheduled");
}
@Override public void jobUnscheduled(TriggerKey triggerKey) {
System.out.println("jobScheduled");
}
@Override public void triggerFinalized(Trigger trigger) {
System.out.println("triggerFinalized");
}
@Override public void triggerPaused(TriggerKey triggerKey) {
System.out.println("triggerPaused");
}
@Override public void triggersPaused(String triggerGroup) {
System.out.println("triggersPaused");
}
@Override public void triggerResumed(TriggerKey triggerKey) {
System.out.println("triggerResumed");
}
@Override public void triggersResumed(String triggerGroup) {
System.out.println("triggersResumed");
}
@Override public void jobAdded(JobDetail jobDetail) {
System.out.println("jobAdded");
}
@Override public void jobDeleted(JobKey jobKey) {
System.out.println("jobDeleted");
}
@Override public void jobPaused(JobKey jobKey) {
System.out.println("jobPaused");
}
@Override public void jobsPaused(String jobGroup) {
System.out.println("jobsPaused");
}
@Override public void jobResumed(JobKey jobKey) {
System.out.println("jobResumed");
}
@Override public void jobsResumed(String jobGroup) {
System.out.println("jobsResumed");
}
@Override public void schedulerError(String msg, SchedulerException cause) {
System.out.println("schedulerError");
}
@Override public void schedulerInStandbyMode() {
System.out.println("schedulerInStandbyMode");
}
@Override public void schedulerStarted() {
System.out.println("schedulerStarted");
}
@Override public void schedulerStarting() {
System.out.println("schedulerStarting");
}
@Override public void schedulerShutdown() {
System.out.println("schedulerShutdown");
}
@Override public void schedulerShuttingdown() {
System.out.println("schedulerShuttingdown");
}
@Override public void schedulingDataCleared() {
System.out.println("schedulingDataCleared");
}
}
監聽器測試類,Job使用HelloQuartz一節中的HelloJob類:
public class ListenerTester {
public static void main(String[] args) throws SchedulerException {
//初始化排程器
SchedulerFactory factory=new StdSchedulerFactory();
Scheduler scheduler=factory.getScheduler();
JobDetail jobDetail=newJob(HelloJob.class)
.withIdentity("printerJob","group2")
.build();
Trigger trigger=newTrigger().withIdentity("jobListenerTrigger","group2")
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(5))
.build();
//例項化監聽器物件
MyJobListener listener=new MyJobListener();
MyTriggerListener triggerListener=new MyTriggerListener();
MySchedulerListener schedulerListener=new MySchedulerListener();
//通過排程器的ListenerManager註冊JobListener和TriggerListener
//scheduler.getListenerManager().addJobListener(listener,and(jobGroupEquals("group2"),keyEquals(jobKey("printerJob","group2"))));
scheduler.getListenerManager().addJobListener(listener,keyEquals(jobKey("printerJob","group2")));
scheduler.getListenerManager().addTriggerListener(triggerListener,keyEquals(triggerKey("jobListenerTrigger","group2")));
scheduler.getListenerManager().addSchedulerListener(schedulerListener);
//刪除JobListener
//scheduler.getListenerManager().removeJobListener(listener.getName());
//刪除TriggerListener
//scheduler.getListenerManager().removeTriggerListener(triggerListener.getName());
//刪除SchedulerListener
//scheduler.getListenerManager().removeSchedulerListener(schedulerListener);
scheduler.start();
scheduler.scheduleJob(jobDetail,trigger);
}
四、Quartz的持久化配置
Quartz提供兩種持久化方式,基於記憶體的RAMJobStore方式和基於磁碟介質的JDBCJobStore方式。上文例項使用的是Quartz的基於記憶體的持久化方式,優點是記憶體儲存執行高效,缺點很明顯,當作業系統崩潰或其他異常導致定時器終止將無法恢復之前狀態。 下面介紹Quartz的JDBCJobStore持久化配置,Quartz提供基於多種資料庫的持久化配置形式。本文以mySql 5.6為例對Quartz進行配置。 官網下載Quartz的壓縮包。 首先建立資料儲存表,Quartz壓縮包下的docsdbTables提供對多種資料庫的sql建表語句支援。使用tables_mysql_innodb.sql在mysql資料庫中建立相關資料表。注意Quartz預設資料表以QRTZ_開頭,可以修改為自己的命名規則。 一共建立11張表,根據名稱可猜測大致 QRTZ_FIRED_TRIGGERS; QRTZ_PAUSED_TRIGGER_GRPS; QRTZ_SCHEDULER_STATE; QRTZ_LOCKS; QRTZ_SIMPLE_TRIGGERS; QRTZ_SIMPROP_TRIGGERS; QRTZ_CRON_TRIGGERS; QRTZ_BLOB_TRIGGERS; QRTZ_TRIGGERS; QRTZ_JOB_DETAILS; QRTZ_CALENDARS;
在專案中進行配置,Quartz使用JDBC進行資料庫連線。匯入最新的mysql jdbc connector資料來源。因為使用的是較新的5.6版本mysql,建議使用最新的msql myconnector,不然有可能會報sql格式錯誤異常。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
resources目錄下建立quartz.properties進行配置,Quartz會自動載入。關鍵配置引數和相關解釋如下:
#叢集配置
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
#如果執行在非叢集環境中,自動產生值將會是 NON_CLUSTERED。假如是在叢集環境下,將會是主機名加上當前的日期和時間。
org.quartz.scheduler.instanceId:AUTO
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
#Quartz 自帶的執行緒池實現類是 org.quartz.smpl.SimpleThreadPool
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
#根據任務的多少靈活配置執行緒池中執行緒的數量
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
#============================================================================
# Configure JobStore
#============================================================================
#預設配置,資料儲存到記憶體
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化配置
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties:true
#資料庫表字首
#org.quartz.jobStore.tablePrefix:qrtz_
#注意這裡設定的資料來源名稱為dbqz
org.quartz.jobStore.dataSource:dbqz
#============================================================================
# Configure Datasources
#============================================================================
#org.quartz.jobStore.selectWithLockSQL = SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE
#org.quartz.dataSource.dbqz.validationQuery=SELECT 1
#JDBC驅動
org.quartz.dataSource.dbqz.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.dbqz.URL:jdbc:mysql://127.0.0.1:3306/quartz?useUnicode=true&characterEncoding=UTF-8
org.quartz.dataSource.dbqz.user:資料庫使用者名稱
org.quartz.dataSource.dbqz.password:密碼
org.quartz.dataSource.dbqz.maxConnection:10
例項程式碼:
public class DBScheduleTest {
private static String JOB_GROUP_NAME = "ddlib";
private static String TRIGGER_GROUP_NAME = "ddlibTrigger";
public static void main(String[] args) throws SchedulerException, ParseException {
// startJob();
resumeJob();
}
public static void startJob() throws SchedulerException {
SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler=factory.getScheduler();
JobDetail jobDetail=newJob(PersistenceJob.class)
.withIdentity("job_1","jobGroup1")
.build();
Trigger trigger=newTrigger()
.withIdentity("trigger_1","triggerGroup1")
.startNow()
.withSchedule(simpleSchedule().repeatSecondlyForTotalCount(100))
.build();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduler.shutdown();
}
public static void resumeJob() throws SchedulerException {
SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
// 獲取排程器中所有的觸發器組
List<String> triggerGroups = scheduler.getTriggerGroupNames();
// 重新恢復在triggerGroup1組中,名為trigger_1觸發器的執行
for (int i = 0; i < triggerGroups.size(); i++) {
List<String> triggers = scheduler.getTriggerGroupNames();
for (int j = 0; j < triggers.size(); j++) {
Trigger tg = scheduler.getTrigger(new TriggerKey(triggers
.get(j), triggerGroups.get(i)));
// 根據名稱判斷
if (tg instanceof SimpleTrigger
&& tg.getDescription().equals("triggerGroup1.trigger_1")) {
// 恢復執行
scheduler.resumeJob(new JobKey(triggers.get(j),
triggerGroups.get(i)));
}
}
}
scheduler.start();
}
}
自定義Job類PersistenceJob:
public class PersistenceJob implements Job {
private static int i=0;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("job執行--"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"--"+i++);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
startJob()執行一個job,並設定觸發器,每隔一秒執行一次,一共執行100次,在10秒之後,執行緒終止並讓schedule關閉。觀察資料庫表結構。該job以及job的執行情況已經更新進資料表。 resumeJob()重新建立schedule,並從資料庫中查詢擁有相同key的觸發器,schedule.resuemeJob()恢復任務的執行。當任務結束刪除資料表中的Job相關注冊資訊。
五、Spring整合Quartz
spring提供對quartz的整合。通過對quartz相關bean的配置實現對quartz的載入。以spring boot為例,首先在maven專案的pom.xml中匯入相關包:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
然後同上文配置quartz的jdbc持久化儲存。 在resources下新增quartz-context.xml,對quartz進行配置。 其中對job的構建方式有兩種,一種是通過org.springframework.scheduling.quartz.JobDetailFactoryBean進行job構建,要實現Job介面。另一種是通過org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean對job進行構建,不用實現job介面。 quartz-context.xml配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!--job任務配置,有兩種方式-->
<!--方式一:使用JobDetailFactoryBean,要求關聯的類要實現Job介面-->
<bean id="jobDetailTester" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="durability" value="true"></property>
<property name="requestsRecovery" value="true"></property>
<property name="jobClass" value="com.czx.job.HelloJob"></property>
<property name="jobDataAsMap">
<map>
<entry key="size" value="10"></entry>
</map>
</property>
</bean>
<!--方式二:使用MethodInvokingJobDetailFactoryBean-->
<!--注意,MethodInvokingJobDetailFactoryBean的提示:
NOTE: JobDetails created via this FactoryBean are not
serializable and thus not suitable for persistent job stores.
You need to implement your own Quartz Job as a thin wrapper for each case
where you want a persistent job to delegate to a specific service method.
因為使用的基於資料庫的持久化儲存,所以在使用這種方式構建job物件時會報java序列化異常:Java.io.NotSerializableException:
Unable to serialize JobDataMap for insertion into database because
the value of property 'methodInvoker' is not serializable:
org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean-->
<!--此種方式不推薦-->
<bean id="methodInvokingJobDetailTester" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="concurrent" value="false"></property>
<property name="targetObject">
<ref bean="springJobTester"></ref><!--要關聯的job任務類-->
</property>
<property name="targetMethod">
<value>service</value>
</property><!--要執行方法的名稱-->
</bean>
<bean id="springJobTester" class="com.czx.job.SpringJobTester"></bean>
<!--quartz 2.x scheduler啟動開始-->
<bean name="scheduleFactory" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="autoStartup" value="true"></property>
<property name="startupDelay" value="10"></property>
<property name="triggers">
<list>
<ref bean="cronTriggerTester"></ref>
<!--<ref bean="simpleTriggerTester"></ref> 會報序列化異常,原因如上所述-->
</list>
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContext"></property>
<property name="configLocation" value="classpath:quartz.properties"></property>
</bean>
<!--觸發器Trigger配置-->
<!--基於CronTrigger的觸發方式-->
<bean id="cronTriggerTester" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetailTester"></property>
<property name="cronExpression" value="0/5 * * ? * MON"></property>
</bean>
<!--基於SimpleTrigger的觸發方式-->
<bean id="simpleTriggerTester" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="methodInvokingJobDetailTester"></property>
<property name="repeatInterval" value="2000"></property>
<property name="startDelay" value="0"></property>
<property name="jobDataAsMap">
<map>
<entry key="size" value="10"></entry>
</map>
</property>
<property name="repeatCount" value="10"></property>
</bean>
</beans>
六、基於Spring QuartzJobBean的Quartz Job配置方式
在實際情況中,自定義的job往往需要呼叫service和dao層的方法,對相關操作進行持久化。為了避免各模組間的高耦合。引入Spring QuartzJobBean,然後通過反射機制對具體業務邏輯方法進行呼叫。Spring QuartzJobBean是一個實現Job介面的抽象類,閱讀原始碼發現executeInternal()在重寫excute()的同時,將JobDetail中定義的DataMap鍵值對映為繼承其子類的成員變數。我們通過繼承QuartzJobBean定義自己的JobBean,然後設定與xml中對應job dataMap鍵值對相同的配置項為成員變數。通過設定jobData的targetClass和targetMethod兩個鍵值對,來傳遞需要呼叫的業務類中的具體方法資訊,最後在自定義的JobBean中通過反射機制獲取該方法。具體例項如下: 首先定義繼承QuartzJobBean的JobBean,MyQuartzJobBean.java
public class MyQuartzJobBean extends QuartzJobBean{
/**目標類*/
private String targetObject;
/**要執行的方法*/
private String targetMethod;
@Override
protected void executeInternal(JobExecutionContext context)//executeInternal方法會將配置的job Data 鍵-值 作為該類的成員變數
throws JobExecutionException {
try {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
Object targetClass=ctx.getBean(targetObject);
Method method=null;
//反射機制呼叫
method=targetClass.getClass().getMethod(targetMethod,new Class[]{JobExecutionContext.class});
method.invoke(targetClass,new Object[]{context});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
}
//不可刪除,QuartzJobBean通過set的方法,將job data值賦值給成員變數
public void setTargetObject(String targetObject) {
this.targetObject = targetObject;
}
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
}
定義具體業務類SpringJobTester.java,實現具體Job的業務邏輯。
public class SpringJobTester{
//實現Job的具體業務方法
public void someService(JobExecutionContext context)
{
SimpleDateFormat df=new SimpleDateFormat("HH:mm:ss");
System.out.println("Hello SpringJobTester!----"+df.format(new Date()));
}
}
quartz-context.xml配置如下:
<bean id="proxyJobBeanTester" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="durability" value="true"></property>
<property name="requestsRecovery" value="true"></property>
<property name="jobClass" value="com.czx.job.MyQuartzJobBean"></property>
<property name="jobDataAsMap"> <!--使用JobData進行傳參指定具體job類和具體的執行方法,與MyQuartzJobBean成員變數對應-->
<map>
<entry key="targetObject" value="springJobTester"></entry><!--具體業務類的引用-->
<entry key="targetMethod" value="someService"></entry><!--具體業務方法名-->
</map>
</property>
</bean>
<bean id="myQuartzJobBean" class="com.czx.job.MyQuartzJobBean"></bean>
<!--通過spring applicationContext獲得該bean-->
<bean id="springJobTester" class="com.czx.job.SpringJobTester"></bean>
<!--scheduler配置啟動-->
<bean name="scheduleFactory" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="autoStartup" value="true"></property>
<property name="startupDelay" value="5"></property>
<property name="triggers">
<list>
<ref bean="simpleTriggerForProxy"/>
</list>
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContext"></property>
<property name="configLocation" value="classpath:quartz.properties"></property>
</bean>
<!--觸發器Trigger配置-->
<!--基於SimpleTrigger的觸發方式-->
<bean id="simpleTriggerForProxy" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="proxyJobBeanTester"></property>
<property name="repeatInterval" value="2000"></property> <!--觸發間隔2秒-->
<property name="startDelay" value="1"></property>
<property name="repeatCount" value="10"></property>
</bean>