springboot启动过程_spring5/springboot2源码学习 -- spring boot 应用的启动过程
推薦閱讀:
- Spring全家桶筆記:Spring+Spring Boot+Spring Cloud+Spring MVC
 - 疫情期間“閉關修煉”,吃透這本Java核心知識,跳槽面試不心慌
 - 2020“閉關”跳槽季,啃透分布式三大技術:限流、緩存、通訊
 
基本環(huán)境
開發(fā)工具:Intellij IDEA 2017(盜)版
java版本:1.8.0_151
spring的github地址:spring-framework
準備:git clone或直接下載github上的spring源碼,導入idea中,在項目路徑下執(zhí)行gradle build (如果本機沒有gradle環(huán)境,或者版本差很多,就用gradlew代替),會build很久,可以事先將阿里的maven倉庫地址加到repositories中,像這樣:
repositories { maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } maven { url "https://repo.spring.io/plugins-release" }}會用到的縮寫:
ApplicationContext -> ACApplicationContextInitializer -> ACIBeanFactory -> BFApplicationListener -> ALEnvironmentPostProcessor -> EPPspring boot 應用的啟動/*** @author pk* @date 2018/02/22*/@SpringBootApplicationpublic class SpringBootTwoStudyApplication {public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(SpringBootTwoStudyApplication.class);//可以在run之前,對AC進行一些自定義的配置,添加點ApplicationListener,ApplicationContextInitializer啥的springApplication.run(args);}}一.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));//1 this.webApplicationType = deduceWebApplicationType();//2 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));//3 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//4 this.mainApplicationClass = deduceMainApplicationClass();//5 }1.設置啟動類primarySources到對應屬性
這個啟動類就是在初始化SpringApplication時候的參數,可以有多個
2.獲取web應用類型
- 根據當前classpath下存在的類來判斷的:
 - 如果存在org.springframework.web.reactive.DispatcherHandler,就是REACTIVE
 - 如果不存在org.springframework.web.servlet.DispatcherServlet或者javax.servlet.Servlet,就是NONE
 - 否則,就是SERVLET
 
3.設置ApplicationContextInitializer
代碼如下:
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//3.1 Set names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader));//3.2 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);//3.3 AnnotationAwareOrderComparator.sort(instances);//3.4 return instances; }3.1 獲取線程上下文ClassLoader
TODO(ClassLoader的相關解釋)
3.2 使用SpringFactoriesLoader加載出classpath下所有路徑為META-INF/spring.factories的資源文件,并讀取key為ApplicationContextInitializer的值
會得到如些幾個類(實際是全稱類名):
SharedMetadataReaderFactoryContextInitializerAutoConfigurationReportLoggingInitializerConfigurationWarningsACIContextIdACIDelegatingACIServerPortInfoACI3.3 利用反射創(chuàng)建出上述ACI的實例
3.4 排序
根據Ordered接口/@PriorityOrder注解/@Order注解去排
其他地方的排序基本也是按照這個規(guī)則來排的
4.設置listener
跟設置ACI的方法一樣,也是讀取spring.factories文件中的對應key項所對應的所有類名,然后實例化、排序.
得到如下幾個ApplicationListener:
BackgroundPreinitializerClearCachesALParentContextCloserALFileEncodingALAnsiOutputALConfigFileALDelegatingALClasspathLoggingALLoggingALLiquibaseServiceLocatorAL5.設置mainApplicationClass
就是找到main方法所在的類.是唯一的,跟上面的primarySources不是一回事
二.SpringApplication的run(String … args)方法
代碼如下:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start();//1 ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList<>(); configureHeadlessProperty();//2 SpringApplicationRunListeners listeners = getRunListeners(args);//3 listeners.starting();//4 try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args);//5 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//6 configureIgnoreBeanInfo(environment);//7 Banner printedBanner = printBanner(environment);//8 context = createApplicationContext();//9 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);//10 prepareContext(context, environment, listeners, applicationArguments, printedBanner);//11 refreshContext(context);//12 afterRefresh(context, applicationArguments);//13 listeners.finished(context, null);//14 stopWatch.stop(); //15 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } callRunners(context, applicationArguments);//16 return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } }1. 創(chuàng)建一個StopWatch用于記錄啟動的時間
2. 配置java.awt.headless屬性,默認為true
這個headless是一種模式,是指缺少顯示屏、鍵盤或者鼠標時的系統配置
我找了spring和spring boot的源碼,只有ImageBanner里面用到了
3.從spring.factories中加載出SpringApplicationRunListener,并構建一個SpringApplicationRunListeners
- StringApplicationListeners就是對對個SpringApplicationRunListener的包裝
 - 現在看只有一個具體實現類
 - EventPublishingRunListener
 - 雖然這個類叫做listener,但其實起的作用是傳播事件,更像是一個ApplicationEventMulticaster。
 - spring boot啟動過程中的事件,主要由這個類來傳播
 
4.發(fā)布一個ApplicationStartingEvent事件
- 關于spring事件發(fā)布機制,請看spring事件機制
 - 響應的AL
 - LoggingAL:初始化日志系統
 - 做法是按照logback>log4j>jul的順序查找classpath下存在的相關類
 - LiquibaseServiceLocatorAL:實在是沒有用過這個東西,不知道這個LiquibaseService是個啥…
 
5.根據啟動時的命令行參數構建一個ApplicationArguments
ApplicationArguments的接口定義:
public interface ApplicationArguments { String[] getSourceArgs(); //--開頭的參數 Set getOptionNames(); boolean containsOption(String name); //--開頭的參數是允許重復定義的,不會覆蓋 List getOptionValues(String name); List getNonOptionArgs();}這個類的作用就是解析所有的啟動參數的
6.prepareEnvironment():創(chuàng)建并配置environment
代碼如下:
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment();//6.1 configureEnvironment(environment, applicationArguments.getSourceArgs());//6.2 listeners.environmentPrepared(environment);//6.3 bindToSpringApplication(environment);//6.4 if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment);//6.5 return environment; }6.1 根據WebApplicationType創(chuàng)建對應類型的environment
關于spring中Environment/PropertySource的介紹:
根據webApplicationType:
SERVLET -> StandardServletEnvironmentNONE/REACTIVE -> StandardEnvironment初始化的時候會在構造函數中調用customizePropertySources()方法,其結果是會在environment的propertySources屬性的propertySourcesList列表中加入以下PropertySource:
- 代表ServletConfig的構建參數的,名為servletConfigInitParams的StubPropertySource(目前只是作為占位符存在)
 - 代表ServletContext的構建參數的,名為servletContextInitParams的StubPropertySource(目前只是作為占位符存在)
 - 代表jvm屬性參數的名為systemProperties的MapPropertySource(來源于System.getProperties()方法)
 - 代表環(huán)境變量的名為systemEnvironment的SystemEnvironmentPropertySource(來源于System.getEnv()方法)
 
6.2 配置environment
配置PropertySources
- 看看SpringApplication的defaultProperties屬性是否為空,如果不為空則加入一個MapPropertySource到propertySources中。這個defaultProperties屬性可以在SpringApplication調用run方法之前通過setDefaultProperties()方法設置
 - 看看有沒有命令號參數,如果有則創(chuàng)建一個SimpleCommandLinePropertySource加入到propertySources中
 
配置activeProfiles:就是將spring.profiles.active屬性的值作為數組讀入
6.3 listeners發(fā)布一個ApplicationEnvironmentPreparedEvent
這是一個很重要的事件,響應的listener有很多:
ConfigFileApplicationListener
- 從spring.factories中讀取所有的EnvironmentPostProcessor,它自己也是個EPP。
 - 排序,然后逐個調用
 - SystemEnvironmentPropertySourceEPP:替換systemEnvironment為其內部類OriginAwareSystemEnvironmentPropertySource
 - SpringApplicationJsonEPP:解析spring.application.json屬性,作為JsonPropertySource加入propertySources中
 
CloudFoundryVcapEPP:VCAP相關,不懂,略過
ConfigFileAL(自己):
- 添加一個RandomValuePropertySource,作用是設置所有random.int/long開頭的屬性為一個隨機值
 - 從spring.factories中讀取PropertySourceLoader:
 - PropertiesPropertySourceLoader:讀取.properties配置文件
 - YamlPropertySourceLoader:讀取.yaml配置文件
 - 根據activeProfiles解析每個對應的配置文件成OriginTrackedMapPropertySource,加入到propertySources中
 
6.4 將environment綁定到SpringApplication中
todo…
6.5 往propertySources中加入一個ConfigurationPropertySourcesPropertySource
7.配置一個spring.beaninfo.ignore屬性,默認為true
8.打印banner
9.根據webApplicationType構建ApplicationContext
SERVLET -> AnnotationConfigServletWebServerACREACTIVE -> ReactiveWebServerACNONE -> AnnotationConfigAC按照繼承結構:
DefaultResourceLoader:ResourceLoader的默認實現,作用就是加載Resource,內部有個比較重要的ProtocolResolver集合屬性,具體使用請看ProtocolResolver解析
AbstractAC:配置了一個ResourcePatternResolver,具體請看Resource相關
GenericAC:初始化了一個BeanFactory,具體類型是DefaultListableBeanFactory
作者:pumpkin_pk
原文鏈接:https://blog.csdn.net/yuxiuzhiai/article/details/79074249
總結
以上是生活随笔為你收集整理的springboot启动过程_spring5/springboot2源码学习 -- spring boot 应用的启动过程的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 后端技术:这35 个细节,提升你的 Ja
 - 下一篇: 计算机老丢失运行库,计算机中丢失api-