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

SchedulingConfigurer實現(xiàn)動態(tài)定時,導致ApplicationRunner無效解決

 更新時間:2023年05月16日 09:59:38   作者:暴躁碼農(nóng)  
這篇文章主要介紹了SchedulingConfigurer實現(xiàn)動態(tài)定時,導致ApplicationRunner無效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

SchedulingConfigurer實現(xiàn)動態(tài)定時,導致ApplicationRunner無效

問題描述

當通過SchedulingConfigurer接口實現(xiàn)動態(tài)定時任務(wù)后,發(fā)現(xiàn)ApplicationRunner接口實現(xiàn)的邏輯不生效了,斷點不進,說明ApplicationRunner接口實現(xiàn)的方法并沒有執(zhí)行。

問題解釋

SchedulingConfigurer接口是使用Spring實現(xiàn)動態(tài)定時任務(wù)必然的一步,而ApplicationRunner接口為的是在容器(服務(wù))啟動完成后,進行一些操作,同樣效果的還有接口CommandLineRunner,那么是因為啥導致實現(xiàn)SchedulingConfigurer接口后ApplicationRunner和CommandLineRunner的接口實現(xiàn)就不生效了呢?

原因剖析

導致ApplicationRunner和CommandLineRunner接口失效的原因還要看他倆的實現(xiàn)原理。

首先我們要明確一個概念,那就是雖然它倆名字含有Runner也有run方法,但它并不是Runnable,先看下源碼

package org.springframework.boot;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
?* Interface used to indicate that a bean should <em>run</em> when it is contained within
?* a {@link SpringApplication}. Multiple {@link ApplicationRunner} beans can be defined
?* within the same application context and can be ordered using the {@link Ordered}
?* interface or {@link Order @Order} annotation.
?*
?* @author Phillip Webb
?* @since 1.3.0
?* @see CommandLineRunner
?*/
@FunctionalInterface
public interface ApplicationRunner {
?? ?/**
?? ? * Callback used to run the bean.
?? ? * @param args incoming application arguments
?? ? * @throws Exception on error
?? ? */
?? ?void run(ApplicationArguments args) throws Exception;
}

@FunctionalInterface注解說明了ApplicationRunner是個函數(shù)式接口,不了解的童鞋看下java8

而CommandLineRunner源碼同樣也是這樣,源碼如下

package org.springframework.boot;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
?* Interface used to indicate that a bean should <em>run</em> when it is contained within
?* a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
?* within the same application context and can be ordered using the {@link Ordered}
?* interface or {@link Order @Order} annotation.
?* <p>
?* If you need access to {@link ApplicationArguments} instead of the raw String array
?* consider using {@link ApplicationRunner}.
?*
?* @author Dave Syer
?* @see ApplicationRunner
?*/
@FunctionalInterface
public interface CommandLineRunner {
?? ?/**
?? ? * Callback used to run the bean.
?? ? * @param args incoming main method arguments
?? ? * @throws Exception on error
?? ? */
?? ?void run(String... args) throws Exception;
}

所以ApplicationRunner與CommandLineRunner除了名字以外的唯一區(qū)別就是入?yún)⒉煌?那么它倆的實現(xiàn)方法在什么時候執(zhí)行的呢?簡單說就是在ApplicationContext.run()方法中,會調(diào)用callRunners方法。

該方法獲取所有實現(xiàn)了ApplicationRunner和CommandLineRunner的接口bean,然后依次執(zhí)行對應的run方法,并且是在同一個線程中執(zhí)行。因此如果有某個實現(xiàn)了ApplicationRunner接口的bean的run方法一直循環(huán)不返回的話,后續(xù)的代碼將不會被執(zhí)行。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
?? ??? ?List<Object> runners = new ArrayList<>();
?? ??? ?runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
?? ??? ?runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
?? ??? ?AnnotationAwareOrderComparator.sort(runners);
?? ??? ?for (Object runner : new LinkedHashSet<>(runners)) {
?? ??? ??? ?if (runner instanceof ApplicationRunner) {
?? ??? ??? ??? ?callRunner((ApplicationRunner) runner, args);
?? ??? ??? ?}
?? ??? ??? ?if (runner instanceof CommandLineRunner) {
?? ??? ??? ??? ?callRunner((CommandLineRunner) runner, args);
?? ??? ??? ?}
?? ??? ?}
?? ?}

所以存在猜測,SchedulingConfigurer的實現(xiàn)方式影響了這倆,看看SchedulingConfigurer的實現(xiàn)方法

@Override
? ? public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
? ? ? ? for(SysTimedTaskEntity timedTaskEntity : timedTaskDao.selectAll()){
? ? ? ? ? ? Class<?> clazz;
? ? ? ? ? ? Object task;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? clazz = Class.forName(timedTaskEntity.getTaskPath());
? ? ? ? ? ? ? ? task = context.getBean(clazz);
? ? ? ? ? ? } catch (ClassNotFoundException e) {
? ? ? ? ? ? ? ? throw new IllegalArgumentException("sys_timed_task表數(shù)據(jù)" + timedTaskEntity.getTaskPath() + "有誤", e);
? ? ? ? ? ? } catch (BeansException e) {
? ? ? ? ? ? ? ? throw new IllegalArgumentException(timedTaskEntity.getTaskPath() + "未納入到spring管理", e);
? ? ? ? ? ? }
? ? ? ? ? ? Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定時任務(wù)類必須實現(xiàn)ScheduledOfTask接口");
? ? ? ? ? ? // 可以通過改變數(shù)據(jù)庫數(shù)據(jù)進而實現(xiàn)動態(tài)改變執(zhí)行周期
? ? ? ? ? ? taskRegistrar.addTriggerTask(((Runnable) task),
? ? ? ? ? ? ? ? ? ? triggerContext -> {
? ? ? ? ? ? ? ? ? ? ? ? String cronExpression = timedTaskEntity.getTaskCron();
? ? ? ? ? ? ? ? ? ? ? ? return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? );
? ? ? ? }
? ? }

其實原因很簡單,就是因為SchedulingConfigurer使用的是單線程的方式,taskRegistrar.addTriggerTask添加完會阻塞,導致后面的ApplicationRunner和CommandLineRunner無法執(zhí)行。

解決辦法

1.只需要修改下SchedulingConfigurer實現(xiàn)

@Override
? ? public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
? ? ? ? for(SysTimedTaskEntity timedTaskEntity : timedTaskDao.selectAll()){
? ? ? ? ? ? Class<?> clazz;
? ? ? ? ? ? Object task;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? clazz = Class.forName(timedTaskEntity.getTaskPath());
? ? ? ? ? ? ? ? task = context.getBean(clazz);
? ? ? ? ? ? } catch (ClassNotFoundException e) {
? ? ? ? ? ? ? ? throw new IllegalArgumentException("sys_timed_task表數(shù)據(jù)" + timedTaskEntity.getTaskPath() + "有誤", e);
? ? ? ? ? ? } catch (BeansException e) {
? ? ? ? ? ? ? ? throw new IllegalArgumentException(timedTaskEntity.getTaskPath() + "未納入到spring管理", e);
? ? ? ? ? ? }
? ? ? ? ? ? Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定時任務(wù)類必須實現(xiàn)ScheduledOfTask接口");
? ? ? ? ? ? // 可以通過改變數(shù)據(jù)庫數(shù)據(jù)進而實現(xiàn)動態(tài)改變執(zhí)行周期
? ? ? ? ? ? taskRegistrar.addTriggerTask(((Runnable) task),
? ? ? ? ? ? ? ? ? ? triggerContext -> {
? ? ? ? ? ? ? ? ? ? ? ? String cronExpression = timedTaskEntity.getTaskCron();
? ? ? ? ? ? ? ? ? ? ? ? return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? );
? ? ? ? ? ? /** 解決辦法如下 */?
? ? ? ? ? ? // 手動創(chuàng)建線程池,防止SchedulingConfigurer導致系統(tǒng)線程阻塞
? ? ? ? ? ? taskRegistrar.setScheduler(new ScheduledThreadPoolExecutor(10, new ThreadFactory() {
? ? ? ? ? ? ? ? int counter = 0;
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public Thread newThread(Runnable r) {
? ? ? ? ? ? ? ? ? ? Thread thread = new Thread(r,"數(shù)據(jù)統(tǒng)計-Thread-"+counter);
? ? ? ? ? ? ? ? ? ? counter++;
? ? ? ? ? ? ? ? ? ? return thread;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }));
? ? ? ? }
? ? }

SpringBoot的ApplicationRunner問題

在開發(fā)中可能會有這樣的情景。需要在容器啟動的時候執(zhí)行一些內(nèi)容。比如讀取配置文件,數(shù)據(jù)庫連接之類的。

SpringBoot給我們提供了兩個接口來幫助我們實現(xiàn)這種需求。

這兩個接口分別為CommandLineRunner和ApplicationRunner。他們的執(zhí)行時機為容器啟動完成的時候。

這兩個接口中有一個run方法,我們只需要實現(xiàn)這個方法即可。

這兩個接口的不同之處在于:ApplicationRunner中run方法的參數(shù)為ApplicationArguments,而CommandLineRunner接口中run方法的參數(shù)為String數(shù)組。

目前我在項目中用的是ApplicationRunner。是這么實現(xiàn)的:

package com.jdddemo.demo.controller;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class JDDRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(args);
        System.out.println("這個是測試ApplicationRunner接口");
    }
}

執(zhí)行結(jié)果如下:

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 一篇文章徹底弄懂SpringBoot項目jdk版本及依賴不兼容問題

    一篇文章徹底弄懂SpringBoot項目jdk版本及依賴不兼容問題

    這篇文章主要給大家介紹了關(guān)于徹底弄懂SpringBoot項目jdk版本及依賴不兼容問題的相關(guān)資料,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2023-01-01
  • 將Java項目提交到云服務(wù)器的流程步驟

    將Java項目提交到云服務(wù)器的流程步驟

    所謂將項目提交到云服務(wù)器即將你的項目打成一個 jar 包然后提交到云服務(wù)器即可,因此我們需要準備服務(wù)器環(huán)境為:Linux + JDK + MariDB(MySQL)+ Git + Maven,文中通過圖文講解的非常詳細,需要的朋友可以參考下
    2025-04-04
  • Java實現(xiàn)的計算稀疏矩陣余弦相似度示例

    Java實現(xiàn)的計算稀疏矩陣余弦相似度示例

    這篇文章主要介紹了Java實現(xiàn)的計算稀疏矩陣余弦相似度功能,涉及java基于HashMap的數(shù)值計算相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • Spring中的@Cacheable緩存注解詳解

    Spring中的@Cacheable緩存注解詳解

    這篇文章主要介紹了Spring中的@Cacheable緩存注解詳解,數(shù)據(jù)庫查找的流程是先要從磁盤拿到數(shù)據(jù),再刷新到內(nèi)存,再返回數(shù)據(jù)。磁盤相比于內(nèi)存來說,速度是很慢的,為了提升性能,就出現(xiàn)了基于內(nèi)存的緩存,需要的朋友可以參考下
    2023-05-05
  • Netty分布式ByteBuf緩沖區(qū)分配器源碼解析

    Netty分布式ByteBuf緩沖區(qū)分配器源碼解析

    這篇文章主要為大家介紹了Netty分布式ByteBuf緩沖區(qū)分配器源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-03-03
  • Java數(shù)據(jù)結(jié)構(gòu)中雙向鏈表的實現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)中雙向鏈表的實現(xiàn)

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)中雙向鏈表的實現(xiàn),雙向鏈表是一種常見的數(shù)據(jù)結(jié)構(gòu),它允許在鏈表中的任意位置進行高效的插入和刪除操作,需要的朋友可以參考下
    2022-05-05
  • jackson 實現(xiàn)null轉(zhuǎn)0 以及0轉(zhuǎn)null的示例代碼

    jackson 實現(xiàn)null轉(zhuǎn)0 以及0轉(zhuǎn)null的示例代碼

    這篇文章主要介紹了jackson 實現(xiàn)null轉(zhuǎn)0 以及0轉(zhuǎn)null的示例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 如何利用Ganymed SSH-2模擬SSH操作

    如何利用Ganymed SSH-2模擬SSH操作

    這幾天看SFTP資料時,無意中看到了Ganymed SSH-2,寫了個簡單demo,通過,感覺挺好用的,下面就和大家分享下。需要的朋友可以過來參考參考
    2013-08-08
  • Spring Boot項目中結(jié)合MyBatis實現(xiàn)MySQL的自動主從切換功能

    Spring Boot項目中結(jié)合MyBatis實現(xiàn)MySQL的自動主從切換功能

    這篇文章主要介紹了Spring Boot項目中結(jié)合MyBatis實現(xiàn)MySQL的自動主從切換功能,本文分步驟給大家介紹的非常詳細,感興趣的朋友一起看看吧
    2025-04-04
  • IDEA 如何導入別人的javaweb項目進行部署

    IDEA 如何導入別人的javaweb項目進行部署

    這篇文章主要介紹了IDEA 如何導入別人的javaweb項目進行部署,本文給大家分享我的詳細部署過程及遇到問題解決方法,需要的朋友可以參考下
    2023-03-03

最新評論

红原县| 普兰店市| 姜堰市| 廊坊市| 翼城县| 虞城县| 和平区| 韶山市| 竹山县| 兴海县| 丰城市| 得荣县| 井陉县| 遂川县| 西峡县| 福清市| 施甸县| 吉隆县| 库尔勒市| 梅河口市| 独山县| 镶黄旗| 屏边| 呼和浩特市| 老河口市| 新龙县| 甘孜县| 东阿县| 靖安县| 深泽县| 武定县| 忻城县| 临江市| 芒康县| 灵川县| 湘阴县| 高邮市| 阿拉善右旗| 南汇区| 福贡县| 庆云县|