spring依赖注入——循环依赖
Spring 如何解决循环依赖?
Spring源码初探-IOC(4)-Bean的初始化-循环依赖的解决
spring源码系列(一)——spring循环引用
Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的
1. 循环依赖
循环依赖其实就是循环引用,也就是两个或则两个以上的bean
互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A
如果在日常开发中我们用new
对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错;
Spring
针对循环依赖问题提供了解决方案,但是对于循环依赖的解决不是无条件的,有些情况引发的循环依赖依旧无解
以下面两个类为例
public class X {
Y y;
public X(){
System.out.println("X create");
}
public X(Y y){
this.y = y;
}
public Y getY() {
return y;
}
public void setY(Y y) {
this.y = y;
}
}
public class Y {
X x;
public Y(){
System.out.println("Y create");
}
public Y(X x){
this.x = x;
}
public X getX() {
return x;
}
public void setX(X x) {
this.x = x;
}
}
① setter
单例循环依赖(可以解决)
一般使用setter
方式注入解决循环依赖的问题
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="x" class="com.minifull.pojo.X">
<property name="y" ref="y"/>
</bean>
<bean id="y" class="com.minifull.pojo.Y">
<property name="x" ref="x"/>
</bean>
<context:component-scan base-package="com.minifull"/>
</beans>
成功
为了说明为什么是这样的,我们需要先了解Bean
实例化的过程
createBeanInstance()
实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进行populate
populateBean()
填充属性,这步对spring xml中指定的property进行populate
initializeBean()
调用spring xml中指定的init方法,或者AfterPropertiesSet方法
如图中前两步骤得知:Spring
是先将Bean
对象实例化之后再设置对象属性的
概述的来说
概述的来说,Spring
先是用构造实例化Bean
对象X,此时Spring
会将这个实例化结束但是还为设置依赖的不完整对象X放到一个Map
中,并且Spring
提供了获取这个未设置属性的实例化对象引用的方法,那么他的依赖Y实例化的时候就可以注入这个不完整的bean
X,打破闭环
① 首先Spring尝试通过ApplicationContext.getBean()方法获取X对象的实例,由于Spring容器中还没有X对象实例,因而其会创建一个A对象
② 然后发现其依赖了Y对象,因而会尝试递归的通过ApplicationContext.getBean()方法获取Y对象的实例
③ 但是Spring容器中此时也没有Y对象的实例,因而其还是会先创建一个Y对象的实例。
④ 此时X对象和Y对象都已经创建了,并且保存在Spring容器中了,只不过X对象的属性y和Y对象的属性x都还没有设置进去。
⑤ 在前面Spring创建Y对象之后,Spring发现Y对象依赖了属性X,因而还是会尝试递归的调用ApplicationContext.getBean()方法获取X对象的实例
⑥ 因为Spring中已经有一个X对象的实例,虽然只是半成品(其属性y还未初始化),但其也还是目标bean,因而会将该X对象的实例返回。
⑦ 此时,Y对象的属性x就设置进去了,然后还是ApplicationContext.getBean()方法递归的返回,也就是将Y对象的实例返回,此时就会将该实例设置到X对象的属性y中。
⑧ 这个时候,注意X对象的属性y和Y对象的属性x都已经设置了目标对象的实例了
从图中我们可以很清楚的看到,Y对象的X属性是在第三步中注入的半成品X对象,而X对象的Y属性是在第二步中注入的成品Y对象,此时半成品的X对象也就变成了成品的X对象,因为其属性已经设置完成了
详细的说,这涉及到了Spring
的三级缓存
我们在注入的时候,起初生成的X是半成品X对象,没有设置Y属性,那么Spring
是如何标记开始生成的X对象是一个半成品,并且是如何保存X对象的?
对于单例对象来说,在Spring
的整个容器的生命周期内,有且只存在一个对象,很容易想到这个对象应该存在Cache
中,Spring
大量运用了Cache
的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
//一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
// 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
// 为什么采用工厂方式,是因为有些Bean是需要被代理的,要被包装之后再使用,总不能把代理前的暴露出去那就毫无意义了
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
// 执行了工厂方法生产出来的Bean,bean被放进去之后,
// 那么当bean在创建过程中,就可以通过getBean方法获取到
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/** Names of beans that are currently in creation. */
//三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Names of beans currently excluded from in creation checks. */
//直接缓存当前不能加载的bean
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
当我们调用getBean
方法获取bean
的时候,会去调用AbstractBeanFactory.doGetBean()
方法
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 把传入的beanName(别名等)转换成容器中的banName
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 尝试从单例缓存集合里获取bean实例
Object sharedInstance = getSingleton(beanName);
//如果先前已经创建过单例Bean的实例,并且调用的getBean方法传入的参数为空,则执行if里面的逻辑
//args之所以要求为空是因为如果有args,则需要做进一步赋值,因此无法直接返回
if (sharedInstance != null && args == null) {
....
// 如果是普通bean,直接返回,如果是FactoryBean,则返回他的getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
//若scope为prototype或者单例模式但是缓存中还不存在bean
else {
//如果scope为prototype并且显示还在创建中,则基本是循环依赖的情况,针对prototype的循环依赖,spring无解,直接抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
// 从当前容器中找不到指定名称的bean,此时递归去parentFactory查找
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// 主要针对FactoryBean,将Bean的&重新加上
String nameToLookup = originalBeanName(name);
//如果parent容器依旧是AbstractBeanFactory的实例
//instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例
if (parentBeanFactory instanceof AbstractBeanFactory) {
//直接递归调用方法来查找
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
// 如果有参数,则委派父级容器根据指定名称和显式的参数查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
//委派父级容器根据指定名称和类型查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
//委派父级容器根据指定名称查找
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
//typeCheckOnly 是用来判断调用 getBean() 是否仅仅是为了类型检查获取 bean,而不是为了创建Bean
if (!typeCheckOnly) {
// 如果不是仅仅做类型检查则是创建bean
markBeanAsCreated(beanName);
}
try {
//将父类的BeanDefinition与子类的BeanDefinition进行合并覆盖
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//对合并的BeanDefinition做验证,主要看属性是否为abstract的
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 获取当前Bean所有依赖Bean的名称
String[] dependsOn = mbd.getDependsOn();
// 如果当前Bean设置了dependsOn的属性
//depends-on用来指定Bean初始化及销毁时的顺序
//<bean id=a Class="com.imooc.A" depends-on="b" />
// <bean id=b Class="com.imooc.B" />
if (dependsOn != null) {
for (String dep : dependsOn) {
//校验该依赖是否已经注册给当前 bean,注意这里传入的key是当前的bean名称
//这里主要是判断是否有以下这种类型的依赖:
//<bean id="beanA" class="BeanA" depends-on="beanB">
//<bean id="beanB" class="BeanB" depends-on="beanA">
//如果有这样的显示的循环依赖,则直接抛出异常,因为depends-on指出了创建的顺序
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
//缓存依赖调用,注意这里传入的key是被依赖的bean名称
registerDependentBean(dep, beanName);
try {
//递归调用getBean方法,注册Bean之间的依赖(如C需要晚于B初始化,而B需要晚于A初始化)
// 初始化依赖的bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
//如果BeanDefinition为单例
if (mbd.isSingleton()) {
//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 显式从单例缓存中删除 bean 实例,因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
destroySingleton(beanName);
throw ex;
}
});
// 如果是普通bean,直接返回,是FactoryBean,返回他的getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
//Prototype每次都会创建一个新的对象
...
......
}
主要有两个步骤
① 第一个步骤调用getSingleton()
方法尝试从缓存中获取目标对象,如果没有获取到,则尝试获取半成品的目标对象;如果第一个步骤没有获取到目标对象的实例,那么就进入第二个步骤;注意这里的getSingleton()
方法是SingletonBeanRegistry
的接口方法
② 第二个步骤,因为获取不到bean,说明这是第一次创建bean,所以调用DefaultSingletonBeanRegistry
的getSingleton()
方法尝试创建目标对象,并且为该对象注入其所依赖的属性
我们前面图中已经标明,在整个过程中会调用三次doGetBean()
方法
第一次调用的时候会尝试获取X对象实例,此时走的是第一个`getSingleton()`方法,由于没有已经创建的X对象的成品或半成品,因而这里得到的是`null`
然后就会调用第二个`getSingleton()`方法,创建X对象的实例,在创建X对象实例的时候会递归的调用`doGetBean()`方法,尝试获取Y对象的实例以注入到X对象中(此时把还未赋属性值的x放到三级缓存)
此时由于`Spring`容器中也没有Y对象的成品或半成品,因而还是会走到第二个`getSingleton()`方法,在该方法中创建Y对象的实例
创建完成之后,把未赋属性值的y放入三级缓存,尝试获取其所依赖的X的实例作为其属性,因而还是会递归的调用`doGetBean()`方法
此时需要注意的是,在前面由于三级缓存已经有了一个半成品的X对象的实例,因而这个时候,再尝试获取X对象的实例的时候,会走第一个`getSingleton()`方法
在该方法中会得到一个半成品的X对象的实例,然后将该实例返回,并且将其注入到Y对象的属性x中,此时Y对象实例化完成。(并且把x实例从三级缓存提升到二级缓存)
然后,将实例化完成的Y对象递归的返回,此时就会将该实例注入到X对象中,这样就得到了一个成品的X对象,并把它们都放在一级缓存
看一下SingletonBeanRegistry
的接口方法getSingleton
方法,也就是第一个尝试获取bean
的getSingleton
方法
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从一级缓存里面获取完备的Bean
//一级缓存是用来保存最终形态的Bean 也就是完备的Bean
Object singletonObject = this.singletonObjects.get(beanName);
//前面有说到过singletonsCurrentlyInCreation是用来保存正在创建的Bean的beanname的
//如果完备的单例还没有创建出来,创建中的Bean的名字会被保存在singletonsCurrentlyInCreation中
//看看是否正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//尝试给一级缓存对象加锁,因为接下来就要对缓存对象操作了
synchronized (this.singletonObjects) {
//尝试从二级缓存earlySingletonObjects这个存储还没进行属性添加操作的Bean实例缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
//如果还没有获取到并且第二个参数为true,为true则表示bean允许被循环引用
if (singletonObject == null && allowEarlyReference) {
//从三级缓存singletonFactories这个ObjectFactory实例的缓存里尝试获取创建此Bean的单例工厂实例
//之所以使用工厂实例,是因为这里的Bean是要被代理的,要被包装后再使用
//(注意不要把ObjectFactory和FactoryBean弄混淆,虽然里面都有一个getBean()方法,不直接使用FactoryBean是为了区分他们两个的不同使用场景)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果获取到工厂实例
if (singletonFactory != null) {
//调用单例工厂的getObject方法返回对象实例
singletonObject = singletonFactory.getObject();
//将实例放入二级缓存里
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存里移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
Spring首先从singletonObjects
(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects
(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories
通过getObject
获取,则通过singletonFactory.getObject()
(三级缓存)获取。如果获取到了则移除对应的singletonFactory
,将singletonObject
放入到earlySingletonObjects
,其实就是将三级缓存提升到二级缓存中
这里我们会存在一个问题就是A的半成品实例是如何实例化的,然后是如何将其封装为一个ObjectFactory
类型的对象,并且将其放到上面的singletonFactories
属性中的。
这主要是在前面的第二个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) {
......
//beforeSingletonCreation判断当前正在实例化的bean是否存在正在创建的集合当中,说白了就是判断当前是否正在被创建
//spring不管创建原型bean还是单例bean,当他需要正式创建bean的时候他会记录一下这个bean正在创建(add到一个set集合当中);故而当他正式创建之前他要去看看这个bean有没有正在被创建(是否存在集合当中)
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//ObjectFactory<?> singletonFactory
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
......
}
catch (BeanCreationException ex) {
......
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//将BeanName从正在创建集合清除
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
//beforeSingletonCreation
protected void beforeSingletonCreation(String beanName) {
//this.inCreationCheckExclusions.contains(beanName)这里是判断当前需要创建的bean是否在Exclusions集合,被排除的bean,程序员可以提供一些bean不被spring初始化(哪怕被扫描到了,也不初始化),那么这些提供的bean便会存在这个集合当中
//this.singletonsCurrentlyInCreation.add(beanName),如果当前bean不在排除的集合当中那么则这个bean添加singletonsCurrentlyInCreation,表示这个bean正在创建
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
其最终会通过其传入的第二个参数,在singletonFactory.getObject
中调用createBean()
方法,该方法的最终调用是委托给了另一个doCreateBean()
方法进行的
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
//bean实例包装类 里面有处理Bean里面属性的方法
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//从未完成创建的包装Bean缓存中清理并获取相关中的包装Bean实例,毕竟是单例的,只能存一份
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//创建bean的时候,这里创建bean的实例有三种方法
//1.工厂方法创建
//2.构造方法的方式注入
//3.无参构造方法注入
//createBeanInstance完成了对象的创建 仅仅是对象的创建
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//获取被包装的Bean,后续对bean的改动相当于对Wrapper的改动,反之依然
final Object bean = instanceWrapper.getWrappedInstance();
//获取实例化对象的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
.....
// 判断当前要创建的bean是否为单例
// spring是否配置了支持提前暴露目标bean,也就是是否支持提前暴露半成品的bean
// 以及判断这个bean是否是正在创建中的bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 如果支持,这里就会将当前生成的半成品的bean放到singletonFactories中,这个singletonFactories就是前面第一个getSingleton()方法中所使用到的singletonFactories属性,也就是说,这里就是封装半成品的bean的地方
// 而这里的getEarlyBeanReference()本质上是直接将放入的第三个参数,也就是目标bean直接返回
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
try {
// 在初始化实例之后,这里就是判断当前bean是否依赖了其他的bean,如果依赖了,
// 就会递归的调用getBean()方法尝试获取目标bean
populateBean(beanName, mbd, instanceWrapper);
} catch (Throwable ex) {
// 省略...
}
return exposedObject;
}
//getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
//如果是SmartInstantiationAwareBeanPostProcessor类型,就进行处理,
// 如果没有相关处理内容,就返回默认的实例。
// 里面的AbstractAutoProxyCreator类是后续AOP的关键
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
//addSingletonFactory
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);
//清除此Bean在二级缓存里的缓存信息
this.earlySingletonObjects.remove(beanName);
//这里为了记录注册单例的顺序
this.registeredSingletons.add(beanName);
}
}
}
这就完成了往三级缓存中添加singletonFactory
的操作
回到当前的getSingleton
方法,在创建完bean以后会调用addSingleton(beanName, singletonObject);
,这个方法就是把以及属性赋值了的,也就是完全创建好的bean
从二级缓存移除,放入一级缓存
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//Bean实例完成创建之后,只保留一级缓存以及注册beanName的顺序,其余的清除
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
所以具体来说,Spring解决上面的循环依赖的过程是这样的
② 构造器循环依赖(无法解决)
通过构造器注入构成的循环依赖,此依赖是无解的,强行依赖只能抛出异常(BeanCreationException
)
Spring
容器将每一个正在创建的bean
标识符放在一个“当前创建bean
池”中,bean
标识符在创建过程中将一直保持在这个池中,因此在创建bean
的过程中如果发现自己已经在池中,则抛出BeanCurrentlyInCreationException
异常表示循环依赖;而对于创建完毕的bean
将从“当前创建bean
池”中清除掉
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="x" class="com.minifull.pojo.X">
<constructor-arg name="y" ref="y"></constructor-arg>
</bean>
<bean id="y" class="com.minifull.pojo.Y">
<constructor-arg name="x" ref="x"></constructor-arg>
</bean>
</beans>
public void test1(){
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
Object x = applicationContext.getBean("x");
System.out.println(x);
}
出现循环依赖错误
前面说到解决循环依赖的关键是先调用addSingletonFactory
方法将刚创建出来但是还未赋属性值的Bean
封装到ObjectFactory
并放置到三级缓存中,之后再调用populateBean
递归为属性赋值;
而对于构造器方法初始化bean
来说,通过源码可以看到它的初始化和属性赋值是发生在三级缓存之前的createBeanInstance
方法中
因为这个时候根本就没有缓存的支持,同时也不存在中间形态,所以不支持构造器的循环依赖
③ prototype
范围的依赖处理(无法解决)
对于prototype
作用域bean
,spring
容器无法完成依赖注入,因为spring
容器不进行缓存prototype
作用域的bean
,因此无法提前暴露一个正在创建中的bean
如果是多例的话,比如a -> b
并且 b -> a
那么,当A a=new A()
; 之后要注入b,b却是多例的,那么究竟该注入哪个b是不确定的
2. 附录
① 三级缓存有什么用呢?为什么一开始要存工厂呢?为什么一开始不直接存二级缓存?
之所以使用工厂实例,是因为Spring的高扩展性;
假设在aop的场景下,x注入y,y也要注入x,但是y需要注入的x需要加代理(aop),但是加代理的逻辑是在注入属性之后的;也就是说假设一开始直接把x的bean存入二级缓存,那么y获取的x是还不是一哥个代理对象;
然而使用工厂就不一样了,y获取x的时候调用工厂的getObject
方法,spring在getObject
方法中通过后置处理器等一系列操作判断这个时候x被aop配置了故而需要返回一个代理的x注入给y
② Spring
解决循环依赖的时候为什么不只使用第三级和第一级缓存
像上面说到的,使用三级缓存存放一个bean
工厂和使用一级缓存存放已经完全的bean
是必要的,那么二级缓存有什么用呢?
这是为了提供效率而设计的,比如x,y,z相互循环依赖,那么第一次y注入x的时候从三级缓存通过工厂返回了一个x,放到了二级缓存,而第二次z注入x的时候便不需要再通过工厂去获得x对象了,提高了效率
③ 关闭循环依赖
Spring的循环依赖是可以手动关闭的,在AnnotationConfigApplicationContext
的构造方法中加了一行setAllowCircularReferences(false);
即关闭循环依赖
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
//关闭循环依赖
setAllowCircularReferences(false);
register(componentClasses);
refresh();
}