作者: | 来源:互联网 | 2023-09-14 12:35
spring定时任务-源码解析
spring定时任务-几种使用
前言
利用spring定时任务源码解析出,自己定义注解,达到同一实践多任务的执行
自定义注解
自己做一个自己需要的注解 ,并且可以采用源码中的方式进行管理
- 将ScheduledAnnotationBeanPostProcessor 类复制 重改名字
- 定义ScheduledAnnotation 注解
- postProcessAfterInitialization 中的代码修改
- 按照自己代码需要修改
&#64;Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler|| bean instanceof ScheduledExecutorService) {return bean;}Class<?> targetClass &#61; AopProxyUtils.ultimateTargetClass(bean);if (!this.nonAnnotatedClasses.contains(targetClass)) {Map<Method, Set<ScheduledAnnotation>> annotatedMethods &#61; MethodIntrospector.selectMethods(targetClass,(MethodIntrospector.MetadataLookup<Set<ScheduledAnnotation>>) method -> {Set<ScheduledAnnotation> scheduledMethods &#61; AnnotatedElementUtils.getMergedRepeatableAnnotations(method, ScheduledAnnotation.class,ScheduledAnnotations.class);return (!scheduledMethods.isEmpty() ? scheduledMethods : null);});if (annotatedMethods.isEmpty()) {this.nonAnnotatedClasses.add(targetClass);if (logger.isTraceEnabled()) {logger.trace("No &#64;Scheduled annotations found on bean class: " &#43; targetClass);}} else {annotatedMethods.forEach((method, scheduledMethods) -> scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));if (logger.isTraceEnabled()) {logger.trace(annotatedMethods.size() &#43; " &#64;Scheduled methods processed on bean &#39;" &#43; beanName &#43; "&#39;: "&#43; annotatedMethods);}}}return bean;}
自定义规则
- 解析完任务 并自定义好规则进行 将任务放到注册器中&#xff1b;
- 上篇文章中动态添加任务到注册器中&#xff0c;相对于隔绝了需要管理任务注册器&#xff0c;并且任务注册器会自动删除任务&#xff0c;也不需要自己清理任务&#xff0c;当然自己清理那最好&#xff0c;因为spring的注解解析器&#xff0c;就是自己也会管理这些任务&#xff0c;并在销毁时清理。
protected void processScheduled(ScheduledAnnotation scheduled, Method method, Object bean) {try {Runnable runnable &#61; createRunnable(bean, method);boolean processedSchedule &#61; false;String errorMessage &#61; "Exactly one of the &#39;cron&#39;, &#39;fixedDelay(String)&#39;, or &#39;fixedRate(String)&#39; attributes is required";Set<ScheduledTask> tasks &#61; new LinkedHashSet<>(4);long initialDelay &#61; scheduled.initialDelay();String initialDelayString &#61; scheduled.initialDelayString();if (StringUtils.hasText(initialDelayString)) {Assert.isTrue(initialDelay < 0, "Specify &#39;initialDelay&#39; or &#39;initialDelayString&#39;, not both");if (this.embeddedValueResolver !&#61; null) {initialDelayString &#61; this.embeddedValueResolver.resolveStringValue(initialDelayString);}if (StringUtils.hasLength(initialDelayString)) {try {initialDelay &#61; parseDelayAsLong(initialDelayString);} catch (RuntimeException ex) {throw new IllegalArgumentException("Invalid initialDelayString value \"" &#43; initialDelayString&#43; "\" - cannot parse into long");}}}
自定义注解一定要屏蔽下面代码
- 你在使用EnableScheduling 开启spring的注解解释器时&#xff0c;会执行两个任务
实践问题
- 多个任务同一时间执行问题
由于初始化任务 java.util.concurrent .ScheduledThreadPoolExecutor 默认为单线程的线程池 - 可以采用 在创建的时候自定义核心线程数的大小
自定义注解器中ScheduledTaskParser设置
- 在实现SchedulingConfigurer 接口里面会获得任务注册器 也可以修改到核心线程数的大小
- Spring是通过动态创建调度器 所以也可以注册bean的方式
一个任务执行时间如果超过了间隔时间
- cron表达式 源码定点去执行
- cron都是按照整点来运行的&#xff0c;比如5秒一次&#xff0c;他会在0,5,10…秒运行&#xff0c;如果任务时间点还没运行结束&#xff0c;那么就会跳过这次任务。
动态添加任务到注册器中 做定时任务
- Spring自定义ScheduledTaskParser&#xff08;注解任务解析器&#xff09;执行任务之前&#xff0c;会调用实现SchedulingConfigurer接口的configureTasks方法&#xff0c;
- 我们可以自定义实现SchedulingConfigurer
接口就能做添加任务进去&#xff0c;并且这个bean要交给spring进行管理&#xff0c;并且也可以设置其核心线程数