本文正在参与「金石计划」

1:为什么会产生循环依靠

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

Spring初始化一个Bean并不是简单的new A()这么简单,需求通过特点注入以及各种后置处理器的处理。

  • 1:当咱们初始化A的时分,在特点注入会发现需求注入一个目标B

  • 2:此刻Spring就会去Bean容器中找是不是有这个B目标,假如有就回来

  • 3:假如没有就会初始化B,此刻在对B进行特点注入的时分发现需求A,又要去初始化A

  • 4:此刻就造成了循环依靠的情况

2:Spring是如何处理循环依靠的

Spring是运用三级缓存来处理循环依靠的,精确来说应该是四层

  • singletonObjects:缓存现已初始化好的Bean
  • earlySingletonObjects:缓存正在初始化的Bean
  • singletonFactories:缓存的创立Bean的ObjectFactory,表明目标工厂,表明用来创立前期bean目标的工厂。

3:为什么需求三级缓存?二层不行吗?一层呢?

Spring为什么需要三级缓存来解决循环依赖

  • 1:创立A,将A这个原始目标放入缓存中
  • 2:特点注入,需求B目标
  • 3:创立B,进行特点注入需求A目标,从缓存中拿到原始目标A进行特点注入
  • 4:B创立完毕,然后A从容器中拿到B进行特点注入

为什么B能够运用缓存中的原始A目标,因为单例在容器中只需一个,虽然现在B中的特点A仍是个原始目标,也便是还没有进行特点赋值,可是后续在A初始化完之后,B运用的仍是这个A目标

从图中能够看出其实咱们运用一层缓存也能够处理循环依靠了,也便是earlySingletonObjects,那Spring为什么还要整个三级缓存呢?

在咱们创立A的运用,缓存中放的是原始A目标,假如这个目标是要通过AOP署理的目标怎样办呢?原本目标B中的A应该是个署理目标,可是现在缓存拿到的并不是署理目标,还有一个问题,关于B来说,我怎样知道要的这个A目标是一般目标仍是需求通过AOP署理过后的署理目标呢?

4: singletonFactories的作用

singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始目标之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持Lambda表达式: () -> getEarlyBeanReference( beanName , mbd , bean )

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;
}

也便是说在初始化A的时分,在特点注入之前会提早创立A目标的ObjectFactory放到这个singletonFactories中,后续B目标在需求A目标的时分就会调用这个 () -> getEarlyBeanReference( beanName , mbd , bean ) 来获取A目标。此刻假如这个A目标不需求通过AOP署理的就会生成一个一般目标,假如后续需求通过AOP署理此刻就会生成一个署理目标回来给B进行特点注入。

所以真实处理循环依靠的是singletonFactories,现在看起来是不是觉得没什么问题了呢

  • 假设这个A目标是需求通过AOP署理生成一个署理目标的,B目标运用的也是A的署理目标,看起来似乎没什么问题,可是AOP署理是在初始化之后履行的,也便是说你现在虽然提早生成了A的署理目标,可是在初始化A目标之后还要通过AOP的一次署理,那么作为单例来说,这二个署理目标是不一样的。

5:第四等级缓存 – earlyProxyReferences

在通过 () -> getEarlyBeanReference(beanName,mbd,bean) 得到的目标假如是通过AOP署理的目标就会放到这个earlyProxyReferences中,这样后续在初始化后置处理器中只需判断earlyProxyReferences中有没有这个Bean,假如有就不必再次做署理了

6:总结

Spring在处理循环依靠虽然运用了三级缓存,能够说是四级缓存,可是真实处理循环依靠的主要仍是singletonFactories这一级的缓存,所以至少是三级缓存才干处理循环依靠