问题描述
spring循环依赖是对于ioc容器。类A、B、C,类A依赖了B,类A依赖了C,类B依赖了A,类C依赖了A。假如现在类A需要放到ioc,属性赋值的时候会去找B这个bean,但是B不存在,于是去创建B这个bean,但是实例化beanB之后再属性赋值的时候需要注入A这个bean。这样就造成了循环。
解决办法
spring通过三级缓存解决循环依赖问题,首次注入beanA的时候会在类A实例化之后,spring会创建一个ObjectFactory用来后续获取beanA的早期引用,并将这个ObjectFactory放到三级缓存中。接着注入beanB,初始化类B之后属性赋值,需要注入beanA,这是spring会去三级缓存中获取A的ObjectFactory调用getObject()获取A的早期引用,如果A需要被代理(如@Transactional),会在此时去创建A的代理对象,放到二级缓存中,然后删除三级缓存的A的工厂对象。此时returnA这时循环有了出口,就解决了循环依赖。
补充
时序图
- 实例化->new A(这里把创建A的ObjectFactory放到三级缓存)
- 属性赋值(循环依赖的时候这里去创建AOP代理)
- 初始化(正常这里创建aop代理)
三级缓存是为了支持aop代理的按需创建。二级缓存的话就是直接把早期对象放到二级缓存,实例化A之后马上把他放到二级缓存。如果直接把早期对象放到二级缓存,(实例化之后)就无法灵活决定是否创建代理(因为AOP代理是在postProcessAfterInitialization(上图初始化中)中创建的)。可能导致重复创建代理或代理失效。
而如果是三级缓存的话
spring创建一个bean的阶段
1.实例化(instantiation) ->new A()
2.属性注入(Populate Properties) ->setB(),setC()
3.初始化(Initialization) ->调用init-method,BeanPostProcessorpostProcessBeforeInitialization//是BeanPostProcessor的方法初始化方法(@PostConstruct,init-method)postProcessAfterInitialization ->AOP代理就在这里创建!//是BeanPostProcessor的方法
4.放入一级缓存(singletonObject)
Spring 提供了 InstantiationAwareBeanPostProcessor.getEarlyBeanReference() 方法,允许 AOP 模块在早期引用阶段就决定是否创建代理。这个方法会在 ObjectFactory.getObject() 被调用时触发,是提前创建代理的关键入口
代码细节
三级缓存:singletonFactories如下
Map<String, ObjectFactory<?>> singletonFactories
其中键是类名首字母小写。