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 delaylong 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 expressionString 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 anymoreif (initialDelay < 0) {initialDelay = 0;}// Check fixed delaylong 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 ratelong 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來完成定時任務。
?
轉載于:https://www.cnblogs.com/davidwang456/p/5680088.html
總結
以上是生活随笔為你收集整理的spring源码分析之定时任务Scheduled注解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring源码分析之spring-co
- 下一篇: GitHub上那些值得一试的JAVA开源