什么是循环依赖?Spring是怎么解决循环依赖的?
一、什么是循环依赖?
我们来看Spring官网文档对这个是怎么解释的:
链接放在这里了:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
简单来说,A对象有一个属性是B,B对象有一个属性是A,当A对象生成的时候依赖B对象,B对象生成的时候需要依赖A对象,这个时候就很容易形成一个闭环,如果这样一直死循环下去。
与此同时,我们从文档中得知,AB循环依赖只要A的注入方式是setter且是Singleton,就不会出现环循环以来的问题。即我们通过setter注入的方式,来间接实现循环依赖的破解。
重要!!!!!!:通常来说,如果问Spring是怎么解决循环依赖的,一定是指默认的单例Bean中,属性相互引用的场景。Prototype的场景不支持循环依赖,会报错。
二、循环依赖的解决
重要:Spring内部通过三级缓存来解决循环依赖的问题。核心类:DefaultSingletonBeanRegistry
所谓三级缓存,就是spring容器解决循环依赖的三个map:
一级缓存:singletonObjects,是一个ConcurrentHashMap,也叫单例池,存放已经经历了完整生命周期的Bean对象;
二级缓存:earlySingletonObjects,是一个HashMap,存放早期暴露出来的Bean对象,Bean的生命周期未结束,属性还未填充完整;
三级缓存:singletonFactories,第三级缓存value是一个工厂,三级缓存存放可以生成bean的工厂。
只有单例的Bean会通过三级缓存来解决循环依赖的问题,而非单例的Bean,每次从容器获取都是一个新的对象,都会重新创建,所以非单例的Bean是没有缓存的,不会放在三级缓存中。
2.1 理论准备
(1)实例化和初始化
- 实例化:内存中申请一块内存空间
- 初始化:属性填充,完成属性的各种赋值
二级缓存就是已经实例化但是未初始化的对象。
(2)关注三个map和四个方法
(3)AB对象再三级缓存中的迁移说明
- A创建过程需要B,于是A先将自己放入到三级缓存中,去实例化B
- B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,就查三级缓存,找到A,然后把三级缓存里面的A放到二级缓存,并删除三级缓存里面的A
- B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中的状态),然后回来继续创建A,此时B已经创建结束,直接从一级缓冲中拿到B,并完成创建,然后A把自己放到一级缓存中去。
2.2 源码分析
2.2.1 demo示例
为了方便断点分析这个过程,给出如下一个简单demo:
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean name="a" class="circulardependency.ClassA" scope="singleton"> <property name="b" ref="b"></property> </bean> <bean name="b" class="circulardependency.ClassB" scope="singleton"> <property name="a" ref="a"></property> </bean> </beans>
ClassA ClassB Main
/** * @Author leijs * @date 2021/8/23 */ @Data public class ClassA { private ClassB b; public ClassA() { System.out.println("A 初始化完成"); } } ========================================================================================= /** * @Author leijs * @date 2021/8/23 */ @Data public class ClassB { private ClassA a; public ClassB() { System.out.println("B 初始化完成"); } }
========================================================================================== import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @Author leijs * @date 2021/8/23 */ public class CircularMain { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); ClassA classA = context.getBean("a", ClassA.class); ClassB classB = context.getBean("b", ClassB.class); } }
2.2.2 断点过程
refresh:这个方法,Spring加载容器初始化的方法
进入refresh的方法:需要关注的:finishBeanFactoryInitialization(beanFactory)
进入方法:protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
也就是DefaultListableBeanFactory#preInstantiateSingletons的来处理。
@Override public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
首先我们要初始化两个Bean出来:
其次来到getBean方法
2.2.2.1 doGetBean
关注这里的doGetBean方法(spring以do开头的方法一般都是具体的实现逻辑)
来看下这里获取单例的方法:
@Nullable public Object getSingleton(String beanName) { return getSingleton(beanName, true); } /** * Return the (raw) singleton object registered under the given name. * <p>Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). * @param beanName the name of the bean to look for * @param allowEarlyReference whether early references should be created or not * @return the registered singleton object, or {@code null} if none found */ @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; }
可以看到是否允许提前暴露对象是true,但是第一次a进来的时候,从单例对象池singletonObjects获取,是拿不到的,所以最后retrurn null;
接下来,标记a正在创建:
核心进入Bean的创建:这里我们肯定是一个单例的Bean.
createBean --> doCreateBean
我们先来看getSingleton这个方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
关注singletonObject = singletonFactory.getObject(); 因为我们这个时候从容器中拿不到“a”这个Bean,通过回调来创建,这里就会来到我们的createBean(),再贴一下刚才这个地方:
mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // A previously detected exception with proper bean creation context already, // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry. throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
看这里的addSingletonFactory方法,首先会看一级缓存有没有这个bean, 此时是没有A的,然后把A加入到三级缓存;然后把A从二级缓存中删除,此时这个删除是空删,因为二级缓存也没有A。
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); } } }
2.2.2.2 属性填充--populateBean
到前面,我们生成了A,
我们A依赖B,
然后我们来处理B:
@Nullable private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { Object bean; String refName = ref.getBeanName(); refName = String.valueOf(doEvaluate(refName)); if (ref.isToParent()) { if (this.beanFactory.getParentBeanFactory() == null) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Can't resolve reference to bean '" + refName + "' in parent factory: no parent factory available"); } bean = this.beanFactory.getParentBeanFactory().getBean(refName); } else { bean = this.beanFactory.getBean(refName); this.beanFactory.registerDependentBean(refName, this.beanName); } if (bean instanceof NullBean) { bean = null; } return bean; } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); } }
看红色getBean, 又是doGetBean,和前面创造A类似创建B。从一级缓存中拿不到B,这个通过工厂创建B
相当于此时B和A都已经通过Bean工厂生成了。此时B发现也需要A。然后populateBean, 同样到resolveReference --> this.beanFactory.getBean --> getSingleton(beanName)
此时A在单例池中没有,但是是正在创建中,所以进来这个if判断。进一步分析,从二级缓存中拿A,此时二级缓存中没有A,从三级缓存中拿A,此时三级缓存是有的。
然后把A放到二级缓存,并从三级缓存中删除。相当于通过三级缓存中lambda表达式建造成的A放到了二级缓存。
有了A,B就可以正常完成属性的填充,
2.2.2.3 initialiezeBean
可以看到此时的B的A属性是已经填充好了。
接下来:AbstractBeanFactory # registerDisposableBeanIfNecessary --> DefaultSingletonBeanRegistry# addSingleton
也就是把B加入到了一级缓存,单例池,并从二级和三级缓存移除。
接下来,我们继续处理A
同样类似步骤,A从一级缓存可以拿到B,初始化完成后,A也加入一级缓存,并从三级缓存和二级缓存中删除。后面的步骤就不重要了。
三、其他问题
3.1 为什么需要三级缓存?
上面的断点过程是一个不包含AOP的简单过程。在Bean的整个生命周期中,有很多的BeanPostProcessor,我们可以Bean进行AOP的代理产生新的代理对象。
顺带贴一张Bean生命周期的图:
当拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用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这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。下面是重点,我们发现这个二级缓存好像显得有点多余,好像可以去掉,只需要一级和三级缓存也可以做到解决循环依赖的问题???
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,下面来看一下bean被AOP进行了切面代理的场景
我们发现A被AOP代理了,getEarlyBeanReference返回的是什么对象返回的是一个A的代理对象,还是被CGLIB代理的。
再执行一遍singletonFactory.getObject()返回的是否是同一个A的代理对象,并不是。singleFactory.getObject()方法又是一个新的代理对象,这就会有问题了,因为A是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为A是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。
所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
四、总结
Spring创建Bean对象主要分为两个步骤:
(1)创建原始Bean对象
(2)填充对象属性和初始化
每次创建Bean的之前,都会从缓存中查下有没有bean,因为是单例,只能有一个;当我们创建完成BeanA 的原始对象后,并把他放到三级缓存中,这时候发现依赖B,接着去创建Bean B , 同样的流程填充时发现依赖bean A又是同样的流程。不同的是,这个时候可以在三级缓存中查到刚放进去的原始对象beanA, 所以不需要继续创建,用它来注入Bean B , 完成B的创建。 既然B创建好了,所以A就可以继续完成填充属性的步骤了,闭环完成。
以上。