本文的起因源于一次quartz的异常,在win2003正常运行的程序放在linux环境就抛出异常了,虽然找出异常没花我多长时间,不过由此加深了对quzrtz的了解;古人说,三折肱,为良医,说明经验对于我们平时开发的重要。
quartz是一个任务调度框架,对于开发者而言通常是透明的,如果不熟悉内部机制,碰到问题往往会束手无策;接下来本人本着开放的精神,来阐述本人对quartz的理解。
本人是采用spring对quartz封装的实现,spring的org.springframework.scheduling.quartz.SchedulerFactoryBean类用于初始化Scheduler对象并启动Scheduler对象主线程(通过实现spring的InitializingBean接口和SmartLifecycle接口)
Scheduler对象的初始化在SchedulerFactoryBean的void afterPropertiesSet()方法
步骤:
1 创建SchedulerFactory对象并初始化
2 通过第一步创建Scheduler工厂对象创建scheduler对象并初始化
3 添加配置文件中的相关监听器和触发器等
//---------------------------------------------------------------------
// Implementation of InitializingBean interface
//--------------------------------------------------------------------- public void afterPropertiesSet() throws Exception {
//这里省略部分代码
// Create SchedulerFactory instance.
SchedulerFactory schedulerFactory = (SchedulerFactory)
BeanUtils.instantiateClass(this.schedulerFactoryClass);
//初始化配置属性
initSchedulerFactory(schedulerFactory); //这里省略部分代码 // Get Scheduler instance from SchedulerFactory.
try {
//实例化scheduler对象
this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
//初始化scheduler对象的上下文
populateSchedulerContext(); //这里省略部分代码
} finally {
//释放资源
} registerListeners();
registerJobsAndTriggers();
}
步骤一用于创建和初始化Scheduler工厂(SchedulerFactory这里默认为Class<?> schedulerFactoryClass = StdSchedulerFactory.class)
initSchedulerFactory(schedulerFactory)方法用于初始化StdSchedulerFactory的配置属性(这些属性用于下一步创建Scheduler对象)
/**
* 初始化配置属性
* Load and/or apply Quartz properties to the given SchedulerFactory.
* @param schedulerFactory the SchedulerFactory to initialize
*/
private void initSchedulerFactory(SchedulerFactory schedulerFactory)
throws SchedulerException, IOException { //这里省略部分代码 Properties mergedProps = new Properties(); if (this.resourceLoader != null) {
mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
ResourceLoaderClassLoadHelper.class.getName());
} if (this.taskExecutor != null) {
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
LocalTaskExecutorThreadPool.class.getName());
}
else {
// Set necessary default properties here, as Quartz will not apply
// its default configuration when explicitly given properties.
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT));
} if (this.configLocation != null) {
if (logger.isInfoEnabled()) {
logger.info("Loading Quartz config from [" + this.configLocation + "]");
}
PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation);
} CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps); if (this.dataSource != null) {
mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());
} // Make sure to set the scheduler name as configured in the Spring configuration.
if (this.schedulerName != null) {
mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);
} ((StdSchedulerFactory) schedulerFactory).initialize(mergedProps);
}
步骤二用于创建scheduler对象并初始化,其中创建scheduler对象方法如下
protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName)
throws SchedulerException { // Override thread context ClassLoader to work around naive Quartz ClassLoadHelper loading.
Thread currentThread = Thread.currentThread();
ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
boolean overrideClassLoader = (this.resourceLoader != null &&
!this.resourceLoader.getClassLoader().equals(threadContextClassLoader));
if (overrideClassLoader) {
currentThread.setContextClassLoader(this.resourceLoader.getClassLoader());
}
try {
SchedulerRepository repository = SchedulerRepository.getInstance();
synchronized (repository) {
Scheduler existingScheduler = (schedulerName != null ? repository.lookup(schedulerName) : null);
Scheduler newScheduler = schedulerFactory.getScheduler();
if (newScheduler == existingScheduler) {
throw new IllegalStateException("Active Scheduler of name '" + schedulerName + "' already registered " +
"in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!");
}
if (!this.exposeSchedulerInRepository) {
// Need to remove it in this case, since Quartz shares the Scheduler instance by default!
SchedulerRepository.getInstance().remove(newScheduler.getSchedulerName());
}
return newScheduler;
}
}
finally {
if (overrideClassLoader) {
// Reset original thread context ClassLoader.
currentThread.setContextClassLoader(threadContextClassLoader);
}
}
}
该方法里面首先设置当前线程的classload对象,然后调用schedulerFactory.工厂实例化scheduler对象
/**
* <p>
* Returns a handle to the Scheduler produced by this factory.
* </p>
*
* <p>
* If one of the <code>initialize</code> methods has not be previously
* called, then the default (no-arg) <code>initialize()</code> method
* will be called by this method.
* </p>
*/
public Scheduler getScheduler() throws SchedulerException {
if (cfg == null) {
initialize();
} SchedulerRepository schedRep = SchedulerRepository.getInstance(); Scheduler sched = schedRep.lookup(getSchedulerName()); if (sched != null) {
if (sched.isShutdown()) {
schedRep.remove(getSchedulerName());
} else {
return sched;
}
} sched = instantiate(); return sched;
}
方法Scheduler instantiate()实例化Scheduler对象,这个方法很长很长,其中关键代码如下
// Get ThreadPool Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//实例化线程池对象
String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, null); if (tpClass == null) {
initException = new SchedulerException(
"ThreadPool class not specified. ",
SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} try {
tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("ThreadPool class '"
+ tpClass + "' could not be instantiated.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
}
tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true);
try {
setBeanProps(tp, tProps);
} catch (Exception e) {
initException = new SchedulerException("ThreadPool class '"
+ tpClass + "' props could not be configured.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} // Get JobStore Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//实例化JobStore对象
String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,
RAMJobStore.class.getName()); if (jsClass == null) {
initException = new SchedulerException(
"JobStore class not specified. ",
SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} try {
js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("JobStore class '" + jsClass
+ "' could not be instantiated.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} SchedulerDetailsSetter.setDetails(js, schedName, schedInstId); tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
try {
setBeanProps(js, tProps);
} catch (Exception e) {
initException = new SchedulerException("JobStore class '" + jsClass
+ "' props could not be configured.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
}
//初始化DBConnectionManager dbMgr try {
PoolingConnectionProvider cp = new PoolingConnectionProvider(
dsDriver, dsURL, dsUser, dsPass, dsCnt,
dsValidation);
dbMgr = DBConnectionManager.getInstance();
dbMgr.addConnectionProvider(dsNames[i], cp);
} catch (SQLException sqle) {
initException = new SchedulerException(
"Could not initialize DataSource: " + dsNames[i],
sqle);
throw initException;
} //创建QuartzSchedulerResources rsrcs对象
QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
rsrcs.setName(schedName);
rsrcs.setThreadName(threadName);
rsrcs.setInstanceId(schedInstId);
//设置JobRunShellFactory
rsrcs.setJobRunShellFactory(jrsf);
rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
rsrcs.setRunUpdateCheck(!skipUpdateCheck);
rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown);
rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait);
rsrcs.setJMXExport(jmxExport);
rsrcs.setJMXObjectName(jmxObjectName); SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId);
//设置ThreadPool
rsrcs.setThreadPool(tp);
//创建并启动工作线程
tp.initialize();
tpInited = true;
//设置JobStore
rsrcs.setJobStore(js); schedCtxt = new SchedulingContext();
schedCtxt.setInstanceId(rsrcs.getInstanceId()); qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);
qsInited = true; // Create Scheduler ref...
Scheduler scheduler = instantiate(rsrcs, qs); // fire up job store, and runshell factory js.setInstanceId(schedInstId);
js.setInstanceName(schedName);
js.initialize(loadHelper, qs.getSchedulerSignaler()); jrsf.initialize(scheduler, schedCtxt); qs.initialize(); // prevents the repository from being garbage collected
qs.addNoGCObject(schedRep);
// prevents the db manager from being garbage collected
if (dbMgr != null) {
qs.addNoGCObject(dbMgr);
} schedRep.bind(scheduler);
该方法最终通过调用Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs)方法创建对象
protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {
SchedulingContext schedCtxt = new SchedulingContext();
schedCtxt.setInstanceId(rsrcs.getInstanceId()); Scheduler scheduler = new StdScheduler(qs, schedCtxt);
return scheduler;
}
通过StdScheduler的构造函数传入QuartzScheduler sched, SchedulingContext schedCtxt参数对象
这里关键是QuartzScheduler sched参数对象是作为StdScheduler对象的代理身份的,我们调用的StdScheduler对象方法都是间接执行QuartzScheduler sched的相关方法
(修正:StdScheduler对象为QuartzScheduler sched参数对象代理,QuartzScheduler sched参数对象为真实对象)
QuartzScheduler对象的构造方法QuartzSchedulerResources resources参数保存了相关资源的引用,具体QuartzScheduler对象具体逻辑下文再分析
现在回到SchedulerFactoryBean的void afterPropertiesSet()方法,populateSchedulerContext()用于初始化scheduler对象的上下文,这里是保存相关对象的引用,在任务执行方法里面方便调用相关引用对象的方法
/**
* Expose the specified context attributes and/or the current
* ApplicationContext in the Quartz SchedulerContext.
*/
private void populateSchedulerContext() throws SchedulerException {
// Put specified objects into Scheduler context.
if (this.schedulerContextMap != null) {
this.scheduler.getContext().putAll(this.schedulerContextMap);
} // Register ApplicationContext in Scheduler context.
if (this.applicationContextSchedulerContextKey != null) {
if (this.applicationContext == null) {
throw new IllegalStateException(
"SchedulerFactoryBean needs to be set up in an ApplicationContext " +
"to be able to handle an 'applicationContextSchedulerContextKey'");
}
this.scheduler.getContext().put(this.applicationContextSchedulerContextKey, this.applicationContext);
}
}
步骤三添加监听器和触发器,这里不具体分析在创建创建Scheduler对象后,接下来就是调用它的启动方法(主线程方法)了
/**
* Start the Quartz Scheduler, respecting the "startupDelay" setting.
* @param scheduler the Scheduler to start
* @param startupDelay the number of seconds to wait before starting
* the Scheduler asynchronously
*/
protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException {
if (startupDelay <= 0) {
logger.info("Starting Quartz Scheduler now");
scheduler.start();
}
else {
if (logger.isInfoEnabled()) {
logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() +
"] in " + startupDelay + " seconds");
}
Thread schedulerThread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(startupDelay * 1000);
}
catch (InterruptedException ex) {
// simply proceed
}
if (logger.isInfoEnabled()) {
logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds");
}
try {
scheduler.start();
}
catch (SchedulerException ex) {
throw new SchedulingException("Could not start Quartz Scheduler after delay", ex);
}
}
};
schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]");
schedulerThread.setDaemon(true);
schedulerThread.start();
}
}
首先是延迟指定时间,然后在线程对象里面调用主线程方法
---------------------------------------------------------------------------
本系列quartz源码解析系本人原创
作者 博客园 刺猬的温驯
邮箱 chenying998179(爬虫绕道)163.com
本文链接 http://www.cnblogs.com/chenying99/p/3151850.html
本文版权归作者所有,未经作者同意,严禁转载及用作商业传播,否则将追究法律责任。