Spring发生循环依赖的原因以及如何解决循环依赖的

一、 Spring发生循环依赖的原因

举个例子:

class Aservice{
  Bservise bservice;
}
class Bservice{
  Aservise aservice;
}

在创建Aservice时,发现需要依赖Bservice,Spring从单例池中查找Bservice,此时Bservice还未创建,则先实例化Bservice并填充Aservice属性,Spring又会去单例池中查找Aservice,由于Aservice还未创建,则实例化Aservice…一直循环下去。具体步骤如下:

1.实例化Aservice。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice

​ 2.1实例化Bservice

​ 2.2填充Bservice属性aservice。首先从单例池中查找Aservice,发现没找到,则开始创建Aservice

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。(AOP)

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。

5.将Aservice放进单例池中。

二、如何解决循环依赖?

出现循环依赖的原因是2.2步骤中Bservice填充aservice属性时,从单例池中查找Aservice没找到,又开始创建Aservice,解决方法可不可以添加一个Map<String,Bean> tempMap,其中key是BeanName,value是原始的Bean对象(还未进行属性填充)。具体步骤如下:

1.实例化Aservice。放入Map<String,Bean> tempMap中。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。放入Map<String,Bean> tempMap中。

​ 2.2填充Bservice属性aservice。首先从单例池中查找Aservice,发现没找到,则从Map<String,Bean> tempMap中查找,找到了!

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。(AOP)

5.将Aservice放进单例池中。

问题似乎得以解决?事实上没那么简单。在aop等场景,bean中需要填充的属性是代理对象,而目前填充的是原对象,且代理对象是在步骤4中才生成的。假设Aservice开启了AOP,步骤如下:

1.实例化Aservice。将原始对象放入Map<String,Bean> tempMap中。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。放入Map<String,Bean> tempMap中。

​ 2.2填充Bservice属性aservice(需要代理对象)。首先从单例池中查找Aservice,发现没找到,则从Map<String,Bean> tempMap中查找,找到了!(但找到的是原始对象)

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。AOP–>此时生成代理对象

5.将Aservice放进单例池中。(将代理对象放入单例池)

这种情况的解决办法就是判断是否会发生AOP等需要填充代理对象的场景。步骤如下:

1.实例化Aservice。是否发生AOP?

是:提前发生AOP,并将代理对象放入Map<String,Bean> tempMap中。

否:将原始对象放入Map<String,Bean> tempMap中。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。放入Map<String,Bean> tempMap中。

​ 2.2填充Bservice属性aservice(需要代理对象)。首先从单例池中查找Aservice,发现没找到,则从Map<String,Bean> tempMap中查找,找到了!(找到的是代理对象)

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。是否已经发生过AOP?如果已发生过就不再进行AOP。

5.将Aservice放进单例池中。(将代理对象放入单例池)

问题:在发生AOP时,是否所有情况下都会进行提前的AOP?
答案:不是的,只有在发生循环依赖的情况下才会进行提前的AOP。

那么如何判断发生了循环依赖呢?加入一个Set creatingSet,存放beanName,表示正在创建中的Bean。如果在属性填充时,发生某个Bean存在creatingSet中,那么一定发生了循环依赖。步骤如下:

0.加入集合creatingSet.add(“Aservice”);

1.实例化Aservice。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。放入Map<String,Bean> tempMap中。

​ 2.2填充Bservice属性aservice(需要代理对象)。首先从单例池中查找Aservice,发现没找到。aservice正在创建中(creatingSet中存在)?是:提前发生AOP,并将代理对象放入Map<String,Bean> tempMap中。否:将原始对象放入Map<String,Bean> tempMap中。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。是否已经发生过AOP?如果已发生过就不再进行AOP。

5.将Aservice放进单例池中。(将代理对象放入单例池)

6.creatingSet.remove(“Aservice”);

看似完美解决了循环依赖,但考虑以下情况:

class Aservice{
  @Autowired
  Bservise bservice;
  @Autowired
  Cservise cservice;
}
class Bservice{
  @Autowired
  Aservise aservice;
}
class Cservice{
  @Autowired
  Aservise aservice;
}

此时步骤为:

0.加入集合creatingSet.add(“Aservice”);

1.实例化Aservice。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。

​ 2.2填充Bservice属性aservice(需要代理对象)。首先从单例池中查找Aservice,发现没找到。aservice正在创建中(creatingSet中存在)?是:提前发生AOP,生成代理对象,并将代理对象放入Map<String,Bean> tempMap中。否:将原始对象放入Map<String,Bean> tempMap中。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

2.填充Aservice属性cservice。首先从单例池中查找cservice,发现没找到,则开始创建cservice。

​ 2.1实例化cservice。

​ 2.2填充Cservice属性aservice(需要代理对象)。首先从单例池中查找Aservice,发现没找到。aservice正在创建中(creatingSet中存在)?是:提前发生AOP,生成代理对象,并将代理对象放入Map<String,Bean> tempMap中。否:将原始对象放入Map<String,Bean> tempMap中。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。是否已经发生过AOP?如果已发生过就不再进行AOP。

5.将Aservice放进单例池中。(将代理对象放入单例池)

6.creatingSet.remove(“Aservice”);

对于以上情况,Aservice发生了两次AOP,产生了两个代理对象,而Aservice是单例的,给其他Bean填充的Aservice属性应该是同一个才对。那么能不能在发生提前AOP的时候就将Aservice代理对象放入单例池,后续需要的时候直接在单例池拿的?答案是不行的,因为单例池存放的Bean一定是完整的Bean,一定要经过Spring创建Bean的全过程,但是提前发生AOP产生的代理对象是不完整的,因此不能放入单例池。

如何解决这个问题?引入二级缓存产生的代理对象放入二级缓存中。步骤如下:

0.加入集合creatingSet.add(“Aservice”);

1.实例化Aservice。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。

​ 2.2填充Bservice属性aservice。单例池–>找不到—>二级缓存—>找不到----->creatingSet—>找到了---->发生提前AOP---->产生的代理对象放入缓存池。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

2.填充Aservice属性cservice。首先从单例池中查找cservice,发现没找到,则开始创建cservice。

​ 2.1实例化cservice。

​ 2.2填充Cservice属性aservice(需要代理对象)。单例池–>找不到—>二级缓存—>找到了。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。是否已经发生过AOP?如果已发生过就不再进行AOP。

5.将Aservice放进单例池中。从二级缓存中取出代理对象,放入单例池。

6.creatingSet.remove(“Aservice”);

通过以上方法,即可为不同的Bean注入同一个单例属性。

事实上,在发生AOP时,需要拿到原始对象,生成代理对象后,将原始对象赋值给代理对象的属性,在代理逻辑中会用到,而以上的实现方法,在发生AOP的时候,拿不到原始对象。解决方法:引入三级缓存。步骤如下:

0.加入集合creatingSet.add(“Aservice”);

1.实例化Aservice。放入三级缓存。

2.填充Aservice属性bservice。首先从单例池中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。

​ 2.2填充Bservice属性aservice。单例池–>找不到—>二级缓存—>找不到----->creatingSet—>找到了---->三级缓存—>拿到原始对象—>发生提前AOP---->产生的代理对象放入二级缓存。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。是否已经发生过AOP?如果已发生过就不再进行AOP。

5.将Aservice放进单例池中。从二级缓存中取出代理对象,放入单例池。

6.creatingSet.remove(“Aservice”);

由于在给代理对象赋值时需要BeanName,BeanClass,原始Bean等信息,Spring的三级缓存结构为:Map<String,Function>。function参数有beanName、BeanDefinition、原始Bean。

至此Spring的三级缓存如下:

一级缓存:Map<String, Object> singletonObjects

二级缓存:Map<String, Object> earlySingletonObjects

三级缓存:Map<String, ObjectFactory<?>> singletonFactories。

以Spring的集合描述步骤如下:

0.加入集合singletonsCurrentlyInCreation.add(“Aservice”);

1.实例化Aservice。放入三级缓存singletonFactories。

2.填充Aservice属性bservice。首先从单例池singletonObjects中查找Bservice,发现没找到,则开始创建Bservice。

​ 2.1实例化Bservice。

​ 2.2填充Bservice属性aservice。单例池singletonObjects–>找不到—>是否发生循环依赖singletonsCurrentlyInCreation---->二级缓存earlySingletonObjects—>找不到---->三级缓存singletonFactories—>拿到原始对象—>发生提前AOP---->产生的代理对象放入二级缓存earlySingletonObjects。

​ 2.3.填充其他属性(以Aware结尾的接口)

​ 2.4.其他重要的事情。

​ 2.5.将Bservice放进单例池中singletonObjects。

3.填充其他属性(以Aware结尾的接口)

4.其他重要的事情。是否已经发生过AOP?如果已发生过就不再进行AOP。

5.将Aservice放进单例池singletonObjects中。从二级缓存earlySingletonObjects中取出代理对象,放入单例池。

6.singletonsCurrentlyInCreation.remove(“Aservice”);

以下情况Spring能否帮我们解决循环依赖?

class Aservice{
  Bservise bservice;
  setBservice(Bservice bservice){this.bservice=bservice}
}
class Bservice{
  Aservise aservice;
}

答案是不能,构造器注入发生在实例化阶段,根本走不到下面的逻辑。解决方法是加上@Lazy注解,用懒加载注入。

class Aservice{
  Bservise bservice;
  @Lazy
  setBservice(Bservice bservice){this.bservice=bservice}
}
class Bservice{
  Aservise aservice;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值