Spring IoC之循环依赖处理

您所在的位置:网站首页 spring如何解决循环引用 Spring IoC之循环依赖处理

Spring IoC之循环依赖处理

2023-10-27 17:48| 来源: 网络整理| 查看: 265

什么是循环依赖如何检测循环依赖解决循环依赖实例分析分析参考文献

什么是循环依赖

循环依赖其实是循环引用,也就是两个或则两个以上的 bean 互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图所示:

注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。Spring 中循环依赖场景有:

构造器的循环依赖 field 属性的循环依赖

对于构造器的循环依赖,Spring 是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖,所以下面我们分析的都是基于 field 属性的循环依赖。

Spring 只解决 scope 为 singleton 的循环依赖,对于scope 为 prototype 的 bean Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。

如何检测循环依赖

检测循环依赖相对比较容易,Bean 在创建的时候可以给该 Bean 做标记,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。

解决循环依赖

我们先从加载 bean 最初始的方法 doGetBean() 开始,该方法位于 AbstractBeanFactory 类中。

在 doGetBean() 中,首先通过 transformedBeanName(name)获取 beanName,然后调用 DefaultSingletonBeanRegistry 类中 getSingleton()方法,该方法会根据 beanName 从单例 bean 缓存中获取,如果不为空则直接返回。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {    Object singletonObject = this.singletonObjects.get(beanName);    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {        synchronized(this.singletonObjects) {            singletonObject = this.earlySingletonObjects.get(beanName);            if (singletonObject == null && allowEarlyReference) {                ObjectFactory singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);                if (singletonFactory != null) {                    singletonObject = singletonFactory.getObject();                    this.earlySingletonObjects.put(beanName, singletonObject);                    this.singletonFactories.remove(beanName);                }            }        }    }

    return singletonObject;}

这个方法主要是从三个缓存中获取,分别是:singletonObjects、earlySingletonObjects、singletonFactories,三者定义如下:

private final Map singletonObjects = new ConcurrentHashMap(256);

private final Map

    

    

    

测试代码

@Testpublic void cycleRely(){    ClassPathResource resource = new ClassPathResource("config/beans.xml");    BeanFactory beanFactory = new XmlBeanFactory(resource);

    Person person = (Person) beanFactory.getBean("person");    System.out.println(person);}

运行结果为:

Person{name='herish', 拥有一辆car=AbstractCar{brand='宝马', money=Money{classification='工资', person=herish}}} 分析

关于上述代码用图解的形式表示如下:

对测试代码进行调试,大概了解其中的逻辑跳转。首先是 beanFactory.getBean("person"),该方法最终会定位在 doGetBean("person"),执行 getSingleton(beanName, allowEarlyReference)返回结果为 null,继续向下执行,如果 scope 为 单例的情况下,执行 createBean(beanName, mbd, args)方法,具体实现在 AbstractAutowireCapableBeanFactory 类中,然后调用 doCreateBean(),在该方法中,createBeanInstance()方法会先初始化对象赋零值,然后在 addSingletonFactory()中将”person“添加到三级缓存中,接着在 populateBean()方法中给对象添加属性内容,该部分的关键代码为 applyPropertyValues()方法,当发现有一个 Car 的引用属性,然后在 BeanDefinitionValueResolver 类中的 resolveValueIfNecessary()方法内跳转到 resolveReference(),进而调用 doGetBean("car")方法。

同上述过程一致,进而调用 doGetBean("money")方法,因为 Monery 类中有对 Person 的引用,所以最后又会执行 doGetBean("person")方法。关于这点就像递归调用一样,我们假设最初的 doGetBean("person")为第一层,则最后的为第四层,我们接下来要做的就是不断返回到上一层。此时 beanFactory 关于缓存的结果为:

再次进入到 getSingleton(beanName, allowEarlyReference)方法时,二级缓存中会添加”person“,三级缓存并删除它。接着会执行 getObjectForBeanInstance()方法,然后返回 person 对象。在 DefaultSingletonBeanRegistry 类中的 registerDependentBean()方法会记录 bean 之间的引用关系,然后返回第三层,在第三层的 populateBean()执行完毕之后,最后 doCreateBean()方法会返回这样的结果:

在 createBean()执行结束后,接着会执行 getSingleton(beanName,singletonFactory),该方法会将 money 对象加入到一级缓存中。在返回 money 对象后,返回第二层。第二层返回结果为:

将 car 对象加入到一级缓存之后,来到了第一层。第一层返回结果为:

然后将 person 对象加入到一级缓存中,再把 singletonsCurrentlyInCreation 清空。最后得到的 beanFactory 结果为:

到这里,关于 Spring 解决 bean 循环依赖就已经分析完毕了。最后来描述下就上面那个循环依赖 Spring 解决的过程(A为person,B为car,C为money):首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来,然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来,这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A),这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories ),通过 ObjectFactory 提前曝光,所以可以通过 ObjectFactory.getObject() 拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中,回到 B ,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

备注:测试案例也尝试了将 bean 之间的关系设置为依赖关系,但是执行结果报错。

参考文献

https://www.cnblogs.com/java-chen-hao/p/11139887.html



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3