三级缓存中包含A和B的2个lambda表达式,A和B对象还没有放入缓存中。
从容器中查询a
一级缓存和二级缓存都没有,三级缓存中虽然没有a对象,但是有ObjectFactory。执行 singletonFactory.getObject()实际上调用的是lambda表达式getEarlyBeanReference(beanName, mbd, bean)。
如果有代理对象,则返回代理对象,如果没有代理对象,则返回原始对象。
因为xml中并没有给a对象配置aop代理,所以这里返回的是a的原始对象。
查看getEarlyBeanReference方法可以看到AOP代理的两种实现方式。
为什么删除三级缓存a的objectFactory,因为查找顺序是先查一级缓存,再查二级缓存,最后查三级缓存,二级缓存已经有了a对象,所以三级缓存内容就没有留着的必要了,这也是三级缓存容量比较小的原因。
获取a对象的目的是给b对象中的a属性赋值。
那么就不需要createBean了,整个逻辑才算结束了。
总结
1、三个map结构中分别存储了什么对象?
一级缓存:成品对象;二级缓存:半成品对象;三级缓存:lambda表达式(getEarlyReference)
2、三个map缓存的查找对象的顺序?
先找一级缓存,查不到,再查二级缓存,再找不到,找三级缓存。
3、如果只有一个map,能否解决循环依赖问题?
不能,如果只有一个map,那么成品对象和半成品对象只能放入一个map中,而半成品对象是不能暴露给外部使用的,所以必须要做区分,否则就有可能暴露半成品对象。
有人可能说添加一个标识,0=半成品、1=成品,每次取的时候都要判断下这个标识别,很麻烦。
4、如果只有两个map,能否解决循环依赖问题?
能,但有前提条件。
修改源码1
把createBean方法中的
this.addSingletonFactory(beanName, () -> { return this.getEarlyBeanReference(beanName, mbd, bean); });
注释掉,然后添加,earlyearlySingletonObjects.put(beanName,bean)
即不往三级缓存中放入ObjectFactory对应的lambda表达式,而是往二级haunch中放入bean对象。
修改源码2
把getSingleton方法中的
这一部分,换成
singletonObject=this.earlySingletonObjects.get(beanName); return singletonObject;
即不从三级缓存中获取,而是直接从二级缓存获取。
运行下发现并没有报错
再来运行代码,就会报错:other beans do not use the final version of the bean即其他bean不能使用最终版本的bean。
所以只要不包含aop就可以使用二级缓存解决循环依赖问题,但是出现aop之后,就必须要使用三级缓存了。
5、为什么三级缓存就可以解决循环依赖中包含的代理对象问题呢?
(1)创建代理对象的时候是否需要创建出原始对象?
在没有生成代理对象之前就可以生成原始对象了
这个方法是创建代理对象的。
(2)同一个容器中能否出现两个不同的对象?不能,对象名都叫a,要么是原始对象要么是代理对象,不能同时出现。
(3)如果一个对象被代理,那么原始对象和和代理对象应该这么去存储?
如果需要代理对象,那么代理对象创建完之后应该覆盖原始对象。
在getEarlyBeanReference方法中,会判断是否需要代理对象,如果创建出了代理对象,就需要覆盖原始对象。
(4)在对象对外暴露的时候,容器怎么知道什么时候需要被暴露呢?或者说在对象对外暴露的时候,如何准确的给出原始对象或代理对象?
因为正常的代理对象的创建是在BeanPostProcessor的后置方法中,在解决循环依赖问题的时候还没有执行到那个地方,所以此时就需要lambda表达式了,类似于一种回调机制,在确定要对外暴露的时候,就唯一性确定到底是代理对象还是原始对象,这也是什么不把对象直接放入二级缓存中,而通过三级缓存lambda表达式的方式来执行的原因。
addSingletonFactory这里把lambda表达式放入三级缓存,该表达式的内容是判断是否需要代理对象,若需要则创建,这里并没有真正的去执行该表达式。
initializeBean这个方法中的applyBeanPostProcessorsBeforeInitialization方法才是真正的通过后置方法beanPostProcessor创建代理对象,而在处理循环依赖的时候并没有执行到这一步呢。
程序没有办法去确定你写的这个对象什么时候被其他对象调用,什么时候需要变成某一个对象的属性,所以把他换成一个lambda表达式,在确定需要对外暴露的时候才执行对象的确定(原始还是代理)。
先创建a对象,再创建b对象,需要给b中的a属性赋值,从三级缓存中找到lambda表达式,然后判断是否需要代理对象。lambda表达式相当于回调机制,并不会立刻执行,当你需要给这个属性赋值的时候,你才会去执行。
刚开始是原始对象,如果需要被代理,则返回被代理之后的对象。
某一个对象的代理只能是一个,如果是多个代理的话,就需要看代理对象的创建顺序了。
(5)spring提供了循环依赖的解决方案,那日常工作中是否也会有遇到循环依赖的问题?
spring是一个跟业务无关的技术框架,它只能预防一些问题,而不是解决所有问题,就跟我们写代码的时候的异常处理一样,你能判断到一些问题,但不是所有的异常情况你都能全部解决掉的。