循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。如下图所示:
循环依赖,其实就是一个死循环的过程,在初始化 A 的时候发现引用了 B,这时就会去初始化 B,然后又发现 B 引用 C,跑去初始化 C,初始化 C 的时候发现引用了 A,则又会去初始化 A,依次循环永不退出,除非有终结条件。
Spring 循环依赖的场景有两种:
对于构造器的循环依赖,Spring 是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖,所以下面我们分析的都是基于 field 属性的循环依赖。
为什么 Spring 不处理 prototype bean 呢?其实如果理解 Spring 是如何解决 singleton bean 的循环依赖就明白了。这里先卖一个关子,我们先来关注 Spring 是如何解决 singleton bean 的循环依赖的。
我们先从加载 bean 最初始的方法 AbstractBeanFactory 的 #doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
方法开始。
在 #doGetBean(...)
方法中,首先会根据 beanName
从单例 bean 缓存中获取,如果不为空则直接返回。代码如下:
// AbstractBeanFactory.javaObject sharedInstance = getSingleton(beanName); |
调用 #getSingleton(String beanName, boolean allowEarlyReference)
方法,从单例缓存中获取。代码如下:
// DefaultSingletonBeanRegistry.java@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从单例缓冲中加载 beanObject singletonObject = (beanName);// 缓存中的 bean 为空,且当前 bean 正在创建if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 加锁synchronized (this.singletonObjects) {// 从 earlySingletonObjects 获取singletonObject = (beanName);// earlySingletonObjects 中没有,且允许提前创建if (singletonObject == null && allowEarlyReference) {// 从 singletonFactories 中获取对应的 ObjectFactoryObjectFactory<?> singletonFactory = (beanName);if (singletonFactory != null) {// 获得 beansingletonObject = Object();// 添加 bean 到 earlySingletonObjects 中this.earlySingletonObjects.put(beanName, singletonObject);// 从 singletonFactories 中移除对应的 ve(beanName);}}}}return singletonObject; } |
这个方法主要是从三个缓存中获取,分别是:singletonObjects
、earlySingletonObjects
、singletonFactories
。三者定义如下:
// DefaultSingletonBeanRegistry.java/*** Cache of singleton objects: bean name to bean instance.** 存放的是单例 bean 的映射。** 对应关系为 bean name --> bean instance*/ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/*** Cache of singleton factories: bean name to ObjectFactory.** 存放的是【早期】的单例 bean 的映射。** 对应关系也是 bean name --> bean instance。** 它与 {@link #singletonObjects} 的区别区别在,于 earlySingletonObjects 中存放的 bean 不一定是完整的。** 从 {@link #getSingleton(String)} 方法中,中我们可以了解,bean 在创建过程中就已经加入到 earlySingletonObjects 中了,* 所以当在 bean 的创建过程中就可以通过 getBean() 方法获取。* 这个 Map 也是解决【循环依赖】的关键所在。**/ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/*** Cache of early singleton objects: bean name to bean instance.** 存放的是 ObjectFactory 的映射,可以理解为创建单例 bean 的 factory 。** 对应关系是 bean name --> ObjectFactory*/ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); |
singletonObjects
:单例对象的 Cache 。singletonFactories
: 单例对象工厂的 Cache 。earlySingletonObjects
:提前曝光的单例对象的 Cache 。它们三,就是 Spring 解决 singleton bean 的关键因素所在,我称他们为三级缓存:
singletonObjects
earlySingletonObjects
singletonFactories
这里,我们已经通过 #getSingleton(String beanName, boolean allowEarlyReference)
方法,看到他们是如何配合的。详细分析该方法之前,提下其中的 #isSingletonCurrentlyInCreation(String beanName)
方法和 allowEarlyReference
变量:
#isSingletonCurrentlyInCreation(String beanName)
方法:判断当前 singleton bean 是否处于创建中。bean 处于创建中,也就是说 bean 在初始化但是没有完成初始化,有一个这样的过程其实和 Spring 解决 bean 循环依赖的理念相辅相成。因为 Spring 解决 singleton bean 的核心就在于提前曝光 bean 。allowEarlyReference
变量:从字面意思上面理解就是允许提前拿到引用。其实真正的意思是,是否允许从 singletonFactories
缓存中通过 #getObject()
方法,拿到对象。为什么会有这样一个字段呢?原因就在于 singletonFactories
才是 Spring 解决 singleton bean 的诀窍所在,这个我们后续分析。#getSingleton(String beanName, boolean allowEarlyReference)
方法,整个过程如下:
singletonObjects
获取。beanName
正在创建,就再从二级缓存 earlySingletonObjects
中获取。如果,还是没有获取到且允许 singletonFactories
通过 #getObject()
获取,则从三级缓存 singletonFactories
获取。如果获取到,则通过其 #getObject()
方法,获取对象,并将其加入到二级缓存 earlySingletonObjects
中,并从三级缓存 singletonFactories
删除。代码如下:
// DefaultSingletonBeanRegistry.javasingletonObject = Object(); this.earlySingletonObjects.put(beanName, singletonObject); ve(beanName); |
#getObject()
方法的执行结果,提早曝光的单例 Bean 对象。上面是从缓存中获取,但是缓存中的数据从哪里添加进来的呢?一直往下跟会发现在 AbstractAutowireCapableBeanFactory 的 #doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
方法中,有这么一段代码:
// AbstractAutowireCapableBeanFactory.javaboolean earlySingletonExposure = (mbd.isSingleton() // 单例模式&& this.allowCircularReferences // 运行循环依赖&& isSingletonCurrentlyInCreation(beanName)); // 当前单例 bean 是否正在被创建 if (earlySingletonExposure) {if (logger.isTraceEnabled()) {ace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 提前将创建的 bean 实例加入到 singletonFactories 中// 这里是为了后期避免循环依赖addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } |
#addSingletonFactory(...)
方法,将它添加到缓存中。三个条件如下: #addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
方法,代码如下:
// DefaultSingletonBeanRegistry.javaprotected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Null(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!ainsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);ve(beanName);isteredSingletons.add(beanName);}} } |
singletonFactories
这个三级缓存才是解决 Spring Bean 循环依赖的诀窍所在。同时这段代码发生在 #createBeanInstance(...)
方法之后,也就是说这个 bean 其实已经被创建出来了,但是它还不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可以根据对象引用定位到堆中对象),能够被认出来了。所以 Spring 在这个时候,选择将该对象提前曝光出来让大家认识认识。介绍到这里我们发现三级缓存 singletonFactories
和 二级缓存 earlySingletonObjects
中的值都有出处了,那一级缓存在哪里设置的呢?在类 DefaultSingletonBeanRegistry 中,可以发现这个 #addSingleton(String beanName, Object singletonObject)
方法,代码如下:
// DefaultSingletonBeanRegistry.javaprotected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);ve(beanName);ve(beanName);isteredSingletons.add(beanName);} } |
这个方法在我们创建 bean 的链路中有哪个地方引用呢?其实在前面博客 LZ 已经提到过了,在 #doGetBean(...)
方法中,处理不同 scope 时,如果是 singleton,则调用 #getSingleton(...)
方法,如下图所示:
我们关注 #getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法,代码如下:
// AbstractBeanFactory.javapublic Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Null(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = (beanName);if (singletonObject == null) {//....try {singletonObject = Object();newSingleton = true;}//.....if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;} } |
至此,Spring 关于 singleton bean 循环依赖已经分析完毕了。所以我们基本上可以确定 Spring 解决循环依赖的方案了:
singletonFactories
缓存中)。#getObject()
方法来获取了到这里,关于 Spring 解决 bean 循环依赖就已经分析完毕了。最后来描述下就上面那个循环依赖 Spring 解决的过程:
singletonFactories
),通过 ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject()
方法来拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中可能逻辑干看比较绕,胖友可以拿出一个草稿纸,画一画上面提到的 A、B、C 初始化的过程。
相信,胖友会很快明白了。
如下是《Spring 源码深度解析》P114 页的一张图,非常有助于理解。
本文发布于:2024-02-04 15:26:36,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170710501556688.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |