Quartz与Spring集成—— SchedulerFactoryBean的初始化分析「建议收藏」

Quartz与Spring集成—— SchedulerFactoryBean的初始化分析「建议收藏」Quartz是一个开源的定时调度框架,支持集群部署。我们可以通过其JavaAPI来使用它,或者通过Spring来配置与管理,也可以结合使用两种方式。本文重点分析Quartz与Spring集成时的初始化过程。

大家好,又见面了,我是你们的朋友全栈君。

前言

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

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中首先会初始化SchedulerFactory,代码如下:

		// 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方法,可以理解其初始化过程如下:

  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实现

    public void initialize(Properties props) throws SchedulerException {        if (propSrc == null) {            propSrc = "an externally provided properties instance.";        }        this.cfg = new PropertiesParser(props);    }

初始化后的动作

在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 初始化后的动作
	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:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。

Quartz与Spring集成—— SchedulerFactoryBean的初始化分析「建议收藏」

京东:http://item.jd.com/11846120.html 

当当:http://product.dangdang.com/23838168.html 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/142186.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • vue格式化时间戳_日期格式转换

    vue格式化时间戳_日期格式转换第一步:下载安装第三方组件:npminstallmoment–save第二步:在你需要格式化时间的页面引入moment组件importmomentfrom’moment’第三步:然后配合我们element-ui,在元素中加入:formatter<el-table-columnprop=”add_time”label=”创建时间”:formatter=”dateFormat”width=”200px”></el-table-column&gt

    2022年9月25日
    4
  • WebRTC 系列文章 ICE服务器搭建 coturn[通俗易懂]

    WebRTC 系列文章 ICE服务器搭建 coturn[通俗易懂]WebRTC系列文章ICE服务器搭建服务器环境安装coturn安装coturn依赖软件克隆代码仓库安装coturn修改配置克隆一份配置文件修改配置项启动coturn检测coturn工作状态coturn是一个C++编写的开源ICE服务器,同时兼顾了STUN和TURN的功能。服务器环境CentOS7云服务器一台安装coturn安装coturn依赖软件yumi…

    2022年6月13日
    100
  • Idea激活码最新教程2023.3.3版本,永久有效激活码,亲测可用,记得收藏

    Idea激活码最新教程2023.3.3版本,永久有效激活码,亲测可用,记得收藏Idea 激活码教程永久有效 2023 3 3 激活码教程 Windows 版永久激活 持续更新 Idea 激活码 2023 3 3 成功激活

    2025年5月27日
    1
  • SQL server分页的四种方法(算很全面了)

    SQL server分页的四种方法(算很全面了)  这篇博客讲的是SQLserver的分页方法,用的SQLserver2012版本。下面都用pageIndex表示页数,pageSize表示一页包含的记录。并且下面涉及到具体例子的,设定查询第2页,每页含10条记录。  首先说一下SQLserver的分页与MySQL的分页的不同,mysql的分页直接是用limit(pageIndex-1),pageSize就可以完成,但是SQLse…

    2022年6月14日
    34
  • 简述Vue的响应式原理

    简述Vue的响应式原理当一个Vue实例创建时,vue会遍历data选项的属性,用Object.defineProperty将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。 巴拉巴拉:==…

    2022年5月7日
    67
  • 计算机网络体系结构

    计算机网络体系结构计算机网络体系结构认识一下什么是计算机网络体系结构计算机网络是一个非常复杂、庞大的系统,涉及到很多组成部分。包括主机、路由器、各种链路、应用、协议、硬件、软件……等。那么是否存在一种体系结构可以

    2022年7月4日
    20

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号