java循环依赖

java循环依赖

什么叫循环依赖?
Java循环依赖是指两个或多个类之间存在相互依赖的情况,造成它们互相持有对方的实例而无法正确地加载和初始化。这种问题通常发生在使用Spring等依赖注入框架时,因为这些框架会自动管理类的实化和注入。
我们看个例子。首先创建两个实体类:A和B。A中有个B对象的属性,B中有个A对象的属性。

@Component
public class A {
	private B b;

	public B getB() {
		return b;
	}

	public void setB(B b) {
		this.b = b;
	}
}
@Component
public class B {
	private A a;

	public A getA() {
		return a;
	}

	public void setA(A a) {
		this.a = a;
	}
}

这是创建对象时的一个流程。当我们创建A对象时,需要把B对象填充到A的b属性,当我们去容器里查找时,没有则会去创建B对象,同样需要把A对象填充到B的a属性,但这时容器里没有A对象,我们就需要去创建A对象,这时就陷入了循环。
java循环依赖_第1张图片

解决方式:使用缓存打破循环。
当A实例完就将A对象放入缓存,这时的A只是创建了,属性全是默认值,可以认为是一个半成品。当B去填充a时,会从缓存里找A,这时就不会形成循环了。
java循环依赖_第2张图片
Sping的三级缓存:
Spring在加载BeanDefinition时,会将其缓存到三个不同的缓存中,分别是BeanDefinitionMap、BeanFactory和singletonObjects。其中:

BeanDefinitionMap缓存保存的是以BeanDefinition名称为键,BeanDefinition对象为值的一个Map。

BeanFactory缓存保存的是以Bean名称为键,对应的ObjectFactory对象为值的一个Map,这里的ObjectFactory是用于创建Bean实例的工厂对象。

singletonObjects缓存保存的是以Bean名称为键,对应的Bean实例为值的一个Map,这里的Bean实例是已经创建好的单例Bean实例。

这三个缓存是按照相应的顺序依次使用的,即先从BeanDefinitionMap中查找,如果没有找到,则从BeanFactory缓存中查找,如果还没有找到,则从singletonObjects缓存中取,如果都没有找到,再创建实例并加入到singletonObjects缓存中。这样,通过使用三级缓存,Spring可以提高Bean的加载效率,同时也避免了重复创建Bean实例的问题。

注:
一般的循环依赖其实二级缓存就可以解决,之所有使用三级缓存,是因为使用AOP时有代理对象的存在,二级缓存就无法解决,后续会更新说明。

你可能感兴趣的:(java,spring)