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示例 ...
随机推荐
- (转)C# XMPP客户端与openfire通信(Matrix Xmpp 授权破解教程)
FROM:http://www.cnblogs.com/crabo/p/CRACK_MATRIX_XMPP.html 如此著名的XMPP , 居然试过jabber-net, agsXmpp,matri ...
- PHP基础知识之类
类中的方法访问方式: class A{ function foo() { }} 1.A::foo(); 2.$a = new A(); $a->foo(); 3 ...
- $.load()的用法
jquery load 事件用法 jquery load 事件用法 如果绑定给window对象,则会在所有内容加载后触发,包括窗口,框架,对象和图像.如果绑定在元素上,则当元素的内容加载完毕后触发. ...
- MapleSim助力长臂挖掘机建模问题解决
1.问题描述 一家机械零部件设计公司需要一个挖掘机模型,验证他们的零部件是否匹配完整的挖掘机系统.由于他们是一个零部件供应商,公司没有足够的资源和研发人员使用传统的工具创建一个完整系统的详细模型.然而 ...
- 学习笔记:Java的一些基础小知识之JVM与GC
一.JVM是什么 Java虚拟机(英语:Java Virtual Machine,缩写为JVM),又名爪哇虚拟器,一种能够运行Java bytecode的虚拟机,以堆栈结构机器来进行实做.最早由太 ...
- [译]MVC网站教程(二):异常管理
介绍 “MVC网站教程”系列的目的是教你如何使用 ASP.NET MVC 创建一个基本的.可扩展的网站. 1) MVC网站教程(一):多语言网站框架 2) MVC网站教程(二):异常管理 3) ...
- Lesson 6 Percy Buttons
Text I have just moved to a house in Bridge Street. Yesterday a bagger knocked at my door. He asked ...
- 深入浅出Alljoyn——实例分析之远程调用(Method)篇
深入浅出就是很深入的学习了很久,还是只学了毛皮,呵呵! 服务端完整代码: #include <qcc/platform.h> #include <assert.h> #incl ...
- Please Call Me NIO
与其他语言相比,Java的IO功能显得异常复杂,各种流操作,通过程序员多次封装才可以达到操作文件的目的.自从jdk1.4之后,java提供了一个新的api完成IO操作,人称New IO(NIO),使用 ...
- imagepool前端图片加载管理器(JavaScript图片连接池)
前言 imagepool是一款管理图片加载的JS工具,通过imagepool可以控制图片并发加载个数. 对于图片加载,最原始的方式就是直接写个img标签,比如:<img src="图片 ...