承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析咱们经常使用的spring中的定时任务的节点配置。备注:此文创建在spring的4.2.3.RELEASE版本html
Spring中的定时任务基本配置样例以下java
<!--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配置的定时任务可细化到具体的类方法,有更好的扩展性spring
task:scheduler
的节点配置与前文所说起的task:executor
节点相似,均是建立线程池,那么有什么不一样呢,咱们能够稍微简单的看下其解析类org.springframework.scheduling.config.SchedulerBeanDefinitionParser
的两个方法app
@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
这个类便可。看下其是如何建立线程池的ide
/** * 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
。详情读者可自行阅读ui
配置相应的定时任务,细化到任何bean的方法可直接关联定时器。咱们一样观察其主要的两个方法getBeanClassName()
和doParse()
方法this
@Override protected String getBeanClassName(Element element) { return "org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar"; }
即实例化并启动定时任务由ContextLifecycleScheduledTaskRegistrar
类来执行,咱们稍后再谈spa
@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
类来加载执行code
task:scheduled-tasks
的子节点名为task:scheduled
task:scheduled
中的ref
和method
属性是必填项,其会包装成ScheduledMethodRunnable
对象;必须指定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
其默认实现了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
的解读。