Spring如何处理循环引用

Spring如何处理循环引用

一,划重点

Spring处理循环依赖记录,要是写错了,喷轻一点。
解题核心:
1.AbstractBeanFactory的2个getSingleton方法
2.earlySingletonObjects Map
3.singletonFactories Map
4.singletonsCurrentlyInCreation Set

二,上图

看图说话:
场景:A对象持有B对象,B对象持有A对象。

在这里插入图片描述

三,解析

A引用B,B引用A。
获取A时:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

然后第一次执行getSingleton()

Object sharedInstance = getSingleton(beanName);

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);

		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					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) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

第一次的时候肯定对是拿不到的,直接返回null。

第二次getSingleton()。

sharedInstance = getSingleton(beanName, () -> {
						try {
							// 创建Bean
							return AbstractBeanFactory.this.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.
							AbstractBeanFactory.this.destroySingleton(beanName);
							throw ex;
						}
					});

会执行getSingleton(),方法,并在执行到:

singletonObject = singletonFactory.getObject();

时,按照接口入参的函数式编程格式执行代码。也就是createBean()
此时仍是创建A。
相当牛逼的方法来了:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

这个方法会:
1.通过构造方法创建bean
2.将对象放入缓存处理循环依赖:singletonsCurrentlyInCreation 和singletonFactories
3.填充对象属性

doCreateBean会调用:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

			// 后置处理器开始处理 注入属性
			// CommonAnnotationBeanPostProcessor - 主要处理@Resource和初始化方法
			// AutowiredAnnotationBeanPostProcessor - 大名鼎鼎的自动注入,处理 @Autowired
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					if (filteredPds == null) {
						filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
					}
					pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						return;
					}
				}
				pvs = pvsToUse;
			}

利用:
postProcessProperties的回调,填充Bean的属性。

如下是:AutowiredAnnotationBeanPostProcessor#postProcessProperties
	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 找到需要注入的类型
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
		// 反射调用 filed.set注入
		// 在注入前,会获取注入类型的bean对象
			metadata.inject(bean, beanName, pvs);
		} catch (BeanCreationException ex) {
			throw ex;
		} catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

此时调用到这里,bean还是A,但是pvs也就是要注入的属性,是B。
B会重新走一遍上面的流程,并且同样在此处,找到要注入的属性A。
当A在走上面流程时,在第一个getSingleton时,会获取到放入singletonFactories的A对象,然后返回。至此循环依赖处理完成。

findAutowiringMetadata这个方法可以看出Spring是怎么找属性的,但是我没看太明白。
inject(),可以讲一下:
这个inject()有点坑,要看清楚是那个inject()

AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			// 解决循环依赖上的路径之一
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
				try {
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				} catch (NoSuchBeanDefinitionException ex) {
					// Unexpected removal of target bean for cached argument -> re-resolve
					value = resolveFieldValue(field, bean, beanName);
				}
			} else {
				// 牛皮glass
				// 1.获取属性值 value = resolveFieldValue(field, bean, beanName);获取的是完整的属性值,举例:
				// A持有B,则value为完整的B对象,且是放到了singletonObjects中的对象
				// A持有B,B也持有A,循环引用,value也为B,且B中持有的A已经填充完毕,并且放入了singletonObjects中,但是A中的B还没填充。
				// 经过下面代码 field.set(bean, value); A中的B就填充完成,至此循环依赖填充完毕
				// 2.反射完成注入 field.set(bean, value);
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}

接着调用到:

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency

result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);

	if (instanceCandidate instanceof Class) {
				// 如果属性是一个类,就从beanFactory中取出来 - 非常重要
				// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(java.lang.String)
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}

接着调用:

	public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {

		// 获取bean
		return beanFactory.getBean(beanName);
	}

这个getBean()是不是很熟悉,于是重复上面的流程。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值