关于java:Spring-是如何解决循环依赖的

23次阅读

共计 2920 个字符,预计需要花费 8 分钟才能阅读完成。

前言

置信很多小伙伴在工作中都会遇到循环依赖,不过大多数它是这样显示的:

还会提醒这么一句:

Requested bean is currently in creation: Is there an unresolvable circular reference?

老铁!这就是产生循环依赖了!

当然这里是一个异常情况。

在我的一篇文章中介绍如何防止 Spring 自调用事务生效,其中网友给倡议,说能够在类中注入本身,而后调用,而注入本身的过程也是循环依赖的处理过程。

上面就一起看一看,什么是循环依赖,以及 Spring 是如何解决循环依赖的?

什么是循环依赖

Dependency Resolution Process

Spring IoC 容器会在运行时检测到 构造函数注入 循环援用,并抛出 BeanCurrentlyInCreationException。

所以要防止构造函数注入,能够应用 setter 注入代替。

依据官网文档阐明,Spring 会主动解决基于 setter 注入的循环依赖。

当然在咱们工作中当初都应用 @Autowired 注解来注入属性。

PS: @Autowired 是通过反射进行赋值。

这里从咱们最常常应用的场景切入,看 Spring 是如何解决循环依赖的?

代码

@Service
public class CircularServiceA {

    @Autowired
    private CircularServiceB circularServiceB;
}

@Service
public class CircularServiceB {

    @Autowired
    private CircularServiceC circularServiceC;
}

@Service
public class CircularServiceC {

    @Autowired
    private CircularServiceA circularServiceA;

}

这里有 A、B、C 三个类,能够看到产生了循环依赖:

然而即便产生了循环依赖,咱们仍然能够启动 OK,应用并没有任何影响。

Spring 是如何解决循环依赖的

在 Spring 单例 Bean 的创立 中介绍介绍了应用三级缓存。

singletonObjects:一级缓存,存储单例对象,Bean 曾经实例化,初始化实现。

earlySingletonObjects:二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。

singletonFactories:三级缓存,存储 singletonFactory。

当然,这里看着比拟长,能够简化一下:

通过 Debug 来阐明生成过程

从 preInstantiateSingletons 办法开始:

增加断点 beanName.equals("circularServiceA")

启动 Debug:

会从缓存中获取单例 Bean

这里很显然获取不到,继续执行,创立单例实例

发现是单例再次获取

这里还会从一级缓存获取一次 circularServiceA,没有获取到,将 circularServiceA 增加到在创立的池子外面(singletonsCurrentlyInCreation 是一个 set 汇合)。

而后会调用工厂办法 createBean(beanName, mbd, args) 创建对象。

在 createBean 中去实例化 Bean。

判断是否是循环援用,是的话须要增加到三级缓存中。

circularServiceA 不在一级缓存中,则将 circularServiceA 的 singletonFactory 增加到 三级缓存(singletonFactories)中,同时从二级缓存中移除。

到这一步为止,circularServiceA 曾经在三级缓存中了。

开始对 Bean 的属性进行赋值。

在 populateBean 办法中执行到

PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);

就会对属性进行赋值

在 injet 办法中,回去解决相干依赖。

持续 Debug,发现解决依赖,最初发现其实又调用回 beanFactory.getBean(beanName);

不过这次创立的是 circularServiceB

上面是调用链:

circularServiceB 的过程和 circularServiceA 的一样,也是创立了三级缓存,而后去创立 circularServiceC

这时候三级缓存外面有它们三个的 singletonFactory。

circularServiceC 也调用到 doGetBean 办法去获取 circularServiceA

不过这次 调用到 Object sharedInstance = getSingleton(beanName); 的时候, circularServiceA 曾经存在了。

这次调用尽管没有从一级缓存(singletonObjects)中获取到 circularServiceA,然而 circularServiceA 创立中,所以进入判断

在这里执行完之后,circularServiceA 从三级缓存降级到二级缓存

应用反射对 circularServiceC 中的 circularServiceA 进行赋值,此时 circularServiceA 是在 二级缓存中。

那就比拟好奇了,这时候 circularServiceC 外面的 circularServiceA 曾经通过反射赋值,这个赋值给的是什么值?

查看代码:

这块是从三级缓存(singletonFactories)中获取的 singletonObject,而后调用

singletonObject = singletonFactory.getObject();

获取的一个对象

这里获取到的是 circularServiceA 的援用,留神 circularServiceA 这时候还没创立实现,只是援用。所以这里赋值的是 circularServiceA 的援用。

到这里 circularServiceC 就创立完了。

而后会将 C 增加到一级缓存和已注册列表中,同时从二级三级缓存中删除 C。

继续执行 B 和 A 的属性赋值以及后续的初始化流程。

至此,循环依赖解决结束。

总结

Spring 应用三级缓存来解决循环依赖的问题,三级缓存别离是:

  • singletonObjects: 一级缓存,存储单例对象,Bean 曾经实例化,初始化实现。
  • earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。
  • singletonFactories: 三级缓存,存储 singletonFactory。

本文也通过 Debug 来验证了应用三级缓存解决依赖的过程。

不过还有一些问题没有阐明:

  1. 循环依赖和代理之间的关系是什么?比方 @Transactional 和 @Async 注解会对循环依赖产生什么影响?
  2. 为什么要用三级缓存?二级缓存不能够么?

相干举荐

  • Spring 源码学习 16:单例 Bean 创立
  • Spring 源码学习 15:finishBeanFactoryInitialization(重点)
  • Spring 源码学习 14:initApplicationEventMulticaster

正文完
 0