spring源码如何解决循环依赖

21 篇文章 1 订阅
11 篇文章 0 订阅

到这里咱们IOC简单流程基本上算结束了,但是有一个非常火热的解决方案值得咱们深入了解下,那就是循环依赖
下面咱们从这三个维度去分析:

  1. 什么是循环依赖?
  2. 造成循环依赖的原因是什么?
  3. spring是怎么解决的循环依赖?
  4. 需要了解的是对象实例化完成不等于初始化完成,分开执行
  5. 最后演示下流程

什么是循环依赖?

咱们都知道,spring创建bean分为实例化、填充属性、初始化等,当对象完成实例化后开始给属性赋值,这时咱们再去想一个场景,如果一个对象A有属性B b,对象B有一个属性是A a,咱们设想下,spring开始创建A对象对,然后给A对象的属性b进行赋值,那么这时候,如果发现容器中不存在对象b,这时就是进行创建B对象,当B对象实例化完成后,发现有一个属性a需要赋值,这时spring再去创建A对象,如果A对象不存在,然后再去创建A对象,坑能大家发现了,这时已经形成了一个闭环,也就是循环依赖问题
图解
在这里插入图片描述

造成循环依赖的原因是什么?

从上图可以看出,spring创建bean的时候就是先循环解析后bean定义信息,然后再getBean,找不到bean对象就会去创建,然后填充属性,属性对象不存在,再去创建,然后形成了一个闭环,大家可能也会发现,最主要的是递归的过程中没有结束条件了,如果有结束条件是不是就解决了这个问题呢?

spring是怎么解决的循环依赖?

这是我个人理解重要的点

  1. 实例化和填充属性分开
  2. 记录对象创建过程标记,用于记录递归调用栈
  3. 提前暴露对象
  4. 三级缓存(二级缓存只能解决普通对象的循环依赖,代理对象解决不了)

咱们先看下怎么从缓存中获取的

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 先从一级缓存中取
	Object singletonObject = this.singletonObjects.get(beanName);
	// 只有创建中的bean才会存在二级缓存和三级缓存
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// 从二级缓存中取
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// 从一级缓存中取
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					// 二级缓存中取
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						// 从三级缓存中取
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							// 这里注意下,三级缓存中取出的是Faction函数
							// Faction函数传递了三个参数,其中一个就是对外暴露的Bean实例,就是解决循环依赖的关键
							singletonObject = singletonFactory.getObject();
							// 放入到二级缓存,这时的对象并没有进行赋值
							this.earlySingletonObjects.put(beanName, singletonObject);
							// 删除三级缓存
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}
记录对象创建过程标记

从getSingleton这个方法可以看到,调用singletonFactory.getObject()方法前后对bean操作进行了标记,这也是

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	// synchronized 是可重入锁
	synchronized (this.singletonObjects) {
		// 这里做了二次检查
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			beforeSingletonCreation(beanName);
			try {
				// 当调用getObject方法时,会进行真正的创建工作
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}catch (BeanCreationException ex) {
			}finally {
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				// 新对象加入三级缓存
				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);
	}
}
提前暴露对象
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
	Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	// 这里进行了一次判断是否加入三级缓存
	// 单例 && 允许循环依赖 && 这个bean在创建中
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		// 这里最关键的是把刚创建的bean对象已参数的方式传给了函数式接口
		// 三级缓存拿到bean后放到二级缓存
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
	if (earlySingletonExposure) {
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
			if (exposedObject == bean) {
				exposedObject = earlySingletonReference;
			}
			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				String[] dependentBeans = getDependentBeans(beanName);
				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
				for (String dependentBean : dependentBeans) {
					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
						actualDependentBeans.add(dependentBean);
					}
				}
				if (!actualDependentBeans.isEmpty()) {
					throw new BeanCurrentlyInCreationException(beanName,
							"Bean with name '" + beanName + "' has been injected into other beans [" +
							StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
							"] in its raw version as part of a circular reference, but has eventually been " +
							"wrapped. This means that said other beans do not use the final version of the " +
							"bean. This is often the result of over-eager type matching - consider using " +
							"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}
	return exposedObject;
}
三级缓存

大家可以看到,bean实例化之后就添加到了三级缓存,删除了二级缓存,别的属性进行获取的这个对象的时候就可以在缓存中获取到了

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

流程演示

当给A对象填充属性的有一个属性B
在这里插入图片描述
B属性是一个引用类型
在这里插入图片描述
从工厂中获取b对象
在这里插入图片描述
这时缓存中是获取不到b对象的
在这里插入图片描述
获取不到也不能白来一趟不是,接着进行创建
在这里插入图片描述
创建b对象之前一样还是进行标记,毕竟是一个递归过程嘛
在这里插入图片描述
函数式接口,调用getObject后,会调用实际干活的方法
在这里插入图片描述
接着咱们又回到了doCreateBean这个方法了,添加完缓存后就有两个对象在三级缓存了
在这里插入图片描述
创建b对象完成后给b对象进行赋值
在这里插入图片描述
给b对象的属性a赋值需要先解决掉a对象
在这里插入图片描述
a属性也是一个引用运行时引用类型
在这里插入图片描述
再去容器中获取a对象
在这里插入图片描述
当咱们去缓存中获取a对象的时候发现,二级缓存中有数据了(这时候a对象只是创建完成状态,这里怎么理解的重点看下getSingleton(String beanName, boolean allowEarlyReference) 方法,因为初始getBean时先验证了下缓存中是否存在,如果三级缓存中有数据,直接获取到非完整对象放到了二级缓存)
在这里插入图片描述
返回到B对象给a属性赋值就获取到了
在这里插入图片描述
这时B对象也完成了初始化
在这里插入图片描述
B对象创建完成后添加到一级缓存
在这里插入图片描述
A对象创建完成后加入到一级缓存
在这里插入图片描述
springIOC流程到这里就结束了,下面springAOP见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值