什么是循环依赖
循环依赖的关键点在循环,为了直观一点,我们看个图
从图中我们看到,要想实例化类A,必须先要实例化类B,但是在实例化B之前也必须先实例化C,可想要实例化C,就要先实例化A,喏,死循环了,三个类相互引用又相互等待。
循环依赖如何产生的
循环依赖的产生可能有多种情况:
- A的构造器中依赖了B的实例,B的构造器中依赖了A的实例
- A的构造器中依赖了B的实例,B的属性或者setter依赖了A的实例
- A的属性或setter依赖了B的实例,B的属性或者setter依赖了A的实例
Spring IOC解决办法
Spring并不是能解决所有的循环依赖情况,比如上方的情况1是无法解决的,并且如果Bean的scope是protype的,也无法解决。
-
scope=prototype
此种情况无法解决循环依赖,因为如果获取bean实例的时候,如果类正在创建中,则会抛出异常,详情查看
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法// Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
从注释我们就可以看出:如果创建bean实例失败则多半因为存在循环依赖。
/** * Return whether the specified prototype bean is currently in creation * (within the current thread). * @param beanName the name of the bean */ protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }
prototype类型的Bean在创建之前会进行标记和创建之后进行解标记
if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); }
protected void beforePrototypeCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal == null) { this.prototypesCurrentlyInCreation.set(beanName); } else ... }
protected void afterPrototypeCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal instanceof String) { this.prototypesCurrentlyInCreation.remove(); } else ... }
-
scope=singleton
Spring通过三级缓存的模式解决非构造器注入引起的循环依赖,详情查看
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法Object sharedInstance = getSingleton(beanName);
我们看下getSingleton方法:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 第一级缓存获取 Object singletonObject = this.singletonObjects.get(beanName); // 第一级未找到缓存且bean处于创建中(例如A定义的构造函数依赖了B对象,得先去创建B对象,或者在populatebean过程中依赖了B对象,得先去创建B对象,此时A处于创建中) 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; }
在singleton方法中出现了三个属性:
singletonObjects
,earlySingletonObjects
,singletonFactories
,这也就是上面说的三级缓存,这个方法的逻辑是先获取一级缓存,若一级缓存中不存在,则获取第二级缓存,二级缓存若也不存在则获取三级缓存,若三级缓存都不存在,则在getBean方法中当成prototype处理。那么这三级缓存中主要存的是什么东西呢?- 一级缓存:已经初始化完成的bean对象Cache
- 二级缓存:提前曝光的bean对象Cache
- 三级缓存:ObjectFactory工厂bean缓存, 存储实例化后的bean factory
Spring创建一个Bean的实例大致经过三个步骤:
- createBeanInstance:调用Bean的构造器进行实例化,调用构造器并未填充属性
- populateBean:填充属性,也就是setter所有的property
- initializeBean:初始化Bean,会调用指定的init方法,或者afterPropertiesSet方法,这个时候类属性已经注入完成了
三级缓存何时设置的
三级缓存的设置点是什么时候?我们来看下类的创建过程
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 1. Instantiate the bean. // 2. 创建三级缓存 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"); } // 获取Bean的ObjectFactory并放入三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { // 填充属性 populateBean(beanName, mbd, instanceWrapper); // 初始化Bean exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } return exposedObject; }
- 可以看到第三级缓存是在createBeanInstance方法之后就被设置,这个时候Bean的对象已经被创建出来了,只不过是还不够完美,只是一个壳,但是在容器中已经可以根据对象引用被认出来了;
- 第二级缓存是在getSingleton方法之后,若第三级缓存中已经存在,则将对象从三级缓存中转移到二级缓存中;
- 完全初始化之后将自己放入到一级缓存singletonObjects中