spring源码分析之定时任务Scheduled注解
1. @Scheduled 可以将一个方法标识为可定时执行的。但必须指明cron(),fixedDelay(),或者fixedRate()属性。
注解的方法必须是无输入参数并返回空类型void的。
@Scheduled注解由注册的ScheduledAnnotationBeanPostProcessor来处理,该processor可以通过手动来注册,更方面的方式是通过<task:annotation-driven/>或者@EnableScheduling来注册。@EnableScheduling可以注册的原理是什么呢?先看定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling { }
可以看到@EnableScheduling的实现由SchedulingConfiguration来完成。
@Configuration
public class SchedulingConfiguration { @Bean(name = AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
} }
从上述代码可以看出,SchedulingConfiguration注册了一个ScheduledAnnotationBeanPostProcessor。
来看一下ScheduledAnnotationBeanPostProcessor来如何处理定时任务的?
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
try {
Assert.isTrue(void.class.equals(method.getReturnType()),
"Only void-returning methods may be annotated with @Scheduled");
Assert.isTrue(method.getParameterTypes().length == 0,
"Only no-arg methods may be annotated with @Scheduled"); if (AopUtils.isJdkDynamicProxy(bean)) {
try {
// Found a @Scheduled method on the target class for this JDK proxy ->
// is it also present on the proxy itself?
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
}
catch (SecurityException ex) {
ReflectionUtils.handleReflectionException(ex);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format(
"@Scheduled method '%s' found on bean target class '%s' but not " +
"found in any interface(s) for a dynamic proxy. Either pull the " +
"method up to a declared interface or switch to subclass (CGLIB) " +
"proxies by setting proxy-target-class/proxyTargetClass to 'true'",
method.getName(), method.getDeclaringClass().getSimpleName()));
}
}
else if (AopUtils.isCglibProxy(bean)) {
// Common problem: private methods end up in the proxy instance, not getting delegated.
if (Modifier.isPrivate(method.getModifiers())) {
LogFactory.getLog(ScheduledAnnotationBeanPostProcessor.class).warn(String.format(
"@Scheduled method '%s' found on CGLIB proxy for target class '%s' but cannot " +
"be delegated to target bean. Switch its visibility to package or protected.",
method.getName(), method.getDeclaringClass().getSimpleName()));
}
} Runnable runnable = new ScheduledMethodRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage =
"Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required"; // Determine initial delay
long initialDelay = scheduled.initialDelay();
String initialDelayString = scheduled.initialDelayString();
if (StringUtils.hasText(initialDelayString)) {
Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
if (this.embeddedValueResolver != null) {
initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
}
try {
initialDelay = Integer.parseInt(initialDelayString);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");
}
} // Check cron expression
String cron = scheduled.cron();
if (StringUtils.hasText(cron)) {
Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
processedSchedule = true;
String zone = scheduled.zone();
if (this.embeddedValueResolver != null) {
cron = this.embeddedValueResolver.resolveStringValue(cron);
zone = this.embeddedValueResolver.resolveStringValue(zone);
}
TimeZone timeZone;
if (StringUtils.hasText(zone)) {
timeZone = StringUtils.parseTimeZoneString(zone);
}
else {
timeZone = TimeZone.getDefault();
}
this.registrar.addCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone)));
} // At this point we don't need to differentiate between initial delay set or not anymore
if (initialDelay < 0) {
initialDelay = 0;
} // Check fixed delay
long fixedDelay = scheduled.fixedDelay();
if (fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
this.registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
}
String fixedDelayString = scheduled.fixedDelayString();
if (StringUtils.hasText(fixedDelayString)) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
if (this.embeddedValueResolver != null) {
fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
}
try {
fixedDelay = Integer.parseInt(fixedDelayString);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");
}
this.registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
} // Check fixed rate
long fixedRate = scheduled.fixedRate();
if (fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
this.registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
}
String fixedRateString = scheduled.fixedRateString();
if (StringUtils.hasText(fixedRateString)) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
if (this.embeddedValueResolver != null) {
fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
}
try {
fixedRate = Integer.parseInt(fixedRateString);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");
}
this.registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
} // Check whether we had any attribute set
Assert.isTrue(processedSchedule, errorMessage);
}
catch (IllegalArgumentException ex) {
throw new IllegalStateException(
"Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
}
}
从上面的代码可以看出:@Scheduled有三个属性,分别是:
cron expression
fixedDelay
fixedRate
根据这些属性的不同,都加入到ScheduledTaskRegistrar来管理定时任务:
/**
* Schedule all registered tasks against the underlying {@linkplain
* #setTaskScheduler(TaskScheduler) task scheduler}.
*/
protected void scheduleTasks() {
long now = System.currentTimeMillis(); if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
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()));
}
}
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()));
}
}
}
}
从上面看出:
3种不同属性的task均由quartz的taskScheduler的不同方法来完成,
scheduleWithFixedDelay,
scheduleAtFixedRate,
schedule
即最终的实现由TaskScheduler来完成定时任务。
spring源码分析之定时任务Scheduled注解的更多相关文章
- Spring 3.1新特性之二:@Enable*注解的源码,spring源码分析之定时任务Scheduled注解
分析SpringBoot的自动化配置原理的时候,可以观察下这些@Enable*注解的源码,可以发现所有的注解都有一个@Import注解.@Import注解是用来导入配置类的,这也就是说这些自动开启的实 ...
- spring源码分析之定时任务概述
Spring框架提供了TaskExcutor的异步执行和TashScheduler的任务定时执行接口,同样spring也提供了线程池或者CommonJ的代理. TaskExecutor的类型 Simp ...
- spring源码分析之spring-core总结篇
1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...
- spring源码分析(二)Aop
创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...
- 【spring源码分析】IOC容器初始化(一)
前言:spring主要就是对bean进行管理,因此IOC容器的初始化过程非常重要,搞清楚其原理不管在实际生产或面试过程中都十分的有用.在[spring源码分析]准备工作中已经搭建好spring的环境, ...
- 【spring源码分析】IOC容器初始化(十)
前言:前文[spring源码分析]IOC容器初始化(九)中分析了AbstractAutowireCapableBeanFactory#createBeanInstance方法中通过工厂方法创建bean ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析
更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...
- spring源码分析系列 (1) spring拓展接口BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
更多文章点击--spring源码分析系列 主要分析内容: 一.BeanFactoryPostProcessor.BeanDefinitionRegistryPostProcessor简述与demo示例 ...
随机推荐
- / fluxChatDemo / 系列 ——fluxDemoChat 组件编写
还是用各部分来表示过程吧,没文采,就先这样记着吧 嘻嘻 梳理问题: 编写es6风格的组件时,需要引入import React from ‘react’ 然后页面就华丽丽的展示出了我写的1.2两个字 在 ...
- zend studio8编辑器乱码问题解决办法
截图一张:
- siteserver cms分页
<stl:pageContents pageNum="10" cellpadding="2" cellspacing="2"> ...
- c#控制打印机杂项
因项目中需要用到控制打印机的相关信息,此贴将网络寻找的资料做了些整理 1. C# 如何设置系统的默认打印机 using System.Runtime.InteropServices; [DllIm ...
- C# RabbitMq .net 使用
本文转载来自 [http://www.cnblogs.com/yangecnu/p/Introduce-RabbitMQ.html]写的很详细. 文件安装包官方DEMO下载地址是:http://pan ...
- Android课程---远程服务器存储
在使用Volley进行获取数据时,需要事先准备环境:在libs里面导入一个Volley.jar包,在网上都有,可以下载下来导入,或者自己电脑上有DT的,自己合成一个包也行. WebActivity.j ...
- wpf 逻辑树与可视化树
XAML天生就是用来呈现用户界面的,这是由于它具有层次化的特性.在WPF中,用户界面由一个对象树构建而成,这棵树叫作逻辑树.逻辑树的概念很直观,但是为什么要关注它呢?因为几乎WPF的每一方面(属性.事 ...
- 公司内部培训SQL Server传统索引结构PPT分享
公司内部培训SQL Server传统索引结构PPT分享 下载地址 http://files.cnblogs.com/files/lyhabc/SQLServer%E4%BC%A0%E7%BB%9F%E ...
- 借助 Lucene.Net 构建站内搜索引擎(上)
前言:最近翻开了之前老杨(杨中科)的Lucene.Net站内搜索项目的教学视频,于是作为老杨脑残粉的我又跟着复习了一遍,学习途中做了一些笔记也就成了接下来您看到的这篇博文,仅仅是我的个人笔记,大神请呵 ...
- ajax的post方式和get方式比较,以及需要注意的地方
说明:测试所用的js框架为kissy,后端语言为php 写在前面 目前我们可以将ajax请求的情形按照不同的类型进行分类,比如页面编码:utf-8 or gbk; ajax 传参方式 post or ...