一、前言
Springboot源碼解析是一件大工程,逐行逐句的去研究代碼,會很枯燥,也不容易堅持下去。
我們不追求大而全,而是試着每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot源碼管中窺豹系列。
二、Runner
假如我們想在springboot項目啓動完成之後,做點什麼,我們應該怎麼辦呢?
注意我們可以寫在bean的初始化方法裏面(我們後面講),但是我們要用到其它已經加載了的bean的能力,又怎麼辦呢?
當然加順序,加依賴也能解決,就是麻煩
這一節我們討論一下springboot項目的Runner,Runner是在spring加載完畢執行的,springboot有兩種Runner:
ApplicationRunner
CommandLineRunner
@FunctionalInterfacepublic interface ApplicationRunner {void run(ApplicationArguments args) throws Exception;}@FunctionalInterfacepublic interface CommandLineRunner {void run(String... args) throws Exception;}複製代碼
兩種除了參數不同,其它沒區別
ApplicationArguments是對傳參數組的封裝,本質也沒區別
只有執行順序上有區別,下面源碼會看到
三、用法
實現接口就可以了
import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.stereotype.Component;@Componentpublic 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 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