-
什么是循环依赖
- A引用了B,而这个时候B也引用了A,那么这种情况实际上就是出现了循环依赖的问题了,实际上也可以把循环依赖称之为循环引用,两个或者两个以上的bean互相持有对方,最终形成闭环
- 这里不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除非有终结条件,否则的话,他就是一个死循环
-
spring的循环依赖
- spring的循环依赖有:
- 构造器的循环依赖
- field属性的循环依赖
- 构造器的循环依赖实际上是个无解的操作,只能抛出BeanCurrentlyInCreationException异常,也就是说,这个构造器导致的循环依赖,spring是没有办法来处理的,也只是给抛出了异常
- spring的循环依赖有:
-
spring解决循环依赖
- spring的单例对象的初始化主要分为三个步骤
- createBeanInstance 实例化
- populateBean 填充属性
- initializeBean 初始化
- createBeanInstance实例化实际上就是调用对象的构造方法实例化对象
- populateBean实际上就是对bean的依赖属性进行一个赋值填充
- initializeBean则是调用spring xml中的int方法
- 但从bean的初始化的过程来看,循环依赖发生的位置就是在createBeanInstance实例化,以及populateBean填充属性中
- 发生的循环依赖是:构造器的循环依赖和field属性的循环依赖
- spring的单例对象的初始化主要分为三个步骤
-
三级缓存
-
singletonFactories:单例对象工厂的cache,用于存放完全初始化好的bean,从该缓存中取出的bean可以直接使用
-
earlySingletonObjects:提前曝光的单例对象的cache,存放原始的bean对象(尚未填充属性),用于解决循环依赖
-
singletonObjects:单例对象的cache,存放bean工厂对象,用于解决循环依赖
-
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
-
-
-
对象创建过程
-
AbstractBeanFactory
中的doGetBean()
方法-
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
-
-
DefaultSingletonBeanRegistry
中的getSingleton()
方法-
protected Object getSingleton(String beanName, boolean allowEarlyReference)
-
在这个方法中,先从一级缓存singletonObjects中去获取,如果获取到就直接return
-
如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取
-
如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()获取
-
如果获取到了,则从singletonFactories中移除,并放入earlySingletonObjects中
-
这相当于把三级缓存中的数据剪贴到了二级缓存中
-
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; }
-
-
AbstractAutowireCapableBeanFactory
中的doCreateBean()
方法-
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) //添加到三级缓存 if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
-
-
AbstractAutowireCapableBeanFactory
中的populateBean()
方法进行属性赋值-
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
-
-
AbstractAutowireCapableBeanFactory
中的initializeBean()
初始化对象-
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
-
-
-
解决
- spring解决循环依赖的诀窍就在于singletonFactories这个三级cache
- 这个cache的类型是ObjectFactory,这里就是解决循环依赖的关键,发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)
- 这哦对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以spring此时将这个对象提前曝光出来让大家认识,让大家使用
-
二级缓存到底能不能解决
-
二级缓存也是能够实现的,如果自己想要实现,那么就得重写AbstractAutowireCapableBeanFactory的doCreateBean的方法
-
//添加到三级缓存 if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); //从三级缓存中取出立刻放入二级缓存 getSingleton(beanName, true); }
-
如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了spring的设计原则
-
spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postPocessAfterInitialization方法中对初始化后的Bean完成AOP代理
-
如果出现了循环依赖,那没有办法,只能给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理
-