承接前文 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, 队列为 DelayedWorkQueue, 拒绝策略为 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);
- }
代码过长, 此处作下小总结
定时任务的初始化与实例是由
org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar
类来加载执行
task:scheduled-tasks
的子节点名为 task:scheduled
task:scheduled 中的 ref 和 method 属性是必填项; 必须指定
cron/fixed-delay/fixed-rate/trigger
其中之一属性
task-scheduled 中 initial-delay 属性不必填, 但其不和 cron/trigger 属性搭配使用
task:scheduled 中的 cron 代表 cron 表达式, 为字符串形式; fixed-delay 和 fixed-rate 可与 initial-delay 搭配使用, 一般选择其中一种即可, 为数字形式; trigger 代表的是触发器, 其关联 bean, 字符串形式
根据第五点, 具体的任务包装类分别为 CronTask,IntervalTask,TriggerTask
ContextLifecycleScheduledTaskRegistrar - 定时任务初始化
其默认实现了
SmartInitializingSingleton
的
afterSingletonsInstantiated()
方法
- @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-task
与 task:scheduler 节点的配置, 具体的任务是如何被执行的, 怎么控制定时任务, 请见下文针对
org.springframework.scheduling.concurrent.ConcurrentTaskScheduler
和
org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
两个任务定时器分别做详细的解读
来源: https://www.cnblogs.com/question-sky/p/8733461.html