- <strong>
- <strong>
- <strong>
基本環境
開發工具:Intellij IDEA 2017(盜)版
java版本:1.8.0_151
spring的github地址:spring-framework
準備:git clone或直接下載github上的spring源碼,導入idea中,在項目路徑下執行gradle build (如果本機沒有gradle環境,或者版本差很多,就用gradlew代替),會build很久,可以事先將阿里的maven倉庫地址加到repositories中,像這樣:
<code>repositories { maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } maven { url "https://repo.spring.io/plugins-release" }}/<code>
會用到的縮寫:
<code>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);}}/<code>
一.SpringApplication的初始化
代碼如下:
<code>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 }/<code>
1.設置啟動類primarySources到對應屬性
這個啟動類就是在初始化SpringApplication時候的參數,可以有多個
2.獲取web應用類型
- 根據當前classpath下存在的類來判斷的:
- 如果存在org.springframework.web.reactive.DispatcherHandler,就是REACTIVE
- 如果不存在org.springframework.web.servlet.DispatcherServlet或者javax.servlet.Servlet,就是NONE
- 否則,就是SERVLET
3.設置ApplicationContextInitializer
代碼如下:
<code>privateCollection /<code>getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//3.1 Set<string> 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; } /<string>
3.1 獲取線程上下文ClassLoader
TODO(ClassLoader的相關解釋)
3.2 使用SpringFactoriesLoader加載出classpath下所有路徑為META-INF/spring.factories的資源文件,並讀取key為ApplicationContextInitializer的值
會得到如些幾個類(實際是全稱類名):
<code>SharedMetadataReaderFactoryContextInitializerAutoConfigurationReportLoggingInitializerConfigurationWarningsACIContextIdACIDelegatingACIServerPortInfoACI/<code>
3.3 利用反射創建出上述ACI的實例
3.4 排序
根據Ordered接口/@PriorityOrder註解/@Order註解去排
其他地方的排序基本也是按照這個規則來排的
4.設置listener
跟設置ACI的方法一樣,也是讀取spring.factories文件中的對應key項所對應的所有類名,然後實例化、排序.
得到如下幾個ApplicationListener:
<code>BackgroundPreinitializerClearCachesALParentContextCloserALFileEncodingALAnsiOutputALConfigFileALDelegatingALClasspathLoggingALLoggingALLiquibaseServiceLocatorAL/<code>
5.設置mainApplicationClass
就是找到main方法所在的類.是唯一的,跟上面的primarySources不是一回事
二.SpringApplication的run(String … args)方法
代碼如下:
<code>public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start();//1 ConfigurableApplicationContext context = null; Collection<springbootexceptionreporter> 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); } }/<springbootexceptionreporter>/<code>
1. 創建一個StopWatch用於記錄啟動的時間
2. 配置java.awt.headless屬性,默認為true
這個headless是一種模式,是指缺少顯示屏、鍵盤或者鼠標時的系統配置
我找了spring和spring boot的源碼,只有ImageBanner裡面用到了
3.從spring.factories中加載出SpringApplicationRunListener,並構建一個SpringApplicationRunListeners
- StringApplicationListeners就是對對個SpringApplicationRunListener的包裝
- 現在看只有一個具體實現類
- EventPublishingRunListener
- 雖然這個類叫做listener,但其實起的作用是傳播事件,更像是一個ApplicationEventMulticaster。
- spring boot啟動過程中的事件,主要由這個類來傳播
4.發佈一個ApplicationStartingEvent事件
- 關於spring事件發佈機制,請看spring事件機制
- 響應的AL
- LoggingAL:初始化日誌系統
- 做法是按照logback>log4j>jul的順序查找classpath下存在的相關類
- LiquibaseServiceLocatorAL:實在是沒有用過這個東西,不知道這個LiquibaseService是個啥…
5.根據啟動時的命令行參數構建一個ApplicationArguments
ApplicationArguments的接口定義:
<code>public interface ApplicationArguments { String[] getSourceArgs(); //--開頭的參數 Set<string> getOptionNames(); boolean containsOption(String name); //--開頭的參數是允許重複定義的,不會覆蓋 List<string> getOptionValues(String name); List<string> getNonOptionArgs();}/<string>/<string>/<string>/<code>
這個類的作用就是解析所有的啟動參數的
6.prepareEnvironment():創建並配置environment
代碼如下:
<code>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; }/<code>
6.1 根據WebApplicationType創建對應類型的environment
關於spring中Environment/PropertySource的介紹:
根據webApplicationType:
<code>SERVLET -> StandardServletEnvironmentNONE/REACTIVE -> StandardEnvironment/<code>
初始化的時候會在構造函數中調用customizePropertySources()方法,其結果是會在environment的propertySources屬性的propertySourcesList列表中加入以下PropertySource:
- 代表ServletConfig的構建參數的,名為servletConfigInitParams的StubPropertySource(目前只是作為佔位符存在)
- 代表ServletContext的構建參數的,名為servletContextInitParams的StubPropertySource(目前只是作為佔位符存在)
- 代表jvm屬性參數的名為systemProperties的MapPropertySource(來源於System.getProperties()方法)
- 代表環境變量的名為systemEnvironment的SystemEnvironmentPropertySource(來源於System.getEnv()方法)
6.2 配置environment
配置PropertySources
- 看看SpringApplication的defaultProperties屬性是否為空,如果不為空則加入一個MapPropertySource到propertySources中。這個defaultProperties屬性可以在SpringApplication調用run方法之前通過setDefaultProperties()方法設置
- 看看有沒有命令號參數,如果有則創建一個SimpleCommandLinePropertySource加入到propertySources中
配置activeProfiles:就是將spring.profiles.active屬性的值作為數組讀入
6.3 listeners發佈一個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
<code>SERVLET -> AnnotationConfigServletWebServerACREACTIVE -> ReactiveWebServerACNONE -> AnnotationConfigAC /<code>
按照繼承結構:
DefaultResourceLoader:ResourceLoader的默認實現,作用就是加載Resource,內部有個比較重要的ProtocolResolver集合屬性,具體使用請看ProtocolResolver解析
AbstractAC:配置了一個ResourcePatternResolver,具體請看Resource相關
GenericAC:初始化了一個BeanFactory,具體類型是DefaultListableBeanFactory
原文鏈接:https://blog.csdn.net/yuxiuzhiai/article/details/79074249
閱讀更多 追逐仰望星空 的文章