SpringFramework核心 - IOC容器的实现 - 循环依赖

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,所以调用DefaultSingletonBeanRegistrygetSingleton()方法尝试创建目标对象,并且为该对象注入其所依赖的属性

我们前面图中已经标明,在整个过程中会调用三次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方法,也就是第一个尝试获取beangetSingleton方法

@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作用域beanspring容器无法完成依赖注入,因为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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值