连杰李头像
《Spring Boot 3:高级与架构设计》第 3 章 Bean 的全生命周期原理 Bean 的实例化 阅读笔记 11封面图

《Spring Boot 3:高级与架构设计》第 3 章 Bean 的全生命周期原理 Bean 的实例化 阅读笔记 11

《Spring Boot 3:高级与架构设计》第 3 章 Bean 的全生命周期原理 Bean 的实例化 阅读笔记 10

3.3 Bean 的实例化

当 BeanDefinition 全部注册到 BeanDefinitionRegistry 后,下一个 Bean 生命周期的阶段是 Bean 的实例化,即将 BeanDefinition 的元信息转化为真实存在的 bean 对象。本节聚焦于 BeanPostProcessor 的注册以及 bean 对象被实例化的几种方式和动作。

Bean 的实例化动作主要分布在 refresh 方法的第 6 步与第 11 步,即代码清单 3-47 中标注的两个环节。可能读者不太理解为什么 Bean 的实例化涉及 BeanPostProcessor 的注册,下面先来回答这个问题。

代码清单 3-47 refresh 方法
在这里插入图片描述

3.3.1 注册 BeanPostProcessor

BeanPostProcessor 的注册仍然由 PostProcessorRegistrationDelegate 的 registerBeanPostProcessors 方法完成,生效的是一个 Delegate 类中的同名方法,如代码清单 3-48 所示。

代码清单 3-48 registerBeanPostProcessors
在这里插入图片描述
在这里插入图片描述
registerBeanPostProcessors 方法的源码篇幅很长,但总体逻辑脉络不难梳理:类似于注册与执行 BeanFactoryPostProcessor,注册BeanPostProcessor 时,首先注册实现了 PriorityOrdered 接口的后置处理器,其次注册实现了 Ordered 的后置处理器,最后注册普通的后置处理器。此外,registerBeanPostProcessors 方法中还有一些对于初读源码者而言比较难理解的问题。

(1)为什么在注册 BeanPostProcessor 之前要先注册一个 BeanPostProcessorChecker?

(2)为什么实现 PriorityOrdered 接口的后置处理器会在分类时就被注册?

(3)为什么在分类筛选后置处理器时要单独筛选 MergedBeanDefinitionPostProcessor 类型的后置处理器,并且在所有后置处理器都注册完毕后,MergedBeanDefinitionPostProcessor 类型的后置处理器又被重复注册了一次?

1. BeanPostProcessorChecker 的作用

从类名上看,BeanPostProcessorChecker 是一个后置处理器的检查器,它本身也是一个后置处理器,它难道是用来检查后置处理器的?借助javadoc 我们可以获取一个很重要的信息:BeanPostProcessorChecker 的作用是检查 BeanPostProcessor 的初始化阶段中是否有 bean 对象的意外创建。
在这里插入图片描述
当某个 Bean 因在 BeanPostProcessor 实例化阶段被提前创建而不能被所有 BeanPostProcessor 处理时,该组件会记录一条日志信息。

注意,​“意外创建” 这个概念比较特殊,如果我们在编写一些 BeanPostProcessor 时内部依赖了其他普通 Bean,根据依赖注入的原则,这些被依赖的 bean 对象会在 BeanPostProcessor 的创建之前率先被创建,又因为当前 BeanPostProcessor 尚未初始化完毕,这些普通 Bean 还没有来得及被 BeanPostProcessor 处理,导致这些 bean 对象有可能 “残缺不全”​。 BeanPostProcessorChecker 的作用就是提醒开发者注意该问题。

如果读者在使用 Spring Framework 或 Spring Boot 开发应用时遇到这种日志:xxxxxx is not eligible for getting processed by all BeanPostProcessors (for example:not eligible for auto-proxying),这就意味着某个 BeanPostProcessor 中注入了普通 Bean,需要调整代码。

从源码角度分析,结合代码清单 3-49 可以看出,PostProcessorRegistrationDelegate 的 registerBeanPostProcessors 方法会将所有的BeanPostProcessor 全部取出,随后计算出所有的 BeanPostProcessor 的数量是beanFactory.getBeanPostProcessorCount()+1+postProcessorNames.length,即所有的后置处理器数量 +1。
另外,BeanPostProcessorChecker 的 postProcessAfterInitialization 方法会检查当前 BeanFactory 中的后置处理器数量是否少于一开始计算的预计后置处理器数量。如果数量出现偏差,就代表有普通 bean 对象被提前创建。

代码清单 3-49 计算普通 Bean 被提前创建的检查
在这里插入图片描述

2. PriorityOrdered 类型的后置处理器被提前创建

如果读者仔细观察代码清单 3-48 中处理 PriorityOrdered 类型的后置处理器这一环节,
在这里插入图片描述
就可以发现这些后置处理器在筛选的过程中就已经创建出对象,这是为何?本小节暂时不解释原因,而是先对这段源码进行两次变式,通过研究变式的运行效果和差异来反推 Spring Framework 源码的设计意图。

(1)变式一:在类型检查的过程中全部初始化
如果认为先获取后置处理器的全限定名再创建对象是多此一举,那么是否可以将其优化为每次循环时直接创建对象?按照这个思路可以得到代码清单 3-50 的逻辑,这样所有后置处理器的名称都循环完毕后,相应的后置处理器对象也都创建完毕。这样做是否会有问题?

代码清单 3-50 在循环获取后置处理器的过程中直接创建对象
在这里插入图片描述
为了推演代码清单 3-50 的风险,下面引入一个 “保安执勤” 的虚拟场景(情节略显 “暴力”​,读者请勿将自身代入其中)来说明。

假设一个公司的后勤部有三位保安 A、B、C,对应 IOC 容器中的 3 个后置处理器。
根据公司的安排,三名保安有各自的工作职责:保安 A 负责在门岗处询问到访来客,保安 B 负责核实访客身份,保安 C 负责根据身份核实结果放行或驱逐。
保安 A 对应实现了 PriorityOrdered 接口的后置处理器,保安 B 和保安 C 则对应实现了 Ordered 接口的后置处理器。
按照规矩,保安 A 的工作最先执行,其次才轮到保安 B 和保安 C,即 PriorityOrdered 接口的处理时机比 Ordered 接口早。

保安 A 的询客行为对应到代码中是普通 bean 对象被 BeanPostProcessor 处理时执行的 postProcessBeforeInitialization 方法回调,
保安 C 的放行和驱逐行为对应代码中 bean 对象初始化阶段 BeanPostProcessor 的 postProcessAfterInitialization 方法回调。
所以我们虚拟的场景有一套对应到程序中的完整逻辑:每个访客是一个普通 bean 对象,bean 对象在初始化阶段会先后经过 3 个后置处理器的拦截和处理(询客、核实、放行)​。

正常情况下,保安 A 都会在来客到访时率先出门问询,问询完毕后告知保安 B 和 C。但某一天保安 A 因故迟到,只有保安 B 和 C 两人在岗,故所有访客没有经过任何问询就被保安 B 带走核实身份。恰巧这天上班之前保安 B 与 C 二人饮酒甚欢,两人在岗期间有些许神志不清,二人稀里糊涂工作时刚好碰见迟到的保安 A 匆匆赶来。由于保安 B 此时尚未醒酒,他错将保安 A 当作访客并核验其身份,通知保安 C 将其驱逐,引发工作意外。

故事演绎至此,请读者回顾其中的不妥之处:在这样的意外场景中,保安 B 和 C 处在职责关系的较前位置,也就相当于处在BeanPostProcessor 处理链条的前置位,导致他们率先执行工作任务(这两个实现 Ordered 接口的后置处理器率先创建对象并完成注册)​。
在这种情况下如果再有一个实现了更高优先级的 PriorityOrdered 接口的后置处理器被读取和注册,那么工作顺序就会发生错误。
总结而言,由于优先级相对低的后置处理器率先注册到 IOC 容器,待优先级高的后置处理器进行注册时,会受到优先级相对低的后置处理器的影响,因此程序可能出现意外情况,产生错误。

由此我们可以得出结论:如果 BeanPostProcessor 的初始化逻辑是边循环边创建对象,则可能会导致优先级相对低的 BeanPostProcessor 干预优先级高的 BeanPostProcessor,从而引发程序的运行异常。正确的做法是首先仅创建高优先级的 BeanPostProcessor 对象,其余的后置处理器只记录全限定名,待所有高优先级的 BeanPostProcessor 创建和初始化完成后,再依次创建较低优先级的 BeanPostProcessor 对象(优先级低的 BeanPostProcessor 可能也需要被优先级高的 BeanPostProcessor 处理)​。

(2)变式二:在类型检查的过程中全部不初始化
通过变式一的演绎推理,我们发现在类型检查的过程中同时初始化是行不通的,那么待全部类型检查完毕后再逐个进行类型初始化是否也会出现问题?据此思路可以编写代码清单 3-51 所示的逻辑。不难看出,这样做并不会对实际的运行结果有任何影响,最终效果与原始代码大致相同。区别仅在于代码清单 3-51 中多创建了一个 List 集合 priorityOrderedPost ProcessorNames,这样做并没有降低时间复杂度,反而增加了一轮遍历动作。
所以 Spring Framework 在设计时对实现了 PriorityOrdered 接口的 BeanPostProcessor 提前创建好对象,这样不仅节省了内存(降低空间复杂度)​,还避免了一轮多余的循环动作(降低时间复杂度)​。

List internalPostProcessors =newArrayList<>(); 
List priorityOrderedPostProcessorNames =newArrayList<>(); 
List orderedPostProcessorNames =newArrayList<>(); 
List nonOrderedPostProcessorNames =newArrayList<>();
for(String ppName : postProcessorNames) { 
// 只循环,不初始化
if(beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    priorityOrderedPostProcessorNames.add(ppName); 
}else if(beanFactory.isTypeMatch(ppName, Ordered.class)) {
    orderedPostProcessorNames.add(ppName); 
}else{ 
    nonOrderedPostProcessorNames.add(ppName); 
} }   
// 此处再依次初始化实现了PriorityOrdered接口的BeanPostProcessor ...... 
// 初始化实现了Ordered接口的BeanPostProcessor ...... 
// ......

(3)PriorityOrdered 接口的实现类

既然 Spring Framework 特别注重 PriorityOrdered 接口的实现类,那么这就意味着 PriorityOrdered 接口是重要且关键的。
借助 IDE 不难发现一个事实,实现了 PriorityOrdered 接口的后置处理器都会在后续的 Bean 初始化过程中大有作为。

  • AutowiredAnnotationBeanPostProcessor—处理 @Autowired。
  • CommonAnnotationBeanPostProcessor—处理 JSR-250 规范的注解。

很明显,如果上述两个后置处理器没有率先初始化完毕,那么后续创建的 bean 对象有可能会丧失组件自动注入、注解式生命周期回调等多种特性,所以实现 PriorityOrdered 接口的组件通常都是 Spring Framework 内置的核心组件,为了保证功能正常运转,就必须要让这些核心组件都率先创建完毕并准备就绪。

3. MergedBeanDefinitionPostProcessor 的重复注册

在代码清单 3-48 的 registerBeanPostProcessors 最后阶段有一个很奇怪的动作,该动作将所有实现了 MergedBeanDefinitionPostProcessor 接口的后置处理器重复注册了一次。这些后置处理器在最初分类注册时就已经注册过了,为什么此处又要重复注册?重复注册后 IOC 容器是否会存放两组相同的后置处理器对象?
在这里插入图片描述
显然不会。结合源码可以发现,注册 BeanPostProcessor 的动作是一个移除再添加的过程,如代码清单 3-52 所示。当执行完重复注册动作后,这些 MergedBeanDefinitionPostProcessor 类型的后置处理器会重新放置到后置处理器列表的末尾。

代码清单 3-52 addBeanPostProcessor
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
请读者回顾 MergedBeanDefinitionPostProcessor 的应用场景:AutowiredAnnotationBeanPostProcessor 作为其实现类,会根据合并后的BeanDefinition 给 bean 对象进行依赖注入,这就需要在普通的 BeanPostProcessor 都处理完毕后,由 MergedBeanDefinitionPostProcessor 收尾。这个策略是 Spring Framework 内置的既定策略,在后续 Bean 的初始化环节中仍会用到。

3.3.2 finishBeanFactoryInitialization

后置处理器注册完毕后,接下来大规模创建 bean 对象,即 IOC 容器刷新的第 11 步 —— finishBeanFactoryInitialization。
在这里插入图片描述
该方法会将IOC容器中注册的非延迟加载的单实例 Bean一一创建。下面我们以 Cat 为例跟踪其实例化过程。

1. preInstantiateSingletons

在整个 finishBeanFactoryInitialization 方法中,创建初始化非延迟加载的单实例 Bean 的动作在最后一行,即调用 BeanFactory 的preInstantiateSingletons 方法,如代码清单 3-53 所示。

代码清单 3-53 preInstantiateSingletons
在这里插入图片描述
在这里插入图片描述
该方法先取出 BeanFactory 中所有的 BeanDefinition 名称,进行 BeanDefinition 的合并后再执行 getBean 方法。
getBean 方法即我们在最初学习 Spring Framework IOC 思想中依赖查找使用的方法,由此也可以得到一个新的小知识点:每次执行 getBean 方法时都有可能创建新的 bean 对象。

下面继续向内部深入,我们将多次见到 “××× 方法调用 do××× 方法” 的模板,请读者区分二者的职责,抓住其中的重点逻辑。

2. getBean → doGetBean

getBean 方法会直接转调 doGetBean,如代码清单 3-54 所示,而 doGetBean 的源码篇幅非常长,本小节先讲解 doGetBean 方法的核心逻辑片段,再讲解其中的重要子逻辑。完整的 doGetBean 方法讲解可参照《Spring Boot源码解读与原理分析》的7.11.2节。

代码清单 3-54 getBean→doGetBean
在这里插入图片描述
(1)循环依赖的探测
首先 doGetBean 方法根据 Bean 的名称探测对应的 bean 对象是否已经被创建,如代码清单 3-55 所示。

代码清单 3-55 doGetBean 片段(1)
在这里插入图片描述
如果 bean 对象已经存在,则它可能是普通 Bean 或者 FactoryBean。if 结构中的 getObjectForBeanInstance 方法负责类型判断,并在判定为FactoryBean 后调用其 getObject 方法获取真正创建的 bean 对象。
在这里插入图片描述
该环节本来用于处理循环依赖:当发生 A→ B→C→A 这类循环依赖场景时,第二次获取 A 对象时不能重复创建,从而避免单实例 Bean 重复创建。
如果没有取到 sharedInstance 对象的值,即当前 Bean 尚未创建,则进入 else 分支继续探测原型 Bean 的循环依赖。如果当前 Bean 是一个原型 Bean,并且在当前创建过程中发现其已经创建过,则此处也会抛出循环依赖的异常,否则 IOC 容器会不断创建新的原型 bean 对象,导致内存溢出、程序崩溃。

有关循环依赖机制和解决方案的详细讲解,可以参照《Spring Boot源码解读与原理分析》7.15节,本书不赘述。

(2)合并 BeanDefinition

IOC 容器创建 bean 对象的依据通常是 BeanDefinition,而第 2 章讲解后置处理器时已经提到过,BeanDefinition 的合并是形成最终BeanDefinition 不可或缺的环节。在 doGetBean 方法的中间try块中,首先执行的就是 BeanDefinition 的合并动作,如代码清单 3-56 所示。

代码清单 3-56 doGetBean 片段(2)
在这里插入图片描述
进入 getMergedLocalBeanDefinition 方法中可以发现,经历一系列 getMergedBeanDefinition 的重载方法的调用后,最终实际执行的是 3 个参数的 getMergedBeanDefinition,如代码清单 3-57 所示。其篇幅较长,代码清单 3-57 有所精简,读者结合源码中的注释通读主干逻辑即可。
如果对其中的细节感兴趣,读者可自行深入学习。整个方法执行的核心是自下而上递归寻找当前Bean的父BeanDefinition,再从上到下合并获得最终的 BeanDefinition。

代码清单 3-57 getMergedLocalBeanDefinition
在这里插入图片描述
(3)createBean 的调用区别
接下来是重要片段:当 IOC 容器中确实没有创建和保存当前单实例 Bean,或者当前正在获取的 Bean 是一个原型 Bean(每次获取创建一个新的 Bean)时,doGetBean会真正执行 bean 对象创建动作,即 doGetBean→ createBean,如代码清单 3-58 所示。

代码清单 3-58 doGetBean片段(3)
在这里插入图片描述
对比单实例 Bean 和原型 Bean 的创建逻辑可知,区别是单实例 Bean 创建时执行 createBean 方法的位置是一个 Lambda 表达式,而原型 Bean的创建逻辑中则直接调用 createBean 方法,没有嵌套。

为什么要这样做?借助 IDE 可以发现,在单实例 Bean 创建的代码中,Lambda 表达式的本质是一个 ObjectFactory 接口的匿名内部类,
在这里插入图片描述
在这里插入图片描述
并且 getSingleton 方法的内部还涉及一些缓存动作,
在这里插入图片描述
因此不难得到答案:单实例 Bean 在创建后需要缓存到 IOC 容器,后续直接从 IOC 容器中获取;而原型 Bean 不需要缓存,每次均重新创建 bean 对象。

(4)getSingleton

既然 getSingleton 方法中包含一些缓存机制,下面我们就进入 getSingleton 方法内部继续探究,如代码清单 3-59 所示。

代码清单 3-59 getSingleton
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不难看出,getSingleton 设计的缓存结合 singleton Objects 实现,利用双检锁机制实现 “懒汉” 单例模型。
在双检锁都通过后调用 ObjectFactory 的 getObject 方法,也就是代码清单 3-58 中 Lambda 表达式内调用的 createBean 方法。
在这里插入图片描述
在这里插入图片描述
简单小结:只要是获取的单实例 Bean 在 IOC 容器中尚未创建,或者需要创建一个原型 Bean,doGetBean 方法就会在安全检查后合并BeanDefinition,并最终调用 createBean 方法。

3. createBean

一旦 createBean 方法被触发,就意味着 doGetBean 方法没有实际获取 bean 对象,此时 createBean 方法就要负责真正的 bean 对象创建工作,即创建一个全新的 bean 对象。createBean 方法的底层源码并不复杂,抽取核心逻辑后的源码如代码清单 3-60 所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通读 createBean 方法的主干逻辑,不难提取出两个实际创建 bean 对象的切入点:

(1)通过 resolveBeforeInstantiation 方法创建 bean 对象;
(2)通过 doCreateBean 方法创建 bean 对象。

其中更为显眼的方法是 doCreateBean,而 resolveBeforeInstantiation 方法同样值得关注,我们先来剖析 resolveBeforeInstantiation 方法的实现。

从字面意思看,resolveBeforeInstantiation 方法可以理解为 “实例化之前的处理”​,由此可知:
doCreateBean 的确是创建 bean 对象的核心逻辑,而 resolveBeforeInstantiation 只是核心逻辑之前的拦截。

代码清单 3-61 resolveBeforeInstantiation
在这里插入图片描述
在这里插入图片描述
通过代码清单 3-61 不难了解,拦截 bean 对象的创建行为是通过 InstantiationAwareBeanPostProcessor 实现的:
如果 IOC 容器中包含这类后置处理器,则会执行拦截动作,尝试创建 bean 对象;否则直接跳过。
正如代码清单 3-60 注释所言,这只是 “给一个机会”​,因为 resolveBeforeInstantiation 方法不一定能创建 bean 对象,
但若真的创建了 bean 对象,则无须再执行 doCreateBean 方法。

注意观察拦截 bean 对象创建动作的过程,一共有两个回调后置处理器的方法,分别是使用 InstantiationAwareBeanPostProcessor 的postProcessBeforeInstantiation 方法创建对象,以及使用 BeanPostProcessor 的 postProcessAfterInitialization 方法增强对象。
可能读者会产生疑惑:为什么通过 postProcessBeforeInstantiation 方法创建出对象后,还需要回调 BeanPostProcessor 的postProcessAfterInitialization 进行后置处理?这个问题要结合 BeanPostProcessor 的经典应用 AOP 来解释:
BeanPostProcessor 的 postProcessAfterInitialization 方法可以用于生成代理对象,
如果一个 bean 对象被 InstantiationAwareBeanPostProcessor 提前创建,而后还需要被 AOP 增强,这时就必须回调 BeanPostProcessor 的postProcessAfterInitialization 方法,触发 AutoProxyCreator 的逻辑,进而生成代理对象。

回调后置处理器的方法在底层源码上基本一致,都是获取到 IOC 容器中注册的所有后置处理器并一一回调,代码清单 3-62 展示了所有回调InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation 方法的实现,逻辑简单,不再赘述。

代码清单 3-62 回调 postProcessBeforeInstantiation 方法
在这里插入图片描述

4. doCreateBean

如果 InstantiationAwareBeanPostProcessor 未能创建 bean 对象,就需要通过 doCreateBean 方法创建 bean 对象实例。doCreateBean 方法可概括为三大步骤:

(1)实例化 bean 对象(此时 bean 对象中所有属性均为空)​;
(2)属性赋值和依赖注入;
(3)bean 对象的初始化(执行完该步骤后 bean 已经完整)​。

doCreateBean 方法在《Spring Boot源码解读与原理分析》7.11.4节中已经详尽剖析,本节仅关注与bean对象创建有关的源码,如代码清单 3-63 所示

代码清单 3-63 doCreateBean 节选(1)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码清单 3-63 中的核心步骤是 createBeanInstance,它会创建真正的 bean 对象。不过,在此之前还有一个小细节:this.factoryBeanInstanceCache.remove(beanName)。它的作用我们可以简单了解一下。

factoryBeanInstanceCache 的作用。借助 IDEA 观察 factoryBeanInstanceCache 的使用位置,唯一可以找到的 put 方法调用是在AbstractAutowireCapableBeanFactory 的 getSingletonFactoryBeanForTypeCheck 方法的末尾部分,如代码清单 3-64 所示。

代码清单 3-64 factoryBeanInstanceCache 的 put 动作触发
在这里插入图片描述
暂且不讨论这部分逻辑的意义,借助 IDEA 继续向上寻找 getSingletonFactoryBeanForTypeCheck 方法的调用,进入 getTypeForFactoryBean 方法。
在这里插入图片描述
同样地,我们不需要深挖这个方法的具体用途,仅看方法名可知,getTypeForFactoryBean 方法可以获取到 FactoryBean 中生成的 bean 对象的类型。
可能读者会产生疑问:我们在编写 FactoryBean 时通常会使用泛型标注当前 FactoryBean 最终创建bean对象的类型,按这个道理此处可以通过读取泛型得到同样的结果,Spring Framework 为什么没有这么做?是因为这种方法无效吗?

答案是肯定的,通过读取泛型得到的不一定全都是落地实现类,也有可能是抽象类或接口。如果此处 Spring Framework 使用泛型读取得到一个抽象类或接口,就没办法创建具体的 bean 对象。由此也就可以明白:FactoryBean 中设计了一个专门获取 Bean 类型的方法 getObjectType,该方法可以编程式地获取最终 Bean 的类型。

但要想调用 FactoryBean 的 getObjectType 方法,就需要先把 FactoryBean 创建出来,这样就会产生新的问题:一旦 FactoryBean 被预先创建出来,而且它本身还是单实例 Bean,那么原则上就不能再触发 FactoryBean 对象的创建动作。
基于这个原则,Spring Framework 给出的解决方案是:如果提前调用了 getTypeForFactoryBean 确认 FactoryBean 中生成 bean 对象的类型,则 IOC 容器会将 FactoryBean 缓存到 factoryBeanInstanceCache 中(此时的 FactoryBean 只会利用 getObjectType 方法,不会调用 getObject创建 bean 对象)​;待后续需要由 FactoryBean 生成 bean 对象时,IOC 容器会直接从 factoryBeanInstanceCache 中取出已经实例化的FactoryBean,继续执行 bean 对象的初始化动作(属性赋值、依赖注入等)​。

5. createBeanInstance

经过前面的动作后,通常情况下第一次执行 doCreateBean 时必然会进入 createBeanInstance 方法。这个方法的复杂度很高,下面分段讲解其逻辑。

(1)解析 Bean 类型

createBeanInstance 方法的第一步是获取当前创建 Bean 的类型并校验,如果获取到的类型无法被创建(无法被 Spring Framework 访问)​,就认定这个 Bean 的类型声明是错误的,进而抛出异常,如代码清单 3-65 所示。

代码清单 3-65 createBeanInstance(1)
在这里插入图片描述
在这里插入图片描述
(2)工厂方法创建
通过类型校验后,下面有两个创建 bean 对象的动作,本质上这两段源码完成的工作是相同的。下面分别简单介绍这两种创建 bean 对象的方式,如代码清单 3-66 所示。

代码清单 3-66 createBeanInstance(2)
在这里插入图片描述
InstanceSupplier 的设计来自 AbstractBeanDefinition,是 Spring Framework 5.0 之后出现的新 API。
自 JDK 8 Java 语言支持函数式编程后,支撑函数式编程的核心接口之一是 Supplier,它是一个生产型接口。
与工厂的思路类似,Supplier 也可以构造 bean 对象,所以我们可以在 BeanDefinition 中引入该设计作为 factory-method 的替代方案,
即在构造 BeanDefinition 时可以传入一个 InstanceSupplier,以自定义 bean 对象创建逻辑,从而代替 BeanDefinition 创建 bean 对象的过程。这种设计在普通的业务场景开发中几乎不会用到,可能的应用场景包括使用 BeanDefinitionRegistryPostProcessor 或ImportBeanDefinitionRegistrar 等。读者仅对其有基本印象即可,无须掌握。

相较而言,我们接触比较多的是 factory-Method。
除了 XML 配置文件中定义的 factory-method,注解配置类中被 @Bean 标注的方法本质上也是工厂方法。
结合代码清单 3-66 可以发现,源码使用 instantiateUsingFactoryMethod 方法来触发 factory-Method。
在这里插入图片描述
由于这个方法的篇幅过长,阅读难度很大,因此这里仅简单概括其实现脉络(感兴趣的读者可以深入源码自行研究)​:instantiateUsingFactoryMethod 方法根据工厂名称找到对应的静态工厂 / 实例工厂 / 注解配置类对象(如果是注解配置类,则还需要解析 @Bean 方法上的参数列表,注入对应的依赖)​,随后反射执行工厂方法以生成 bean 对象。

(3)针对原型 Bean 的小优化

接下来的部分是一个小优化。我们可以观察代码清单 3-67,

代码清单 3-67 createBeanInstance(3)
在这里插入图片描述
其后半部分的 if(resolved) 分支一旦成立,会立即执行注入 / 实例化 bean 对象的动作(尽管下面还有一些代码没有执行)​。
这个部分的设计缘由从何而来?请读者简单思考一个问题:原型 Bean 创建过程中每次的执行流程都一致,那么这些准备动作是否可以缓存?
很明显是可以的,当原型 Bean 的创建过程被触发一次后,解析好的构造器和工厂方法等数据都可以缓存起来,
以备下次创建相同原型 Bean 时直接使用。如果读者对缓存的内容感兴趣,可以结合源码深入。

(4)实例化 bean 对象

createBeanInstance 方法的最后阶段必须完成 bean 对象的创建,如代码清单 3-68 所示:

代码清单 3-68 createBeanInstance(4)
在这里插入图片描述
首先解析实例化 bean 对象所需要的构造方法,如果 Bean 对应的类中有显式声明非空参数的构造方法,那么依次解析构造方法参数列表并尝试进行注入,最后反射创建 bean 对象;
如果没有声明,则会调用最后一行的 instantiateBean 方法,使用默认空参构造方法反射创建 bean 对象。

如果创建 bean 对象的构造方法可以被获取,那么使用构造方法注入的 autowireConstructor 方法就可以创建 bean 对象;
而如果当前 Bean 的类中只有默认的无参构造方法,则 instantiateBean 会尝试将其取出并创建对象,如代码清单 3-69 所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
创建 bean 对象的具体动作由策略类 InstantiationStrategy 的默认实现类 SimpleInstantiationStrategy 来触发,它的 instantiate 方法会获取 Bean所属类的默认无参构造方法,最后借用工具类 BeanUtils 调用 instantiateClass 方法创建 bean 对象并返回。

当 doCreateBean 方法执行完毕后,此时的 bean 对象是一个 “光秃秃” 的没有属性值和组件对象依赖的空对象。至此,一个 Bean 的实例化阶段结束。

3.3.3 实例化阶段总结

Spring 将 Bean 的完整生命周期拆分为四大步骤:
(1)bean 对象的实例化;
(2)属性赋值和依赖注入;
(3)bean 对象的初始化回调;
(4)bean 对象的销毁(见 3.5 节)​。

在所有非延迟加载的单实例 bean 对象初始化之前,容器会先初始化所有的 BeanPostProcessor;
后置处理器初始化完毕后,在 ApplicationContext 的 refresh 方法中,finishBeanFactoryInitialization 会初始化所有的非延迟加载的单实例Bean。
实例化 bean 对象的入口是 getBean→doGetBean,该阶段会合并 BeanDefinition,并根据 Bean 的 scope 选择实例化 bean 对象的策略。

具体创建由 createBean 方法统筹:先执行所有 InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation 方法,尝试创建 bean 对象,
如果成功则会直接调用 postProcessAfterInitialization 方法初始化 bean 对象后返回;
如果没有成功,则会调用 doCreateBean 方法创建 bean 对象。
doCreateBean 方法先根据 Bean 的 Class 中的构造方法定义决定如何实例化 bean 对象;
如果没有定义构造方法,则使用无参构造方法反射创建 bean 对象。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/Rockandrollman/article/details/168029858

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--