啟動過程結論
- 推測web應用型別。
- spi的方式獲取BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationContextInitializer物件。
- 通過呼叫棧推測出main()方法所在的類。
- 呼叫啟動方法:run(String... args)。後面的步驟在這個方法內部!
- 觸發SpringApplicationRunListener的starting()。
- 建立Environment物件。
- 觸發SpringApplicationRunListener的environmentPrepared()。
- 列印Banner。
- 建立Spring容器物件(ApplicationContext)。
- 利用ApplicationContextInitializer初始化Spring容器物件。
- 觸發SpringApplicationRunListener的contextPrepared()。
- 呼叫DefaultBootstrapContext物件的close()。
- 將啟動類作為設定類註冊到Spring容器中(load()方法)。
- 觸發SpringApplicationRunListener的contextLoaded()。
- 重新整理Spring容器。
- 觸發SpringApplicationRunListener的started()。
- 呼叫ApplicationRunner和CommandLineRunner。
- 觸發SpringApplicationRunListener的ready()。
- 上述過程拋異常了就觸發SpringApplicationRunListener的failed()。
入口位置
- 第一步構造物件:new SpringApplication(primarySources)
- 第二步呼叫SpringApplication.run(String... args)
// 我們自己寫的main方法
@SpringBootApplication
public class ZfcqApp {
public static void main(String[] args) {
SpringApplication.run(ZfcqApp.class, args);
}
}
/**
* 他會呼叫SpringApplication的run方法
* primarySource:我們傳入的類的class
* args:我們傳入的引數,一般是啟動的時候-D制定的引數
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
/**
* 繼續看內部呼叫的run方法
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 倆步,第一步構造物件:new SpringApplication(primarySources)
// 第二步呼叫SpringApplication.run(String... args)
// 這裡我們可以在main方法中分開倆步去寫,從而可以在中間設定SpringApplication物件的資訊
return new SpringApplication(primarySources).run(args);
}
構造SpringApplication原始碼分析
/**
* 呼叫倆個引數的構造方法
* primarySources:我們主類的class
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
/**
* 倆個產生的構造方法
*/
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 賦值到全域性,這裡第一次傳入的是null
this.resourceLoader = resourceLoader;
// 主類存在的判斷
Assert.notNull(primarySources, "PrimarySources must not be null");
// 賦值到全域性,這裡一般傳入的是我們的main方法的類
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推測WEB應用的型別(NONE、SERVLET、REACTIVE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 從spring.factories中獲取BootstrapRegistryInitializer物件的值
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 從spring.factories中獲取ApplicationContextInitializer物件的值
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 從spring.factories中獲取ApplicationListener物件的值。非常重要的有一個是EnvironmentPostProcessorApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推測出main方法所在的類!SpringApplication.run(ZfcqApp.class, args);可以傳任意的類!
this.mainApplicationClass = deduceMainApplicationClass();
}
推測web應用型別
- REACTIVE:web應用。
- NONE:無Servlet,不是web應用。
- SERVLET:除去上面倆種的其他應用。
static WebApplicationType deduceFromClasspath() {
// 如果專案依賴中存在org.springframework.web.reactive.DispatcherHandler,並且不存在org.springframework.web.servlet.DispatcherServlet,那麼應用型別為WebApplicationType.REACTIVE
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 如果專案依賴中不存在org.springframework.web.reactive.Dispatche#rHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那麼應用型別為WebApplicationType.NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 否則,應用型別為WebApplicationType.SERVLET
return WebApplicationType.SERVLET;
}
推測出Main類(main()方法所在的類)
private Class<?> deduceMainApplicationClass() {
try {
// 獲取呼叫棧陣列
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 找到呼叫棧中的main方法!
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
核心的啟動方法:run(String... args)
public ConfigurableApplicationContext run(String... args) {
// 開始時間
long startTime = System.nanoTime();
// 建立引導啟動器,類似一個ApplicationContext,可以往裡面新增一些物件。
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// spring容器物件,開始的時候為空
ConfigurableApplicationContext context = null;
// 與awt有關,一般用不上
configureHeadlessProperty();
// spring boot的啟動監聽器
// 從spring.factories中獲取SpringApplicationRunListener物件的值
// 預設會拿到EventPublishingRunListener,他會啟動各個地方的ApplicationEvent事件。
SpringApplicationRunListeners listeners = getRunListeners(args);
// 釋出開始啟動的事件:ApplicationstartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 把run方法的引數進行封裝
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 準備environment物件:包括作業系統、jvm、ymal、properties....設定
// 釋出一個ApplicationEnvironmentPreparedEvent事件。
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 預設spring.beaninfo.ignore為true,表示不需要jdk去快取BeanInfo資訊,spring自己快取。
// 這裡是spring在建立bean的時候會利用jdk的一些工具來解析一個類的相關資訊,jdk在解析一個類的資訊的時候會進行快取,這裡就是禁止了jdk的快取。
configureIgnoreBeanInfo(environment);
// 列印Banner
Banner printedBanner = printBanner(environment);
// 根據應用型別,建立spring容器
context = createApplicationContext();
// jdk9的一個機制,預設沒做任何操作
context.setApplicationStartup(this.applicationStartup);
// 準備容器的操作
// 利用ApplicationContextInitializer初始化spring容器
// 釋出ApplicationContextInitializedEvent(容器初始化完成)事件
// 釋出BootstrapContextClosedEvent(關閉引導容器)事件
// 註冊primarySources類(run方法存進來的設定類)
// 釋出ApplicationPreparedEvent事件
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// refresh容器:解析設定類、掃描、啟動webServer。spring相關的邏輯!
refreshContext(context);
// 空方法,類似spring的onRefresh方法,可以由子類實現。
afterRefresh(context, applicationArguments);
// 啟動的事件
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 釋出一個ApplicationStartedEvent事件,表示spring已經啟動完成
listeners.started(context, timeTakenToStartup);
// 從spring容器中獲取ApplicationRunner和CommandLineRunner,並執行他的run方法。
// 這倆個可以自己定義Bean
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 失敗之後,釋出一個失敗的事件:ApplicationFailedEvent
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 計算下事件
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 一切都成功,釋出ApplicationStartedEvent事件
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
// 失敗之後,釋出一個失敗的事件:ApplicationFailedEvent
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
建立引導啟動器
private DefaultBootstrapContext createBootstrapContext() {
// 構建一個DefaultBootstrapContext物件,這個物件是2.4.0之後才會有!
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 利用bootstrapRegistryInitializers初始化bootstrapContext。可以在spring.factories中設定bootstrapRegistryInitializers
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
準備environment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment 這句話是原始碼中本身的註釋!
// 建立ApplicationServletEnvironment。
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 新增SimpleCommandLinePropertySource放在首位
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 把所有的PropertySource封裝為ConfigurationPropertySourcesPropertySource,然後新增到environment中,放在首位!
ConfigurationPropertySources.attach(environment);
// 釋出ApplicationEnvironmentPreparedEvent(應用Environment準備完成)事件,表示環境已經準備好了。預設EnvironmentPostProcessorApplicationListener去處理這個事件!
listeners.environmentPrepared(bootstrapContext, environment);
// 把DefaultProperties放到最後
DefaultPropertiesPropertySource.moveToEnd(environment);
// 環境中spring.main.environment-prefix引數校驗
Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
// 環境中spring.main引數校驗
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
準備spring容器的操作
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 設定環境變數到spring容器
context.setEnvironment(environment);
// 設定在SpringApplication上的BeanNameGenerator、resourceLoader設定到spring容器中
postProcessApplicationContext(context);
// 使用ApplicationContextInitializer初始化spring容器
applyInitializers(context);
// 容器初始化完成,釋出ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
// 關閉引導的容器
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 註冊一些單例Bean
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// spring容器設定AllowCircularReferences和allowBeanDefinitionOverriding
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 拿到啟動設定(run方法傳遞進來的)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 將啟動設定類解析為BeanDefinition註冊到spring容器
load(context, sources.toArray(new Object[0]));
// 釋出ApplicationPreparedEvent事件,表示已經啟動好spring容器
listeners.contextLoaded(context);
}
結束語
- 你的點贊是我提高文章質量最大的動力!!!
- 獲取更多本文的前置知識文章,以及新的有價值的文章,讓我們一起成為架構師!
- 目前已經完成了並行程式設計、MySQL、spring原始碼、Mybatis的原始碼。可以在公眾號下方選單點選檢視之前的文章!
- 這個公眾號的所有技術點,會分析的很深入!
- 這個公眾號,無廣告!!!