之前在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的绑定。