Quartz与Spring集成—— SchedulerFactoryBean的初始化分析 (转)

前言

Quartz是一个开源的定时调度框架,支持集群部署。我们可以通过其Java API来使用它,或者通过spring来配置与管理,也可以结合使用两种方式。

本文重点分析Quartz2.2.3与Spring4.3.0.RELEASE集成时的初始化过程。

SchedulerFactoryBean

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

  1. <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  2.     <property name="dataSource" ref="dataSource"/>  
  3.     <property name="overwriteExistingJobs" value="true"/>  
  4.     <property name="configLocation" value="classpath:quartz.properties"/>  
  5. </bean>  

再来看看SchedulerFactoryBean的类定义:

  1. public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>, BeanNameAware,  
  2.         ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {  

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

初始化SchedulerFactory

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

  1. // Create SchedulerFactory instance...  
  2. SchedulerFactory schedulerFactory = BeanUtils.instantiateClass(this.schedulerFactoryClass);  
  3. initSchedulerFactory(schedulerFactory);  

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

代码清单1 初始化SchedulerFactory

  1. private void initSchedulerFactory(SchedulerFactory schedulerFactory) throws SchedulerException, IOException {  
  2.     if (!(schedulerFactory instanceof StdSchedulerFactory)) {  
  3.         if (this.configLocation != null || this.quartzProperties != null ||  
  4.                 this.taskExecutor != null || this.dataSource != null) {  
  5.             throw new IllegalArgumentException(  
  6.                     "StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);  
  7.         }  
  8.         // Otherwise assume that no initialization is necessary...  
  9.         return;  
  10.     }  
  11.   
  12.     Properties mergedProps = new Properties();  
  13.   
  14.     if (this.resourceLoader != null) {  
  15.         mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,  
  16.                 ResourceLoaderClassLoadHelper.class.getName());  
  17.     }  
  18.   
  19.     if (this.taskExecutor != null) {  
  20.         mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,  
  21.                 LocalTaskExecutorThreadPool.class.getName());  
  22.     }  
  23.     else {  
  24.         // Set necessary default properties here, as Quartz will not apply  
  25.         // its default configuration when explicitly given properties.  
  26.         mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());  
  27.         mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT));  
  28.     }  
  29.   
  30.     if (this.configLocation != null) {  
  31.         if (logger.isInfoEnabled()) {  
  32.             logger.info("Loading Quartz config from [" + this.configLocation + "]");  
  33.         }  
  34.         PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation);  
  35.     }  
  36.   
  37.     CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps);  
  38.   
  39.     if (this.dataSource != null) {  
  40.         mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());  
  41.     }  
  42.   
  43.     // Make sure to set the scheduler name as configured in the Spring configuration.  
  44.     if (this.schedulerName != null) {  
  45.         mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);  
  46.     }  
  47.   
  48.     ((StdSchedulerFactory) schedulerFactory).initialize(mergedProps);  
  49. }  

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

  1. 对于非StdSchedulerFactory的其他SchedulerFactory,需要对参数进行检查;
  2. 设置内置的属性并存入mergedProps这个字典中。这些属性包括:
    • org.quartz.scheduler.classLoadHelper.class:用于Quartz与Spring集成时加载Spring资源;
    • org.quartz.threadPool.class:执行Quartz中Task的线程池;
    • org.quartz.threadPool.threadCount:执行Quartz中Task的线程池的线程数量。
  3. 加载configLocation属性指定的属性文件中的属性并合并到mergedProps中,这说明属性文件中的配置可以覆盖内置的属性参数。
  4. 向mergedProps中设置其它属性:
    • org.quartz.jobStore.class:作业持久化存储的类,值为LocalDataSourceJobStore;
    • org.quartz.scheduler.instanceName:值为Spring配置文件中设置的值;
  5. 调用StdSchedulerFactory的initialize方法进一步初始化,实质上不过是创建PropertiesParser对mergedProps进行包装(见代码清单2);

代码清单2 StdSchedulerFactory的initialize实现

  1. public void initialize(Properties props) throws SchedulerException {  
  2.     if (propSrc == null) {  
  3.         propSrc = "an externally provided properties instance.";  
  4.     }  
  5.   
  6.     this.cfg = new PropertiesParser(props);  
  7. }  

初始化后的动作

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

  1. 使用ThreadLocal技术持有resourceLoader、taskExecutor、dataSource、nonTransactionalDataSource;
  2. 调用createScheduler方法创建调度器(具体内容请阅读《Quartz与Spring集成——创建调度器》一文);
  3. 调用populateSchedulerContext,指定调度上下文(SchedulerContext)的属性和它有的Spring的ApplicationContext;
  4. 给调度器设置作业工厂类JobFactory;
  5. 调用registerListeners方法注册有关调度、作业、触发器等内容的监听器(见代码清单4);
  6. 调用registerJobsAndTriggers方法注册作业和触发器(具体内容将会在另一篇博文单独介绍);
代码清单3 初始化后的动作
  1. this.scheduler = createScheduler(schedulerFactory, this.schedulerName);  
  2. populateSchedulerContext();  
  3.   
  4. if (!this.jobFactorySet && !(this.scheduler instanceof RemoteScheduler)) {  
  5.     // Use AdaptableJobFactory as default for a local Scheduler, unless when  
  6.     // explicitly given a null value through the "jobFactory" bean property.  
  7.     this.jobFactory = new AdaptableJobFactory();  
  8. }  
  9. if (this.jobFactory != null) {  
  10.     if (this.jobFactory instanceof SchedulerContextAware) {  
  11.         ((SchedulerContextAware) this.jobFactory).setSchedulerContext(this.scheduler.getContext());  
  12.     }  
  13.     this.scheduler.setJobFactory(this.jobFactory);  
  14. }  
  15.     //省略次要代码  
  16. registerListeners();  
  17. registerJobsAndTriggers();  
 
代码清单4 注册监听器
  1. protected void registerListeners() throws SchedulerException {  
  2.     ListenerManager listenerManager = getScheduler().getListenerManager();  
  3.     if (this.schedulerListeners != null) {  
  4.         for (SchedulerListener listener : this.schedulerListeners) {  
  5.             listenerManager.addSchedulerListener(listener);  
  6.         }  
  7.     }  
  8.     if (this.globalJobListeners != null) {  
  9.         for (JobListener listener : this.globalJobListeners) {  
  10.             listenerManager.addJobListener(listener);  
  11.         }  
  12.     }  
  13.     if (this.globalTriggerListeners != null) {  
  14.         for (TriggerListener listener : this.globalTriggerListeners) {  
  15.             listenerManager.addTriggerListener(listener);  
  16.         }  
  17.     }  
  18. }  

总结

对于熟悉Java的开发人员而言,任何Java对象(基本对象和复合对象)在使用之前都需要初始化,Quartz作为一个大的组件,其本身也是一个对象。从SchedulerFactoryBean的

类定义中,我们可以看到其充分利用了Spring提供的各种扩展接口,以便于在调度上下文中使用Spring支持的丰富功能。在SchedulerFactory的初始化过程中,

我们看到SchedulerFactoryBean支持多种注入属性,而且这些属性可以覆盖内置的属性设置,使用者可以根据自身需要进行配置。另外通过configLocation属性指定属性文件,

可以在单独的属性文件中配置属性,当要配置的属性很多时,可以避免xml配置臃肿。添加对调度、作业及触发器等内容的监听器添加,以便于感兴趣的组件,

在以上内容发生变化时,进行一些操作。这种方式也能够将其他组件与SchedulerFactoryBean之间的关系进行解耦。

 

转自http://blog.csdn.net/beliefer/article/details/51578546

posted on 2017-06-26 19:39  xiaopangzhi  阅读(895)  评论(0)    收藏  举报