@Scheduled定時(shí)器原理及@RefreshScope相互影響
1.ScheduledAnnotationBeanPostProcessor
@EnableScheduling
- @Import(SchedulingConfiguration.class)
- 注冊(cè)了ScheduledAnnotationBeanPostProcessor

@RestController
@RefreshScope //動(dòng)態(tài)感知修改后的值
public class TestController implements ApplicationListener<RefreshScopeRefreshedEvent>{
@Value("${common.age}")
String age;
@Value("${common.name}")
String name;
@GetMapping("/common")
public String hello() {
return name+","+age;
}
//觸發(fā)@RefreshScope執(zhí)行邏輯會(huì)導(dǎo)致@Scheduled定時(shí)任務(wù)失效
@Scheduled(cron = "*/3 * * * * ?") //定時(shí)任務(wù)每隔3s執(zhí)行一次
public void execute() {
System.out.println("定時(shí)任務(wù)正常執(zhí)行。。。。。。");
}
@Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
this.execute();
}
}1.1 SmartInitializingSingleton#afterSingletonsInstantiated
ScheduledAnnotationBeanPostProcessor#afterSingletonsInstantiated
- DefaultListableBeanFactory#preInstantiateSingletons
- SmartInitializingSingleton#afterSingletonsInstantiated
- 沒干啥重要事情
1.2 RefreshScope處理ContextRefreshedEvent創(chuàng)建refresh中的bean
并調(diào)用ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization找出TestController中加了@Scheduled注解的方法。
ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization
- 發(fā)布ContextRefreshedEvent
- RefreshScope#onApplicationEvent
- Object bean = this.context.getBean(name); 獲取scope為refresh的bean:scopedTarget.testController
- 會(huì)調(diào)用至postProcessAfterInitialization
- 找到有@Scheduled注解的方法execute()
- tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
1.3 ScheduledAnnotationBeanPostProcessor本身也能處理ContextRefreshedEvent
這里真正開始調(diào)度1.2中找到的任務(wù)。
ScheduledAnnotationBeanPostProcessor#onApplicationEvent
- ScheduledAnnotationBeanPostProcessor也會(huì)處理ContextRefreshedEvent
- ScheduledAnnotationBeanPostProcessor#finishRegistration
- this.taskScheduler設(shè)置為ThreadPoolTaskScheduler(哪里配置的?)
- ScheduledTaskRegistrar#afterPropertiesSet
- ScheduledTaskRegistrar#scheduleTasks
- 開始執(zhí)行任務(wù),這cronTasks不為空,則執(zhí)行該任務(wù)
addScheduledTask(scheduleCronTask(task)); - ScheduledTaskRegistrar#scheduleCronTask
- scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
- ThreadPoolTaskScheduler#schedule
- ReschedulingRunnable#schedule以及ReschedulingRunnable#run實(shí)現(xiàn)定時(shí)調(diào)度,線程池為ScheduledThreadPoolExecutor
2.@RefreshScope的影響
當(dāng)Nacos Config配置中心發(fā)布配置時(shí),會(huì)調(diào)用RefreshScope#refreshAll。
此時(shí)會(huì)ScheduledAnnotationBeanPostProcessor#postProcessBeforeDestruction會(huì)將加了@RefreshScope的TestController里面的任務(wù)全部cancel掉。
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
RefreshScope#refreshAll
- GenericScope.BeanLifecycleWrapper#destroy
- DisposableBeanAdapter#run
- ScheduledAnnotationBeanPostProcessor#postProcessBeforeDestruction會(huì)將TestController里面的任務(wù)全部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
- 構(gòu)造bean:nacosWatch:NacosWatch時(shí)會(huì)創(chuàng)建一個(gè)
- 構(gòu)造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>
這樣做為什么能夠保證定時(shí)任務(wù)正常執(zhí)行?
- RefreshScope#refreshAll
- 發(fā)布RefreshScopeRefreshedEvent事件
- 調(diào)用到TestController代理對(duì)象#onApplicationEvent
- CglibAopProxy.DynamicAdvisedInterceptor#intercept
- SimpleBeanTargetSource#getTarget
- AbstractBeanFactory#getBean獲取scopedTarget.testController
- GenericScope#get
- 會(huì)創(chuàng)建真正的TestController實(shí)例,然后初始化后調(diào)用ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization找出TestController中加了@Scheduled注解的方法
- TestController#onApplicationEvent
- this.execute();
- 真正調(diào)用的是ReschedulingRunnable#run
以上就是@Scheduled定時(shí)器原理及@RefreshScope相互影響的詳細(xì)內(nèi)容,更多關(guān)于Scheduled定時(shí)器RefreshScope的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot依賴和代碼分開打包的實(shí)現(xiàn)步驟
本文主要介紹了SpringBoot依賴和代碼分開打包的實(shí)現(xiàn)步驟,,這種方法將依賴和代碼分開打包,一般更新只有代碼修改,Pom文件是不會(huì)經(jīng)常改動(dòng)的,感興趣的可以了解一下2023-10-10
SpringBoot整合Thymeleaf小項(xiàng)目及詳細(xì)流程
這篇文章主要介紹了SpringBoot整合Thymeleaf小項(xiàng)目,本項(xiàng)目使用SpringBoot開發(fā),jdbc5.1.48,主要涉及到Mybatis的使用,Thymeleaf的使用,用戶密碼加密,驗(yàn)證碼的設(shè)計(jì),圖片的文件上傳(本文件上傳到本地,沒有傳到數(shù)據(jù)庫(kù))登錄過濾,需要的朋友可以參考下2022-03-03
在IntelliJ IDEA中多線程并發(fā)代碼的調(diào)試方法詳解
這篇文章主要介紹了在IntelliJ IDEA中多線程并發(fā)代碼的調(diào)試方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Springboot接入MyBatisPlus的實(shí)現(xiàn)
最近web端比較熱門的框架就是SpringBoot和Mybatis-Plus,這里簡(jiǎn)單總結(jié)集成用法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
解決IDEA克隆代碼后在右下角沒有g(shù)it分支的問題
這篇文章主要介紹了解決IDEA克隆代碼后在右下角沒有g(shù)it分支的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2021-02-02
Java實(shí)現(xiàn)將Word轉(zhuǎn)換成Html的示例代碼
在業(yè)務(wù)中,常常會(huì)需要在瀏覽器中預(yù)覽Word文檔,或者需要將Word文檔轉(zhuǎn)成HTML文件保存,本文主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)Word轉(zhuǎn)換成Html的相關(guān)方法,希望對(duì)大家有所幫助2024-02-02

