Spring中的循环依赖(单例)

本文详细探讨了Spring中的循环依赖问题,特别是针对单例模式的处理。解释了什么是循环依赖,提供了案例,并深入解析了解决循环依赖的原理,包括三级缓存的作用和在实例化过程中的关键步骤。同时说明了为什么Spring无法解决构造器的循环依赖,并阐述了使用三级缓存的原因。
摘要由CSDN通过智能技术生成

什么是 Spring 的循环依赖

循环依赖就是在多个 bean 中,相互依赖对方,导致在创建的时候无法加载。如:beanA 依赖了 beanBbeanB 又依赖了 beanCbeanC 最后又依赖了 beanA,成了一个闭环。循环依赖是 beanbean 之间才会发生的,而方法之间的相互调用的情况,叫做循环调用,此招无解最终会因为方法之间调用过多导致内存溢出

在这里插入图片描述
在代码中表现大概就是如下的情况:

public class BeanA {
   

    private BeanB beanB;
    
    public BeanB getBeanB() {
   
    	return beanB;
    }
    
    public void setBeanB(BeanB beanB) {
   
    	this.beanB = beanB;
    }
}
 
 
public class BeanB {
   

    private BeanC beanC;
    
    public BeanC getBeanC() {
   
    	return beanC;
    }
    
    public void setBeanC(BeanC beanC) {
   
    	this.beanC = beanC;
    }
}
 
 
public class BeanC {
   

    private BeanA beanA;
    
    public BeanA getBeanA() {
   
    	return beanA;
    }
    
    public void setBeanA(BeanA beanA) {
   
    	this.beanA = beanA;
    }
}

Spring 如何解决循环依赖

循环依赖的解决之前

循环依赖案列

@Component
public class ClassA {
   

	@Autowired
    private ClassB classB ;
}
 
@Component
public class ClassB {
   

	@Autowired
    private ClassA classA;
}

循环依赖的三级缓存

/*一级缓存,理解为:已经实例化,属性填充,初始化完成的 bean*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
 
/*三级缓存*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
 
/*二级缓存,理解为:实例化完成但属性填充未完成的 bean*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

spring 循环依赖的解决

想要探究 spring 循环依赖的原理,必须知道最基本的 springbean 创建流程,而在文章 spring源码之getBean(获取 bean)方法解读(二) 中已赘述了 bean 的创建流程步骤

先从 doGetBean() 方法开始,首先创建 ClassA

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

	/**
	 * 通过 name 获取 beanName。这里不使用 name 直接作为 beanName 有两个原因
	 * 1、name 可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean
	 *   实现类所创建的 bean。在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储
	 *   方式是一致的,即 <beanName, bean>,beanName 中是没有 & 这个字符的。所以我们需要
	 *   将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。
	 * 2、还是别名的问题,转换需要 &beanName
	 */
	final String beanName = transformedBeanName(name);
	Object bean;

	// 先从缓存中获取,因为在容器初始化的时候或者其他地方调用过getBean,已经完成了初始化
	Object sharedInstance = getSingleton(beanName);
	// 如果已经初始化过,直接从缓存中获取
	if (sharedInstance != null && args == null) {
   
		// 如果beanName的实例存在于缓存中
		if (logger.isDebugEnabled()) {
   
			if (isSingletonCurrentlyInCreation(beanName)) {
   
				logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
   
				logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
	
	   /**
		* 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
		* sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
		* bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
		* 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
		*/
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}

	else {
   
		// 如果是原型不应该在初始化的时候创建,在这里直接抛出异常
		if (isPrototypeCurrentlyInCreation(beanName)) {
   
			throw new BeanCurrentlyInCreationException(beanName);
		}

		// 获取parentBeanFactory
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
   
			// 将别名解析成真正的beanName
			String nameToLookup = originalBeanName
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值