Spring-framework源码分析(八)getBean单例循环依赖解决思路
循环依赖 1 2 3 4 5 6 7 8 9 10 11 @Component public class A { @Autowired private B b; }@Component public class B { @Autowired private A a; }
A 需要 B,B 需要 A,此时就会有循环依赖,spring 自动帮我们解决了此类循环依赖,但是如果是构造器注入 spring 是解决不了的,因为 spring 解决循环依赖的做法是未等Bean创建完成就提前将实例暴露出去,方便其他 Bean 进行引用。
spring 解决循环依赖
当 spring 完成bean的实例化后,在调用 populateBean
填充属性之前,会先将实例暴露到第三级缓存 singletonFactories
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Nullable protected Object getSingleton (String beanName, boolean allowEarlyReference) { Object singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this .singletonObjects) { singletonObject = this .earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this .singletonFactories.get(beanName); if (singletonFactory != null ) { singletonObject = singletonFactory.getObject(); this .earlySingletonObjects.put(beanName, singletonObject); this .singletonFactories.remove(beanName); } } } } return singletonObject; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 boolean earlySingletonExposure = (mbd.isSingleton() && this .allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references" ); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } Object exposedObject = bean;try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); }
三级缓存
singletonObject
一级缓存,该缓存key = beanName, value = bean;这里的bean是已经创建完成的,该bean经历过实例化->属性填充->初始化以及各类的后置处理。因此,一旦需要获取bean时,我们第一时间就会寻找一级缓存
earlySingletonObjects
二级缓存,该缓存key = beanName, value = bean;这里跟一级缓存的区别在于,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。也就是说获取到的bean只能确保已经进行了实例化,但是属性填充跟初始化肯定还没有做完,因此该bean还没创建完成,仅仅能作为指针提前曝光,被其他bean所引用
singletonFactories
三级缓存,该缓存key = beanName, value = beanFactory;在bean实例化完之后,属性填充以及初始化之前,如果允许提前曝光,spring会将实例化后的bean提前曝光,也就是把该bean转换成beanFactory并加入到三级缓存。在需要引用提前曝光对象时再通过singletonFactory.getObject()获取。
getEarlyBeanReference 1 2 3 4 5 6 7 8 9 protected Object getEarlyBeanReference (String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }
getEarlyBeanReference
在 spring 内部只有一个实现 AbstractAutoProxyCreator
,此处是 spring AOP对象创建的一个时机
1 2 3 4 5 6 @Override public Object getEarlyBeanReference (Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this .earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); }
getEarlyBeanReference目的就是为了后置处理,给一个在提前曝光时操作bean的机会
Spring AOP 与 循环依赖 如果循环依赖的时候,所有类又都需要 Spring AOP
自动代理,那 Spring
如何提前曝光?曝光的是原始 bean
还是代理后的 bean
?
要解答这个问题还是需要回到 getEarlyBeanReference
这个方法
getEarlyBeanReference
方法是 SmartInstantiationAwareBeanPostProcessor
所规定的接口。再通过UML的类图查看实现类,仅有 AbstractAutoProxyCreator
进行了实现。也就是说,除了用户在子类重写,否则仅有 AbstractAutoProxyCreator
一种情况
1 2 3 4 5 6 7 8 public Object getEarlyBeanReference (Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this .earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); }
wrapIfNecessary
是用于 Spring AOP
自动代理的。 Spring
将当前 bean
缓存到 earlyProxyReferences
中标识提前曝光的bean在被提前引用之前,然后进行了 Spring AOP
代理。
但是经过 Spring AOP
代理后的 bean
就已经不再是原来的 bean
了,经过代理后的 bean
是一个全新的 bean
,也就是说代理前后的2个 bean
连内存地址都不一样了。这时将再引出新的问题:B提前引用A将引用到A的代理,这是符合常理的,但是最原始的bean A在B完成创建后将继续创建,那么 Spring Ioc
最后返回的 Bean
是 Bean A
呢还是经过代理后的 Bean
呢?