spring的循环依赖


很多时候,写代码写的多,就会出现循环依赖,有时候会报错,有时候不会报错。这引起了我的好奇。通过对spring解决循环依赖思路的学习,充分理解了循环依赖,也解答了我的好奇。

一、什么是循环依赖

循环依赖是指循环引用,也就是两个或者两个以上的bean互相持有对方,互相引用,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。

二、循环依赖的场景

Java中的循环依赖有两种,一种是构造器的循环依赖,另一种是属性的循环依赖。

1、构造器的循环依赖

构造器的循环依赖就是在构造器中有属性循环依赖。

@Service
public class A {  
    public A(B b) {
     }
}

@Service
public class B {  
    public B(C c) {  
    }
}

@Service
public class C {  
    public C(A a) {  }
}

2、属性的循环依赖

@Service
public class A {  
    @Autowired  
    private B b;
}

@Service
public class B {  
    @Autowired  
    public C c;
}

@Service
public class C {  
    @Autowired  
    public A a;
}

三、怎么解决循环依赖

1、如何发现循环依赖

spring在创建对象之前的时候,会将放在一个Set容器里(singletonsCurrentlyInCreation),将beanName做为key,在这个容器中,表示该对象正在被创建中。

	// Names of beans that are currently in creation 
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

如果是构造器循环依赖,比如上文中的ABC构造器循环依赖,在创建A的时候singletonsCurrentlyInCreation中有A的beanName,然后发现需要B对象,需要获取B,然后发现B不存在,
那么继续创建B,于是在singletonsCurrentlyInCreation中存放B的beanName,在创建B对象的时候,需要获取C,查询C,发现C不存在,
那么现在singletonsCurrentlyInCreation中存放C的beanName,但是创建C的时候发现需要先创建A,获取A的时候spring发现singletonsCurrentlyInCreation存在A的beanName,那么表示A当前正在被创建中,
最后spring抛出异常。

2、如何解决循环依赖

首先,我们了解一下Spring它创建Bean的流程
createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
populateBean:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired)
initializeBean:调用spring的配置的init方法,比如:initMethod、InitializingBean等方法

我们了解,循环依赖主要存在于第一步、第二步中
在Spring容器的整个生命周期中,单例Bean有且仅有一个对象,所以,会大量使用缓存。
那么,spring会使用三级缓存来解决循环依赖的问题。

三级缓存

什么是三级缓存?我们来看spring的一段代码
上代码

	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
 
	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
 
	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

解决过程

bean对象填充属性的时候,如果bean对象是单例,并且是许提前暴露(一般都为true),那么调用 addSingletonFactory方法,将创建bean对象的工厂类(对象的引用)存放到第三级缓存(registeredSingletons)中,如果这个bean在创建过程中,该bean对象被完整创建,那么该对象的工厂类会被从registeredSingletons移除 然后加入到一级缓存singletonObjects中。

用之前的ABC对象举个循环引用的例子。

A对象装配之前,首先将自己的对象引用存放三级缓存中registeredSingletons,然后发现需要对象B,调用getSingleton(B)方法。在获取B的过程中,先去一级缓存中查询,如果没找到,再去二级缓存、三级缓存查找,这个时候因为B还没有创建,所以需要创建B对象。创建B对象的过程与A一致,B对象先将自己的对象引用存放到registeredSingletons中 ,然后发现这个时候需要对象C,再创建C。

这个时候循环引用出现了,C的创建时需要A,所以调用getSingleton(A)方法,但是之前A虽然没有创建完全,不在一级缓存singletonObjects,但是A的对象引用存在三级缓存registeredSingletons中,C获取到A之后,将A从三级缓存registeredSingletons中删除,移到二级缓存earlySingletonObjects中,然后C创建完成,放在一级缓存singletonObjects中,B也创建完成,放在一级缓存singletonObjects中,随后A也创建完成,放置一级缓存singletonObjects中。
循环引用的问题就此解决。
这样不利于理解,接下来进行图文结合的方式再次讲解
未完,待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖指的是在Spring中,多个Bean之间存在相互依赖的情况。具体来说,当一个Bean A依赖于另一个Bean B,同时Bean B也依赖于Bean A时,就形成了循环依赖。这种情况下,Spring需要解决Bean的创建和依赖注入的顺序问题。 在Spring中,循环依赖问题是由于Bean的生命周期所引起的。Spring的Bean生命周期包括了Bean的实例化、属性注入、初始化以及销毁等过程。当出现循环依赖时,Spring会通过使用“提前暴露”的方式来解决这个问题。 具体来说,当Spring创建Bean A时,发现它依赖于Bean B,于是会创建一个A的半成品对象,并将其暂时放入一个缓存中。然后,Spring会继续创建Bean B,并将其注入到A的属性中。接着,Spring会继续完成B的创建,并将其放入缓存中。最后,Spring会将A的半成品对象交给B进行依赖注入,完成A的创建,并将其从缓存中移除。 需要注意的是,Spring循环依赖有一定的限制条件。例如,如果Bean A和Bean B都是单例模式,那么它们之间的循环依赖是无法解决的。因为单例模式下,Bean的创建和依赖注入是同时进行的,无法通过缓存来解决循环依赖。在这种情况下,程序员需要手动调整Bean的依赖关系或使用其他解决方案来避免循环依赖的问题。 综上所述,Spring循环依赖是指在Spring中多个Bean之间存在相互依赖的情况。Spring通过使用缓存和提前暴露的方式来解决循环依赖问题,但在某些情况下有一定的限制条件需要注意。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值