-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
有代理对象解决循环依赖这节内容有些不理解的内容 #51
Comments
@ccl125 特别开心你这么用心地仔细地看完项目!!! 2、我也有同样的疑惑,其实两级缓存就能解决有代理对象时的循环依赖问题,就像上一节中所说的,只使用一级缓存就能解决没有代理对象时的循环依赖问题(只需要调整一下顺序,当bean实例化后就放进singletonObjects单例缓存中,提前暴露引用,然后再设置属性)。 |
感谢作者的解答,我最近又看了些资料,还是有些问题要请教下。 没有代理对象的情况下,在bean实例化后就放入singletonObjects中,提前暴露。这样做可以解决循环依赖带来的闭环调用问题。但是这样能否解决没有代理对象的循环依赖问题,我感觉不可以解决,因为如果这样做的话,相当于没有半成品与成品对象的区分了,singletonObjects这一个缓存中可能既存在一个bean对象的完整态,也存在bean的半成品状态(未设置属性),那么如果恰好在某个bean处于半成品的时机,其他地方调用了getBean()方法想获取到完整的bean对象,获取到的bean对象却是一个未设置属性的bean,这就有问题了吧。这是我的理解。 2、 2个缓存,是否能解决循环依赖? 结合网上的资料。 文章会首先说两级缓存可以解决普通的循环依赖,但是有AOP的情况,就无法解决,因为最终返回的对象与代理对象不一致(也就是本项目循环依赖两节内容所讲的问题)接着引入三级缓存,提前暴露singletonFactory解决有代理对象的循环依赖。 这个观点没有问题,Spring的源码就是这样,如果不使用三级缓存,只使用二级缓存就像本项目第一节循环依赖那样,显然是没办法解决的。 第二种观点是说两级缓存可以解决循环依赖(也是作者大大回复我的观点)。解决的办法就是提前将生成的代理对象放入缓存中,因为Spring解决循环依赖。三级缓存的设计实际上就是在1、实例化,2、设置属性、3初始化。第三步3初始化后通过BeanPostProcessor生成代理的。那么如果只用二级缓存的话,就是在实例化后不管有没有AOP都生成代理对象放入缓存中,对应代码其实就是在bean实例化后,直接调用getEarlyBeanReference()方法,生成代理对象后放入二级缓存,而不是采用三级缓存的方式再最后完成整个bean创建过程后再通过提前暴露的ObjectFactory去获取代理对象。采用这种方式,就是在步骤1,实例化后就生成代理对象,这样就可以用两级缓存解决循环依赖。接着再说为什么Spring采用三级缓存而不采用二级缓存的方式,实际上是Spring的一个设计理念所决定的,也就是说Spring用三级缓存这种方式去处理更加的“优雅”。 上面这两个观点一个说二级缓存不能解决,一个说可以解决,其实是不矛盾的,因为是从两个角度去看问题的。 总结: |
@ccl125 问:只使用一级的缓存是否能解决没有代理对象的循环依赖?我感觉不可以解决,因为如果这样做的话,相当于没有半成品与成品对象的区分了,singletonObjects这一个缓存中可能既存在一个bean对象的完整态,也存在bean的半成品状态(未设置属性),那么如果恰好在某个bean处于半成品的时机,其他地方调用了getBean()方法想获取到完整的bean对象,获取到的bean对象却是一个未设置属性的bean,这就有问题了吧。 答:可以的。A对象依赖B对象,a对象持有的是b对象的引用,虽然getBean(b)时获取的b没有设置属性,但最终b会设置属性,因此b最终会是成品。 问:二级缓存的设计(实例化后就将代理放入缓存中)到底能否解决循环依赖?如果可以,怎么区分缓存中的真实对象还是代理对象,getBean的时候从缓存中获取,可能有的地方是要代理对象,有的地方是要真实对象,怎么保证获取的就是我想要的呢? 答:二级缓存可以解决有代理对象时的循环依赖问题。放进缓存中的都是代理对象。 源码面前无秘密,可以把一级缓存去除,再跑单测debug有无代理对象时的循环依赖加深理解,去除一级缓存的代码如下:
|
谢谢作者的解答,我已经理解了。 再次感谢作者开源的本项目,学习到了很多知识,进步很多。从一开始的阅读源码不知道从哪里入手到现在已经能根据整个bean的生命周期独立的阅读spring的源码,当然学无止境,后续还要不断的学习消化 |
@ccl125 奉献得到认可的感觉真的很棒,谢谢啦!!!学完本项目再去阅读原始spring/springboot源码会轻松很多,mini-spring-cloud也请支持下,有问题及时沟通。 |
作者大大好,整个项目快看到尾声了,在这节内容上有些问题。
分支:circular-reference-with-proxy-bean
AbstractAutowireCapableBeanFactory#doCreateBean()
方法,这个方法在75行的时候,通过bean复制一个finalBean对象,然后放入singletonFactories三级缓存中,接着在第104行通过getSingleton(beanName)
这个方法就是会先从一级缓存找,然后接着二级缓存找,最后再三级缓存中找,对应到项目实际debug的过程中,实际上从三级缓存中去获取,就是调用了singletonFactory.getObject()
该方法。也就是上面75行存入的三级对象的getObject方法,最终返回这个代理对象。这是我的理解。那这样的话问题就来了,就是在整个bean的生命周期,还有其他很多的操作步骤,比如BeanPostProcessor修改属性值,填充属性,以及初始化前后的处理,那么实际上对应的代码就是刚刚上面提到75行和104行中间的很多操作步骤。也就是说我们最终获取到的代理bean是根据一开始75行存入三级缓存中的finalBean。而在这个之后,代码中的很多步骤都是对原先bean去做的操作。那75-104行在bean生命周期内所做的操作岂不是对代理对象都不生效了?那代理对象的属性什么的设置是不是就都有问题了。addSingleton(beanName, exposedObject)
,这里紧接着将对象放入一级缓存中,并将二级三级缓存进行清空,结合104行的代码,整个过程看起来像是获取到代理对象后,先放入二级缓存中,然后将三级缓存中的清空,接着再放入一级缓存中,再将二级三级缓存清空。所以这里看起来好像放入二级缓存中什么也没操作,那不如直接放入一级缓存,因为放入二级缓存后什么都没操作就又取出来放入一级缓存中了,这里不是太理解。这节内容debug过程中很多嵌套的,所以理解起来不是很容易,可能自己陷入了某个单独流程上,没办法从整体上去理解。再次感谢作者,学习到了很多东西
The text was updated successfully, but these errors were encountered: