承接前文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
的解讀。