springboot啟動過程(基于版本2.0.5.release)
直接上代碼
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
進到run
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
//第一步 new SpringApplication
//第二步 執(zhí)行run
return new SpringApplication(primarySources).run(args);
}
第一步new SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判斷是否是web程序并且賦值
this.webApplicationType = deduceWebApplicationType();
// 從spring.factories文件中找出key為ApplicationContextInitializer的類并實例化后設置到SpringApplication的initializers屬性中。這個過程也就是找出所有的應用程序初始化器
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 從spring.factories文件中找出key為ApplicationListener的類并實例化后設置到SpringApplication的listeners屬性中晤愧。這個過程就是找出所有的應用程序事件監(jiān)聽器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//找到main方法
this.mainApplicationClass = deduceMainApplicationClass();
}
第二步 run
public ConfigurableApplicationContext run(String... args) {
//任務執(zhí)行觀察器
StopWatch stopWatch = new StopWatch();
//開始執(zhí)行祈餐,記錄時間
stopWatch.start();
//定義一個容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//獲取所有的監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
//執(zhí)行相應的事件
listeners.starting();
try {
//構造應用參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//創(chuàng)建應用環(huán)境信息
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//配置環(huán)境信息
configureIgnoreBeanInfo(environment);
//打印啟動banner
Banner printedBanner = printBanner(environment);
//創(chuàng)建spring容器
context = createApplicationContext();
//獲取并打印Spring啟動過程中的異常信息
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//刷新容器前準備 環(huán)境 回調(diào)方法 初始化器 廣播 注冊一些東西
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新容器
refreshContext(context);
//初始化容器后的一些操作
afterRefresh(context, applicationArguments);
//執(zhí)行結(jié)束。記錄時間
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//發(fā)布事件
listeners.started(context);
//找出Spring容器中Runner接口的實現(xiàn)類,然后執(zhí)行(ApplicationRunner仲翎,CommandLineRunner)
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
prepareContext
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//設置環(huán)境信息
context.setEnvironment(environment);
//回調(diào)
postProcessApplicationContext(context);
//初始化器開始工作
applyInitializers(context);
//監(jiān)聽器廣播
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
//給容器注冊應用參數(shù)
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
總結(jié)
SpringBoot啟動的時候,不論調(diào)用什么方法,都會構造一個SpringApplication的實例库倘,然后調(diào)用這個實例的run方法曹锨,這樣就表示啟動SpringBoot孤个。
在run方法調(diào)用之前,也就是構造SpringApplication的時候會進行初始化的工作沛简,初始化的時候會做以下幾件事:
- 把參數(shù)sources設置到SpringApplication屬性中齐鲤,這個sources可以是任何類型的參數(shù)斥废。本文的例子中這個sources就是MyApplication的class對象
- 判斷是否是web程序,并設置到webEnvironment這個boolean屬性中
- 找出所有的初始化器给郊,默認有5個牡肉,設置到initializers屬性中
- 找出所有的應用程序監(jiān)聽器,默認有9個淆九,設置到listeners屬性中
- 找出運行的主類(main class)
SpringApplication構造完成之后調(diào)用run方法统锤,啟動SpringApplication,run方法執(zhí)行的時候會做以下幾件事:
- 構造一個StopWatch炭庙,觀察SpringApplication的執(zhí)行
- 找出所有的SpringApplicationRunListener并封裝到SpringApplicationRunListeners中饲窿,用于監(jiān)聽run方法的執(zhí)行。監(jiān)聽的過程中會封裝成事件并廣播出去讓初始化過程中產(chǎn)生的應用程序監(jiān)聽器進行監(jiān)聽
- 構造Spring容器(ApplicationContext)焕蹄,并返回
3.1 創(chuàng)建Spring容器的判斷是否是web環(huán)境逾雄,是的話構造AnnotationConfigEmbeddedWebApplicationContext,否則構造AnnotationConfigApplicationContext
3.2 初始化過程中產(chǎn)生的初始化器在這個時候開始工作
3.3 Spring容器的刷新(完成bean的解析腻脏、各種processor接口的執(zhí)行嘲驾、條件注解的解析等等) - 從Spring容器中找出ApplicationRunner和CommandLineRunner接口的實現(xiàn)類并排序后依次執(zhí)行
刷新容器:http://www.reibang.com/p/ab0b7df24c01
參考鏈接:https://fangjian0423.github.io/2017/04/30/springboot-startup-analysis/