Spring学习:循环依赖问题

今天在公交车上看到一篇关于Spring面试题的文章,里面有一个Spring循环依赖的问题,因为这是一个很常见的问题,但是这篇文章里面关于Spring循环依赖的问题讲解的确很简单,所以就深入研究了一下,就发现有个疑问一直没有弄懂。导致今天一天都在搞清楚这个问题,工作都没有开始做的。

Spring面试文章的链接是https://blog.csdn.net/qq_39390545/article/details/118905918?spm=1001.2014.3001.5501

一.循环依赖如何解决?

利用三级缓存来解决循环依赖。

springbean的初始化过程分为三步

1.调用构造方法实例化。

2.注入属性值。

3.初始化,初始化包括下面的相关过程

实现相关Aware接口,调用这些接口的重写方法。

实现BeanPostProcessor接口,重写了postProcessorBeforeInitialization方法,就执行postProcessorBeforeInitialization方法

实现了InitialzingBean接口,执行afterPropertiesSet方法

执行init-method方法

执行BeanPostProcessor的postProcessorAfterInitialization方法。

假设A依赖B,B依赖A。初始化A

在初始化A的时候,会先实例化A,然后将A存入到第三级缓存中。然后填充属性B,但发现B没有被初始化,就会去初始化B,B的初始化也是先实例化B,存入到三级缓存中,填充属性A,会先从一级缓存中查找,没有查找到,再从二级缓存中查找,还是没有查找到,就会从三级缓存中查找,查找到了A,然后会从三级缓存中取出存入二级缓存,并从三级缓存中移除,返回给B,这个时候B初始化完成,初始化完成后会将B存入一级缓存,然后删除二三级缓存中的B,再接着初始化A,A从一级缓存中取到了B,将B注入,然后初始化A,初始化A完成后会将A放入到一级缓存中,并删除二三级缓存中的A。

A 依赖了 B

可以看出关键在于A还没有初始化完成的时候,先在A实例化了后放入到了三级缓存中,然后才去注入属性,这样在B完成初始化的时候能从三级缓存中获取到B。

看一下创建Bean的源码

 AbstractAutowireCapableBeanFactory类的doCreateBean方法是创建bean的开始,我们可以看到首先需要实例化这个bean,也就是在堆中开辟一块内存空间给这个对象,createBeanInstance方法里面逻辑大概就是采用反射生成实例对象,进行到这里表示对象还并未进行属性的填充,也就是@Autowired注解的属性还未得到注入

 我们可以看到第二步就是填充bean的成员属性,populateBean方法里面的逻辑大致就是对使用到了注入属性的注解就会进行注入,如果在注入的过程发现注入的对象还没生成,则会跑去生产要注入的对象,第三步就是调用initializeBean方法初始化bean,也就是调用我们上述所提到的接口

 三级缓存中每一级缓存存储内容:

名称描述
singletonObjects一级缓存,存放完整的 Bean。
earlySingletonObjects二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行 init 方法。
singletonFactories三级缓存,存放的是 Bean 工厂,主要是生产 Bean,存放到二级缓存中。

其实想一想我们在实例化好了后,将Bean工厂放入到三级缓存中去,不要二级缓存,然后取的时候从一级缓存中取,发现没有,再从三级缓存中获取到Bean工厂,通过Bean工厂获取到实例好像也是可以的。这里有个问题就是假设A被AOP代理了,也就是A的某一个方法作为切入点了,那么通过Bean工厂获取到的A对象其实是一个代理对象,而且每次获取到的代理对象都不是同一个,这就和我们spring中将bean设计成单例模式是相矛盾的。所以不能去掉中间的二级缓存。

然后还有一个问题就是

答案也在评论中

其实是可以将通过ObjectFactory创建好的对象放入到二级缓存中的,如果这样做的话,每次创建这个Bean不管这个Bean有没有被循环依赖,都会去执行getObject方法,但是这里用空间换了时间,存入ObjectFactory对象,在需要的时候才去执行ObjectFactory的getObject方法。什么时候需要,就是循环依赖的时候才需要

二级缓存是可以解决循环依赖的,spring引入三级缓存,是利用第三级缓存返回动态代理对象。一般会有一个疑问能否直接调用ObjectFactory的getObject方法返回动态代理,存入二级缓存,这样就可以丢掉三级缓存的疑问?其实这样是可以的,但是用到三级缓存就是利用空间换时间,因为循环依赖不是很常见,只有在真正循环依赖的时候,才会去三级缓存中查找对象,如果没有循环依赖的话,我们还要调用ObjectFactory的getObject方法获取对象放入三级缓存的话,我们创建完对象是直接放入到一级缓存中的,这个时候二级缓存里面的东西就没有用了,相对于放入三级缓存中的是ObjectFactory,然后不执行ObjectFactory的getObject方法来讲,少了一步执行ObjectFactory的getObject方法。

最后总结一下是这样的

1.一级缓存是IOC容器,我们可以在一级缓存中获取到完整的对象。

2.二级缓存存储的是没有注入属性和初始化的对象,解决的是每次通过ObjectFactory的getObject对象获取新的对象不一样的,符合单例模式,二级缓存解决的是循环依赖中的AOP代理问题。那有这么一种场景,B 和 C 都依赖了 A。而多次调用 singletonFactory.getObject() 返回的代理对象是不同的,就会导致 B 和 C 依赖了不同的 A。

B 和 C 都依赖 A

 

3.三级缓存存储的是ObjectFactory对象,如果这个Bean引入了AOP的话,ObjectFactory的getObject方法返回的AOP对象,解决的是循环依赖的问题。

 

然后我在弄懂Spring循环依赖的问题中有两篇很好的文章,分别是:

https://blog.csdn.net/weixin_45727359/article/details/114696668?utm_term=spring%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-0-114696668&spm=3001.4430

https://blog.csdn.net/qq_36535538/article/details/113466972

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值