之前在Trigger組件中的withSchedule(ScheduleBuilder scheduleBuilder)方法中,接觸過ScheduleBuilder抽象類,通過將ScheduleBuilder對象傳入用於調度參數的設置。本文主要講述的是Scheduler組件,Scheduler組件主要用於將前文講述的Job組件中的JobDetail對象和Trigger對象綁定在一起,進行完整的調度運行。
1.SchedulerFactory
之前JobDetail和Trigger創建對象都是使用對應的Builder類進行創建的,而Scheduler對象則稍有不同,而是使用工廠類進行創建。首先創建一個工廠類對象,即SchedulerFactory對象,其中SchedulerFactory是一個接口,裏面有三個關於創建Scheduler對象的方法。
public interface SchedulerFactory {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Returns a client-usable handle to a <code>Scheduler</code>.
* </p>
*
* @throws SchedulerException
* if there is a problem with the underlying <code>Scheduler</code>.
*/
Scheduler getScheduler() throws SchedulerException;
/**
* <p>
* Returns a handle to the Scheduler with the given name, if it exists.
* </p>
*/
Scheduler getScheduler(String schedName) throws SchedulerException;
/**
* <p>
* Returns handles to all known Schedulers (made by any SchedulerFactory
* within this jvm.).
* </p>
*/
Collection<Scheduler> getAllSchedulers() throws SchedulerException;
}
創建SchedulerFactory對象需要找到SchedulerFactory接口的實現類,本文講述的實現類是StdSchedulerFactory,該類實現了接口的三個方法,因此創建完SchedulerFactory對象後,可以調用相應的方法獲取Scheduler對象,如以下代碼所示:
SchedulerFactory sf=new StdSchedulerFactory();
Scheduler scheduler=sf.getScheduler();
2.啓動與綁定
Scheduler也是一個接口,它有三個實現類,分別是:StdScheduler、RemoteScheduler、RemoteMBeanScheduler(抽象類,其中JBoss4RMIRemoteMBeanScheduler是抽象類的實現類)。本文以StdScheduler爲例,關於scheduler對象的啓動,StdScheduler有兩個啓動的方法,一個是即時啓動,另一個是延遲啓動。
public void start() throws SchedulerException {
sched.start();
}
/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>.
* </p>
*/
public void startDelayed(int seconds) throws SchedulerException {
sched.startDelayed(seconds);
}
其中即時啓動調用了對象名爲sched的start()方法,延遲啓動則是在方法內部調用了對象名爲sched的startDelayed(int seconds)方法,而在StdScheduler類聲明變量時,已經聲明瞭private QuartzScheduler sched;說明sched是QuartzScheduler類型的,可以通過查看QuartzScheduler的源碼,瞭解start()和startDelayed(int seconds)方法。
public void start() throws SchedulerException {
if (shuttingDown|| closed) {
throw new SchedulerException(
"The Scheduler cannot be restarted after shutdown() has been called.");
}
// QTZ-212 : calling new schedulerStarting() method on the listeners
// right after entering start()
notifySchedulerListenersStarting();
if (initialStart == null) {
initialStart = new Date();
this.resources.getJobStore().schedulerStarted();
startPlugins();
} else {
resources.getJobStore().schedulerResumed();
}
schedThread.togglePause(false);
getLog().info(
"Scheduler " + resources.getUniqueIdentifier() + " started.");
notifySchedulerListenersStarted();
}
public void startDelayed(final int seconds) throws SchedulerException
{
if (shuttingDown || closed) {
throw new SchedulerException(
"The Scheduler cannot be restarted after shutdown() has been called.");
}
Thread t = new Thread(new Runnable() {
public void run() {
try { Thread.sleep(seconds * 1000L); }
catch(InterruptedException ignore) {}
try { start(); }
catch(SchedulerException se) {
getLog().error("Unable to start secheduler after startup delay.", se);
}
}
});
t.start();
}
其中可以看出start()是即時啓動,而start(int seconds)就是在方法裏定義了一個線程,利用Thread的sleep方法進行等待實現延遲啓動。
至於綁定,調用scheduler對象的scheduleJob(JobDetail jobDetail, Trigger trigger)方法。
public Date scheduleJob(JobDetail jobDetail, Trigger trigger)
throws SchedulerException {
return sched.scheduleJob(jobDetail, trigger);
}
以下是QuartzScheduler類的scheduleJob(JobDetail jobDetail, Trigger trigger)方法,該方法對JobDetail對象和Trigger對象進行綁定。並且傳入scheduler對象中。
public Date scheduleJob(JobDetail jobDetail,
Trigger trigger) throws SchedulerException {
validateState();
if (jobDetail == null) {
throw new SchedulerException("JobDetail cannot be null");
}
if (trigger == null) {
throw new SchedulerException("Trigger cannot be null");
}
if (jobDetail.getKey() == null) {
throw new SchedulerException("Job's key cannot be null");
}
if (jobDetail.getJobClass() == null) {
throw new SchedulerException("Job's class cannot be null");
}
OperableTrigger trig = (OperableTrigger)trigger;
if (trigger.getJobKey() == null) {
trig.setJobKey(jobDetail.getKey());
} else if (!trigger.getJobKey().equals(jobDetail.getKey())) {
throw new SchedulerException(
"Trigger does not reference given job!");
}
trig.validate();
Calendar cal = null;
if (trigger.getCalendarName() != null) {
cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());
}
Date ft = trig.computeFirstFireTime(cal);
if (ft == null) {
throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
}
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
notifySchedulerListenersJobAdded(jobDetail);
notifySchedulerThread(trigger.getNextFireTime().getTime());
notifySchedulerListenersSchduled(trigger);
return ft;
}
以上分析可知,首先創建一個SchedulerFactory對象,通過SchedulerFactory對象的方法構造Scheduler對象,其中Scheduler是一個抽象類,如果使用scheduler對象的start()方法啓動,則需要調用QuartzScheduler類中的start()方法。然後調用QuartzScheduler類中的schduleJob()方法進行trigger和detail的綁定。