Spring源码学习 第三章 Spring是如何解决循环依赖源码解析


一、第一步从缓存中获取对象

在AbstractBeanFactory类中的doGetBean方法中第一步先尝试去一二三级缓存中获取我们的bean

//尝试去缓存中获取对象
		Object sharedInstance = getSingleton(beanName);
	Object singletonObject = this.singletonObjects.get(beanName);
		/**
		 * 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
		 * IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
		 */
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				/**
				 * 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
				 * 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象(纯净态)
				 * 就是早期对象
				 */
				singletonObject = this.earlySingletonObjects.get(beanName);
				/**
				 * 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
				 */
				if (singletonObject == null && allowEarlyReference) {
					/**
					 * 直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
					 * 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
					 * 暴露到三级缓存中
					 */
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					//从三级缓存中获取到对象不为空
					if (singletonFactory != null) {
						/**
						 * 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
						 * 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
						 */
						singletonObject = singletonFactory.getObject();
						//把早期对象放置在二级缓存,
						this.earlySingletonObjects.put(beanName, singletonObject);
						//ObjectFactory 包装对象从三级缓存中删除掉
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}

从一二三级缓存中获取,第一次进来当然都没有所以不会获取到,然后会进入创建Bean的流程.

二、创建Bean(第一次调用doGetBean)

在这个方法中开始创建我们真的bean对象

//把beanName 和一个singletonFactory 并且传入一个回调对象用于回调
					sharedInstance = getSingleton(beanName, () -> {
						try {
							//进入创建bean的逻辑
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

在这个方法把我们创建的bean放入三级缓存

		//把我们的早期对象包装成一个singletonFactory对象 该对象提供了一个getObject方法,该方法内部调用getEarlyBeanReference方法
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

在这个方法会给bean属性赋值,因为有循环依赖所以会调用创建bean的方法创建依赖的bean

		//属性赋值 给我们的属性进行赋值(调用set方法进行赋值)
			populateBean(beanName, mbd, instanceWrapper);

有引用对像会在这个方法调用getBean()方法去创建我们的引用对象,也就是循环到上边第一个方法去

			//循环我们当前bean的属性
		for (String propertyName : propertyNames) {
			//判断该属性名称是不是引用对象
			if (containsBean(propertyName)) {
				//显示的调用getBean所有属性的名称bean显示调用BeanFactory
				Object bean = getBean(propertyName);
				//把我们依赖的属性添加到pvs中
				pvs.add(propertyName, bean);
				//注册当前bean和属性依赖bean的依赖关系
				registerDependentBean(propertyName, beanName);
				if (logger.isDebugEnabled()) {
					logger.debug("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			}

2.引用对象创建(第二次调用doGetBean)

引用对象创建如上一样,最终会将引用对象缓存到三级缓存,这时我们的的三级缓存中就有我们本生的对象和引用对象,最后也会走到属性赋值然后再次调用doGetBean

3.解决循环依赖(第三次调用doGetBean)

因为此时三级缓存中能得到我们本来Bean的缓存对象所以不会再次去创建,所以此时就跳出循环,并且将三级缓存存到二级缓存。

//尝试去缓存中获取对象
		Object sharedInstance = getSingleton(beanName);
if (singletonFactory != null) {
						/**
						 * 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
						 * 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
						 */
						singletonObject = singletonFactory.getObject();
						//把早期对象放置在二级缓存,
						this.earlySingletonObjects.put(beanName, singletonObject);
						//ObjectFactory 包装对象从三级缓存中删除掉
						this.singletonFactories.remove(beanName);
					}

最后会通过此方法getSingleton,的钩子函数完成bean创建然后 ,将二级缓存的Bean存入一级缓存

sharedInstance = getSingleton(beanName, () -> {
						try {
							//进入创建bean的逻辑
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
							destroySingleton(beanName);
							throw ex;
						}

总结

解决循环依赖的思路是 A 依赖B 然后B 依赖A然后先后创建了A 和B并缓存到了三级缓存,此时再次调用doGetBean 获取A时三级缓存有数据就跳出循环,等所有bean创建好就会放入一级缓存。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值