配色: 字号:
Quartz与Spring集成—— SchedulerFactoryBean的初始化分析
2016-12-19 | 阅:  转:  |  分享 
  
Quartz与Spring集成——SchedulerFactoryBean的初始化分析



前言

Quartz是一个开源的定时调度框架,支持集群部署。我们可以通过其JavaAPI来使用它,或者通过spring来配置与管理,也可以结合使用两种方式。本文重点分析Quartz2.2.3与Spring4.3.0.RELEASE集成时的初始化过程。



SchedulerFactoryBean

与Spring集成时通常需要在Spring配置文件中加入SchedulerFactoryBean这个工厂Bean,例如:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片











再来看看SchedulerFactoryBean的类定义:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassSchedulerFactoryBeanextendsSchedulerAccessorimplementsFactoryBean,BeanNameAware,

ApplicationContextAware,InitializingBean,DisposableBean,SmartLifecycle{

从中看到其实现了FactoryBean、BeanNameAware、ApplicationContextAware、InitializingBean、DisposableBean等常用接口,这些接口的具体意义本文不作赘述,不了解的可以专门研究下Spring的原理和源码实现。根据Spring的原理我们知道,如果Bean本身实现了InitializingBean接口,那么在Spring加载解析BeanDefinition,并初始化Bean后会调用SchedulerFactoryBean的afterPropertiesSet方法,这里只会挑出其中的关键代码进行分析。



初始化SchedulerFactory

在afterPropertiesSet中首先会初始化SchedulerFactory,代码如下:



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

//CreateSchedulerFactoryinstance...

SchedulerFactoryschedulerFactory=BeanUtils.instantiateClass(this.schedulerFactoryClass);

initSchedulerFactory(schedulerFactory);

属性schedulerFactoryClass的默认值是StdSchedulerFactory.class,因此这里默认会初始化StdSchedulerFactory,用户也可以使用Spring的配置文件修改schedulerFactoryClass的值为其他SchedulerFactory接口的实现(比如RemoteScheduler或者继承RemoteMBeanScheduler的子类)。在使用Spring的BeanUtils工具类对SchedulerFactory实例化后,调用initSchedulerFactory方法(见代码清单1)对SchedulerFactory初始化。



代码清单1初始化SchedulerFactory



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

privatevoidinitSchedulerFactory(SchedulerFactoryschedulerFactory)throwsSchedulerException,IOException{

if(!(schedulerFactoryinstanceofStdSchedulerFactory)){

if(this.configLocation!=null||this.quartzProperties!=null||

this.taskExecutor!=null||this.dataSource!=null){

thrownewIllegalArgumentException(

"StdSchedulerFactoryrequiredforapplyingQuartzproperties:"+schedulerFactory);

}

//Otherwiseassumethatnoinitializationisnecessary...

return;

}



PropertiesmergedProps=newProperties();



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{

//Setnecessarydefaultpropertieshere,asQuartzwillnotapply

//itsdefaultconfigurationwhenexplicitlygivenproperties.

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("LoadingQuartzconfigfrom["+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());

}



//MakesuretosettheschedulernameasconfiguredintheSpringconfiguration.

if(this.schedulerName!=null){

mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME,this.schedulerName);

}



((StdSchedulerFactory)schedulerFactory).initialize(mergedProps);

}

仔细阅读initSchedulerFactory方法,可以理解其初始化过程如下:



对于非StdSchedulerFactory的其他SchedulerFactory,需要对参数进行检查;

设置内置的属性并存入mergedProps这个字典中。这些属性包括:

org.quartz.scheduler.classLoadHelper.class:用于Quartz与Spring集成时加载Spring资源;

org.quartz.threadPool.class:执行Quartz中Task的线程池;

org.quartz.threadPool.threadCount:执行Quartz中Task的线程池的线程数量。

加载configLocation属性指定的属性文件中的属性并合并到mergedProps中,这说明属性文件中的配置可以覆盖内置的属性参数。

向mergedProps中设置其它属性:

org.quartz.jobStore.class:作业持久化存储的类,值为LocalDataSourceJobStore;

org.quartz.scheduler.instanceName:值为Spring配置文件中设置的值;

调用StdSchedulerFactory的initialize方法进一步初始化,实质上不过是创建PropertiesParser对mergedProps进行包装(见代码清单2);

代码清单2StdSchedulerFactory的initialize实现



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicvoidinitialize(Propertiesprops)throwsSchedulerException{

if(propSrc==null){

propSrc="anexternallyprovidedpropertiesinstance.";

}



this.cfg=newPropertiesParser(props);

}

初始化后的动作

在SchedulerFactory初始化完成后,还会执行代码清单3中代码,其步骤归纳如下:



使用ThreadLocal技术持有resourceLoader、taskExecutor、dataSource、nonTransactionalDataSource;

调用createScheduler方法创建调度器(具体内容请阅读《Quartz与Spring集成——创建调度器》一文);

调用populateSchedulerContext,指定调度上下文(SchedulerContext)的属性和它有的Spring的ApplicationContext;

给调度器设置作业工厂类JobFactory;

调用registerListeners方法注册有关调度、作业、触发器等内容的监听器(见代码清单4);

调用registerJobsAndTriggers方法注册作业和触发器(具体内容将会在另一篇博文单独介绍);

代码清单3初始化后的动作

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

this.scheduler=createScheduler(schedulerFactory,this.schedulerName);

populateSchedulerContext();



if(!this.jobFactorySet&&!(this.schedulerinstanceofRemoteScheduler)){

//UseAdaptableJobFactoryasdefaultforalocalScheduler,unlesswhen

//explicitlygivenanullvaluethroughthe"jobFactory"beanproperty.

this.jobFactory=newAdaptableJobFactory();

}

if(this.jobFactory!=null){

if(this.jobFactoryinstanceofSchedulerContextAware){

((SchedulerContextAware)this.jobFactory).setSchedulerContext(this.scheduler.getContext());

}

this.scheduler.setJobFactory(this.jobFactory);

}

//省略次要代码

registerListeners();

registerJobsAndTriggers();



代码清单4注册监听器

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

protectedvoidregisterListeners()throwsSchedulerException{

ListenerManagerlistenerManager=getScheduler().getListenerManager();

if(this.schedulerwww.baiyuewang.netListeners!=null){

for(SchedulerListenerlistener:this.schedulerListeners){

listenerManager.addSchedulerListener(listener);

}

}

if(this.globalJobListeners!=null){

for(JobListenerlistener:this.globalJobListeners){

listenerManager.addJobListener(listener);

}

}

if(this.globalTriggerListeners!=null){

for(TriggerListenerlistener:this.globalTriggerListeners){

listenerManager.addTriggerListener(listener);

}

}

}



总结

对于熟悉Java的开发人员而言,任何Java对象(基本对象和复合对象)在使用之前都需要初始化,Quartz作为一个大的组件,其本身也是一个对象。从SchedulerFactoryBean的类定义中,我们可以看到其充分利用了Spring提供的各种扩展接口,以便于在调度上下文中使用Spring支持的丰富功能。在SchedulerFactory的初始化过程中,我们看到SchedulerFactoryBean支持多种注入属性,而且这些属性可以覆盖内置的属性设置,使用者可以根据自身需要进行配置。另外通过configLocation属性指定属性文件,可以在单独的属性文件中配置属性,当要配置的属性很多时,可以避免xml配置臃肿。添加对调度、作业及触发器等内容的监听器添加,以便于感兴趣的组件,在以上内容发生变化时,进行一些操作。这种方式也能够将其他组件与SchedulerFactoryBean之间的关系进行解耦。

献花(0)
+1
(本文系thedust79首藏)