美文网首页
@Scheduled定时器原理(以及@RefreshScope

@Scheduled定时器原理(以及@RefreshScope

作者: 王侦 | 来源:发表于2023-03-07 19:07 被阅读0次

    1.ScheduledAnnotationBeanPostProcessor

    @EnableScheduling

    • @Import(SchedulingConfiguration.class)
    • 注册了ScheduledAnnotationBeanPostProcessor
    @RestController
    @RefreshScope  //动态感知修改后的值
    public class TestController implements ApplicationListener<RefreshScopeRefreshedEvent>{
    
        @Value("${common.age}")
         String age;
        @Value("${common.name}")
         String name;
    
        @GetMapping("/common")
        public String hello() {
            return name+","+age;
        }
    
        //触发@RefreshScope执行逻辑会导致@Scheduled定时任务失效
        @Scheduled(cron = "*/3 * * * * ?")  //定时任务每隔3s执行一次
        public void execute() {
            System.out.println("定时任务正常执行。。。。。。");
        }
    
        @Override
        public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
            this.execute();
        }
    }
    

    1.1 SmartInitializingSingleton#afterSingletonsInstantiated

    ScheduledAnnotationBeanPostProcessor#afterSingletonsInstantiated

    • DefaultListableBeanFactory#preInstantiateSingletons
    • SmartInitializingSingleton#afterSingletonsInstantiated
    • 没干啥重要事情

    1.2 RefreshScope处理ContextRefreshedEvent创建refresh中的bean

    并调用ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization找出TestController中加了@Scheduled注解的方法。

    ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization

    • 发布ContextRefreshedEvent
    • RefreshScope#onApplicationEvent
    • Object bean = this.context.getBean(name); 获取scope为refresh的bean:scopedTarget.testController
    • 会调用至postProcessAfterInitialization
    • 找到有@Scheduled注解的方法execute()
    • tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));

    1.3 ScheduledAnnotationBeanPostProcessor本身也能处理ContextRefreshedEvent

    这里真正开始调度1.2中找到的任务。

    ScheduledAnnotationBeanPostProcessor#onApplicationEvent

    • ScheduledAnnotationBeanPostProcessor也会处理ContextRefreshedEvent
    • ScheduledAnnotationBeanPostProcessor#finishRegistration
    • this.taskScheduler设置为ThreadPoolTaskScheduler(哪里配置的?)
    • ScheduledTaskRegistrar#afterPropertiesSet
    • ScheduledTaskRegistrar#scheduleTasks
    • 开始执行任务,这cronTasks不为空,则执行该任务
      addScheduledTask(scheduleCronTask(task));
    • ScheduledTaskRegistrar#scheduleCronTask
    • scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
    • ThreadPoolTaskScheduler#schedule
    • ReschedulingRunnable#schedule以及ReschedulingRunnable#run实现定时调度,线程池为ScheduledThreadPoolExecutor

    2.@RefreshScope的影响

    当Nacos Config配置中心发布配置时,会调用RefreshScope#refreshAll。
    此时会ScheduledAnnotationBeanPostProcessor#postProcessBeforeDestruction会将加了@RefreshScope的TestController里面的任务全部cancel掉。

        public void refreshAll() {
            super.destroy();
            this.context.publishEvent(new RefreshScopeRefreshedEvent());
        }
    

    RefreshScope#refreshAll

    • GenericScope.BeanLifecycleWrapper#destroy
    • DisposableBeanAdapter#run
    • ScheduledAnnotationBeanPostProcessor#postProcessBeforeDestruction会将TestController里面的任务全部cancel掉。

    ScheduledAnnotationBeanPostProcessor#postProcessBeforeDestruction

        @Override
        public void postProcessBeforeDestruction(Object bean, String beanName) {
            Set<ScheduledTask> tasks;
            synchronized (this.scheduledTasks) {
                tasks = this.scheduledTasks.remove(bean);
            }
            if (tasks != null) {
                for (ScheduledTask task : tasks) {
                    task.cancel();
                }
            }
        }
    

    取消核心流程GenericScope#destroy()

        public void destroy() {
            List<Throwable> errors = new ArrayList<Throwable>();
            Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
            for (BeanLifecycleWrapper wrapper : wrappers) {
                try {
                    Lock lock = this.locks.get(wrapper.getName()).writeLock();
                    lock.lock();
                    try {
                        wrapper.destroy();
                    }
                    finally {
                        lock.unlock();
                    }
                }
                catch (RuntimeException e) {
                    errors.add(e);
                }
            }
            if (!errors.isEmpty()) {
                throw wrapIfNecessary(errors.get(0));
            }
            this.errors.clear();
        }
    

    2.1 ThreadPoolTaskScheduler在哪里配置的?

    ThreadPoolTaskScheduler

    • 构造bean:nacosWatch:NacosWatch时会创建一个
    • 构造bean:taskScheduler:ThreadPoolTaskScheduler
    public class TaskSchedulingAutoConfiguration {
    
        @Bean
        @ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
        @ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class })
        public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
            return builder.build();
        }
    
    
        @Bean
        @ConditionalOnMissingBean
        public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties,
                ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
            TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
            builder = builder.poolSize(properties.getPool().getSize());
            Shutdown shutdown = properties.getShutdown();
            builder = builder.awaitTermination(shutdown.isAwaitTermination());
            builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
            builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
            builder = builder.customizers(taskSchedulerCustomizers);
            return builder;
        }
    

    2.2 TestController改造成ApplicationListener<RefreshScopeRefreshedEvent>

    这样做为什么能够保证定时任务正常执行?

    • RefreshScope#refreshAll
    • 发布RefreshScopeRefreshedEvent事件
    • 调用到TestController代理对象#onApplicationEvent
    • CglibAopProxy.DynamicAdvisedInterceptor#intercept
    • SimpleBeanTargetSource#getTarget
    • AbstractBeanFactory#getBean获取scopedTarget.testController
    • GenericScope#get
    • 会创建真正的TestController实例,然后初始化后调用ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization找出TestController中加了@Scheduled注解的方法
    • TestController#onApplicationEvent
    • this.execute();
    • 真正调用的是ReschedulingRunnable#run

    相关文章

      网友评论

          本文标题:@Scheduled定时器原理(以及@RefreshScope

          本文链接:https://www.haomeiwen.com/subject/jvuvldtx.html