spring配置文件中配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.2.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.2.xsdhttp://www.springframework.org/schema/taskhttp://www.springframework.org/schema/task/spring-task-3.0.xsd"><bean class="com.wjz.quartz.RepayQuartz"/><task:annotation-driven/></beans>

定时任务处理类

packagecom.wjz.quartz;importorg.springframework.scheduling.annotation.Scheduled;public classRepayQuartz {@Scheduled(cron="0/10 * * * * ?")public voidrepay() {System.out.println("sping 定时任务");}
}

<task:annotation-driven/>标签使用TaskNamespaceHandler 来处理

public class TaskNamespaceHandler extendsNamespaceHandlerSupport {@Overridepublic voidinit() {     // 注册了一些解析器其中AnnotationDrivenBeanDefinitionParser解析@Scheduled注解this.registerBeanDefinitionParser("annotation-driven", newAnnotationDrivenBeanDefinitionParser());this.registerBeanDefinitionParser("executor", newExecutorBeanDefinitionParser());this.registerBeanDefinitionParser("scheduled-tasks", newScheduledTasksBeanDefinitionParser());this.registerBeanDefinitionParser("scheduler", newSchedulerBeanDefinitionParser());}}

注册了两个后置处理器

org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor

org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor

我们主要来看ScheduledAnnotationBeanPostProcessor

我们可以看到他实现了BeanPostProcesser,ApplicationListener接口

先执行初始化后的后置处理

@Overridepublic Object postProcessAfterInitialization(finalObject bean, String beanName) {Class<?> targetClass =AopUtils.getTargetClass(bean);if (!this.nonAnnotatedClasses.contains(targetClass)) {       // 获得bean的所有的方法,获得方法信息和@Scheduled注解信息Map<Method, Set<Scheduled>> annotatedMethods =MethodIntrospector.selectMethods(targetClass,new MethodIntrospector.MetadataLookup<Set<Scheduled>>() {@Overridepublic Set<Scheduled>inspect(Method method) {Set<Scheduled> scheduledMethods =AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);return (!scheduledMethods.isEmpty() ? scheduledMethods : null);}});if(annotatedMethods.isEmpty()) {this.nonAnnotatedClasses.add(targetClass);if(logger.isTraceEnabled()) {logger.trace("No @Scheduled annotations found on bean class: " +bean.getClass());}}else{for (Map.Entry<Method, Set<Scheduled>>entry : annotatedMethods.entrySet()) {Method method=entry.getKey();for(Scheduled scheduled : entry.getValue()) {              // 处理加工方法和注解信息,后文详解processScheduled(scheduled, method, bean);}}if(logger.isDebugEnabled()) {logger.debug(annotatedMethods.size()+ " @Scheduled methods processed on bean '" + beanName +"': " +annotatedMethods);}}}returnbean;}

    protected voidprocessScheduled(Scheduled scheduled, Method method, Object bean) {try{Method invocableMethod=AopUtils.selectInvocableMethod(method, bean.getClass());       // 创建了一个Runnable对象,主要是用来反射定时方法的,后文详解#1Runnable runnable= newScheduledMethodRunnable(bean, invocableMethod);boolean processedSchedule = false;String errorMessage="Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";// 初始化一个定时任务集合 Set<ScheduledTask> tasks = this.scheduledTasks.get(bean);if (tasks == null) {tasks= new LinkedHashSet<ScheduledTask>(4);this.scheduledTasks.put(bean, tasks);}//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=Long.parseLong(initialDelayString);}catch(NumberFormatException ex) {throw newIllegalArgumentException("Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");}}//解析表达式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();}          // 将表达式解析成任务计划表填充到任务集合中,后文详解#2tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, newCronTrigger(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;tasks.add(this.registrar.scheduleFixedDelayTask(newIntervalTask(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=Long.parseLong(fixedDelayString);}catch(NumberFormatException ex) {throw newIllegalArgumentException("Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");}tasks.add(this.registrar.scheduleFixedDelayTask(newIntervalTask(runnable, fixedDelay, initialDelay)));}//Check fixed ratelong fixedRate =scheduled.fixedRate();if (fixedRate >= 0) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule= true;tasks.add(this.registrar.scheduleFixedRateTask(newIntervalTask(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=Long.parseLong(fixedRateString);}catch(NumberFormatException ex) {throw newIllegalArgumentException("Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");}tasks.add(this.registrar.scheduleFixedRateTask(newIntervalTask(runnable, fixedRate, initialDelay)));}//Check whether we had any attribute set
Assert.isTrue(processedSchedule, errorMessage);}catch(IllegalArgumentException ex) {throw newIllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " +ex.getMessage());}}

书接前文#1

Runnable的run方法中是定时方法反射

@Overridepublic voidrun() {try{ReflectionUtils.makeAccessible(this.method);this.method.invoke(this.target);}catch(InvocationTargetException ex) {ReflectionUtils.rethrowRuntimeException(ex.getTargetException());}catch(IllegalAccessException ex) {throw newUndeclaredThrowableException(ex);}}

书接前文#2

创建一个CronTask任务对象,其中有Runnable对象和CronTrigger触发对象(表达式,时区),后文详解触发器的构造#2-1

publicScheduledTask scheduleCronTask(CronTask task) {ScheduledTask scheduledTask= this.unresolvedTasks.remove(task);boolean newTask = false;if (scheduledTask == null) {scheduledTask= newScheduledTask();newTask= true;}if (this.taskScheduler != null) {        // 执行定时任务scheduledTask.future= this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());}else{        // 添加定时任务addCronTask(task);this.unresolvedTasks.put(task, scheduledTask);}return (newTask ? scheduledTask : null);}

再看监听处理

@Overridepublic voidonApplicationEvent(ContextRefreshedEvent event) {if (event.getApplicationContext() == this.applicationContext) {//Running in an ApplicationContext -> register tasks this late...//giving other ContextRefreshedEvent listeners a chance to perform//their work at the same time (e.g. Spring Batch's job registration).
finishRegistration();}}

this.registrar.afterPropertiesSet();

protected voidscheduleTasks() {if (this.taskScheduler == null) {        // 初始化一个线程池调度器this.localExecutor =Executors.newSingleThreadScheduledExecutor();        // 指定了任务调度器this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);}if (this.triggerTasks != null) {for (TriggerTask task : this.triggerTasks) {addScheduledTask(scheduleTriggerTask(task));}}if (this.cronTasks != null) {for (CronTask task : this.cronTasks) {addScheduledTask(scheduleCronTask(task));}}if (this.fixedRateTasks != null) {for (IntervalTask task : this.fixedRateTasks) {addScheduledTask(scheduleFixedRateTask(task));}}if (this.fixedDelayTasks != null) {for (IntervalTask task : this.fixedDelayTasks) {addScheduledTask(scheduleFixedDelayTask(task));}}}

publicScheduledTask scheduleCronTask(CronTask task) {ScheduledTask scheduledTask= this.unresolvedTasks.remove(task);boolean newTask = false;if (scheduledTask == null) {scheduledTask= newScheduledTask();newTask= true;}     // 这次指定了ConcurrentTaskScheduler任务调度器,开始调度定时任务if (this.taskScheduler != null) {scheduledTask.future= this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());}else{addCronTask(task);this.unresolvedTasks.put(task, scheduledTask);}return (newTask ? scheduledTask : null);}

@Overridepublic ScheduledFuture<?>schedule(Runnable task, Trigger trigger) {try{if (this.enterpriseConcurrentScheduler) {return new EnterpriseConcurrentTriggerScheduler().schedule(decorateTask(task, true), trigger);}else{ErrorHandler errorHandler= (this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));          // 这里就是核心的地方了, Runnable的run方法内,定时调用自己的run方法实现功能,巧妙之极return new ReschedulingRunnable(task, trigger, this.scheduledExecutor, errorHandler).schedule();}}catch(RejectedExecutionException ex) {throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " +task, ex);}}

public ScheduledFuture<?>schedule() {synchronized (this.triggerContextMonitor) {        // 拿到下一次执行任务的时间,后文详解#2-2this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);if (this.scheduledExecutionTime == null) {return null;}        // 固定延迟即延迟多长时间执行long initialDelay = this.scheduledExecutionTime.getTime() -System.currentTimeMillis();this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);return this;}}@Overridepublic voidrun() {Date actualExecutionTime= newDate();     // 这就是上文所提到的定时方法反射执行定时业务方法super.run();Date completionTime= newDate();synchronized (this.triggerContextMonitor) {        // 记录上一次任务执行的时间this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);if (!this.currentFuture.isCancelled()) {          // 继续无限调度schedule();}}}

书接前文#2-1

CronTrigger触发对象的构造,初始化了计划时间表生成器

publicCronTrigger(String expression, TimeZone timeZone) {this.sequenceGenerator = newCronSequenceGenerator(expression, timeZone);}

publicCronSequenceGenerator(String expression, TimeZone timeZone) {this.expression =expression;this.timeZone =timeZone;     // 解析表达式生成计划时间表parse(expression);}

private void parse(String expression) throwsIllegalArgumentException {String[] fields= StringUtils.tokenizeToStringArray(expression, " ");if (!areValidCronFields(fields)) {throw newIllegalArgumentException(String.format("Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));}     // 主要使用了BitSet来制造计划时间表setNumberHits(this.seconds, fields[0], 0, 60);setNumberHits(this.minutes, fields[1], 0, 60);setNumberHits(this.hours, fields[2], 0, 24);setDaysOfMonth(this.daysOfMonth, fields[3]);setMonths(this.months, fields[4]);setDays(this.daysOfWeek, replaceOrdinals(fields[5], "SUN,MON,TUE,WED,THU,FRI,SAT"), 8);if (this.daysOfWeek.get(7)) {//Sunday can be represented as 0 or 7this.daysOfWeek.set(0);this.daysOfWeek.clear(7);}}

private void setNumberHits(BitSet bits, String value, int min, intmax) {String[] fields= StringUtils.delimitedListToStringArray(value, ",");for(String field : fields) {       // 检查是否包含'/',不包含的话列出最小值到最大值的范围if (!field.contains("/")) {//Not an incrementer so it must be a range (possibly empty)int[] range =getRange(field, min, max);bits.set(range[0], range[1] + 1);}else{// 包含'/'的话列出每xx秒(或分、时)的时间表如{0,10,20,30,40,50}String[] split= StringUtils.delimitedListToStringArray(field, "/");if (split.length > 2) {throw new IllegalArgumentException("Incrementer has more than two fields: '" +field+ "' in expression \"" + this.expression + "\"");}int[] range = getRange(split[0], min, max);if (!split[0].contains("-")) {range[1] = max - 1;}int delta = Integer.valueOf(split[1]);if (delta <= 0) {throw new IllegalArgumentException("Incrementer delta must be 1 or higher: '" +field+ "' in expression \"" + this.expression + "\"");}          // 以一定数值叠加为BitSet赋值for (int i = range[0]; i <= range[1]; i +=delta) {bits.set(i);}}}}

private int[] getRange(String field, int min, intmax) {int[] result = new int[2];     // 包含*就是最大到最小if (field.contains("*")) {result[0] =min;result[1] = max - 1;returnresult;}     // 不包含'/'和'-'返回值if (!field.contains("-")) {result[0] = result[1] =Integer.valueOf(field);}else{String[] split= StringUtils.delimitedListToStringArray(field, "-");if (split.length > 2) {throw new IllegalArgumentException("Range has more than two fields: '" +field+ "' in expression \"" + this.expression + "\"");}result[0] = Integer.valueOf(split[0]);result[1] = Integer.valueOf(split[1]);}if (result[0] >= max || result[1] >=max) {throw new IllegalArgumentException("Range exceeds maximum (" + max + "): '" +field+ "' in expression \"" + this.expression + "\"");}if (result[0] < min || result[1] <min) {throw new IllegalArgumentException("Range less than minimum (" + min + "): '" +field+ "' in expression \"" + this.expression + "\"");}if (result[0] > result[1]) {throw new IllegalArgumentException("Invalid inverted range: '" + field +"' in expression \"" + this.expression + "\"");}returnresult;}

书接前文#2-2

拿到下一个执行调度的时间

@OverridepublicDate nextExecutionTime(TriggerContext triggerContext) {     // 拿到上一次执行调度的时间Date date=triggerContext.lastCompletionTime();if (date != null) {Date scheduled=triggerContext.lastScheduledExecutionTime();if (scheduled != null &&date.before(scheduled)) {date =scheduled;}}else{        // 没拿到的话就拿当前时间date= newDate();}     // 拿到一下一次执行调度的时间return this.sequenceGenerator.next(date);}

publicDate next(Date date) {Calendar calendar= newGregorianCalendar();calendar.setTimeZone(this.timeZone);calendar.setTime(date);calendar.set(Calendar.MILLISECOND, 0);long originalTimestamp =calendar.getTimeInMillis();     // 为日历设置下一次执行调度的时间doNext(calendar, calendar.get(Calendar.YEAR));if (calendar.getTimeInMillis() ==originalTimestamp) {calendar.add(Calendar.SECOND, 1);doNext(calendar, calendar.get(Calendar.YEAR));}// 返回设置好下一次调度的日历时间returncalendar.getTime();}

private void doNext(Calendar calendar, intdot) {List<Integer> resets = new ArrayList<Integer>();// 拿到当前时间的秒数int second =calendar.get(Calendar.SECOND);List<Integer> emptyList =Collections.emptyList();     // 拿到下一次执行调度的秒数int updateSecond = findNext(this.seconds, second, calendar, Calendar.SECOND, Calendar.MINUTE, emptyList);if (second ==updateSecond) {resets.add(Calendar.SECOND);}int minute =calendar.get(Calendar.MINUTE);int updateMinute = findNext(this.minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets);if (minute ==updateMinute) {resets.add(Calendar.MINUTE);}else{doNext(calendar, dot);}int hour =calendar.get(Calendar.HOUR_OF_DAY);int updateHour = findNext(this.hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets);if (hour ==updateHour) {resets.add(Calendar.HOUR_OF_DAY);}else{doNext(calendar, dot);}int dayOfWeek =calendar.get(Calendar.DAY_OF_WEEK);int dayOfMonth =calendar.get(Calendar.DAY_OF_MONTH);int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, resets);if (dayOfMonth ==updateDayOfMonth) {resets.add(Calendar.DAY_OF_MONTH);}else{doNext(calendar, dot);}int month =calendar.get(Calendar.MONTH);int updateMonth = findNext(this.months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets);if (month !=updateMonth) {if (calendar.get(Calendar.YEAR) - dot > 4) {throw new IllegalArgumentException("Invalid cron expression \"" + this.expression +"\" led to runaway search for next trigger");}doNext(calendar, dot);}}

private int findNext(BitSet bits, int value, Calendar calendar, int field, int nextField, List<Integer>lowerOrders) {     // 拿到下一个执行调度的秒数如当前时间的秒数是23任务是每10秒调度则拿到30,如果是58则返回-1int nextValue =bits.nextSetBit(value);        if (nextValue == -1) {        // 加一分(时,日)calendar.add(nextField,1);        // 重置秒(分、时)reset(calendar, Arrays.asList(field));nextValue= bits.nextSetBit(0);}if (nextValue !=value) {        // 为日历设置下一个执行调度的秒(分、时)数calendar.set(field, nextValue);reset(calendar, lowerOrders);}returnnextValue;}

转载于:https://www.cnblogs.com/BINGJJFLY/p/7485599.html

spring 定时器任务深入理解相关推荐

  1. linux定时器多次,Spring 定时器执行两次

    Spring错误笔记 Spring定时器执行两次因为导入了两次 关于配置文件如下 对应的类有个定时执行检查的动作,但是动作中的日志每次输出两遍,一开始以为是log4j的输出导致的两条,找了半天没办法还 ...

  2. Spring定时器的使用-多实例下定时重建索引

    2019独角兽企业重金招聘Python工程师标准>>> 前几天接到项目需要定时重建索引的任务,一开始试了试Java自带的Timer,不知道是不是自己对Timer的了解还不够的原因,感 ...

  3. Spring 定时器

    本文向您介绍Spring定时器的两种实现方式,包括Java Timer定时和Quartz定时器,两种Spring定时器的实现方式各有优点,可结合具体项目考虑是否采用. 有两种流行Spring定时器配置 ...

  4. spring定时器分析

    spring定时器如何使用? 步骤1. 定义job bean 1.TaskScheduler构造 2.初始化相关服务 <bean id="xxxxScheduler" cla ...

  5. spring定时器(@Scheduled)

    spring定时器需要额外添加下面配置 一.配置文件 xmlns 额外添加下面的内容: xmlns:task="http://www.springframework.org/schema/t ...

  6. Java怎么使用spring定时器_浅析spring定时器的使用

    原生的Java定时器 使用Java.util包下的定时器也很简单,具体代码如下: //设置定时器开始时间 Date time = sdf.parse("2020-10-01 16:40:00 ...

  7. spring 定时器注释_带注释的控制器– Spring Web / Webflux和测试

    spring 定时器注释 Spring Webflux和Spring Web是两个完全不同的Web堆栈. 但是, Spring Webflux继续支持基于注释的编程模型 使用这两个堆栈定义的端点可能看 ...

  8. [spring-framework]Spring定时器的配置和使用

    开发中我们常常会做一些定时任务,这些任务有开始时间,并会按一定的周期或规则执行.如此我们在Java程序开发中使用定时器来处理定时任务. <!-- MessageRequestTask类中包含了m ...

  9. spring定时器,定时器一次执行两次的问题

    Spring 定时器 方法一:注解形式 配置文件头加上如下: xmlns:task="http://www.springframework.org/schema/task"http ...

最新文章

  1. 用心真诚对待,懂你的人
  2. 《Java8实战》笔记(08):重构、测试和调试
  3. JavaScript-字符串
  4. C#中委托和事件的区别
  5. 使用ASP.NET Core开始使用gRPC客户端和服务器
  6. css两列显示,div+css如何控制信息分两列显示?
  7. 动态规划解决约瑟夫环问题
  8. 猿创征文|时间序列分析算法之平稳时间序列预测算法和自回归模型(AR)详解+Python代码实现
  9. Java中的JDK动态代理
  10. 复试口语常见话题整理以及华师18 19年topic
  11. FFmpeg学习(四)-- libavformat 代码组成
  12. UML之Astah的基本使用教程-4(活动图、序列图、Stereotype Icon)
  13. linux运行海康的sdk,海康摄像头SDK在Linux、windows下的兼容问题(二)已解决
  14. 台式计算机开机风扇不转,电脑开机显卡风扇不转是怎么回事|电脑开机风扇不转的解决方法...
  15. 增量式编码器 绝对值编码器
  16. pixhawk之NSH调试
  17. CAD 二次开发 图层操作(3)取得指定图层下的所有对象id
  18. 文件实时同步备份软件那个比较好用?
  19. 人工智能研究的内容:_更深入:人工智能研究的思想史
  20. 【面试】TCP、UDP、Socket、HTTP网络编程面试题

热门文章

  1. Android 框架炼成 教你怎样写组件间通信框架EventBus
  2. android 设颜色透明值
  3. USB HID report descriptor
  4. 快要普通话考试了,急需最后一题的根据话题自由讲话的演讲稿!(不要那些已经被用过...
  5. Mysql数据库 sql 语句调优
  6. WPF入门知识(学习)
  7. AjaxControlToolkit中CalendarExtender日历控件的用法
  8. mysql函数GROUP_CONCAT,Cast,convert
  9. 把button伪装成超链接
  10. dreamweaver在onLoad运行RecordsetFind.htm时出错