spring中的循环依赖

导读:本篇文章讲解 spring中的循环依赖,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

示例:

@Component
@EnableTransactionManagement
public class CircularRefA{

	@Autowired
    CircularRefB circularRefB;
    
	@Override
	public String toString() {
		return "CircularRefA";
	}

	@Transactional
	public void test(){
		System.out.println("------------------");
	}
}
@Component
public class CircularRefB {

	@Autowired
    CircularRefA circularRefA;

	@Override
	public String toString() {
		return "CircularRefB";
	}
}

如上图这种,出现相互引用的情况,A在创建之后填充属性B,B在创建之后又要填充属性A,会导致A又创建…会一直循环下去,但实际情况却不是,看spring源码时如何解决的。

如果是先实例化CircularRefA,会触发CircularRefA的getBean操作
CircularRefA

代码片段一:doGetBean()方法

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		.....

		//从缓存中拿实例
		Object sharedInstance = getSingleton(beanName);
		.....

		else {
			//如果singletonObjects缓存里面没有,则走下来
			//如果是scope 是Prototype的,校验是否有出现循环依赖,如果有则直接报错
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			.....

			try {
				//父子BeanDefinition合并
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				.....

				//单例
				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					//FactoryBean接口的调用入口
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				.....
				}
			}
			.....
		}
		return (T) bean;
	}

首先从缓存中拿CircularRefA实例,调用代码片段一中的getSingleton(beanName)方法。如下图所示:
在这里插入图片描述
假设spring作用域是单例的,会走到代码片段一如下图所示的部分:
在这里插入图片描述
看这个getSingleton的代码:
在这里插入图片描述

上图中的这个函数式接口,会进入createBean()方法中的doCreateBean()方法,返回创建好的实例,然后放入一级缓存中。

代码片段二: doCreateBean方法

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

		.....
		if (instanceWrapper == null) {
			//创建实例
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					//收集类中的注解@Resouce和@Autowired等等注解
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		
		//是否允许单例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));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//在这里进行依赖注入
			populateBean(beanName, mbd, instanceWrapper);
			.....
		}
		

		.....

		return exposedObject;
	}

在代码片段二中,判断是否允许bean提前暴露的条件是:
1、必须是单例
2、允许循环依赖
3、正在创建当中

如果条件满足,会添加一个三级缓存,为属性注入做铺垫,代码如图所示:

在这里插入图片描述

在这里插入图片描述

接下来在代码片段二中会调用 populateBean方法进行依赖注入CircularRefB,大致的流程如图所示:
在这里插入图片描述
又重新回到这里进行CircularRefB的实例化
在这里插入图片描述
这个流程跟CircularRefA的差不多,然后CircularRefB会进行属性装配CircularRefA,又重新回到下图所示进行CircularRefA的实例化。
在这里插入图片描述
还是先去缓存中查找CircularRefA的实例
在这里插入图片描述
三级缓存 singletonFactory.getObject() 函数式接口会走到getEarlyBeanReference()方法
在这里插入图片描述
在这里插入图片描述
因为CircularRefA中有@Transactional注解,所以最终会给CircularRefB注入一个CircularRefA的代理对象

在这里插入图片描述
这样就完成了CircularRefB的装配,接下来集合中删除表示正在创建的实例CircularRefB,并将CircularRefB添加到一级缓存当中,然后删除二级三级缓存。
在这里插入图片描述
在这里插入图片描述
CircularRefB实例化之后CircularRefA也就装配完成了,接下来的流程和CircularRefB的流程差不多,不再赘述。补充:多例实例不允许循环依赖,会直接报错,有兴趣的可自行查看源码。在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13825.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!