springboot原始碼解析-管中窺豹系列之Runner(三)

豐極發表於2021-01-11

一、前言

  • Springboot原始碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
  • 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot原始碼管中窺豹系列。

 簡介

二、Runner

  • 假如我們想在springboot專案啟動完成之後,做點什麼,我們應該怎麼辦呢?
  • 注意我們可以寫在bean的初始化方法裡面(我們後面講),但是我們要用到其它已經載入了的bean的能力,又怎麼辦呢?
  • 當然加順序,加依賴也能解決,就是麻煩

這一節我們討論一下springboot專案的Runner,Runner是在spring載入完畢執行的,springboot有兩種Runner:

  • ApplicationRunner
  • CommandLineRunner
@FunctionalInterface
public interface ApplicationRunner {

	void run(ApplicationArguments args) throws Exception;

}

@FunctionalInterface
public interface CommandLineRunner {

	void run(String... args) throws Exception;

}

  • 兩種除了引數不同,其它沒區別
  • ApplicationArguments是對傳引數組的封裝,本質也沒區別
  • 只有執行順序上有區別,下面原始碼會看到

三、用法

實現介面就可以了

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class HelloRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("hello runner");
    }
}
  • 因為這時候spring已經載入完畢,你可以引入其它bean
  • 啟動專案,你會發現在日誌最下方列印了上面的話

四、原始碼解讀

我們直接找SpringApplication類的run方法,想看整體框架的去第一節。

public ConfigurableApplicationContext run(String... args) {
		
    ...

    try {
        
        ...

        callRunners(context, applicationArguments);
        
        ...

    }
    catch (Throwable ex) {
        
        ...

    }

    ...

    return context;
}

我們直接定位到callRunners方法。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    // (1) 找到ApplicationRunner的實現類,加到list裡面
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    // (2) 找到CommandLineRunner的實現類,加到list裡面
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    // (3) 排序
    AnnotationAwareOrderComparator.sort(runners);
    // (4) 鉤子回撥
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

總共分四步:

  • (1) 找到ApplicationRunner的實現類,加到list裡面

  • (2) 找到CommandLineRunner的實現類,加到list裡面

  • (3) 排序

  • (4) 鉤子回撥

我們看一下canllRunner

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args);
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
    }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args.getSourceArgs());
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
    }
}
  • 除了傳參方式,都一樣。
  • 上面說的執行順序問題,是先新增的ApplicationRunner,如果只有@Component,先執行ApplicationRunner

歡迎關注公眾號:豐極,更多技術學習分享。

相關文章