1. 什么是循环依赖
循环依赖其实就是循环引用,也就是两个或者两个以上的Bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C依赖于A。
注意,这里不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除非有终结条件。
Spring中循环依赖场景有:
·构造器的循环依赖(构造器注入)
·Field属性的循环依赖(set注入)
其中,构造器的循环依赖问题无法解决,只能抛出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。
2.循环依赖处理机制
·单例bean构造器参数循环依赖(无法解决)
·prototype原型bean循环依赖(无法解决)
对于原型bean的初始化过程中无论是通过构造器参数循环依赖还是通过setXXX方法产生循环依赖,spring都会直接报错处理。
AbstractBeanFactory.doGetBean()方法
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }
在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会删除标记。
try { //创建原型bean之前添加标记 beforePrototypeCreation(beanName); //创建原型bean prototypeInstance = createBean(beanName, mbd, args); } finally { //创建原型bean之后删除标记 afterPrototypeCreation(beanName); }
总结:Spring不支持原型bean的循环依赖。
·单例bean通过setXXX或者@Autowired进行循环依赖
Spring的循环依赖的理论依据基于java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前。
Spring通过setXXX或者@Autowired方法解决依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说classA在调用构造器完成对象初始化之后,在调用classA的setClassB方法之前就把classA实例化的对象通过ObjectFactory提前暴露到spring容器。
·Spring容器初始化classA通过构造器初始化对象后提前暴露到spring容器。
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"); }
//将初始化后的对象提前已ObjectFactory对象注入到容器中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
·ClassA调?setClassB?法,Spring?先尝试从容器中获取ClassB,此时ClassB不存在Spring 容器中。
·Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
·ClassB调?setClassA?法,Spring从容器中获取ClassA ,因为第?步中已经提前暴露了 ClassA,因此可以获取到ClassA实例
ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
·这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
3. 源码分析
首先了解在DefaultSingletonBeanRegistry类中对缓存的定义。
/** 一级缓存:单例对象池,存放完整的SpringBean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存:单例工厂对象 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存:早期单例对象 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
AbstractBeanFactory类的doGetBean方法中有如下代码来创建bean实例。getSingleton这个方法就是从一级缓存中获取bean,如果为null,则创建bean从三级缓存获取对象,
主要看下createBean方法。
通过前面springIOC源码剖析,我们可以直接把代码定位到AbstractApplicationContext的refresh方法-finishBeanFactoryInitialization(实例化bean实例)-
beanFactory.preInstantiateSingletons(在此方法中遍历初始化的所有beanName)-AbstractBeanFactory.getBean-doGetBean
doGetBean方法中会先从一级缓存去拿发现没有,且对象没有在创建中,返回null。
Object sharedInstance = getSingleton(beanName);
//已经存在则返回 if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean ‘" + beanName + "‘ that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean ‘" + beanName + "‘"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); his.singletonFactories.remove(beanName); } } } } } } return singletonObject; }
此时是空的,则需要去创建对象,往下看有如下代码。
//创建单例bean
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
进入createBean方法,继续走到AbstractAutowireCapableBeanFactory类的doCreateBean方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) {
//创建bean实例,仅仅调用构造方法,但是尚未设置属性 instanceWrapper = createBeanInstance(beanName, mbd, args); } ...
这个类往下看有如下方法如果单例、允许循环依赖、bean在创建过程中则加入三级缓存,存放的是lambda表达式,会去创建代理对象。
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)); }
并且从二级缓存移除。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
接着是属性的填充,在这个方法中完成依赖对象的创建。
Object exposedObject = bean; try { //属性填充 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); }
//属性填充
applyPropertyValues(beanName, mbd, bw, pvs);
//得到需要注入的名字
String propertyName = pv.getName(); Object originalValue = pv.getValue();
//查找bean Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
//进入解决引用方法
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) { // We must check each value to see whether it requires a runtime reference // to another bean to be resolved. if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; //解决引用 return resolveReference(argName, ref); }
else {
//从bean工厂获取引用bean bean = this.beanFactory.getBean(refName); this.beanFactory.registerDependentBean(refName, this.beanName); }
接着会走到方法doGetBean,尝试去缓存中获取依赖的bean对象,此时也是null,会去创建bean对象并实例化,将实例化好的对象放入三级缓存中,接着会进行属性注入实现方法填充,进入解决值方法那个方法中,从bean工厂获取bean,
会走到getSingleton方法,先从一级缓存获取,此时对象在创建中,接着从二级缓存获取,为null且运行引用,继续判断,从三级缓存获取,发现不为null,通过工厂创建依赖所需要的对象,将对象放入二级缓存暴露自己,从三级缓存中移除,返回代理对象,完成深属性的注入。然后一直返回到getSingleton中有如下方法
if (newSingleton) {
//添加一级缓存 从三级缓存和二级缓存中移除 addSingleton(beanName, singletonObject); }
接着返回doCreateBean的下面方法。此时一级缓存没有,二级缓存中拿到直接返回,重新赋值没有代理的对象。接着在添加到一级缓存。
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name ‘" + beanName + "‘ has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "‘getBeanNamesForType‘ with the ‘allowEagerInit‘ flag turned off, for example."); } } } }
为什么需要二级缓存?
如果将三级缓存的代理对象直接放入一级缓存,则一级缓存中存在两种状态的bean,一个已经走完spring完整周期的bean
,一个是刚生成完代理对象的bean,违反了单一原则。
二级缓存还有个重要作用,起到公用的作用,如果一个bean依赖两个bean,同时这两个bean依赖又都依赖于这个bean,当在二级缓存放入对象后,另一个bean可以直接从二级缓存去获取bean。
存放涉及到循环依赖的对象。
为什么需要三级缓存?
产生代理对象。如果注入的bean不是代理对象,则需要一二级缓存就可以了,不需要三级缓存,三级缓存通过后置处理器产生代理对象完成注入。