【框架源码】Spring源码解析之Bean创建源码流程

news2024/11/25 19:31:16

在这里插入图片描述

问题:Spring中是如何初始化单例bean的?

我们都知道Spring解析xml文件描述成BeanDefinition,解析BeanDefinition最后创建Bean将Bean放入单例池中,那么Spring在创建Bean的这个过程都做了什么。

Spring核心方法refresh()中最最重要的一个方法 finishBeanFactoryInitialization() 方法,该方法负责初始化所有的单例bean。

finishBeanFactoryInitialization()方法位于refresh()中第11步。

在这里插入图片描述

走到这一步的时候,Spring容器中所有的BeanFactory都已经实例化完成了,也就是实现BeanFactoryPostProcessor接口的 Bean 都已经初始化完成了。剩下的就是初始化singleton beans,在我们的业务bean中大多数都是单例的,finishBeanFactoryInitialization这一步就是去实例化单例的并且没有设置懒加载的Bean。

Spring会在finishBeanFactoryInitialization这个方法里面初始化所有的singleton bean

Ok,我们先来看一下finishBeanFactoryInitialization方法内部的逻辑。这个方法的核心就在于完成BeanFactory的配置。该阶段完成了上下文的实例化,包含所有单例Bean对象已经实例化。

在这里插入图片描述

核心在于 preInstantiateSingletons() 方法,preInstantiateSingletons方法主要任务是进行初始化,在初始化前同样是一系列判断,如,是否是懒加载的,是否是一个factorybean(一个特别的bean,负责工厂创建的bean),最后调用getBean()方法。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

注释中提到的SmartInitializingSingleton接口,是让bean初始化后做一些操作。
在这里插入图片描述
在这里插入图片描述

OK,那么主要的方法还是getBean()方法,getBean()方法的作用就是加载、实例化Bean。方法内部调用了doGetBean(),我们直接看**doGetBean()**方法内部。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

代码很多,我们主要看createBean()方法。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

我们继续往doCreateBean 这个方法里面看。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

方法很多,我们主需要关注三个方法即可。

  • createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
  • populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
  • initializeBean:调用spring xml中的init 方法。

从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

什么是Spring的三级缓存?

Spring的IOC容器里面的三级缓存都是Map结构。

  • 一级缓存(成熟的bean)
    • singletonObjects 单例池 ,存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  • 二级缓存
    • earlySingletonObjects 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  • 三级缓存
    • singletonFactories 单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

在这里插入图片描述

OK,了解完三级缓存我们再来看下 getSingleton() 这个方法。这个方法主要是用于从单例池中获取指定名称的单例Bean实例,方法内部实现了三级缓存查找机制,通过三级查找的机制来获取指定名称的单例Bean实例对象,同时该方法会使用同步代码块保证多线程环境下的线程安全性。

在这里插入图片描述

OK,那么到这里我们会有一个疑问,Spring为什么要用三级缓存来解决循环依赖的问题。

首先我们要明确一点,Spring可以解决setter的依赖注入,但是不能解决构造器的依赖注入。

假如我们现在有个A对象B对象A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。

A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。

此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

在这里插入图片描述

知道了这个原理时候,肯定就知道为啥Spring不能解决"A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象"这类问题啦!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

好的,到现在整一个Bean的创建流程,就已经完成啦。我们在看一下以下三个方法的具体实现。

首先第一个就是 createBeanInstance() 方法
在这里插入图片描述在这里插入图片描述在这里插入图片描述

然后是 populateBean() 方法
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

最后是 initializeBean() 方法
在这里插入图片描述在这里插入图片描述

ok,到这里Spring的bean的创建过程就已经梳理完成啦。

记得点个赞+关注哦!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/647486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp小程序订单页面UI

前言 之前用模板写了订单页面,由于需求改了导致这个页面做更新麻烦,弄了一下午,索性全部删除了自己写了,上面的tabs用的是b-ui框架写的,其他的都是原生写法。 🙏如果这篇文章对你有帮助还请收藏点赞关注&…

【计算机视觉】不仅能分割一切简单物体,而且还能高精度分割一切复杂物体的SAM升级版本HQ-SAM来了

文章目录 一、SAM 导读二、SAM 的应用场景2.1 SAM-RBox-生成旋转矩形框2.2 Prompt-Segment-Anything-生成矩形框和掩2.3 Grounded-Segment-Anything-开放数据集检测与分割2.4 segment-anything-video-视频分割2.5 Open-vocabulary-Segment-Anything-开放词典分割2.6 SegDrawer-…

快速入门 Lua 编程

以下是一个简单的 Lua 快速编程教程,帮助你快速入门 Lua 编程。 安装 Lua 首先,你需要安装 Lua。你可以从 Lua 官网(https://www.lua.org/download.html)下载适合你的操作系统的安装包。安装完成后,你可以在命令行中…

CTFHub | 命令注入

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习,实训平台。提供优质的赛事及学习服务,拥有完善的题目环境及配套 writeup ,降低 CTF 学习入门门槛,快速帮助选手成长,跟随主流比赛潮流。 0x01 题目描述…

记录一次scala项目导入编译失败的问题

文章目录 Scala项目环境问题记录小结 Scala项目环境 Java8 scala-2.11.12 MacBook Apple m2芯片 问题记录 1、scala版本安装 我的本地环境: ➜ ~ java --version openjdk 11.0.18 2023-01-17 OpenJDK Runtime Environment Homebrew (build 11.0.180) OpenJDK 64-…

【Flutter】Flutter 如何实现开屏广告

文章目录 一、前言二、实现开屏广告页面三、实现广告数据的加载四、开屏广告的完整代码五、总结 一、前言 开屏广告这个功能在商业应用中非常常见,它可以在应用启动时向用户展示广告,增加应用的商业价值。 如果你想深入学习 Flutter,掌握更…

BUUCTF 传统知识+古典密码 1

题目描述: 小明某一天收到一封密信,信中写了几个不同的年份 辛卯,癸巳,丙戌,辛未,庚辰,癸酉,己卯,癸巳。 信的背面还写有“甲子”,请解出这段密文。 key值&a…

大学生实习周记总结

大学生实习周记总结1 经过两个月的实习,我收获了很多,也懂得了许多,同时也成熟了不少。下面我将把我两个月的实习生活分成五个部分进行总结:教学经验、班主任工作、做事态度、学生友谊、感恩的心。 教学经验:如何上好一…

使用大型语言模(LLM)构建系统(六):构建端到端系统

今天我学习了DeepLearning.AI的 Building Systems with LLM 的在线课程,我想和大家一起分享一下该门课程的一些主要内容。之前我们已经学习了下面这些知识: 使用大型语言模(LLM)构建系统(一):分类使用大型语言模(LLM)构建系统(二):内容审核、…

如何系统的学习python中的numpy,pandas,matplotlib?太有用了!!!

前言 随着大数据、云计算和人工智能的发展,越来越多的企业需要以数据为基础做出决策。数据分析是处理大量数据的过程,分析数据以识别趋势和模式,并从数据中提取有用的信息来支持业务决策。数据分析可以应用于各种不同的领域,如营…

性能测试如何做?性能测试-稳定性场景设计详细,晋升之路...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 我们谈到测试设计…

liunx服务器安装kafka

liunx服务器安装kafka 1. 初始化安装环境1.1 安装jdk1.1.1 找到对应的jdk版本1.1.2 下载并安装1.1.2.1 配置jdk环境变量 1.2 安装 zookeeper1.2.1 查找kafka对应zookeeper版本启动 2. 下载kafka 安装包 kafka 3.0.0 之前 (包括3.0.0版本)支持jdk 8 &…

浏览器唤醒本地应用

我们在使用一些应用的时候,需要通过本地浏览器来唤醒本地应用的需求。 就像我们以前使用QQ一样,在网页上点击了某个按钮,会唤醒本地安装的QQ应用。 这里来介绍一下怎么使用自定义协议来唤醒本地的Electron应用(其他框架写的应用同…

汽车电子Autosar之以太网SOME/IP(续)

前言 首先,请问大家几个小小问题,你清楚: 你知道什么是SOME/IP SD吗?SOME/IP-SD有何作用呢?SOME/IP-SD 包含哪些内容呢?SOME/IP-TP 为什么会存在? 今天,我们就来一起探索并回答这…

Mysql数据库表管理和用户管理与授权

一、表结构管理 1. 修改表名 ALTER TABLE 旧表名 RENAME 新表名 2.扩展表结构(增加字段) ALTER TABLE 表名 ADD 字段名 数据类型; 3.修改字段名,添加唯一健 change ALTER TABLE 表名 CHANGE 旧字段名 新字段名 [数据类型] [约束]; 4.删除字段…

Linux 多路转接 —— select

目录 传统艺能😎select😍fd_set 结构😒timeval 结构🤣 socket 就绪条件😁读条件🤣写就绪😍异常就绪😒 select 工作流程😘select 服务器实现😂socket 类&#…

极致呈现系列之:Echarts饼图的千变万化

目录 创建一个最简单的饼图美化饼图修改颜色修改饼图的边框线条样式修改饼图的标签样式添加饼图的阴影效果添加修改饼图的图例样式 添加交互饼图的变化环形图动画装饰仪表盘 创建一个最简单的饼图 这个没什么好说的,懂的都懂,直接上代码 //安装 Echart…

LlamaIndex 简介:LLM 应用程序的数据框架

LlamaIndex 是一个非凡的工具,创建为一个全面的“数据框架”,以促进 LLM(大型语言模型)应用程序的开发。该框架与 ChatGPT 集成,充当大型语言模型和用户私人数据之间的桥梁。 借助 LlamaIndex,用户可以轻松…

Apache Zeppelin系列教程第九篇——Zeppelin NoteBook数据缓存

背景 在使用Zeppelin JDBC Intercepter 对于Hive 数据进行查询过程中,如果遇到非常复杂的sql,查询效率是非常慢 比如: select dt,count(*) from table group by dt做过数据开发的同学都知道,在hive sql查询过程中,hive…

MySQL - 第1节 - MySQL数据库基础

1.数据库的概念 数据库是按照数据结构来组织、存储和管理数据的仓库,是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。 虽然单纯的使用文件也可以存储数据,但会存在如下缺点: • 安全性问题:数据误操…