SpringBoot啟動過程分析,首先打開SpringBoot的啟用入口Main類:
1
2
3
4
5
6
|
@SpringBootApplication public class ApplicationMain{ public static void main(String[] args) { SpringApplication.run(ApplicationMain. class , args); } } |
可以看到main方法里面只有一行核心啟用類:SpringApplication.run(ApplicationMain.class, args);這個是關鍵,在改行打上斷點,debug模式啟動該main類。點擊下一步進入SpringApplication的源碼對應的run方法:
1
2
3
|
public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return ( new SpringApplication(sources)).run(args); } |
初始化SpringApplication
SpringApplication實例化之前會調用構造方法進行初始化:
1
2
3
4
5
6
7
8
9
|
public SpringApplication(Object... sources) { this .bannerMode = Mode.CONSOLE; this .logStartupInfo = true ; this .addCommandLineProperties = true ; this .headless = true ; this .registerShutdownHook = true ; this .additionalProfiles = new HashSet(); this .initialize(sources); } |
而SpringApplication構造方法的核心是:this.initialize(sources);初始化方法,SpringApplication通過調用該方法來初始化。
1
2
3
4
5
6
7
8
9
10
|
private void initialize(Object[] sources) { if (sources != null && sources.length > 0 ) { this .sources.addAll(Arrays.asList(sources)); } this .webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer. class )); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener. class )); this .mainApplicationClass = deduceMainApplicationClass(); } |
1.deduceWebEnvironment方法是用來判斷當前應用的環境,該方法通過獲取這兩個類來判斷當前環境是否是web環境,如果能獲得這兩個類說明是web環境,否則不是。
1
2
|
javax.servlet.Servlet org.springframework.web.context.ConfigurableWebApplicationContext |
2.getSpringFactoriesInstances方法主要用來從spring.factories文件中找出key為ApplicationContextInitializer的類并實例化,然后調用setInitializers方法設置到SpringApplication的initializers屬性中。這個過程就是找出所有的應用程序初始化器。
1
2
3
4
5
6
7
|
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = this .createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { //從spring.factories文件中找出key為ApplicationContextInitializer的類 Enumeration<URL> urls = classLoader != null ? classLoader.getResources( "META-INF/spring.factories" ) : ClassLoader.getSystemResources( "META-INF/spring.factories" ); ArrayList result = new ArrayList(); while (urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties( new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException var8) { throw new IllegalArgumentException( "Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]" , var8); } } |
當前的初始化器有如下幾個:
3.同理調用getSpringFactoriesInstances從spring.factories文件中找出key為ApplicationListener的類并實例化,然后調用setListeners方法設置到SpringApplication的listeners屬性中。這個過程就是找出所有的應用程序事件監聽器。
當前的事件監聽器有如下幾個:
4.調用deduceMainApplicationClass方法找出main類,就是這里的ApplicationMain類。
運行SpringApplication
初始化SpringApplication完成之后,調用run方法運行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public ConfigurableApplicationContext run(String... args) { //計時器,統計任務的執行時間 StopWatch stopWatch = new StopWatch(); //開始執行 stopWatch.start(); ConfigurableApplicationContext context = null ; FailureAnalyzers analyzers = null ; this .configureHeadlessProperty(); // 獲取SpringApplicationRunListeners啟動事件監聽器,這里只有一個EventPublishingRunListener SpringApplicationRunListeners listeners = this .getRunListeners(args); // 封裝成SpringApplicationEvent事件然后廣播出去給SpringApplication中的listeners所監聽 listeners.starting(); try { // 構造一個應用程序參數持有類 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 準備并配置環境 ConfigurableEnvironment environment = this .prepareEnvironment(listeners, applicationArguments); // 打印banner圖形 Banner printedBanner = this .printBanner(environment); // 創建Spring容器 context = this .createApplicationContext(); new FailureAnalyzers(context); // 配置Spring容器 this .prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 容器上下文刷新 this .refreshContext(context); // 容器創建完成之后調用afterRefresh方法 this .afterRefresh(context, applicationArguments); // 調用監聽器,廣播Spring啟動結束的事件 listeners.finished(context, (Throwable) null ); // 停止計時器 stopWatch.stop(); if ( this .logStartupInfo) { ( new StartupInfoLogger( this .mainApplicationClass)).logStarted( this .getApplicationLog(), stopWatch); } return context; } catch (Throwable var9) { this .handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9); throw new IllegalStateException(var9); } } |
SpringApplicationRunListeners
1.獲取啟動事件監聽器,可以看看該方法:
SpringApplicationRunListeners listeners = this.getRunListeners(args);
1
2
3
4
|
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class[]{SpringApplication. class , String[]. class }; return new SpringApplicationRunListeners(logger, this .getSpringFactoriesInstances(SpringApplicationRunListener. class , types, this , args)); } |
同樣的通過調用getSpringFactoriesInstances方法去META-INF/spring.factories文件中拿到SpringApplicationRunListener監聽器,當前的SpringApplicationRunListener事件監聽器只有一個EventPublishingRunListener廣播事件監聽器:
SpringApplicationRunListeners內部持有SpringApplicationRunListener集合和1個Log日志類。用于SpringApplicationRunListener監聽器的批量執行。
SpringApplicationRunListener用于監聽SpringApplication的run方法的執行,它定義了5個步驟:
1.starting:run方法執行的時候立馬執行,對應的事件類型是ApplicationStartedEvent
2.environmentPrepared:ApplicationContext創建之前并且環境信息準備好的時候調用,對應的事件類型是ApplicationEnvironmentPreparedEvent
3.contextPrepared:ApplicationContext創建好并且在source加載之前調用一次,沒有具體的對應事件
4.contextLoaded:ApplicationContext創建并加載之后并在refresh之前調用,對應的事件類型是ApplicationPreparedEvent
5.finished:run方法結束之前調用,對應事件的類型是ApplicationReadyEvent或ApplicationFailedEvent
SpringApplicationRunListener目前只有一個實現類EventPublishingRunListener,詳見獲取SpringApplicationRunListeners。它把監聽的過程封裝成了SpringApplicationEvent事件并讓內部屬性ApplicationEventMulticaster接口的實現類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件對象會被SpringApplication中的listeners屬性進行處理。
所以說SpringApplicationRunListener和ApplicationListener之間的關系是通過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯系起來的
2.啟動事件監聽器
通過listeners.starting()可以啟動事件監聽器SpringApplicationRunListener ,SpringApplicationRunListener 是一個啟動事件監聽器接口:
1
2
3
4
5
6
7
8
9
10
11
|
public interface SpringApplicationRunListener { void starting(); void environmentPrepared(ConfigurableEnvironment var1); void contextPrepared(ConfigurableApplicationContext var1); void contextLoaded(ConfigurableApplicationContext var1); void finished(ConfigurableApplicationContext var1, Throwable var2); } |
SpringApplicationRunListener 接口的具體實現就是EventPublishingRunListener類,我們主要來看一下它的startting方法,該方法會封裝成SpringApplicationEvent事件然后廣播出去給SpringApplication中的listeners所監聽。
1
2
3
|
public void starting() { this .initialMulticaster.multicastEvent( new ApplicationStartedEvent( this .application, this .args)); } |
配置并準備環境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 創建應用程序的環境信息。如果是web程序,創建StandardServletEnvironment;否則,創建StandardEnvironment ConfigurableEnvironment environment = getOrCreateEnvironment(); // 配置環境信息。比如profile,命令行參數 configureEnvironment(environment, applicationArguments.getSourceArgs()); // 廣播出ApplicationEnvironmentPreparedEvent事件給相應的監聽器執行 listeners.environmentPrepared(environment); // 環境信息的校對 if (! this .webEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment; } |
判斷環境,如果是web程序,創建StandardServletEnvironment;否則,創建StandardEnvironment。
1
2
3
4
5
6
7
|
private ConfigurableEnvironment getOrCreateEnvironment() { if ( this .environment != null ) { return this .environment; } else { return (ConfigurableEnvironment)( this .webEnvironment ? new StandardServletEnvironment() : new StandardEnvironment()); } } |
創建Spring容器上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this .applicationContextClass; if (contextClass == null ) { try { // 判斷是否是web應用, // 如果是則創建AnnotationConfigEmbeddedWebApplicationContext,否則創建AnnotationConfigApplicationContext contextClass = Class.forName( this .webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass" , ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); } |
配置Spring容器上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 設置Spring容器上下文的環境信息 context.setEnvironment(environment); // Spring容器創建之后做一些額外的事 postProcessApplicationContext(context); // SpringApplication的初始化器開始工作 applyInitializers(context); // 遍歷調用SpringApplicationRunListener的contextPrepared方法。目前只是將這個事件廣播器注冊到Spring容器中 listeners.contextPrepared(context); if ( this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } // 把應用程序參數持有類注冊到Spring容器中,并且是一個單例 context.getBeanFactory().registerSingleton( "springApplicationArguments" , applicationArguments); if (printedBanner != null ) { context.getBeanFactory().registerSingleton( "springBootBanner" , printedBanner); } // 加載sources,sources是main方法所在的類 Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty" ); // 將sources加載到應用上下文中。最終調用的是AnnotatedBeanDefinitionReader.registerBean方法 load(context, sources.toArray( new Object[sources.size()])); // 廣播出ApplicationPreparedEvent事件給相應的監聽器執行 // 執行EventPublishingRunListener.contextLoaded方法 listeners.contextLoaded(context); } |
Spring容器創建之后回調方法postProcessApplicationContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
protected void postProcessApplicationContext(ConfigurableApplicationContext context) { // 如果SpringApplication設置了實例命名生成器,則注冊到Spring容器中 if ( this .beanNameGenerator != null ) { context.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this .beanNameGenerator); } // 如果SpringApplication設置了資源加載器,設置到Spring容器中 if ( this .resourceLoader != null ) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader( this .resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context) .setClassLoader( this .resourceLoader.getClassLoader()); } } } |
初始化器開始工作
1
2
3
4
5
6
7
8
9
|
protected void applyInitializers(ConfigurableApplicationContext context) { // 遍歷每個初始化器,調用對應的initialize方法 for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer. class ); Assert.isInstanceOf(requiredType, context, "Unable to call initializer." ); initializer.initialize(context); } } |
Spring容器創建完成之后會調用afterRefresh方法
ApplicationRunner、CommandLineRunner類都是在在afterRefresh方法中調用的,也就是說在Spring容器創建之后執行的。
1
2
3
4
5
6
7
8
9
|
protected void applyInitializers(ConfigurableApplicationContext context) { // 遍歷每個初始化器,調用對應的initialize方法 for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer. class ); Assert.isInstanceOf(requiredType, context, "Unable to call initializer." ); initializer.initialize(context); } } |
參考:https://blog.wangqi.love/articles/Spring/SpringBoot啟動過程.html
到此這篇關于SpringBoot啟動過程的實現的文章就介紹到這了,更多相關SpringBoot啟動過程內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/chengbinbbs/article/details/88557162