承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置。备注:此文建立在spring的4.2.3.RELEASE版本

附例

Spring中的定时任务基本配置样例如下

<!--create schedule thread pool-->
<task:scheduler id="baseScheduler" pool-size="5"></task:scheduler> <!--define bean for schedule task-->
<bean id="taskBean" class="com.jing.test.spring.task.TaskBean"></bean> <!--apply schedule action to above taskBean-->
<task:scheduled-tasks scheduler="baseScheduler">
<task:scheduled ref="taskBean" method="doInit" cron="0 0 0 ? * *"></task:scheduled> <task:scheduled ref="taskBean" method="doClear" cron="0 0 23 ? * *"></task:scheduled>
</task:scheduled-tasks>

其中task:scheduler的配置是不必须的,并且由上述配置可知Spring配置的定时任务可细化到具体的类方法,有更好的扩展性

task:scheduler节点配置的作用

task:scheduler的节点配置与前文所提及的task:executor节点类似,均是创建线程池,那么有什么不同呢,我们可以稍微简单的看下其解析类org.springframework.scheduling.config.SchedulerBeanDefinitionParser的两个方法

	@Override
protected String getBeanClassName(Element element) {
return "org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler";
} @Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String poolSize = element.getAttribute("pool-size");
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
}

恩,也就是我们直接关注ThreadPoolTaskScheduler这个类即可。看下其是如何创建线程池的

	/**
* Create a new {@link ScheduledExecutorService} instance.
* <p>The default implementation creates a {@link ScheduledThreadPoolExecutor}.
* Can be overridden in subclasses to provide custom {@link ScheduledExecutorService} instances.
* @param poolSize the specified pool size
* @param threadFactory the ThreadFactory to use
* @param rejectedExecutionHandler the RejectedExecutionHandler to use
* @return a new ScheduledExecutorService instance
* @see #afterPropertiesSet()
* @see java.util.concurrent.ScheduledThreadPoolExecutor
*/
protected ScheduledExecutorService createExecutor(
int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { return new ScheduledThreadPoolExecutor(poolSize, threadFactory, rejectedExecutionHandler);
}

即默认创建的是ScheduledThreadPoolExecutor线程池,创建核心线程个数为pool-size指定的大小(默认为1),最大线程为Integer.MAX_VALUE,队列为DelayQueue,拒绝策略为AbortPolicy。详情读者可自行阅读

task:sheduled-tasks解析器

配置相应的定时任务,细化到任何bean的方法可直接关联定时器。我们同样观察其主要的两个方法getBeanClassName()doParse()方法

	@Override
protected String getBeanClassName(Element element) {
return "org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar";
}

即实例化并启动定时任务由ContextLifecycleScheduledTaskRegistrar类来执行,我们稍后再谈

	@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.setLazyInit(false); // lazy scheduled tasks are a contradiction in terms -> force to false
// 存放不同类型定时任务集合
ManagedList<RuntimeBeanReference> cronTaskList = new ManagedList<RuntimeBeanReference>();
ManagedList<RuntimeBeanReference> fixedDelayTaskList = new ManagedList<RuntimeBeanReference>();
ManagedList<RuntimeBeanReference> fixedRateTaskList = new ManagedList<RuntimeBeanReference>();
ManagedList<RuntimeBeanReference> triggerTaskList = new ManagedList<RuntimeBeanReference>();
// 解析子节点task:scheduled
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (!isScheduledElement(child, parserContext)) {
continue;
}
Element taskElement = (Element) child;
String ref = taskElement.getAttribute("ref");
String method = taskElement.getAttribute("method"); // ref 和 method属性必须同时指定,表示对哪个方法关联定时器
if (!StringUtils.hasText(ref) || !StringUtils.hasText(method)) {
parserContext.getReaderContext().error("Both 'ref' and 'method' are required", taskElement);
// Continue with the possible next task element
continue;
} String cronAttribute = taskElement.getAttribute("cron");
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
String triggerAttribute = taskElement.getAttribute("trigger");
String initialDelayAttribute = taskElement.getAttribute("initial-delay"); boolean hasCronAttribute = StringUtils.hasText(cronAttribute);
boolean hasFixedDelayAttribute = StringUtils.hasText(fixedDelayAttribute);
boolean hasFixedRateAttribute = StringUtils.hasText(fixedRateAttribute);
boolean hasTriggerAttribute = StringUtils.hasText(triggerAttribute);
boolean hasInitialDelayAttribute = StringUtils.hasText(initialDelayAttribute); // 必须指定cron/fixed-delay/fixed-rate/trigger属性
if (!(hasCronAttribute || hasFixedDelayAttribute || hasFixedRateAttribute || hasTriggerAttribute)) {
parserContext.getReaderContext().error(
"one of the 'cron', 'fixed-delay', 'fixed-rate', or 'trigger' attributes is required", taskElement);
continue; // with the possible next task element
} //initial-delay属性不与cron/trigger属性搭配
if (hasInitialDelayAttribute && (hasCronAttribute || hasTriggerAttribute)) {
parserContext.getReaderContext().error(
"the 'initial-delay' attribute may not be used with cron and trigger tasks", taskElement);
continue; // with the possible next task element
} // 将bean类下的method方法包装成ScheduledMethodRunnable.class实体类
String runnableName =
runnableReference(ref, method, taskElement, parserContext).getBeanName(); if (hasFixedDelayAttribute) {
// 包装成IntervalTask类
fixedDelayTaskList.add(intervalTaskReference(runnableName,
initialDelayAttribute, fixedDelayAttribute, taskElement, parserContext));
}
if (hasFixedRateAttribute) {
// 包装成IntervalTask类
fixedRateTaskList.add(intervalTaskReference(runnableName,
initialDelayAttribute, fixedRateAttribute, taskElement, parserContext));
}
if (hasCronAttribute) {
// 包装成CronTask类
cronTaskList.add(cronTaskReference(runnableName, cronAttribute,
taskElement, parserContext));
}
if (hasTriggerAttribute) {
// 包装成TriggerTask类
String triggerName = new RuntimeBeanReference(triggerAttribute).getBeanName();
triggerTaskList.add(triggerTaskReference(runnableName, triggerName,
taskElement, parserContext));
}
}
// scheduler属性
String schedulerRef = element.getAttribute("scheduler");
if (StringUtils.hasText(schedulerRef)) {
builder.addPropertyReference("taskScheduler", schedulerRef);
}
builder.addPropertyValue("cronTasksList", cronTaskList);
builder.addPropertyValue("fixedDelayTasksList", fixedDelayTaskList);
builder.addPropertyValue("fixedRateTasksList", fixedRateTaskList);
builder.addPropertyValue("triggerTasksList", triggerTaskList);
}

代码过长,此处作下小总结

  1. 定时任务的初始化与实例是由org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar类来加载执行

  2. task:scheduled-tasks的子节点名为task:scheduled

  3. task:scheduled中的refmethod属性是必填项,其会包装成ScheduledMethodRunnable对象;必须指定cron/fixed-delay/fixed-rate/trigger其中之一属性

  4. task-scheduledinitial-delay属性不必填,但其不和cron/trigger属性搭配使用

  5. task:scheduled中的cron代表cron表达式,为字符串形式;fixed-delayfixed-rate可与initial-delay搭配使用,一般选择其中一种即可,为数字形式;trigger代表的是触发器,其关联bean,字符串形式

  6. 根据第五点,具体的任务包装类分别为CronTaskIntervalTaskTriggerTask

ContextLifecycleScheduledTaskRegistrar-定时任务初始化

其默认实现了SmartInitializingSingletonafterSingletonsInstantiated()方法

	@Override
public void afterSingletonsInstantiated() {
scheduleTasks();
}

直接查看父类的schduleTasks()方法

	protected void scheduleTasks() {
long now = System.currentTimeMillis();
// 如果不指定scheduler属性,则默认使用单线程池模型
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
// trigger集合和cron集合统一调用任务定时器的schedule()方法
if (this.triggerTasks != null) {
for (TriggerTask task : this.triggerTasks) {
this.scheduledFutures.add(this.taskScheduler.schedule(
task.getRunnable(), task.getTrigger()));
}
}
if (this.cronTasks != null) {
for (CronTask task : this.cronTasks) {
this.scheduledFutures.add(this.taskScheduler.schedule(
task.getRunnable(), task.getTrigger()));
}
}
// fixedRate集合和fixedDelayTasks集合则分别调用任务定时器的scheduleAtFixedRate()和scheduleAtFixedDelay()方法
if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) {
if (task.getInitialDelay() > 0) {
Date startTime = new Date(now + task.getInitialDelay());
this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(
task.getRunnable(), startTime, task.getInterval()));
}
else {
this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(
task.getRunnable(), task.getInterval()));
}
}
}
if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) {
if (task.getInitialDelay() > 0) {
Date startTime = new Date(now + task.getInitialDelay());
this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(
task.getRunnable(), startTime, task.getInterval()));
}
else {
this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(
task.getRunnable(), task.getInterval()));
}
}
}
}

由上述代码可以得出,如果task:scheduled-tasks不指定scheduler属性,则默认会采用org.springframework.scheduling.concurrent.ConcurrentTaskScheduler任务定时器来管理任务集合,反之一般则是由org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler任务定时器来管理任务集合

小结

本文则是解析了task:scheduled-tasktask:scheduler节点的配置,具体的任务是如何被执行的,怎么控制定时任务,请见下文针对org.springframework.scheduling.concurrent.ConcurrentTaskScheduler的解读。

Spring源码情操陶冶#task:scheduled-tasks解析器的更多相关文章

  1. Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器

    本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...

  2. Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器

    承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ...

  3. Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器

    本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析 spring配置文件应用 <context: ...

  4. Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器

    aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...

  5. Spring源码情操陶冶-自定义节点的解析

    本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...

  6. Spring源码情操陶冶#task:executor解析器

    承接Spring源码情操陶冶-自定义节点的解析.线程池是jdk的一个很重要的概念,在很多的场景都会应用到,多用于处理多任务的并发处理,此处借由spring整合jdk的cocurrent包的方式来进行深 ...

  7. SpringMVC源码情操陶冶-ResourcesBeanDefinitionParser静态资源解析器

    解析mvc:resources节点,控制对静态资源的映射访问 查看官方注释 /** * {@link org.springframework.beans.factory.xml.BeanDefinit ...

  8. Spring源码情操陶冶-AOP之Advice通知类解析与使用

    阅读本文请先稍微浏览下上篇文章Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器,本文则对aop模式的通知类作简单的分析 入口 根据前文讲解,我们知道通知类的 ...

  9. Spring源码情操陶冶-任务定时器ConcurrentTaskScheduler

    承接前文Spring源码情操陶冶#task:scheduled-tasks解析器,本文在前文的基础上讲解单核心线程线程池的工作原理 应用附例 承接前文的例子,如下 <!--define bean ...

随机推荐

  1. 多线程(三) java中线程的简单使用

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

  2. HIVE和HADOOP的一些东西

    今天刚上班就要更新一个hive表(新年好呀我想说...),由于建立的外表直接替换hdfs文件就行了,但是替换完发现少了二行数据,原来之前做了关联,这就要用到hive的insert了! 先来说一下hiv ...

  3. The summary of Interview

    面试汇总 运维工程师日常工作内容 自我介绍对公司的了解 系统管理员的职责 进行系统资源管理.设备管理.系统性能管理.安全管理和系统性能监测: 管理的对象 服务器.用户.服务器的进程及系统的各种资源等. ...

  4. 在使用mybatis的selectFromExample时出现Invalid bound statement (not found)错误

    主要原因:运行项目在构建的时候只会默认的去加载resource资源文件里面的资源,其他地方的配置资源不会加载 .故没有读取到mybatis的MapperXml映射 结构如下 ============= ...

  5. 初探云服务器ECS(Linux系统)

    PS: 购买的阿里云服务器(ECS,Linux系统),使用的弹性公网IP(EIP). 一.使用Xshell链接ECS 1.将公网IP填入主机即可 2.用户名一般为root,密码是自己设置的,填入即可. ...

  6. shell基本命令学习

    Shell是一种脚步语言,那么,就必须有解释器来执行这些脚步. Unix/Linux上常见的shell脚步解释器有bash,sh,csh,ksh等,习惯把它们称为shell. 例如: #!/bin/b ...

  7. centos7下更改docker镜像和容器的默认路径

    笔者近期在服务器上搭建docker环境,可由于笔者是普通用户,在安装的时候就跳了很多坑,现在记录一下. 一.docker权限问题 据官方解释,搭建docker环境必须使用root权限,或者sudo装, ...

  8. c# 基于FTP协议的简易软件自动升级程序

    最近在重写了一个老的产品条码扫描程序,客户端数越有30个,因为经常有更新,C/S维护非常不方便,所以做一个自动更新程序特别有必要. 在网上随便找了找自动更新的方案,大多使用VS的发布/更新功能,不太喜 ...

  9. C++标准库vector类型的使用和操作总结

    vector是一种类型对象的集合,它是一种顺序容器,容器中的所有对象必须都是同一种类型.想了解顺序容器的更多内容:C++顺序容器知识总结.vector的对象是可以动态生长的,这说明它在初始化时可以不用 ...

  10. openjudge(二)

    强制类型转换的作用:按照规范输出.