spring是如何解决循环依赖的,这是一道很常见的面试题,可能java开发者已经都背的很熟了,是使用了三级缓存,然后为什么需要三级而不是二级,因为要解决动态代理类的问题。
这个问题我其实一直没想明白,为什么三级才能解决代理类,我直接在二级缓存中存不需要增强的对象或者需要增强就存代理对象,不就行了。为什么非要加一级缓存来解决呢?
这是我从网上找的一张图,bean生命周期第一步就是实例化,基本就是通过无参构造方法创建一个对象,当然有参的需要先完成参数bean的初始化这里不展开了。第二步是设置属性populate,这也就是依赖注入的过程,第三步是初始化了,初始化这个过程下面图里面拆了好几个步骤。
aware
相关接口主要是实现里面的方法用来感知bean的开始初始化的,能拿到beanname等属性,一般很少用到;
BeanPostProcessor
也是一个接口,需要实现前置和后置处理两个方法,这俩方法可以用来增强对象,然后返回代理对象,因为这俩方法是有返回值的,spring会用返回值来替换原来实例化这一步new出来的对象的,前置和后置处理主要是看是不是需要在5和6两步中使用代理类,不需要的话就放到后置处理去增强,需要的话就放到前置处理,大多数都是放到后置处理的;
InitailizingBean
接口和@PostConstruct
代表的init-method
都是执行一个无返回值(或者说返回值不参与到后续流程)的方法,这两个仅仅就是在这个时间点运行一下函数,可以用于一些bean创建完成后的基础操作。这俩的作用完全一致,只不过前者是JSR 250
规范下的,后者是spring自己的。
其他后续的流程就没那么重要了。
上图并没有提到三级缓存,因为三级缓存算是一些细节处理,对于没有产生循环引用的单例bean来说,这个图就是主流程的很好抽象了。
那三级缓存是怎么起作用呢,首先三级缓存是三个map
Map<String, Object> singletonObjects
: 1级,存放能被直接使用的完全体单例bean对象。Map<String, Object> earlySingletonObjects
: 2级,存放非完全体的对象。Map<String, ObjectFactory<?>> singletonFactories
: 3级,存放能产生bean的工厂对象。下面map1,map2,map3来代替三级缓存。
下面我们来说下bean的创建流程:
A a = new A()
,把a和def信息mbd加入到3级缓,map3.put("beanName",() -> getEarlyBeanReference(beanName, mbd, bean))
,map3存的工厂对象,工厂对象中捕捉了这个创建的对象和一些bean的元数据信息mbd,例如需不需要aop增强,怎么个增强法都在mbd记录。然后删除map2中的值。getEarlyBeanReference
来产生对应的对象,产生的过程其实就是根据mbd
判断是不是要增强,需要的话就增强并返回增强过的代理对象,不需要就返回1中创建的初始对象,这个函数名叫wrapIfNecessary
。这里需要注意一下,该方法还做了个两个额外的操作就是1往earlyProxyReferences
这个map里塞了原对象,2往map2塞了wrapIfNecessary
后的对象,这俩后面有用的。processor
列表,依次运行,并把运行的结果替换原来的bean对象,
earlyProxyReferences
中的value是否有当前beanName
wrapIfNecessary
并返回。getEarlyBeanReference
获得bean。这个流程一下子变复杂了,好像比图里多出来很多存储和判断,一下子看上去是有点乱的,我们用以下几个例子来过一遍。
A a = new A()
,塞到三缓map3.put("a", ()->getEarlyBeanReference(beanName, mbd, a))
。B b = new B()
,塞到三缓map3.put("b", ()->getEarlyBeanReference(beanName, mbd, b))
。wrapIfNecessary
,也就是会变成b'。map1.put("b", b')
a.b=b'
。map1.put("a", a')
,完成a的装配。b.a
,从map123中挨着找,在map3中找到了
wrapIfNecessary
将a增强为a',b.a=a'
,把a'放到map2中,并且把原始对象a放到earlyProxyReferences
,即map2.put("a", a')
,earlyProxyReferences.put("a",a)
。map1.put("b", b')
a.b=b'
。earlyProxyReferences
拿出来发现是a和第一步是同一个对象
map1.put("a", a')
只说一下不一样的,就是