Quartz与Spring集成的初始化分析
2016-07-25 15:29
387 查看
前言
Quartz是一个开源的定时调度框架,支持集群部署。我们可以通过其Java API来使用它,或者通过Spring来配置与管理,也可以结合使用两种方式。本文重点分析Quartz与Spring集成时的初始化过程。SchedulerFactoryBean
与Spring集成时通常需要在Spring配置文件中加入SchedulerFactoryBean这个工厂Bean,例如:<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="overwriteExistingJobs" value="true"/> <property name="configLocation" value="classpath:quartz.properties"/> </bean>再来看看SchedulerFactoryBean的类定义:
public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>, BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {从中看到其实现了FactoryBean、BeanNameAware、ApplicationContextAware、InitializingBean、DisposableBean等常用接口,这些接口的具体意义本文不作赘述,不了解的可以专门研究下Spring的原理和源码实现。根据Spring的原理我们知道,如果Bean本身实现了InitializingBean接口,那么在Spring加载解析BeanDefinition,并初始化Bean后会调用SchedulerFactoryBean的afterPropertiesSet方法,这里只会挑出其中的关键代码进行分析。
初始化SchedulerFactory
在afterPropertiesSet中首先会初始化SchedulerFactoryBean,代码如下:// Create SchedulerFactory instance... SchedulerFactory schedulerFactory = BeanUtils.instantiateClass(this.schedulerFactoryClass); initSchedulerFactory(schedulerFactory);属性schedulerFactoryClass的默认值是StdSchedulerFactory.class,因此这里默认会初始化StdSchedulerFactory,用户也可以使用Spring的配置文件修改schedulerFactoryClass的值为其他SchedulerFactory接口的实现(比如RemoteScheduler或者继承RemoteMBeanScheduler的子类)。在使用Spring的BeanUtils工具类对SchedulerFactory实例化后,调用initSchedulerFactory方法(见代码清单1)对SchedulerFactory初始化。
代码清单1 初始化SchedulerFactory
private void initSchedulerFactory(SchedulerFactory schedulerFactory) throws SchedulerException, IOException { if (!(schedulerFactory instanceof StdSchedulerFactory)) { if (this.configLocation != null || this.quartzProperties != null || this.taskExecutor != null || this.dataSource != null) { throw new IllegalArgumentException( "StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory); } // Otherwise assume that no initialization is necessary... return; } 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); }仔细阅读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);
代码清单2 StdSchedulerFactory的initialize实现
public void initialize(Properties props) throws SchedulerException { if (propSrc == null) { propSrc = "an externally provided properties instance."; } this.cfg = new PropertiesParser(props); }
初始化后的动作
在SchedulerFactory初始化完成后,还会执行代码清单3中代码,其步骤归纳如下:使用ThreadLocal技术持有resourceLoader、taskExecutor、dataSource、nonTransactionalDataSource;调用createScheduler方法创建调度器(具体内容将会在另一篇博文单独介绍);
调用populateSchedulerContext,指定调度上下文(SchedulerContext)的属性和它有的Spring的ApplicationContext;
给调度器设置作业工厂类JobFactory;
调用registerListeners方法注册有关调度、作业、触发器等内容的监听器(见代码清单4);
调用registerJobsAndTriggers方法注册作业和触发器(具体内容将会在另一篇博文单独介绍);
代码清单3 初始化后的动作
this.scheduler = createScheduler(schedulerFactory, this.schedulerName); populateSchedulerContext(); if (!this.jobFactorySet && !(this.scheduler instanceof RemoteScheduler)) { // Use AdaptableJobFactory as default for a local Scheduler, unless when // explicitly given a null value through the "jobFactory" bean property. this.jobFactory = new AdaptableJobFactory(); } if (this.jobFactory != null) { if (this.jobFactory instanceof SchedulerContextAware) { ((SchedulerContextAware) this.jobFactory).setSchedulerContext(this.scheduler.getContext()); } this.scheduler.setJobFactory(this.jobFactory); } //省略次要代码 registerListeners(); registerJobsAndTriggers();代码清单4 注册监听器
protected void registerListeners() throws SchedulerException { ListenerManager listenerManager = getScheduler().getListenerManager(); if (this.schedulerListeners != null) { for (SchedulerListener listener : this.schedulerListeners) { listenerManager.addSchedulerListener(listener); } } if (this.globalJobListeners != null) { for (JobListener listener : this.globalJobListeners) { listenerManager.addJobListener(listener); } } if (this.globalTriggerListeners != null) { for (TriggerListener listener : this.globalTriggerListeners) { listenerManager.addTriggerListener(listener); } } }
总结
对于熟悉Java的开发人员而言,任何Java对象(基本对象和复合对象)在使用之前都需要初始化,Quartz作为一个大的组件,其本身也是一个对象。从SchedulerFactoryBean的类定义中,我们可以看到其充分利用了Spring提供的各种扩展接口,以便于在调度上下文中使用Spring支持的丰富功能。在SchedulerFactory的初始化过程中,我们看到SchedulerFactoryBean支持多种注入属性,而且这些属性可以覆盖内置的属性设置,使用者可以根据自身需要进行配置。另外通过configLocation属性指定属性文件,可以在单独的属性文件中配置属性,当要配置的属性很多时,可以避免xml配置臃肿。添加对调度、作业及触发器等内容的监听器添加,以便于感兴趣的组件,在以上内容发生变化时,进行一些操作。这种方式也能够将其他组件与SchedulerFactoryBean之间的关系进行解耦。后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。
京东:http://item.jd.com/11846120.html
当当:http://product.dangdang.com/23838168.html
相关文章推荐
- javaWeb学习(1)——servlet生命周期
- JAXB方式读写xml实例
- 获取本机Ip
- 本人所看Java书籍推荐(长期更新,更新速度与本人进度相同)
- spring创建单例bean和非单例bean的方式
- JAVA实践最小生成树---kruskal算法
- Build RESTful APIs with Spring MVC:Security
- Java单例设计模式(实现Java的一个类只有一个对象)
- Java & json
- eclipse没有(添加)"Dynamic Web Project"选项的方法
- Java 设计模式的学习(含代码),未完待续
- java I/O流——File类的基本用法
- Class类的常见方法示例,记不住,先保存下
- Build RESTful APIs with Spring MVC:Exception
- Build RESTful APIs with Spring MVC: Swagger
- Java利用Math.random()方法随机生成A-Z的字符
- Java提高篇——equals()与hashCode()方法详解
- Java:详解Java中super的几种用法并与this的区别
- java基础四 [构造器和垃圾回收](阅读Head First Java记录)
- 25个让Java程序员更高效的Eclipse插件