【Spring6源码・IOC】Bean的初始化 - 终结篇

news2024/11/19 6:28:41

前面两篇,我们着重讲解了一下《BeanDefinition的加载》和《bean的实例化》。

这一篇我们来讲解一下bean的初始化。

在这里插入图片描述

我们这里的案例依旧是以SpringBoot3.0、JDK17为前提,案例代码如下:

@Component
public class A {

    @Autowired
    private B b;
    
}
@Component
public class B {

    @Autowired
    private A a;
}

首先,先明确一下这个三级缓存:

一级缓存 singletonObjects 中存放完全初始化好的 bean 的实例。
二级缓存 earlySingletonObjects中存放早期对象(未完全初始化完成的 bean 实例)。
三级缓存 singletonFactories 中存放 bean 工厂对象。

bean 创建起来比较复杂,所以将bean缓存起来,方便之后使用。

上一篇,我们实例化了bean之后,将bean放入了第三级缓存,看一下这个addSingletonFactory方法,如果一级缓存中没有对应的bean,那么会将未初始化的bean放入三级缓存,会将bean提前暴露出来。
在这里插入图片描述

在这里插入图片描述
接下来,会通过populateBean()方法对bean的属性进行填充。
在这里插入图片描述
步入populateBean()方法,首先会获取BeanPostProcessors,主要是在bean初始化之前做一些事情,如下,我们总共获取到4个BeanPostProcessors ,最后一个AutowiredAnnotationBeanPostProcessor是本节较为关键的BeanPostProcessor,它主要是用来处理我们@Autowired注解。

在这里插入图片描述
我们进入到AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,首先有个findAutowiringMetadata方法来处理@Autowired

在这里插入图片描述
我们步入findAutowiringMetadata方法中,发现会从injectionMetadataCache的map集合中获取。

在这里插入图片描述

但是我们没有往这个缓存中put对象啊,所以,这个metadata是null,而后,我们会简单的判断一下:

public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class<?> clazz) {
		return (metadata == null || metadata.needsRefresh(clazz));
	}

如果为null,我们会调用buildAutowiringMetadata方法,在这里我们会看到很熟悉的设计,就像单例模式一样的双重锁/双重校验锁(DLC,即double-checked locking)

而后会进入buildAutowiringMetadata方法去创建相关信息。

这里主要有两点,一个是针对属性上的注解,一个是针对方法上的注解,大部分的时候会放在属性上,也有时候会放在方法上,比如set方法上。
在这里插入图片描述
我们来一起看一下这个处理属性的doWithLocalFields方法,首先通过getDeclaredFields方法获取类的属性集合,然后进行遍历调用函数去处理。

在这里插入图片描述
我们来看一下是如何获取类的属性数组的,还用想嘛,当然是反射了,不信你看。第一次从属性缓存中获取,肯定获取不到啊,然后反射获取,在加入缓存中。

在这里插入图片描述
然后包装成AutowiredFieldElement对象,add到currElements列表中,最后在把各个bean的所有元素add到elements列表中。

在这里插入图片描述
最后会通过InjectionMetadata#forElements方法将所有的元素封装成InjectionMetadata对象返回。
在这里插入图片描述
最后把注入元素的信息放进injectionMetadataCache的map集合中,以便下次就可以从缓存中获取。

到这里,我们就解析完了带有@Autowired注解的属性,接下来进入我们的核心方法inject。
在这里插入图片描述

在这里首先会判断bean中是否有属性,如果有,循环遍历属性,调用inject方法。
在这里插入图片描述
在这里插入图片描述

属性准备就绪,着重看一下resolveFieldValue方法

在这里插入图片描述

步入上图的789行

在这里插入图片描述
步入doResolveDependency方法,最后会对这个属性B进行解析
在这里插入图片描述
步入resolveCandidate方法,这不是beanFactory么,真巧,来了老弟。
这不是想去缓存里找么,嘿嘿在这里插入图片描述

此时b还没有实例化,缓存中肯定找不到啊。
在这里插入图片描述

找不到就去创建吧
在这里插入图片描述
嗖,创建完了。
在这里插入图片描述
创建完又要填充属性了,B的属性是A,来吧,在走一圈。
在这里插入图片描述
最后还是要去beanFactory中去找,因为在a填充属性的时候,a就已经实例化过了,也已经put进三级缓存暴漏出来了,我们来看看是如何处理的。
在这里插入图片描述
老样子,看看248行代码。
在这里插入图片描述
哎,卧槽,怎么直接跳到下面来了?bean工厂里没有?那不对啊,按理来说应该可以从三级缓存中找到啊。服了,看看吧。
在这里插入图片描述

经过再一次的debug发现,这个allowCircularReferences属性默认是false,在SpringBoot2.6.x之前都是默认为true。

如果需要开启循环依赖需要在配置文件配置:

spring:
  main:
    allow-circular-references: true

坑,SpringBoot2.6.x默认禁用循环依赖。
在这里插入图片描述
在这里插入图片描述
开启之后,再来一遍吧。

获取a —》先去缓存中取,结果都没有—》创建,a实例化—》添加进三级缓存beanFactory—》填充属性b—》获取b—》先去缓存中取,缓存中也没有—》创建,b实例化—》添加进三级缓存beanFactory—》填充属性a—》先去缓存中找,这下肯定有了,不信我们看看

在这里插入图片描述
你看,有了吧,啥都有了。也能从三级缓存中取到,并且把这个bean放进二级缓存earlySingletonObjects中。

然后就会进入如下这个if判断中,getObjectForBeanInstance方法中会做一些判断如果是当前实例,则会直接返回该实例。

在这里插入图片描述
在这里插入图片描述
最后得到a,退出解析方法

在这里插入图片描述

在这里插入图片描述
然后将这个属性值放进缓存中

在这里插入图片描述
退出该方法
在这里插入图片描述

然后把得到的A赋值给B的属性

在这里插入图片描述
就此,B的属性A赋值结束。此时可以看到B是有值的,属性A也是有值的,可是A的属性B还没有赋值呢。
在这里插入图片描述

此时我们已经创建完b,也就是说已经调完了上述的那个lambda函数,紧接着会调用addSingleton方法,将bean放进一级缓存。
在这里插入图片描述
在这里插入图片描述
将b放进一级缓存同样也标志着b初始化结束,紧接着,a会将b设置为自己的属性。最后也会把a放进一级缓存,就此over~
在这里插入图片描述

在这里插入图片描述
当然,这里可能还存在很多问题,我给的这个demo,并不能完全展示出二级缓存的作用,因为a只有一个属性b,那么填充属性之后,a就已经存在一级缓存中。

可以这样,A同时依赖于B和C,B和C都依赖于A。这样就可以看出二级缓存的作用了。
在这里插入图片描述

其实对于解决循环依赖来说,两个缓存就完全足够,一个存放未初始化的bean,一个存放已经初始化的bean,那么要三级缓存干什么?

具体原因我们可以放在下一篇aop的源码解析。

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

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

相关文章

Windows+iis+php+mysql搭建wordpress

准备工作 WindowsServer一台 IIS&#xff0c;在Server上开启 PHP:PHP: Downloads Mysql:MySQL :: MySQL Downloads wordpress下载 | WordPress.org China 简体中文 PHP程序在IIS上以fastcgi方式运行&#xff0c;在安装mysql和php之前确保vc库已安装。 IIS确保开启CGI模块…

JAVA开发(AOP之ProceedingJoinPoint)

我们在开发过程中经常使用到自定义注解来实现在一些类或者方法执行过程中切面&#xff0c;统一实现某些业务操作。例如自定义注解import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang…

YOLOv7:面向实时检测的目标检测器 | 附结构图

YOLOv7 在 5 FPS 到 160 FPS 范围内的速度和准确度都超过了所有已知的目标检测器&#xff0c;并且在 GPU V100 上 30 FPS 或更高的所有已知实时目标检测器中具有最高的准确度 56.8% AP。 YOLOv7-E6 目标检测器&#xff08;56 FPS V100&#xff0c;55.9% AP&#xff09;比基于Tr…

小孩护眼灯什么牌子的好?分享四款最好的台灯品牌

最近发现&#xff0c;在接送我家神兽上下学时&#xff0c;小朋友们会揉眼睛&#xff0c;眼睛始终没睁开的感觉&#xff0c;还有不少小学就戴上了眼镜&#xff0c;我深知戴眼镜&#xff0c;真的很麻烦&#xff0c;所以更加看重孩子的护眼工作。市面上越来越多护眼灯&#xff0c;…

Java高手速成 | 实现人物拼图游戏

拼图游戏指将一幅图片分割成若干拼块&#xff0c;并随机打乱顺序&#xff0c;当将所有拼块都放回原位置时就完成了拼图(游戏结束)。 01、游戏介绍 在游戏中&#xff0c;拼块以随机顺序排列&#xff0c;网格上有一个位置是空的。完成拼图的方法是利用这个空位置移动拼块&#xf…

服务搭建常见问题

怎么将myeclipse项目部署到tomcat服务器 https://www.laike.net/article-162-238315-0.html eclipse提示错误&#xff1a;save could not be completed Dynamic Web Module 4.0 requires Java 1.8 or newer. https://blog.csdn.net/xixihaha_coder/article/details/118345378 …

微星 MPG B460I GAMING EDGE WIFI +i5-10400电脑 Hackintosh 黑苹果efi引导文件

硬件型号驱动情况主板微星 MPG B460I GAMING EDGE WIFI (MS-7C86)&#xff08;LPC Controller B460芯片组&#xff09;处理器英特尔 Core i5-10400 2.90GHz 六核已驱动内存16 GB ( 芝奇 DDR4 2666MHz 8GB x 2 )已驱动硬盘朗科科技 NVMe SSD 480GB (480 GB / 固态硬盘)已驱动显…

React学习笔记:实用又好用的Hooks函数

React框架以前是采用Class类编程&#xff0c;在类编程中使用生命周期比较方便&#xff0c;但是随着迭代更新&#xff0c;官方开始推荐使用函数式编程&#xff0c;但是函数式编程就没有状态这一个概念&#xff0c;于是乎官方就定义了一系列钩子函数来弥补在这一缺陷&#xff0c;…

Rabbitmq(七) -- rabbitmq的工作模式

1. 简单模式&#xff1a;无需交换机 消息产生消息&#xff0c;将消息放入队列消息的消费者(consumer) 监听 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患 消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失&#xff0c;这里可以设置…

VTK-数据集vtkUnstructuredGrid

前言&#xff1a;本博文主要介绍vtkUnstructuredGrid的特点、结构组成&#xff0c;vtkUnstructuredGrid的创建方法&#xff0c;及其vtkUnstructuredGrid相关的接口及示例。 特点 非结构化网格数据&#xff0c;是最常见的数据集类型&#xff0c;它的拓扑结构和几何结构都是非结…

Pycharm调试功能介绍

文章目录pycharm中的debug模式debug的断点调试pycharm中的debug模式 在pycharm中&#xff0c;一共有4中方法开启debug调试&#xff0c;如下&#xff1a; 点击导航栏的run >> debug 双击打开py文件 >> 右上角点击小虫子图标。 写好if name ‘main’: >> 点…

React 类组件你不知道的细节+案例

React基础-组件-类组件 1.组件概述 目标&#xff1a;了解React组件的作用和创建组件的方式 什么是组件组件的设计思想 1.what is 组件啊&#xff1f; 在前端开发中组件就是用户界面当中一块独立的区域,在组件内部会包含这块区域中的视图代码,样式代码以及逻辑代码 React是采用…

Cadence PCB仿真使用Allegro PCB SI配置差分对的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,配置方法3,总结1,概述 本文简单介绍使用Allegro PCB SI配置差分对的方法。 2,配置方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PCB SI则可这样切换 执行Fil…

天下苦“个人公众号认证”久矣,吾闻今可

大家好&#xff0c;我是小悟 一看到个人公众号可以认证&#xff0c;便以迅雷不及掩耳之势准备资料&#xff0c;一顿操作猛如虎后&#xff0c;我的号终于认证啦。 看到别人的个人公众号有认证的&#xff0c;这两天我就在想要怎么才能认证&#xff0c;于是就去搜索相关的内容&am…

电子采购系统的优势是什么 常用的电子采购系统介绍

采购是企业发展中的重要环节之一。在企业采购流程中&#xff0c;并不是简单的完成买和卖就行了&#xff0c;这其中还会涉及到各个方面。例如&#xff0c;在企业采购活动中&#xff0c;常常会遇到供应商数据维护难&#xff0c;采购成本把控难&#xff0c;供应商筛选难等问题。而…

面对集中式缓存实现上的挑战,Redis交出的是何种答卷?聊聊Redis在分布式方面的能力设计

大家好&#xff0c;又见面了。 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容&#xff0c;将会通过系列专题&#xff0c;讲清楚缓存的方方面面。如果感兴趣&#xff0c;欢迎关注以获取后续更新。 在本专栏前面的文章中&#xff0c;我们介绍了各种本地缓存框…

数据结构与算法1—线性表

1. 线性表的定义 线性表L是n&#xff08;n≥0&#xff09;个具有相同属性的数据元素a1&#xff0c;a2&#xff0c;a3&#xff0c;…&#xff0c;an组成的有限序列&#xff0c;其中序列中元素的个数n称为线性表的长度。当n0时称为空表&#xff0c;即不含有任何元素。常常将非空…

express接口

文章目录什么是接口创建 API 路由模块编写 GET 接口编写 POST 接口完整代码CORS 跨域资源共享使用 CORS 中间件解决跨域问题实现 JSONP 接口什么是接口 API (Application Programming Interface&#xff0c;应用程序编程接口 ) 是一些预先定义的函数&#xff0c;目的是提供应用…

TCP/IP 网络模型有哪几层

备注&#xff1a;本文参考小林coding相关内容&#xff0c;侵权请联系作者删除 1.应用层 最上层的&#xff0c;也是我们能直接接触到的就是应用层&#xff08;Application Layer&#xff09;&#xff0c;我们电脑或手机使用的应用软件都是在应用层实现。那么&#xff0c;当两个…

高并发系统设计 -- 登录系统设计

同源策略 同源策略是一种安全策略。是游览器最核心最基本的安全功能。防止XSS&#xff0c;CSFR等攻击具体表现是游览器在执行脚本之前&#xff0c;会判断脚本是否与打开的网页是同源的&#xff0c;也就是协议&#xff0c;域名&#xff0c;端口是否都相同&#xff0c;相同就是同…