一、前言
- 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
目錄
springboot源碼解析-管中窺豹系列之總體結構(一)
springboot源碼解析-管中窺豹系列之項目類型(二)
springboot源碼解析-管中窺豹系列之Runner(三)
springboot源碼解析-管中窺豹系列之Initializer(四)
springboot源碼解析-管中窺豹系列之排序(五)
springboot源碼解析-管中窺豹系列之aware(六)
springboot源碼解析-管中窺豹系列之web服務器(七)
springboot源碼解析-管中窺豹系列之BeanDefinition(八)
springboot源碼解析-管中窺豹系列之自動裝配(九)
springboot源碼解析-管中窺豹系列之EnableXXX(十)
springboot源碼解析-管中窺豹系列之BeanFactoryPostProcessor(十一)
springboot源碼解析-管中窺豹系列之BeanPostProcessor(十二)
springboot源碼解析-管中窺豹系列之BeanDefine如何加載(十三)
springboot源碼解析-管中窺豹系列之bean如何生成?(十四)
歡迎關注公眾號:豐極,更多技術學習分享。