最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring的定時任務(wù)@Scheduled源碼詳解

 更新時間:2023年09月25日 09:20:46   作者:HengTian_real  
這篇文章主要介紹了Spring的定時任務(wù)@Scheduled源碼詳解,@Scheduled注解是包org.springframework.scheduling.annotation中的一個注解,主要是用來開啟定時任務(wù),本文提供了部分實現(xiàn)代碼與思路,需要的朋友可以參考下

Spring的定時任務(wù)@Scheduled源碼詳解

EnableScheduling

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})	//@Import注解將SchedulingConfiguration導(dǎo)入到IOC中
@Documented
public @interface EnableScheduling {
}

SchedulingConfiguration

將ScheduledAnnotationBeanPostProcessor加入到Spring容器中 @Configuration //當(dāng)結(jié)合@Bean注解時,@Bean注解的類可以類比在spring.xml中定義

@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }
    @Bean(
        name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

ScheduledAnnotationBeanPostProcessor

1、先看下ScheduledAnnotationBeanPostProcessor有哪些屬性

//string屬性解析器,用來解析${}對應(yīng)的配置文件的屬性,aware接口注入
@Nullable
private StringValueResolver embeddedValueResolver;
@Nullable
private String beanName;
@Nullable
private BeanFactory beanFactory;	//aware接口注入
@Nullable
private ApplicationContext applicationContext;	//aware接口注入
@Nullable
//定時任務(wù)線程池,如果不為空使用這個scheduler當(dāng)作ScheduledTaskRegistrar的線程池
private Object scheduler;
//定時任務(wù)的注冊器,通過這個類將定時任務(wù)委托給定時任務(wù)線程池
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
//已檢測的沒有scheduled注解的類的集合
private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap(64));
//保存class與scheduled方法的映射
private final Map<Object, Set<ScheduledTask>> scheduledTasks = new IdentityHashMap(16);

2、根據(jù)上面的定時任務(wù)流程,在每個Bean的屬性填充完之后,調(diào)用postProcessAfterInitialization方法,將帶有@Scheduled 注解的方法,在拿到@Scheduled注解的方法后,調(diào)用processScheduled

public Object postProcessAfterInitialization(Object bean, String beanName) {
    Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
    if (!this.nonAnnotatedClasses.contains(targetClass)) {
        Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (method) -> {
            Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);
            return !scheduledMethods.isEmpty() ? scheduledMethods : null;
        });
        if (annotatedMethods.isEmpty()) {
            this.nonAnnotatedClasses.add(targetClass);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("No @Scheduled annotations found on bean class: " + bean.getClass());
            }
        } else {
            annotatedMethods.forEach((method, scheduledMethods) -> {
                scheduledMethods.forEach((scheduled) -> {
                    //這里調(diào)用
                    this.processScheduled(scheduled, method, bean);
                });
            });
            if (this.logger.isDebugEnabled()) {
                this.logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods);
            }
        }
    }
    return bean;
}

3、要了解processScheduled方法做了什么,可以先看下@Scheduled 注解的定義

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    String cron() default "";
    String zone() default "";
    long fixedDelay() default -1L;
    String fixedDelayString() default "";
    long fixedRate() default -1L;
    String fixedRateString() default "";
    long initialDelay() default -1L;
    String initialDelayString() default "";
}

可以看到,processScheduled方法主要是使用embeddedValueResolver對帶String后綴的屬性進行從配置文件讀取的操作,根據(jù)每個方法上使用的注解判斷定時任務(wù)的類型是CronTask還是FixedRateTask,將這些任務(wù)添加到ScheduledTaskRegistrar中的unresolvedTasks

protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
    try {
        Assert.isTrue(method.getParameterCount() == 0, "Only no-arg methods may be annotated with @Scheduled");
        Method invocableMethod = AopUtils.selectInvocableMethod(method, bean.getClass());
        Runnable runnable = new ScheduledMethodRunnable(bean, invocableMethod);
        boolean processedSchedule = false;
        String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
        Set<ScheduledTask> tasks = new LinkedHashSet(4);
        long initialDelay = scheduled.initialDelay();
        String initialDelayString = scheduled.initialDelayString();
        if (StringUtils.hasText(initialDelayString)) {
            Assert.isTrue(initialDelay < 0L, "Specify 'initialDelay' or 'initialDelayString', not both");
            if (this.embeddedValueResolver != null) {
                initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
            }
            if (StringUtils.hasLength(initialDelayString)) {
                try {
                    initialDelay = parseDelayAsLong(initialDelayString);
                } catch (RuntimeException var25) {
                    throw new IllegalArgumentException("Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into long");
                }
            }
        }
        String cron = scheduled.cron();
        if (StringUtils.hasText(cron)) {
            String zone = scheduled.zone();
            if (this.embeddedValueResolver != null) {
                cron = this.embeddedValueResolver.resolveStringValue(cron);
                zone = this.embeddedValueResolver.resolveStringValue(zone);
            }
            if (StringUtils.hasLength(cron)) {
                Assert.isTrue(initialDelay == -1L, "'initialDelay' not supported for cron triggers");
                processedSchedule = true;
                TimeZone timeZone;
                if (StringUtils.hasText(zone)) {
                    timeZone = StringUtils.parseTimeZoneString(zone);
                } else {
                    timeZone = TimeZone.getDefault();
                }
                tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
            }
        }
        //省略了一部分解析過程,和解析cron是一樣的
        ...
            ...
            Assert.isTrue(processedSchedule, errorMessage);
        synchronized(this.scheduledTasks) {
            Set<ScheduledTask> registeredTasks = (Set)this.scheduledTasks.get(bean);
            if (registeredTasks == null) {
                registeredTasks = new LinkedHashSet(4);
                this.scheduledTasks.put(bean, registeredTasks);
            }
            ((Set)registeredTasks).addAll(tasks);
        }
    } catch (IllegalArgumentException var26) {
        throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + var26.getMessage());
    }
}

對于3中,比較關(guān)鍵的代碼

tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));

跟蹤到this.registrar.scheduleCronTask(),這里跳轉(zhuǎn)到ScheduledTaskRegistrar類的scheduleCronTask()

public ScheduledTask scheduleCronTask(CronTask task) {
    ScheduledTask scheduledTask = (ScheduledTask)this.unresolvedTasks.remove(task);
    boolean newTask = false;
    if (scheduledTask == null) {
        //創(chuàng)建ScheduledTask
        scheduledTask = new ScheduledTask(task);
        newTask = true;
    }
    //可以看到ScheduledTaskRegistrar的初始化方法中沒有對taskScheduler賦值
    //所以此時this.taskScheduler = null
    if (this.taskScheduler != null) {
        scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
    } else {
        //進入這里
        this.addCronTask(task);
        this.unresolvedTasks.put(task, scheduledTask);
    }
    return newTask ? scheduledTask : null;
}

4、在所有單例的Bean實例化完成后,調(diào)用afterSingletonsInstantiated() ,在Spring容器初始化完成后,觸發(fā)ContextRefreshedEvent 事件,調(diào)用onApplicationEvent方法,執(zhí)行finishRegistration()

private void finishRegistration() {
    	//對應(yīng)a
       if (this.scheduler != null) {
           this.registrar.setScheduler(this.scheduler);
       }
   	//對應(yīng)b
       if (this.beanFactory instanceof ListableBeanFactory) {
           Map<String, SchedulingConfigurer> beans = ((ListableBeanFactory)this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
           List<SchedulingConfigurer> configurers = new ArrayList(beans.values());
           AnnotationAwareOrderComparator.sort(configurers);
           Iterator var3 = configurers.iterator();
           while(var3.hasNext()) {
               SchedulingConfigurer configurer = (SchedulingConfigurer)var3.next();
               configurer.configureTasks(this.registrar);
           }
       }
   	//對應(yīng)c
       if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
           Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
           try {
               this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false));
           } catch (NoUniqueBeanDefinitionException var9) {
               this.logger.debug("Could not find unique TaskScheduler bean", var9);
               try {
                   this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(this.beanFactory, TaskScheduler.class, true));
               } catch (NoSuchBeanDefinitionException var8) {
                   if (this.logger.isInfoEnabled()) {
                       this.logger.info("More than one TaskScheduler bean exists within the context, and none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' (possibly as an alias); or implement the SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " + var9.getBeanNamesFound());
                   }
               }
           } catch (NoSuchBeanDefinitionException var10) {
               this.logger.debug("Could not find default TaskScheduler bean", var10);
               try {
                   this.registrar.setScheduler(this.resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, false));
               } catch (NoUniqueBeanDefinitionException var6) {
                   this.logger.debug("Could not find unique ScheduledExecutorService bean", var6);
                   try {
                       this.registrar.setScheduler(this.resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, true));
                   } catch (NoSuchBeanDefinitionException var5) {
                       if (this.logger.isInfoEnabled()) {
                           this.logger.info("More than one ScheduledExecutorService bean exists within the context, and none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' (possibly as an alias); or implement the SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " + var6.getBeanNamesFound());
                       }
                   }
               } catch (NoSuchBeanDefinitionException var7) {
                   this.logger.debug("Could not find default ScheduledExecutorService bean", var7);
                   this.logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
               }
           }
       }
       this.registrar.afterPropertiesSet();
   }

這個方法主要實現(xiàn)的內(nèi)容是:

  • 用容器中的SchedulingConfigurer配置ScheduledTaskRegistrar,這里是根據(jù)ScheduledTaskRegistrar的引用,調(diào)用其set方法設(shè)置一些屬性
public interface SchedulingConfigurer {
void configureTasks(ScheduledTaskRegistrar var1);
}
  • 如果此時ScheduledTaskRegistrar的scheduler還是空,就從容器中取TaskScheduler(byName和byType),如果沒有取到就根據(jù)容器中的ScheduledExecutorService實例化TaskScheduler
this.registrar.afterPropertiesSet();

所以在容器中注入TaskScheduler或ScheduledExecutorService的類或者實現(xiàn)SchedulingConfigurer接口都可以配置定時任務(wù)的線程池

5、afterPropertiesSet

public void afterPropertiesSet() {
    this.scheduleTasks();
}
protected void scheduleTasks() {
    if (this.taskScheduler == null) {
        this.localExecutor = Executors.newSingleThreadScheduledExecutor();
        this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    }
    //省略了內(nèi)容
    ...
        ...
        if (this.cronTasks != null) {
            var1 = this.cronTasks.iterator();
            while(var1.hasNext()) {
                CronTask task = (CronTask)var1.next();
                this.addScheduledTask(this.scheduleCronTask(task));
            }
        }
    //省略了內(nèi)容
    ...
    ...
}

又進入了熟悉的方法scheduleCronTask,在這里將任務(wù)提交給taskScheduler

public ScheduledTask scheduleCronTask(CronTask task) {
    ScheduledTask scheduledTask = (ScheduledTask)this.unresolvedTasks.remove(task);
    boolean newTask = false;
    if (scheduledTask == null) {
        scheduledTask = new ScheduledTask(task);
        newTask = true;
    }
    if (this.taskScheduler != null) {
        //走到了這里
        scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
    } else {
        this.addCronTask(task);
        this.unresolvedTasks.put(task, scheduledTask);
    }
    return newTask ? scheduledTask : null;
}

6、如果想看taskScheduler是怎么執(zhí)行定時任務(wù)的,可以看taskScheduler的一個默認(rèn)實現(xiàn)ConcurrentTaskScheduler,大體是有一個任務(wù)隊列WorkerQueue,這個隊列是按小頂堆排序的,排序規(guī)則是任務(wù)執(zhí)行的時間,每次取出任務(wù)時,將任務(wù)提交給線程池執(zhí)行,在執(zhí)行任務(wù)的時候,計算下一次執(zhí)行的時間,提交隊列…

到此這篇關(guān)于Spring的定時任務(wù)@Scheduled源碼詳解的文章就介紹到這了,更多相關(guān)Spring定時任務(wù)@Scheduled內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入解析Java中的InterruptedException從異常處理到最佳實踐方案

    深入解析Java中的InterruptedException從異常處理到最佳實踐方案

    在Java多線程編程中,InterruptedException 是一個常見但又容易被忽視的異常,本文將通過一個實際的日志案例,分析 InterruptedException 的產(chǎn)生原因、影響,并提供合理的解決方案和最佳實踐,感興趣的朋友一起看看吧
    2025-06-06
  • SpringBoot SSMP 整合案例分享

    SpringBoot SSMP 整合案例分享

    這篇文章主要介紹了SpringBoot SSMP 整合案例分享,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08
  • java隨機驗證碼生成實現(xiàn)實例代碼

    java隨機驗證碼生成實現(xiàn)實例代碼

    這篇文章主要介紹了java隨機驗證碼生成實現(xiàn)實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • String s = new String(''a '') 到底產(chǎn)生幾個對象

    String s = new String(''a '') 到底產(chǎn)生幾個對象

    這篇文章主要介紹了String s = new String(" a ") 到底產(chǎn)生幾個對象,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • SpringMVC注解的入門實例詳解

    SpringMVC注解的入門實例詳解

    這篇文章主要為大家介紹了SpringMVC注解的入門實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Java ThreadLocal 線程本地存儲工具思路詳解

    Java ThreadLocal 線程本地存儲工具思路詳解

    文章詳細(xì)介紹了Java的ThreadLocal類,包括其核心作用、定位、特性、工作原理、用法、內(nèi)存泄漏風(fēng)險、父子線程共享問題、線程安全邊界以及與synchronized和并發(fā)容器的區(qū)別,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • SpringBoot項目如何連接MySQL8.0數(shù)據(jù)庫

    SpringBoot項目如何連接MySQL8.0數(shù)據(jù)庫

    這篇文章主要介紹了SpringBoot項目如何連接MySQL8.0數(shù)據(jù)庫,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java實現(xiàn)KFC點餐系統(tǒng)

    java實現(xiàn)KFC點餐系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)KFC點餐系統(tǒng),模擬肯德基快餐店的收銀系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • SpringBoot項目中分頁插件PageHelper無效的問題及解決方法

    SpringBoot項目中分頁插件PageHelper無效的問題及解決方法

    這篇文章主要介紹了解決SpringBoot項目中分頁插件PageHelper無效的問題,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • easyexcel讀取excel合并單元格數(shù)據(jù)的操作代碼

    easyexcel讀取excel合并單元格數(shù)據(jù)的操作代碼

    這篇文章主要介紹了easyexcel讀取excel合并單元格數(shù)據(jù)的操作代碼,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05

最新評論

平远县| 玉树县| 固始县| 稷山县| 抚松县| 如皋市| 武胜县| 浪卡子县| 尉氏县| 贵州省| 大姚县| 古丈县| 牡丹江市| 都昌县| 嘉定区| 剑河县| 盐山县| 高安市| 古交市| 诏安县| 阳原县| 钟祥市| 三明市| 黔江区| 炉霍县| 乃东县| 张掖市| 平度市| 丹东市| 大安市| 永平县| 驻马店市| 郧西县| 庆元县| 垣曲县| 曲沃县| 河间市| 桓台县| 玉屏| 郸城县| 古交市|