147行ApplicationArguments applicationArguments = new DefaultApplicationArguments(args),入参的args被包装成DefaultApplicationArguments对象,应该是为了方便后续调用,也就是args参数目前用作了两种用途,一种是传递给监听事件,一种是包装成该对象
148行ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments),创建ConfigurableEnvironment对象,首先简单了解一下ConfigurableEnvironment接口是用来干嘛的,它继承了Environment接口,用来表示整个应用运行时的环境,Environment在容器中是一个抽象的集合,是指应用环境的2个方面:profiles和properties。准备这个对象用来干什么,暂时未知 Q5?
debug跟一下prepareEnvironment方法
首先是getOrCreateEnvironment方法,默认创建StandardServletEnvironment对象,看一下默认得到的对象
在这不深入研究这些属性
简单看一下然后做些猜测
propertyResolver里的东西,看起来挺像spring的@Value注解读取配置文件配置的字符,比如@Value("${name:'aaa'}") 该注解表示获取配置文件中的name,如果没配置,默认aaa
propertySources里的东西,应该就是获取到的配置文件,比如name='systemProperties'和name='systemEnvironment'这两个,是获取了java.lang.System系统属性和环境配置,另外两个servletConfigInitParams和servletContextInitParams,因为创建的是StandardServletEnvironment,所以这两个值应该跟servlet有关
这里仅做一些猜测,如果后续正好研究到,再深入分析
接下来执行configureEnvironment方法
conversionService,顾名思义,转换器,应该是从配置文件的配置转换为不同类型属性时用到
configurePropertySources,配置属性来源,上面已经看到默认的有4个,这里是配置其它来源的属性
通过SpringApplication对象的defaultProperties属性配置,该属性一般为空
通过启动类的入参配置,这里介绍一下这个入参,如果对打jar包后启动的命令使用过的话,应该了解过可以通过命令去手动做一些配置,比如java -jar xxx.jar --name=zhangsan 这样去配置参数,演示一下
该配置放入了environment里,最后也能通过@Value得到,而且会覆盖application.yml里对应的同名配置
configureProfiles,配置profile(不知道怎么翻译,直接上代码)
运行到这里时,由于还没开始加载classpath下的yml文件,所以需要的配置spring.profiles.active如果在文件里配置了,这个阶段走进这个方法,还是为null的,如果需要此时就能得到想加载的配置文件,可以通过命令java -jar xxx.jar --spring.profiles.active=test,这样启动会在这个阶段就生效
然后执行listeners.environmentPrepared((ConfigurableEnvironment)environment),在上一章有说过listeners.starting方法的作用,查看源码不难发现它跟starting差不多,也是发送监听事件,这次监听器ConfigFileApplicationListener加载了classpath下的配置文件进environment,具体怎么加载的这里就不研究了
接下来执行this.bindToSpringApplication((ConfigurableEnvironment)environment),该方法提取spring.main开头的配置绑定到SpringApplication对象上,比如配置spring.main.headless
到这也差不多完成了生成environment对象的工作,包括了创建默认SERVLET环境对象(其它的如StandardReactiveWebEnvironment、StandardEnvironment大体应该差不多),配置转换器、配置其它属性来源、配置active profile,发送事件到监听器ConfigFileApplicationListener读取classpath下配置文件,把environment对象里已读取到的属性前缀为spring.main的相关配置绑定回SpringApplication对象上。spring的Environment和Binder后续可以深入研究一下
把run方法的源码重新截图一下
149行,configureIgnoreBeanInfo方法,该方法默认配置键为spring.beaninfo.ignore,值为true的property给java.lang.System,具体作用不重点分析了,有缘再说
150行,打印banner,即
可以更换打印内容,跟踪源码可以知道如何更换,这里只作其中一种方式的演示
在定义的文件夹路径下创建对应的文件
启动后打印如图
151行,开始创建ApplicationContext对象
进入方法可以看到默认创建了AnnotationConfigServletWebServerApplicationContext对象,看一下类结构图
目前我们接触并分析到的有ApplicationContext,它是spring容器,装了各种各样的东西,另外还有一个EnvironmentCapable,实现类需要实现它的getEnvironment方法,environment也就是前面分析到的那些,跟踪源码会发现AnnotationConfigServletWebServerApplicationContext构造方法还会默认初始化一个(在父类构造方法中),这个初始化是为了生成AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner对象,这两个对象的作用后面再看,Q6?
这里来实践一个demo,也就是模拟ApplicationContext接口和EnvironmentCapable接口,做个简化版的出来便于理解
写两个的原因是想描述spring不同的容器可能会加载不同的环境对象出来,从之前的分析中可以知道environment中放了很多属性,包括从配置文件中读取出来的,这里仅作最简单的处理(知道有这些途径就行)
接下来152行,这一行暂时从字面意思理解了,异常上报,从源码包中spring.factories也可以看到SpringBootExceptionReporter默认指定实现类FailureAnalyzers,应该能通过它分析并解决异常,Q7?
到目前问题总结
Q1 启动类run方法的两个入参,已经知道第一个参数用于创建SpringApplication对象,args用于监听事件传递参数(监听事件中具体用来干嘛应该不需要研究,springboot会有很多默认处理机制),args另一方面用于构建Environment,可以通过启动项目时添加相应的参数进Environment,然后应用于整个项目
Q2 ConfigurableApplicationContext如何创建对象并启动的,默认创建AnnotationConfigServletWebServerApplicationContext对象,通过一个简单demo描述了一下ApplicationContext是如何装载环境和bean的,spring中也是选择ApplicationContext的一个实现类通过构造方法创建对象,后面分析在springboot中是怎么启动的
Q3 java.lang.System中的prop对象赋key为java.awt.headless的值,作用未知
Q4 SpringApplicationRunListeners listeners对象目前分析了它的starting和environmentPrepared方法,最终发送事件给各启动监听,完整作用待分析,environmentPrepared时会加载classpath下的配置文件进environment
Q5 ConfigurableEnvironment的作用,就是加载各种运行时的环境配置,从系统System配置到配置文件的配置再到启动项目时的入参配置等,最终这些配置会加载进容器,被容器管理的bean可以使用这些配置
Q6 AnnotationConfigServletWebServerApplicationContext生成的AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner对象,作用未知
Q7 异常上报的作用,待分析