一张图了解 Spring 的 循环依赖

226 阅读3分钟

问题

public class A{
    @Autowire
    private B b;
}
public class B{
    @Autowire
    private A a;
}

这种情况是不是spring需要先实例化A(或者先B),实例化A之后填充属性的时候发现还需要依赖B然后需要实例化B,实例化B之后发现还需要填充属性A,然后是不是又要去实例化A,这不就造成死循环了吗,spring是怎么解决这种互相依赖的?

图解:

总结

其实最主要的就是 singletonFactories 这个map,A会先实例化好对象,然后把对象放到这个map中去,然后再填充属性的时候需要依赖注入B,实例化B的之后,往B填充属性的时候,依赖注入A,这时候会从singletonFactories这个map中获取对象,但是这个对象还没有完成属性填充和初始化,B拿到A的对象之后,B进行属性填充和bean的初始化,然后依赖注入给了A对象,完成循环依赖。

面试

  • 1.三级缓存
  • 2.bean工厂提前暴露

三级缓存

spring 三级缓存是指什么

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从一级缓存尝试获取 :singletonObjects 
		Object singletonObject = this.singletonObjects.get(beanName);
 //一级缓存获取不到,判断 bean是否在创建中:isSingletonCurrentlyInCreation
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
            //从三级缓存获取数据:earlySingletonObjects
				singletonObject = this.earlySingletonObjects.get(beanName);
                //如果三级缓存获取不到,allowEarlyReference:表示是否可以循环依赖,可以设置false
                //表示不能循环依赖
				if (singletonObject == null && ,allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                    //三级缓存获取失败,从bean工厂获取bean(提前暴露bean工厂)
						singletonObject = singletonFactory.getObject();
                      //把从二级缓存获取到的数据放到三级缓存,之后如果还有循环依赖,就可以从
                      //三级缓存获取,这样就不用从bean工厂获取bean,因为bean工厂重新获取bean
                      //是比较慢
						this.earlySingletonObjects.put(beanName, singletonObject);
                        //删除二级缓存的数据
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

总结

  • 1.一级缓存是:singletonObjects,也就是单例池
  • 2.二级缓存是:singletonFactories:也就是提前暴露bean工厂,存放着获取bean的方法
  • 3.三级缓存是:earlySingletonObjects:一级获取不到,从三级获取,三级获取不到,从二级的bean工厂获取,从二级的bean工厂获取到之后删除,二级的bean工厂,把bean放到三级缓存。

bean工厂提前暴露

在上面的三级缓存中,可以从二级缓存获取到bean,这个bean是怎么获取到的。

//在实例化bean之后,将bean工厂提前暴露在 singletonFactories 二级缓存中
//() -> getEarlyBeanReference(beanName, mbd, bean) 这是lamb表达式
//等到循环依赖的时候调用 getObject 会调用 getEarlyBeanReference 方法
//这就是bea 工厂的提前暴露
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

getEarlyBeanReference

故名思意,就是获取早期的bean引用其实就是调用bean后置处理器的 getEarlyBeanReference

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

getEarlyBeanReference 其实是aop才用到的,只有开启了aop,然后需要进行切面织入的时候。

AbstractAutoProxyCreator#getEarlyBeanReference

这就设计到spring的aop知识了

	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			this.earlyProxyReferences.add(cacheKey);
		}
        //动态代理
		return wrapIfNecessary(bean, beanName, cacheKey);
	}