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

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/51578546 前言 Quartz是一个开源的定时调度框架,支持集群部署。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/51578546

前言

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的线程池的线程数量。
加载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中代码,其步骤归纳如下:

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


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

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


相关文章
|
10月前
|
数据可视化 Java BI
将 Spring 微服务与 BI 工具集成:最佳实践
本文探讨了 Spring 微服务与商业智能(BI)工具集成的潜力与实践。随着微服务架构和数据分析需求的增长,Spring Boot 和 Spring Cloud 提供了构建可扩展、弹性服务的框架,而 BI 工具则增强了数据可视化与实时分析能力。文章介绍了 Spring 微服务的核心概念、BI 工具在企业中的作用,并深入分析了两者集成带来的优势,如实时数据处理、个性化报告、数据聚合与安全保障。同时,文中还总结了集成过程中的最佳实践,包括事件驱动架构、集中配置管理、数据安全控制、模块化设计与持续优化策略,旨在帮助企业构建高效、智能的数据驱动系统。
466 1
将 Spring 微服务与 BI 工具集成:最佳实践
|
12月前
|
XML 人工智能 Java
Spring Boot集成Aviator实现参数校验
Aviator是一个高性能、轻量级的Java表达式求值引擎,适用于动态表达式计算。其特点包括支持多种运算符、函数调用、正则匹配、自动类型转换及嵌套变量访问,性能优异且依赖小。适用于规则引擎、公式计算和动态脚本控制等场景。本文介绍了如何结合Aviator与AOP实现参数校验,并附有代码示例和仓库链接。
712 0
|
10月前
|
监控 Cloud Native Java
Spring Integration 企业集成模式技术详解与实践指南
本文档全面介绍 Spring Integration 框架的核心概念、架构设计和实际应用。作为 Spring 生态系统中的企业集成解决方案,Spring Integration 基于著名的 Enterprise Integration Patterns(EIP)提供了轻量级的消息驱动架构。本文将深入探讨其消息通道、端点、过滤器、转换器等核心组件,以及如何构建可靠的企业集成解决方案。
861 0
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
1067 2
|
Java 数据库连接 数据库
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1425 0
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1337 0
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
586 0
|
9月前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
642 4

热门文章

最新文章