@Scheduled定时器原理及@RefreshScope相互影响

寻技术 JAVA编程 2023年12月23日 75

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相互影响的详细内容,更多关于Scheduled定时器RefreshScope的资料请关注寻技术其它相关文章!

关闭

用微信“扫一扫”