资讯动态

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

发布时间:2026/10/17 6:53:50 来源:尧图企业网站定制
《Spring Boot 3高级与架构设计》第 3 章 Bean 的全生命周期原理 Bean 的实例化 阅读笔记 103.3 Bean 的实例化当 BeanDefinition 全部注册到 BeanDefinitionRegistry 后下一个 Bean 生命周期的阶段是 Bean 的实例化即将 BeanDefinition 的元信息转化为真实存在的 bean 对象。本节聚焦于 BeanPostProcessor 的注册以及 bean 对象被实例化的几种方式和动作。Bean 的实例化动作主要分布在 refresh 方法的第 6 步与第 11 步即代码清单 3-47 中标注的两个环节。可能读者不太理解为什么 Bean 的实例化涉及 BeanPostProcessor 的注册下面先来回答这个问题。代码清单 3-47 refresh 方法3.3.1 注册 BeanPostProcessorBeanPostProcessor 的注册仍然由 PostProcessorRegistrationDelegate 的 registerBeanPostProcessors 方法完成生效的是一个 Delegate 类中的同名方法如代码清单 3-48 所示。代码清单 3-48 registerBeanPostProcessorsregisterBeanPostProcessors 方法的源码篇幅很长但总体逻辑脉络不难梳理类似于注册与执行 BeanFactoryPostProcessor注册BeanPostProcessor 时首先注册实现了 PriorityOrdered 接口的后置处理器其次注册实现了 Ordered 的后置处理器最后注册普通的后置处理器。此外registerBeanPostProcessors 方法中还有一些对于初读源码者而言比较难理解的问题。1为什么在注册 BeanPostProcessor 之前要先注册一个 BeanPostProcessorChecker2为什么实现 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()1postProcessorNames.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 提前创建好对象这样不仅节省了内存降低空间复杂度​还避免了一轮多余的循环动作降低时间复杂度​。ListinternalPostProcessorsnewArrayList();ListpriorityOrderedPostProcessorNamesnewArrayList();ListorderedPostProcessorNamesnewArrayList();ListnonOrderedPostProcessorNamesnewArrayList();for(StringppName:postProcessorNames){// 只循环不初始化if(beanFactory.isTypeMatch(ppName,PriorityOrdered.class)){priorityOrderedPostProcessorNames.add(ppName);}elseif(beanFactory.isTypeMatch(ppName,Ordered.class)){orderedPostProcessorNames.add(ppName);}else{nonOrderedPostProcessorNames.add(ppName);}}// 此处再依次初始化实现了PriorityOrdered接口的BeanPostProcessor ......// 初始化实现了Ordered接口的BeanPostProcessor ......// ......3PriorityOrdered 接口的实现类既然 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 → doGetBeangetBean 方法会直接转调 doGetBean如代码清单 3-54 所示而 doGetBean 的源码篇幅非常长本小节先讲解 doGetBean 方法的核心逻辑片段再讲解其中的重要子逻辑。完整的 doGetBean 方法讲解可参照《Spring Boot源码解读与原理分析》的7.11.2节。代码清单 3-54 getBean→doGetBean1循环依赖的探测首先 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合并 BeanDefinitionIOC 容器创建 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 getMergedLocalBeanDefinition3createBean 的调用区别接下来是重要片段当 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 对象。4getSingleton既然 getSingleton 方法中包含一些缓存机制下面我们就进入 getSingleton 方法内部继续探究如代码清单 3-59 所示。代码清单 3-59 getSingleton不难看出getSingleton 设计的缓存结合 singleton Objects 实现利用双检锁机制实现 “懒汉” 单例模型。在双检锁都通过后调用 ObjectFactory 的 getObject 方法也就是代码清单 3-58 中 Lambda 表达式内调用的 createBean 方法。简单小结只要是获取的单实例 Bean 在 IOC 容器中尚未创建或者需要创建一个原型 BeandoGetBean 方法就会在安全检查后合并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属性赋值和依赖注入3bean 对象的初始化执行完该步骤后 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 createBeanInstance12工厂方法创建通过类型校验后下面有两个创建 bean 对象的动作本质上这两段源码完成的工作是相同的。下面分别简单介绍这两种创建 bean 对象的方式如代码清单 3-66 所示。代码清单 3-66 createBeanInstance2InstanceSupplier 的设计来自 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 createBeanInstance3其后半部分的 if(resolved) 分支一旦成立会立即执行注入 / 实例化 bean 对象的动作尽管下面还有一些代码没有执行​。这个部分的设计缘由从何而来请读者简单思考一个问题原型 Bean 创建过程中每次的执行流程都一致那么这些准备动作是否可以缓存很明显是可以的当原型 Bean 的创建过程被触发一次后解析好的构造器和工厂方法等数据都可以缓存起来以备下次创建相同原型 Bean 时直接使用。如果读者对缓存的内容感兴趣可以结合源码深入。4实例化 bean 对象createBeanInstance 方法的最后阶段必须完成 bean 对象的创建如代码清单 3-68 所示代码清单 3-68 createBeanInstance4首先解析实例化 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 的完整生命周期拆分为四大步骤1bean 对象的实例化2属性赋值和依赖注入3bean 对象的初始化回调4bean 对象的销毁见 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 对象。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑