本文已参与「新人创作礼」活动,一起开启掘金创作之路。
10.准备上下文环境 prepareContext
进入prepareContext()方法如下:
1.将环境 enviorment 设置给上下文 context
设置环境对象,传入的对象是解析完毕 profiles 的对象,Context 内部则是不完整的对象.
2.设置上下文参数
进入postProcessApplicationContext()方法如下:
2.1 以单例模式注册 beanNameGenerator,beanName 生成器, 用来生成 bean 对象的名称
2.2 为上下文设置资源加载器和类加载器
2.3 为上下文设置转换服务
经过 DEBUG,实际上只有 2.3 运行了。
3.加载 ApplicationContextInitializers
进入applyInitializers()方法如下:
首先调用getInitializers()方法,获取所有的 initializer 初始化器,初始化器是在之前创建 SpringApplication 实例时就已经加载好的。getInitializers() 方法内部对其进行了排序然后返回。然后循环执行 initializer 的initialize()方法,SpringBoot 中自带的 initializer 有如下 7 个:
3.1 DelegatingApplicationContextInitializer
用于初始化配置文件中context.initializer.classes属性中指定的 ApplicationContextInitializer 实现类。源码如下:
在 SpringBoot 中实现 ApplicationContextInitializer 并确保其被加载有三种方法:
- 直接使用代码添加
new SpringApplication(Example.class).addInitializers(new 实现类());
- 以 SPI 方式配置
在 /META-INF/spring.factories 文件中配置:
org.springframework.context.ApplicationContextInitializer=实现类A, 实现类B
- 以配置文件方式配置
context.initializer.classes = 实现类A, 实现类B
而这里,DelegatingApplicationContextInitializer类的作用就是从配置中拿到初始化器,创建其实例,并执行其 initialize() 方法。
3.2 SharedMetadataReaderFactoryContextInitializer
向上下文中,添加了一个CachingMetadataReaderFactoryPostProcessor的Bean工厂后置处理器。
具体作用,TOOD.
\
3.3 ContextIdApplicationContextInitializer
用来设置上下文 context 的 id。
\
3.4 RestartScopeInitializer
没太懂
\
3.5 ConfigurationWarningsApplicationContextInitializer
向上下文中,添加了一个ConfigurationWarningsPostProcessor的Bean工厂后置处理器,用于打印配置错误的日志。
\
3.6 ServerPortInfoApplicationContextInitializer
用于设置配置文件中server.ports属性的值,它是Web服务器实际监听的应用程序端口。同时实现了 ApplicationListener 接口用于监听 WebServerInitializedEvent 事件,WebServer 初始化完毕之后设置端口。
① 向上下文监听器列表中添加当前类(作为监听器加入)
② 在监听事件中设置端口,默认属性为local.server.port
③ 若设置了服务器Namespace,则属性值为local.Namespace的值.port
④ 将监听端口信息填入server.ports属性下
\
3.7 ConditionEvaluationReportLoggingListener
虽然名字是以 Listener 结尾,但它实现的是 ApplicationContextInitializer 接口,即实际上是 Initializer,其目的是将 ConditionEvaluationReport 写入到日志,使用 DEBUG 级别输出。程序崩溃报告会触发一个消息输出,建议用户使用调试模式显示报告。
用于设置 ConditionEvaluationReportListener 监听器,此监听器监听 ContextRefreshedEvent 和 ApplicationFailedEvent,分别打印出上下文容器成功刷新和失败的日志报告。
4.执行监听器的准备上下文事件
由SpringApplicationRunListeners#contextPrepared()方法触发。
当上下文 context 加载完所有的 Initializer 之后,就会触发该事件,通知那些关注了此事件的监听器进行下一步操作。每次发布事件的时候可能已经有新的监听器被加进来,但我们只需要关注那些监听了当前事件的监听器即可。
5.注册 springApplicationArguments
从 context 中获取 beanFactory,并以单例模式,注册 springApplicationArguments 对象,beanName 为 springApplicationArguments。
6.注册 printedBanner
将 printedBanner 对象注册到 beanFactory,beanName 为 springBootBanner。
7.设置是否允许覆盖 BeanDefinition
allowBeanDefinitionOverriding 属性用来设置是否允许覆盖 BeanDefinition,对应的配置为spring.main.allow-bean-definition-overriding设置为 true 时,后定义的 bean 会覆盖之前定义的相同名称的 bean。
在 SpringBoot 中,该值默认为 false。而在 Spring 中,该值默认为 true。
8.加载资源,注册主启动类
首先是获取了加载的 bean 所在的位置,这里是主启动类所在的包。然后调用 load() 方法将主启动类进行注册。
这个方法运行完毕后,主启动类便被实例化并加入到了 beanDefinitionMap 中。
进入 load() 方法如下:
首先是创建了BeanDefinitionLoader对象。BeanDefinitionLoader 是负责加载 Bean 的工具。进入createBeanDefinitionLoader()方法如下:
方法内部是创建了 BeanDefinitionLoader 对象。而 BeanDefinitionLoader 的构造方法中,则创建了从不同形式获取 Bean 的加载器类。
BeanDefinitionLoader 对加载 Bean 的整体功能做了封装,内部由不同的资源加载类来完成不同类型的资源加载,例如:
AnnotatedBeanDefinitionReader 从基于注解的类来开始加载。
XmlBeanDefinitionReader 从 XML 文件开始加载。
GroovyBeanDefinitionReader
ClassPathBeanDefinitionScanner 从 classpath 下加载。
然后是执行中间的 三个 if 判断,这里是都不满足,未执行。
最后执行 BeanDefinitionLoader 的 load() 方法,如下:
这里的 count 用来统计加载的资源数量。
load() 方法中,根据 source 的类型,使用不同方式加载:
如果是 Class,尝试使用 Groovy 和 注解 的方式进行加载。
如果是 Resource,尝试使用 Groovy 和 xml 的方式进行加载。
如果是 Package,尝试使用 注解 的方式进行加载。
如果是 CharSequence,尝试使用 xml 和 注解 的方式进行加载。
这里传入的是 Class,进入 load() 方法如下:
首先判断了 Groovy 方式能否加载,肯定是不能。
然后哦安短了 class 类上是否存在@Component注解,有该注解才会注册,虽然启动类上没有该注解,但是其注解的子注解中存在,所以,这里使用注解的方式,来注册启动类。
进入register()方法如下:
实际上调用的是doRegisterBean()方法,进入该方法:
8.1 首先,创建了AnnotatedGenericBeanDefinition对象,这是注解方式的 Bean 对应的 BeanDefinition。其类结构如下:
8.2 判断是否有@Conditional注解,以确定是否满足条件需要排除
8.3 指定了创建 bean 的回调,这里传入的是 null
8.4 获取当前 Bean 的作用域范围,并设置给 AnnotatedGenericBeanDefinition 对象。
进入resolveScopeMetadata()方法,
首先创建了ScopeMetadata对象,ScopeMetadata 包含类的作用域描述 singleton / prototype 和代理模式描述 ScopedProxyMode,默认为单例模式,不使用代理。
然后判断 Bean 上是否存在@Scope注解,有的话就给 ScopeMetadata 设置对应的属性,没有的话,返回默认的 ScopeMetadata。
8.5 获取 beanName
这里的 name 是入参中传入的,但是传入的是 null ,所以还需要调用beanNameGenerator.generateBeanName()方法来生成 beanName。
这里是注解方式的 Bean,其 beanName 生成规则,其他文章细说,这里略过。
8.6 处理常用注解
从AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);的方法名上推出这是处理了常用的注解。
进入该方法中可以看到,常见的注解包括 @Lazy、@Primary、@Role、@Description、@DependsOn,都被解析并设置到 AnnotatedGenericBeanDefinition 的对应属性中。
8.7 检查传入的 Qualifiers
这里方法入参中传入的是 null,所以没有进行任何处理。
8.8 暂时不知道干啥的,以后再说
8.9 注册 Bean,如果使用代理的话,创建对应代理
首先创建了 BeanDefinitionHolder 对象,然后在AnnotationConfigUtils.applyScopedProxyMode()方法中,对其进行处理,如果不使用代理,则直接返回 BeanDefinitionHolder,如果使用了代理,则通过 JDK 代理还是 CGLIB 代理生成对应代理对象。
最后呢是注册 Bean.
\
9.触发 listener 的上下文加载完毕事件
进入contextLoaded()方法,方法内部遍历了 listeners,并执行其 contextLoaded 方法。这里的 listeners 只有一个 EventPublishingRunListener。
在真正触发事件之前,它处理了 ApplicationContextAware 实现,为其设置了上下文 context。