Spring源碼情操陶冶#task:scheduled-tasks解析器

承接前文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: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

task:sheduled-tasks解析器

配置相應的定時任務,細化到任何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);
	}

代碼過長,此處做下小總結線程

  1. 定時任務的初始化與實例是由org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar類來加載執行code

  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的解讀。

相關文章
相關標籤/搜索