Spring 之 循环依赖详解

概念

如果 class A 中依赖了 class B并且class B 中也依赖了class A,形成一个闭环就会产生循环依赖的问题。

解决

构造器注入方式的循环依赖,无法解决;

Setter注入方式的循环依赖,解决方式:

  1. Spring先用构造器实例化Bean对象,将实例化结束的对象放到一个Map中,并且Spring提供获取这个未设置属性的实例化对象的引用方法;
  2. 在进行属性注入的时候,依次获取到对应的Bean实例对象进行注入;

原理

在 AbstractBeanFactory 的 doGetBean 方法中,会首先通过缓存中去查找单例 Bean 对象。调用 getSingleton 方法,源码如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//检查缓存中是否存在实例
	Object singletonObject = this.singletonObjects.get(beanName);
	//如果单例bean为空但工厂内正在加载
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		//如果为空,则锁定全局变量并进行处理
		synchronized (this.singletonObjects) {
			//如果此bean正在加载则不处理
			singletonObject = this.earlySingletonObjects.get(beanName);
			//是否允许从提前曝光缓存singletonFactories中通过getObject拿到对象,解决属性注入循环依赖问题的关键
			if (singletonObject == null && allowEarlyReference) {
				//当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//调用预先设定的getObject方法
					singletonObject = singletonFactory.getObject();
					//记录在缓存中,earlySingletonObjects和singletonFactories互斥
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

从这个方法上我们可以看到 Spring 使用三级缓存解决循环依赖问题,每个缓存存储实例对象引用的不同生命周期:

  • singletonFactories : 单例对象工厂的cache,缓存第一步实例化后的对象
  • earlySingletonObjects :提前曝光的单例对象的Cache ,缓存第二步填充属性后的对象(用于检测循环引用,与singletonFactories互斥)
  • singletonObjects:单例对象的cache,缓存第三步初始化完成后的对象

在通过上一篇Spring 之 DI 详解,我们知道 Bean 初始化的三个重要步骤为:

  1. 调用 createBeanInstance 方法进行实例化对象
  2. 调用 populateBean 方法进行依赖注入
  3. 调用 initializeBean 方法执行 BeanPostProcessor 后置处理器

我们来看看实例化完之后在什么时候存储到 singletonFactories 缓存中,在 AbstractAutowireCapableBeanFactory.doCreateBean() 方法中。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
		throws BeanCreationException {

	//封装被创建的Bean对象
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		//根据指定bean使用对应的策略创建实例对象,如:指定的工厂方法、根据参数选择构造函数、默认无参构造方法
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	
	//...

	// 向容器中缓存单例模式的Bean对象,以防止循环引用
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		if (logger.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		//为避免后期循环依赖,尽早持有对象的引用
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	
	//...
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	//提前曝光实例化完成暂未进行属性注入的bean对象,以解决属性注入的循环引用问题
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

可以看到在 createBeanInstance 方法实例化对象之后(此时还未进行依赖注入)就会暴露到 singletonFactories 三级缓存集合中,以解决属性注入的循环引用问题。

在 AbstractBeanFactory 的 doGetBean 方法中,调用 createBean 方法

//创建单例模式的Bean的实例对象
if (mbd.isSingleton()) {
	//调用匿名内部类创建Bean实例对象,创建Bean实例对象,并且注册给所依赖的对象
	sharedInstance = getSingleton(beanName, () -> {
		try {
			//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			//从单例模式的Bean缓存中清除实例对象
			destroySingleton(beanName);
			throw ex;
		}
	});
	//获取给定Bean的实例对象,主要完成FactoryBean获取实例化对象过程
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

继续跟进 DefaultSingletonBeanRegistry.getSingleton() 方法,源码如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
	//全局变量需要同步
	synchronized (this.singletonObjects) {
		//首先检查对应的bean是否已经加载过,因为singleton模式其实就是复用已创建的bean
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果为空才可以进行singleton的bean的初始化
		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 {
				//初始化bean
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
				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) {
				//加入代表已经初始化完成的单例bean缓存中
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}
protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

从上面可看到,当 Bean 实例初始化完成后将会删除 singletonFactories 和 earlySingletonObjects 缓存中的引用,添加到 singletonObjects 缓存中。

总结

最后梳理一下Spring解决循环依赖的流程

1. 类 A 与 B 中属性相互引用,造成循环依赖
2. A 实例化完成,将自己提前曝光到 singletonFactories 缓存中
3. A 实例进行依赖注入,发现自己依赖对象 B,就尝试获取 B 实例引用
4. B 此时还没初始化,先进行实例化并将自己曝光到 singletonFactories 缓存中
5. B 实例进行依赖注入,发现自己依赖对象 A,就尝试获取 A 实例引用
6. 由于 A 实例尚未初始化完成,从 singletonObjects 中获取不到,从 earlySingletonObjects 中获取也没有,最后从 singletonFactories 中获取到
7. A 实例从 singletonFactories 中删除,添加到 earlySingletonObjects 缓存中去
8. B 实例拿到 A 实例引用后顺利完成依赖注入及初始化,并将自己从 singletonFactories 缓存中删除,添加到 singletonObjects 缓存中
9. A 实例获取 B 实例引用后,A 实例也能继续完成依赖注入及后续初始化操作
10. A 是从 earlySingletonObjects 中删除,添加到 singletonObjects 缓存中

在整个过程中,A 和 B 的实例引用并未改变过。只是在 Bean 生命周期的不同阶段。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值