从零搭建并分析springboot(三)

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方法

prepareEnvironment方法

首先是getOrCreateEnvironment方法,默认创建StandardServletEnvironment对象,看一下默认得到的对象

得到的默认environment对象的一些属性

在这不深入研究这些属性

简单看一下然后做些猜测

propertyResolver里的东西,看起来挺像spring的@Value注解读取配置文件配置的字符,比如@Value("${name:'aaa'}") 该注解表示获取配置文件中的name,如果没配置,默认aaa

propertySources里的东西,应该就是获取到的配置文件,比如name='systemProperties'和name='systemEnvironment'这两个,是获取了java.lang.System系统属性和环境配置,另外两个servletConfigInitParams和servletContextInitParams,因为创建的是StandardServletEnvironment,所以这两个值应该跟servlet有关

这里仅做一些猜测,如果后续正好研究到,再深入分析

接下来执行configureEnvironment方法

configureEnvironment方法

conversionService,顾名思义,转换器,应该是从配置文件的配置转换为不同类型属性时用到

configurePropertySources,配置属性来源,上面已经看到默认的有4个,这里是配置其它来源的属性

configurePropertySources方法

通过SpringApplication对象的defaultProperties属性配置,该属性一般为空

通过启动类的入参配置,这里介绍一下这个入参,如果对打jar包后启动的命令使用过的话,应该了解过可以通过命令去手动做一些配置,比如java -jar xxx.jar --name=zhangsan 这样去配置参数,演示一下

添加入参
添加入参后启动效果

该配置放入了environment里,最后也能通过@Value得到,而且会覆盖application.yml里对应的同名配置

configureProfiles,配置profile(不知道怎么翻译,直接上代码)

configureProfiles方法

运行到这里时,由于还没开始加载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方法的源码重新截图一下

重新贴的run源码

149行,configureIgnoreBeanInfo方法,该方法默认配置键为spring.beaninfo.ignore,值为true的property给java.lang.System,具体作用不重点分析了,有缘再说

150行,打印banner,即

springboot默认banner

可以更换打印内容,跟踪源码可以知道如何更换,这里只作其中一种方式的演示

定义spring.banner.location

在定义的文件夹路径下创建对应的文件

创建banner.txt

启动后打印如图

自定义的banner打印效果

151行,开始创建ApplicationContext对象

进入方法可以看到默认创建了AnnotationConfigServletWebServerApplicationContext对象,看一下类结构图

AnnotationConfigServletWebServerApplicationContext结构图

目前我们接触并分析到的有ApplicationContext,它是spring容器,装了各种各样的东西,另外还有一个EnvironmentCapable,实现类需要实现它的getEnvironment方法,environment也就是前面分析到的那些,跟踪源码会发现AnnotationConfigServletWebServerApplicationContext构造方法还会默认初始化一个(在父类构造方法中),这个初始化是为了生成AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner对象,这两个对象的作用后面再看,Q6?

这里来实践一个demo,也就是模拟ApplicationContext接口和EnvironmentCapable接口,做个简化版的出来便于理解

模仿LifeCycle接口,控制生命周期
模仿Environment接口,可获取属性
模仿EnvironmentCapable接口,可获取环境对象
模仿BeanFactory接口,有getBean方法
模仿ApplicationContext接口,作为容器,它装有运行的环境,所有的Bean对象,可控制的生命周期
CustomBeanFactory简单实现类,实现getBean方法,提供addBean方法装入Bean对象
CustomEnvironment简单实现类1
CustomEnvironment简单实现类2

写两个的原因是想描述spring不同的容器可能会加载不同的环境对象出来,从之前的分析中可以知道environment中放了很多属性,包括从配置文件中读取出来的,这里仅作最简单的处理(知道有这些途径就行)

CustomApplicationContext简单实现抽象类part1
CustomApplicationContext简单实现抽象类part2
CustomApplicationContext简单实现1,加载环境CustomSimple1Environment
CustomApplicationContext简单实现2,加载环境CustomSimple2Environment
测试两个容器,可以看到加载了不同的环境

接下来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 异常上报的作用,待分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容