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

 更新时间:2023年07月13日 14:35:58   作者:王侦  
这篇文章主要为大家介绍了@Scheduled定时器原理及@RefreshScope相互影响详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

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的资料请关注脚本之家其它相关文章!

相关文章

  • mybatis一对一查询功能

    mybatis一对一查询功能

    所谓的一对一查询,就是说我们在查询一个表的数据的时候,需要关联查询其他表的数据。这篇文章主要介绍了mybatis一对一查询功能,需要的朋友可以参考下
    2017-02-02
  • Java中的守护线程问题

    Java中的守护线程问题

    这篇文章主要介绍了Java中的守护线程问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • java Nio使用NioSocket客户端与服务端交互实现方式

    java Nio使用NioSocket客户端与服务端交互实现方式

    这篇文章主要介绍了java Nio使用 NioSocket 客户端与服务端交互实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • java中如何执行xshell命令

    java中如何执行xshell命令

    这篇文章主要介绍了java中如何执行xshell命令,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • java9中gc log参数迁移

    java9中gc log参数迁移

    本篇文章给大家详细讲述了java9中gc log参数迁移的相关知识点,对此有需要的朋友可以参考学习下。
    2018-03-03
  • Java的MybatisPlus详解

    Java的MybatisPlus详解

    这篇文章主要介绍了Java的MybatisPlus详解,MyBatis-Plus是一个 MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生,需要的朋友可以参考下
    2023-07-07
  • JavaCV获取视频文件时长的方法

    JavaCV获取视频文件时长的方法

    这篇文章主要为大家详细介绍了JavaCV获取视频文件时长的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Spring使用xml方式整合第三方框架流程详解

    Spring使用xml方式整合第三方框架流程详解

    这篇文章主要介绍了Spring使用xml方式整合第三方框架流程,Spring会在应用上下文中为某个bean寻找其依赖的bean,Spring中bean有三种装配机制,分别是:在xml中显式配置、在java中显式配置、隐式的bean发现机制和自动装配
    2023-02-02
  • 解决使用json-lib包实现xml转json时空值被转为空中括号的问题

    解决使用json-lib包实现xml转json时空值被转为空中括号的问题

    网上能查到的xml转json的jar包大部分是net.sf.json-lib,但是JSON json =xmlSerializer.read(xml); 方法会出现将空值转化为[]的问题,下面为大家提供两种解决方法
    2018-03-03
  • mybatis-plus实现打印完整sql语句

    mybatis-plus实现打印完整sql语句

    这篇文章主要介绍了mybatis-plus实现打印完整sql语句方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07

最新评论