zoukankan      html  css  js  c++  java
  • Spring源码情操陶冶#task:scheduled-tasks解析器

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

  • 相关阅读:
    mysql5.7 编码统一utf-8
    Spring+Swagger文档无法排序问题解决
    git的常用命令
    maven常用命令
    在centos6.5中安装zookeeper集群
    在centos6.5中安装github的客户端git
    rpm安装和卸载软件
    在centos6.5中安装scp和lrzsz
    用cxf开发restful风格的WebService
    cxf的soap风格+spirng4+maven 客户端
  • 原文地址:https://www.cnblogs.com/question-sky/p/8733461.html
Copyright © 2011-2022 走看看