通过源码理解Spring中@Scheduled的实现原理并且实现调度任务动态装载 (3)

FixedRateTask需要配置固定间隔值(fixedRate或者fixedRateString)和可选的起始延迟执行时间(initialDelay或者initialDelayString),这里注意一点是fixedRateString和initialDelayString都支持从EmbeddedValueResolver(简单理解为配置文件的属性处理器)读取和Duration(例如P2D就是parses as 2 days,表示86400秒)支持格式的解析:

// 注解声明式使用 - 延迟一秒开始执行,每隔5秒执行一次 @Scheduled(fixedRate = 5000, initialDelay = 1000) public void processTask(){ } // 注解声明式使用 - spring-boot配置文件中process.task.fixedRate=5000 process.task.initialDelay=1000 @Scheduled(fixedRateString = "${process.task.fixedRate}", initialDelayString = "${process.task.initialDelay}") public void process(){ } // 编程式使用 public class Tasks { static DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) throws Exception { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(10); taskScheduler.initialize(); FixedRateTask fixedRateTask = new FixedRateTask(() -> { System.out.println(String.format("[%s] - FixedRateTask触发...", F.format(LocalDateTime.now()))); }, 5000, 1000); Date startTime = new Date(System.currentTimeMillis() + fixedRateTask.getInitialDelay()); taskScheduler.scheduleAtFixedRate(fixedRateTask.getRunnable(), startTime, fixedRateTask.getInterval()); Thread.sleep(Integer.MAX_VALUE); } } // 某次执行输出结果 [2020-03-16 23:58:25] - FixedRateTask触发... [2020-03-16 23:58:30] - FixedRateTask触发... ...... 简单分析核心流程的源代码

在SpringBoot注解体系下,Scheduling模块的所有逻辑基本在ScheduledAnnotationBeanPostProcessor和ScheduledTaskRegistrar中。一般来说,一个类实现的接口代表了它能提供的功能,先看ScheduledAnnotationBeanPostProcessor实现的接口:

ScheduledTaskHolder接口:返回Set<ScheduledTask>,表示持有的所有任务实例。

MergedBeanDefinitionPostProcessor接口:Bean定义合并时回调,预留空实现,暂时不做任何处理。

BeanPostProcessor接口:也就是MergedBeanDefinitionPostProcessor的父接口,Bean实例初始化前后分别回调,其中,后回调的postProcessAfterInitialization()方法就是用于解析@Scheduled和装载ScheduledTask,需要重点关注此方法的逻辑。

DestructionAwareBeanPostProcessor接口:具体的Bean实例销毁的时候回调,用于Bean实例销毁的时候移除和取消对应的任务实例。

Ordered接口:用于Bean加载时候的排序,主要是改变ScheduledAnnotationBeanPostProcessor在BeanPostProcessor执行链中的顺序。

EmbeddedValueResolverAware接口:回调StringValueResolver实例,用于解析带占位符的环境变量属性值。

BeanNameAware接口:回调BeanName。

BeanFactoryAware接口:回调BeanFactory实例,具体是DefaultListableBeanFactory,也就是熟知的IOC容器。

ApplicationContextAware接口:回调ApplicationContext实例,也就是熟知的Spring上下文,它是IOC容器的门面,同时是事件广播器、资源加载器的实现等等。

SmartInitializingSingleton接口:所有单例实例化完毕之后回调,作用是在持有的applicationContext为NULL的时候开始调度所有加载完成的任务,这个钩子接口十分有用,笔者常用它做一些资源初始化工作。

ApplicationListener接口:监听Spring应用的事件,具体是ApplicationListener<ContextRefreshedEvent>,监听上下文刷新的事件,如果事件中携带的ApplicationContext实例和ApplicationContextAware回调的ApplicationContext实例一致,那么在此监听回调方法中开始调度所有加载完成的任务,也就是在ScheduledAnnotationBeanPostProcessor这个类中,SmartInitializingSingleton接口的实现和ApplicationListener接口的实现逻辑是互斥的。

DisposableBean接口:当前Bean实例销毁时候回调,也就是ScheduledAnnotationBeanPostProcessor自身被销毁的时候回调,用于取消和清理所有的ScheduledTask。

上面分析的钩子接口在SpringBoot体系中可以按需使用,了解回调不同钩子接口的回调时机,可以在特定时机完成达到理想的效果。

@Scheduled注解的解析集中在postProcessAfterInitialization()方法:

public Object postProcessAfterInitialization(Object bean, String beanName) { // 忽略AopInfrastructureBean、TaskScheduler和ScheduledExecutorService三种类型的Bean if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler || bean instanceof ScheduledExecutorService) { // Ignore AOP infrastructure such as scoped proxies. return bean; } // 获取Bean的用户态类型,例如Bean有可能被CGLIB增强,这个时候要取其父类 Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean); // nonAnnotatedClasses存放着不存在@Scheduled注解的类型,缓存起来避免重复判断它是否携带@Scheduled注解的方法 if (!this.nonAnnotatedClasses.contains(targetClass) && AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) { // 因为JDK8之后支持重复注解,因此获取具体类型中Method -> @Scheduled的集合,也就是有可能一个方法使用多个@Scheduled注解,最终会封装为多个Task Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> { Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations( method, Scheduled.class, Schedules.class); return (!scheduledMethods.isEmpty() ? scheduledMethods : null); }); // 解析到类型中不存在@Scheduled注解的方法添加到nonAnnotatedClasses缓存 if (annotatedMethods.isEmpty()) { this.nonAnnotatedClasses.add(targetClass); if (logger.isTraceEnabled()) { logger.trace("No @Scheduled annotations found on bean class: " + targetClass); } } else { // Method -> @Scheduled的集合遍历processScheduled()方法进行登记 annotatedMethods.forEach((method, scheduledMethods) -> scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean))); if (logger.isTraceEnabled()) { logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods); } } } return bean; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxzxz.html