从根儿上学习spring 十 之run方法启动第四段(4)
我们接着上一节已经准备开始分析AbstractAutowireCapableBeanFactory#doCreateBean方法,该方法是spring真正开始创建bean实例并初始化bean的入口方法,属于核心逻辑,所以我们新开一节开始分析。
图12

图12-530到536行
这几行的主要就是创建bean实例,也就是new或者反射创建一个对象出来。beanWrapper大家可以理解成刚创建的bean实例的存放容器,就像刚出生的人类宝宝需要放到一个专业容器中一样,后面用到我们再细说。这几行没什么逻辑,有些逻辑的就是535行的createBeanInstance(beanName, mbd, args)方法。我们来看下该方法的逻辑
图13-AbstractAutowireCapableBeanFactory#createBeanInstance方法
从该方法的注释可知,该方法可以使用合适的策略来创建bean实例,如工厂方法,构造器自动注入,和简单的实例化。
图13-1090到1093行
这几行的意思是如果该beanDefinition中提供了创建bean的Supplier接口,则调用该接口的get方法来创建bean对象。那么什么时候可以向beanDefinition中注册该接口呢?我们还没讲过ConfigurationClassPostProcessor类的逻辑,该类是扫描获取我们需要被spring管理的所有beanDefinition的入口,但是这个阶段spring并没有提供给我们扩展点去修改beanDefinition,似乎只有在我们前面讲beanFactoryProcessor接口的子接口BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法才可以通过registry参数获取到需要操作的beanDefiniction去注册Supplier。
1902行很简单就是调用Supplier接口get方法创建对象,不再多说。
图13-1095到1097行
1095行先判断beanDefinition的factoryMethodName属性是否为空,那么factoryMethodName属性是怎么来的呢?又是干嘛用的呢?
factoryMethodName是方法上加了@Bean注解的方法名。通过该方法spring会通过反射来调用该方法创建bean实例。instantiateUsingFactoryMethod(beanName, mbd, args);方法的具体逻辑就不带大家看了,就是通过beanDefiniton里的factoryBeanName(拥有factoryMethodName的bean名称)获取对应的bean实例,然后获取factoryMethodName方法参数,最终调用反射创建bean实例对象。
图14-AbstractAutowireCapableBeanFactory#createBeanInstance方法
图14-1100到1109行
这几行主要作用是针对prototype类型的bean才有作用,因为这几行是针对bean的多次创建场景,校验之前是否已经解析出需要用的构造器进行创建bean对象。这样就可以把解析出来的构造器缓存起来,不需要每次创建bean实例时都去解析一遍所要用的构造器。
通过1102行可以看出能走缓存是有前提的,就是参数args必须为空,这个args是getBean(String name, Object... args)方法的参数,也就是spring对用户传构造器参数的情况下每次都会走一遍构造器解析,而没有传构造器参数的情况下spring会根据构造器的参数类型自动解析并注入。这种情况下,下次创建时才会走构造器缓存。至于这么设计的原因,我猜测是因为走缓存的情况下spring也会把构造器的参数缓存起来,而用户动态传的参数是没法缓存的毕竟可能每次都不一样,所以针对这种情况才没走构造器缓存逻辑吧。
这里的resolved变量代表的就是能不能走构造器缓存逻辑,如果mbd.resolvedConstructorOrFactoryMethod不为空说明构造器之前已经解析过。这里的autowireNecessary表示是否可以走构造器的自动注入,如果mbd.constructorArgumentsResolved为true表示构造器的参数已经解析过了可以使用自动注入,否则就会调用1115行的instantiateBean(beanName, mbd)方法使用默认构造器创建bean实例。
图14-1120到1125行
这几行就是spring找合适的构造器并且自动为我们自动注入构造器参数,从if判断语句可知,至于能找到构造器且args不为空都会走自动注入逻辑
1125行--前面都不满足的情况下使用默认构造器创建bean对象
图12-537到541行
通过前面我们对createBeanInstance(beanName, mbd, args)方法spring已经为我们把bean的实例给创建出来了,537--541这几行逻辑很简单就是将bean的class类型设置到beanDefinition的resolvedTargetType属性中
图12-544到555行
这几行的核心就是调用了applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)方法,点进这个方法可知也就是对当前的beanDefinition执行所有的MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition方法。MergedBeanDefinitionPostProcessor接口作为一个扩展接口,允许我们在postProcessMergedBeanDefinition方法中对beanDefinition进行操作。
或许大家会疑惑,这时候bean的实例已经创建出来了还能对beanDefinition做什么操作呢?这就得结合bean的初始化生命周期来看,这时候的bean实例是创建出来了,相当于new出来一个完全新的对象,而对象的属性还没设值,所以这时候对beanDefinition的操作也最多是影响bean的属性初始化。spring内部提供的MergedBeanDefinitionPostProcessor也确实大多都是跟bean的属性设值有关,如果大家有需求在bean实例化后操作beanDefinition也可以实现MergedBeanDefinitionPostProcessor接口。
图15-AbstractAutowireCapableBeanFactory#doCreateBean方法
接下来我们继续看AbstractAutowireCapableBeanFactory#doCreateBean方法的剩余部分。
图15-559到583行
这几行比较核心,是用来解决spring的循环依赖的。相信大家对spring循环依赖并不陌生,我就不赘述了。只是希望大家看完全部文章之后自己可以看源码真正了解哪些情况下spring才会有循环依赖问题,并加以总结。
559到560行:主要是判断当前的bean实例是否允许提前暴露,所谓的提前暴露就是这个bean在没有完全初始化好之后被其它bean引用。大家一定要对实例化和初始化有个基本了解,实例化是创建对象,而初始化对对创建好的对象就行设置属性等一些其它的初始化动作。从这里的判断条件我们可知允许提前暴露必须满足3个条件。1.当前bean是单例 2.spring容器被配置为允许循环依赖 3.当前bean正处于创建中时。
第2点很好理解,如果容器都不支持循环依赖都没必要提前暴露,因为提前暴露的前提就是出现了循环依赖。
第3点如果大家还记得调用链路前面的DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)方法逻辑的话也很容易理解,这个方法会调用DefaultSingletonBeanRegistry#beforeSingletonCreation方法将beanNamt添加到singletonsCurrentlyInCreation容器,所以这里的第3点肯定是true。
第1点也不难理解,只有单例的bean才支持循环依赖,因为单例的bean一旦被引用就不会变,提前暴露出去才有意义。如果被引用的bean会变会生成不同的对象,那提前暴露出去后后面又生成新的对象,之前的对象不再被使用,那暴露出去就没有任何意义。
561到567行:在满足提前暴露的情况下,调用DefaultSingletonBeanRegistry#addSingletonFactory方法添加实例工厂,这个方法的核心逻辑就是向单例工厂map容器singletonFactories中以beanName作为key添加一个由lambda表达式构成的单例工厂singletonFactory,最终会调用AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,等后面调用时我们再说。
之所以说这里是为了解决循环依赖问题,我这里提前透露下,因为接下来要执行下面的populateBean(beanName, mbd, instanceWrapper)方法就是给我们的bean属性进行设值,这时候有循环依赖的话会再次走到当前bean的初始化过程(比如A依赖B,B依赖A。当前bean是A,在初始化属性B的时候又会尝试去获取A,所以会再次走到A的初始化过程)
为了故事的完整我们假设当前的beanName叫beanA吧,暂先不看下面的populateBean(beanName, mbd, instanceWrapper)且假设有循环依赖的情况,这时beanA又将被spring调用getBean(beanName)方法尝试获取beanA实例,这时再次走到AbstractBeanFactory#doGetBean调用getSingleton(beanName)方法获取单例对象时就可以从单例工厂singletonFactories中获取beanA的单例工厂并调用getObject方法获取实例,获取到的实例会放到earlySingletonObjects容器中以便后面有其它循环依赖时无需再次调用工厂获取beanA,而是直接通过earlySingletonObjects容器返回可以提前暴露的beanA实例。大家这时候一定要配合着源码理解。
上面已经说到spring的所谓三级缓存在循环依赖中使用场景,大概总结下就是,spring在没有循环依赖时bean的初始化过程完完整整且不会被打断的话,初始化好完整的bean实例会放进singletonObjects容器中(一级缓存);在实例化好对象初始化bean属性前为了防止有循环依赖,会提前向单例工厂容器(二级缓存)中添加单例工厂,以便在发生循环依赖时可以通过单例工厂提前暴露bean实例;在发生循环依赖使用单例工厂获取提前暴露的bean实例后,会将bean实例放入earlySingletonObjects容器中以便后面有其它循环依赖时直接从该容器中获取已经创建好的提前暴露对象。
上面我们已经了解了三级缓存的使用场景,那么大家想下为什么需要使用三级缓存,只保留一级缓存或者两级缓存不行吗?比如为了防止发生循环依赖,我们不向单例工厂容器中添加单例工厂而是直接向singletonObjects容器中添加未初始化好的bean,后面有了循环依赖也直接从singletonObjects容器里拿未初始化好的bean不就行了吗?
我说些自己的看法,首先如果只使用一个缓存,那么势必将出现这个缓存里既有提前暴露的bean实例也有初始化好的bean实例情况,从单一原则设计原则考虑这是不合适的。那单例工厂SingletonFactory缓存是否有可以省略呢?在出现循环依赖时直接从缓存中获取提前暴露的bean实例不就好了吗?毕竟在添加单例工厂后初始化bean前已经实例化好bean了,如果出现循环依赖把这个实例化好的bean暴露出去不就好了,为什么还要从单例工厂获取呢?这其实主要是为了防止这个bean存在动态代理的情况,如果存在动态代理那这个bean的引用就会更改,从普通的bean变成动态代理的bean,如果这时候简单的把刚实例化好的普通bean暴露出去那就会有问题。而通过单例工厂可以对这个bean进行检查和包装,如果这个bean被动态代理了就会暴露动态代理的bean。
相关文章:
从根儿上学习spring 十 之run方法启动第四段(4)
我们接着上一节已经准备开始分析AbstractAutowireCapableBeanFactory#doCreateBean方法,该方法是spring真正开始创建bean实例并初始化bean的入口方法,属于核心逻辑,所以我们新开一节开始分析。 图12 图12-530到536行 这几行的主要就是创建b…...
如果我的发明有修改,需要如何处理?
如果我的发明有修改,需要如何处理?...
java:File与MultipartFile互转
1 概述 当我们在处理文件上传的功能时,通常会使用MultipartFile对象来表示上传的文件数据。然而,有时候我们可能已经有了一个File对象,而不是MultipartFile对象,需要将File对象转换为MultipartFile对象进行进一步处理。 在Java中…...
高级java每日一道面试题-2024年8月04日-web篇-如果客户端禁止cookie能实现session还能用吗?
如果有遗漏,评论区告诉我进行补充 面试官: 如果客户端禁止cookie能实现session还能用吗? 我回答: 当客户端禁用了Cookie时,传统的基于Cookie的Session机制会受到影响,因为Session ID通常是通过Cookie在客户端和服务器之间传递的。然而,尽…...
leetcode 107.二叉树的层序遍||
1.题目要求: 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)2.此题步骤: 1.先创建好队列,出队和入队函数: //创建队列 typedef struct que…...
C++在网络安全领域的应用
前言: 在当今的数字化时代,网络安全已经成为一个至关重要的领域。随着网络威胁和攻击手段的不断演变,开发高效、安全的系统和工具变得尤为重要。C作为一种功能强大且高性能的编程语言,在网络安全领域发挥着不可替代的作用。本文简…...
Chapter 26 Python魔术方法
欢迎大家订阅【Python从入门到精通】专栏,一起探索Python的无限可能! 文章目录 前言一、什么是魔术方法?二、常见的魔术方法① __init__构造方法② __str__字符串方法③ __lt__比较方法④ __le__比较方法⑤ __eq__比较方法 前言 本章将详细讲…...
基于Transformer的语音识别与音频分类
重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…...
leetcode数论(1362. 最接近的因数)
前言 经过前期的基础训练以及部分实战练习,粗略掌握了各种题型的解题思路。现阶段开始专项练习。 数论包含最大公约数(>2个数)、最大公约数性质、最小公倍数、区间范围质因素计数(最下间隔)、质因素分解、判断质数、平方根、立方根、互质、同余等等。 描述 给…...
sqli-labs-master less1-less6
目录 通关前必看 1、判断是否存在sql注入以及是字符型还是数值型: 2、各种注入方式以及方法 有回显型: 报错注入(只有ok和no的提示以及报错提示): 详细思路,后面的题都可以这样去思考 关卡实操 less…...
力扣287【寻找重复数】
给定一个包含 n 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。 你设计的解决方案必须 不修改 数组 nums 且只用常…...
【2024蓝桥杯/C++/B组/传送阵】
题目 问题代码 #include<bits/stdc.h> using namespace std;const int N 1e610; int n; int porter[N]; int ans; int sign[N]; bool used;void dfs(int now, int cnt) {if(sign[now] && used){ans max(ans, cnt);return;}if(!sign[now]){cnt, sign[now] 1; …...
(四十一)大数据实战——spark的yarn模式生产环境部署
前言 Spark 是一个开源的分布式计算系统。它提供了高效的数据处理能力,支持复杂的数据分析和处理任务,是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。Spark Core:实现了Spark的基本功能,包含任务调度、内存管理、错误…...
【深度学习实战(53)】classification_report()
classification_report()是python在机器学习中常用的输出模型评估报告的方法。 classification_report()函数介绍 classification_report()语法如下:classification_report( y_true, y_pred, labelsNone, …...
计算机网络基础之网络套接字socket编程(初步认识UDP、TCP协议)
绪论 “宿命论是那些缺乏意志力的弱者的借口。 ——罗曼.罗兰”,本章是为应用层打基础,因为在写应用层时将直接通过文本和代码的形式来更加可视化的理解网络,本章主要写的是如何使用网络套接字和udp、tcp初步认识。 话不多说安…...
手撕Python!模块、包、库,傻傻分不清?一分钟带你弄明白!
哈喽,各位小伙伴们!今天咱们来聊聊Python中的模块、包和库,很多新手小白经常搞混,别担心,看完这篇,保证你一分钟就能搞定! 打个比方: 模块 (Module): 就好比是一块块乐高积木&#…...
Linux--序列化与反序列化
序列化 序列化是指将数据结构或对象状态转换成可以存储或传输的格式的过程。在序列化过程中,对象的状态信息被转换为可以保持或传输的格式(如二进制、XML、JSON等)。序列化后的数据可以被写入到文件、数据库、内存缓冲区中,或者通…...
使用C#和 aspose.total 实现替换pdf中的文字(外语:捷克语言的pdf),并生成新的pdf导出到指定路径
程序主入口: Program.cs using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks;namespace PdfEditor {public class Progra…...
【Material-UI】Autocomplete中的高亮功能(Highlights)详解
文章目录 一、简介二、实现高亮功能示例代码代码解释 三、实际应用场景1. 搜索功能2. 表单自动完成 四、总结 在现代Web开发中,提供清晰的用户反馈是提升用户体验的重要组成部分。Material-UI的Autocomplete组件通过高亮功能,帮助用户快速识别搜索结果中…...
Android 11(R)启动流程 初版
启动流程 bootloader会去启动android第一个进程Idle,pid为0,会对进程 内存管理等进行初始化。Idle还被称作swapper。Idle会去创建两个进程,一个是init,另外一个是kthread。 kthread会去启动内核,用户是由init进行启动。…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
