Spring的3级缓存和循环引用的理解
此处是我自己的一个理解,防止以后忘记,如若那个地方理解不对,欢迎指出。
一、背景
在我们写代码的过程中一般会使用 @Autowired
来注入另外的一个对象,但有些时候发生了 循环依赖
,但是我们的代码没有报错,这个是什么原因呢?
二、前置知识
1、考虑循环依赖的类型
此处我们考虑 单例
+ @Autowired
的循环依赖,不考虑使用构造器注入
或原型作用域的Bean
的注入。
2、代理对象何时创建
注意:
正常情况下,即没有发生 循环依赖的时候,aop增强
是在 bean 初始化完成之后的 BeanPostProcessor#postProcessAfterInitialization
方法中,但是如果有循环依赖发生的话,就需要提前,在 getEarlyBeanReference
中提前创建代理对象。
3、3级缓存中保存的是什么对象
缓存字段名 | 缓存级别 | 数据类型 | 解释 |
---|---|---|---|
singletonObjects | 1 | Map<String, Object> | 保存的是完整的Bean,即可以使用的Bean |
earlySingletonObjects | 2 | Map<String, Object> | 保存的是半成品的Bean,即属性还没有设置,没有完成初始化工作 |
singletonFactories | 3 | Map<String, ObjectFactory<?>> | 主要是生成Bean,然后放到二级缓存中 |
注意:
ObjectFactory#getObject()
每调用一次,都会产生一个新的对象或返回旧对象,取决于是否存在代理等等。
4、从3级缓存中获取对象
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
5 Spring Bean的简化创建过程
1、实例化一个bean
Object bean = instanceWrapper.getWrappedInstance();
实例化Bean 即 new Bean()
2、加入到三级缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
加入到三级缓存中是有一些条件判断的,一般都会是成立的,此处认为需要加入到三级缓存。
3、设置bean的属性
populateBean(beanName, mbd, instanceWrapper);
第一步实例化了bean,但是此时是没有填充需要注入的属性的,通过这一步进行属性的填充。
4、初始化bean
Object exposedObject = initializeBean(beanName, exposedObject, mbd);
初始化Bean,执行初始化方法、Aware回调、执行 BeanPostProcessor#postProcessAfterInitialization 方法 (aop的增强
是在这个里面实现的)
如果有循环引用
的话,则aop的增强需要提前。
5、加入到一级缓存中
addSingleton(......)
三、理解
@Component
class A {
@Autowired
private B b;
}
@Transaction (存在代理)
@Component
class B{
@Autowired
private A a;
}
1、假设只有singletonObjects和earlySingletonObjects可否完成循环依赖
缓存字段名 | 缓存级别 | 数据类型 | 解释 |
---|---|---|---|
singletonObjects | 1 | Map<String, Object> | 保存的是完整的Bean,即可以使用的Bean |
earlySingletonObjects | 2 | Map<String, Object> | 保存的是半成品的Bean,即属性还没有设置,没有完成初始化工作 |
此时需要获取 B
的实例,即 getBean("b")
,由上方了解到的 Bean 的简化流程可知
由上图可知,对象存在代理时
,2级缓存无法解决问题。因为代理对象是通过BeanPostProcessor
来完成,是在设置属性之后才产生的代理对象
。
此时可能有人会说,那如果我在构建完B的实例后,就立马进行Aop代理,这样不就解决问题了吗?那假设A和B之间没有发生循环依赖,这样设计会不会不优雅?
2、假设只有singletonObjects和singletonFactories可否完成循环依赖
由图中可知也是不可以实现的。
3、3级缓存如何实现
1、解决代理问题
因为默认情况下,代理是通过BeanPostProcessor
来完成,为了解决代理,就需要提前创建代理,那么这个代理的创建就放到3级缓存中来进行创建。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
getEarlyBeanReference
此方法会返回代理bean
2、解决单例通过第3级缓存多次获取的值不一致
从上图中可知,对象是先从 一级->二级->三级缓存
这样查找,当三级缓存产生了对象后就放入二级缓存中缓存起来,同时删除三级缓存。
3、流程图
四、总结
1、一级缓存 singletonObjects
存放可以使用的单例。
2、二级缓存earlySingletonObjects
存放的是早期的Bean,即是半成品,此时还是不可用的。
3、三级缓存singletonFactories
是一个对象工厂,用于创建对象,然后放入到二级缓存中。同时对象如果有Aop代理的话,这个对对象工厂返回的就是代理对象。
那可以在earlySingletonObjects
中直接存放创建后的代理对象吗?这样是可以解决问题,但是设计可能就不合理了。因为在Spring中 Aop
的代理是在对象完成之后创建的。而且如果没有发生循环依赖的话,有必要提前创建代理对象吗?分成三级缓存,代码结构更清楚,更合理。
Spring的3级缓存和循环引用的理解的更多相关文章
- Spring如何使用三级缓存解决循环依赖
Spring如何使用三级缓存解决循环依赖 首先来了解一下什么是循环依赖 @Component public class A { @Autowired B b; } @Component public ...
- Spring如何解决循环引用
概念 什么是循环引用? 故名思义,多个对象形成环路. 有哪几种循环引用? 在Spring中存在如下几种循环引用,一一举例分析一下 注入循环引用(Set注入 注解注入) package c.q.m; i ...
- Spring 循环引用(二)源码分析
Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环引用相关文章: & ...
- Spring IOC 源码简单分析 03 - 循环引用
### 准备 ## 目标 了解 Spring 如何处理循环引用 ##测试代码 gordon.study.spring.ioc.IOC03_CircularReference.java ioc03. ...
- spring源码学习(三)--spring循环引用源码学习
在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例 @Component public class Add ...
- Spring 循环引用(三)源码深入分析版
@ 目录 前言 正文 分析 doGetBean 为什么Prototype不可以 createBean doCreateBean getEarlyBeanReference getSingleton b ...
- 【源码】spring循环引用
spring在单例,非构造方法注入的情况下允许循环依赖 1.循环依赖 a引用b,b引用a.a创建的时候需要b,但是b没有创建,需要先去创建b,b创建的时候又没有a,这就出现的循环依赖的问题 2.为什么 ...
- Spring三级缓存解决循环依赖
前提知识 1.解决循环依赖的核心依据:实例化和初始化步骤是分开执行的 2.实现方式:三级缓存 3.lambda表达式的延迟执行特性 spring源码执行逻辑 核心方法refresh(), popula ...
- spring jpa 实体互相引用返回restful数据循环引用报错的问题
spring jpa 实体互相引用返回restful数据循环引用报错的问题 Java实体里两个对象有关联关系,互相引用,比如,在一对多的关联关系里 Problem对象,引用了标签列表ProblemLa ...
随机推荐
- Axure7.0 以及 中文汉化语言包下载 axure汉化包
支持 Axure RP Pro 正式版 当前最新版本 7.0.0.3184 不兼容6.5及以下版本! Axure7.0 下载地址:http://pan.baidu.com/s/1dEuR8YX Axu ...
- 【自动化基础】手把手教零基础小白搭建APP的UI自动化环境
前言 帮助零基础小白一步步搭建UI自动化环境,完成Python+Appium+模拟器/真机的UI自动化环境搭建. 环境准备: jdk1.8.0 sdk Node.js appium python Ap ...
- Http请求的Get和Post的区别?
1. get从地址栏以明文的方式提交请求信息内容?username=admin&password=123,用户可见, 而post从请求正文提交请求信息内容,用户不可见. 2. get提交因为是 ...
- Could not find the main class
最近开发了一个短信报警的服务,打成程序包之后,再本地windows启动(start.bat)没有问题,但是发到生产环境,报如下错: Could not find the main class 莫名其妙 ...
- C++11最常用的新特性如下
1.auto关键字:编译器可以根据初始值自动推导出类型.但是不能用于函数传参.定义数组以及非静态成员变量. 2.nullptr关键字:是一种特殊类型的字面值,它可以被转换成任意其它类型的指针:而NUL ...
- 用maven建立一个工程4
在命令行里面输入cd C:\Users\admin\Documents\hello 然后按回车 再输入这行代码 mvn archetype:generate -DgroupId=com.liyongz ...
- DRF 简单使用(详细注释版)
1.djangorestframework使用 下载安装 pip install djangorestframework ## djangorestframework pip install djan ...
- ACM - 图论 - P3385 负环
P3385 负环 题目描述 给定一个 \(n\) 个点的有向图,请求出图中是否存在从顶点 \(1\) 出发能到达的负环. 负环的定义是:一条边权之和为负数的回路. 输入格式 本题单测试点有多组测试数据 ...
- Docker镜像构建之docker commit
我们可以通过公共仓库拉取镜像使用,但是,有些时候公共仓库拉取的镜像并不符合我们的需求.尽管已经从繁琐的部署工作中解放出来了,但是在实际开发时,我们可能希望镜像包含整个项目的完整环境,在其他机器上拉取打 ...
- css 垂直居中方法汇总
查看原文可以有更好的排版效果哦 前言 居中是平时工作中的最常见的一种需求,各种图片居中或者各种弹窗,水平居中还好,特别是垂直居中,很多初学者表示太难写了,现在列举一些常用的方法. 实战 这里只讲述cs ...