Spring3.1.0实现原理分析(五).关于循环引用的探讨

       所谓循环引用是指对象之间的互相依赖形成了一个闭环,比如A依赖B,B依赖C,而C又反过来依赖A,这个是关于循环引用的基本认知。然后在Spring中循环引用又可细分成三种场景,我们逐一来分析下。

一. 单例bean基于构造函数的循环引用

       1). bean定义 

<!-- 基于构造函数的循环引用-->
           <bean id="circleA" class="entity.CircleA">
                 <constructor-arg index="0" ref="circleB" />
            </bean>
           <bean id="circleB" class="entity.CircleB">
                <constructor-arg index="0" ref="circleC" />
           </bean>
           <bean id="circleC" class="entity.CircleC">
               <constructor-arg index="0" ref="circleA" />
           </bean>

      上面一共定义三个对象,分别在自己的构造函数中引用其它对象。

       2). 执行结果

            执行失败,Spring会抛出异常

       3). 原因分析

            Spring创建一个对象大致可以分为四个步骤,分别是“实例化”,“填充属性值”,“初始化”“登记善后处理”,“注册单例Bean”(上述步骤暂时不考虑AOP的情况),这几个步骤后续博客会做详细分析,这里不展开了。根据上面的配置,Spring首先会尝试创建A对象,由于调用是非默认构造函数,所以实例化之前需要先获取实参对象(也就是B对象),Spring会先把正在实例化的A对象的ID置入DefaultSingletonBeanRegistry类的Set<String> singletonsCurrentlyInCreation集合中,然后去创建B对象。

            在实例化B对象的过程中,由于调用的也是非默认构造函数,所以Spring会把正在实例化的B对象的ID置入缓存中,然后尝试去创建C对象。实例化C对象的步骤跟上面一样,Spring发现实例化C对象必须要先创建A对象,会先把C对象的ID置入缓存。目前缓存中已经有A,B,C三个对象的ID。

            然后Spring又重新尝试去创建A对象,发现对B对象有依赖,当Spring把A对象的ID再次置入缓存中时,发现缓存中已经存在A对象的ID,于是乎,Spring觉得这是个死循环,就抛出异常了。
           

二. 单例bean基于setter的循环引用

      1). bean定义

 <!-- 基于setter的循环引用-->
       <bean id="circleA" class="entity.CircleA" >
            <property name="circleB" ref="circleB"/>
       </bean>
       <bean id="circleB" class="entity.CircleB" >
            <property name="circleC" ref="circleC"/>
       </bean>
       <bean id="circleC" class="entity.CircleC" >
            <property name="circleA" ref="circleA"/>
       </bean> 

      2). 执行结果

           执行成功

      3). 原因分析

           Spring首先实例化A对象,由于调用是的默认构造函数,在实例化这个步骤不存在对其它对象的依赖,所以A对象实例化成功,然后Spring创建一个ObjectFactory接口的实现类,并把它置入DefaultSingletonBeanRegistry类的Map<String, ObjectFactory> singletonFactories中。

           实例化A对象成功后,进入创建对象的第二个步骤,“填充属性值”,此时Spring发现需要先创建B对象,过程跟创建A对象一样,实例化B对象是成功的,在填充B对象的属性值时发现对C对象有依赖,然后创建C对象,实例化C对象成功后,开始填充C对象的属性,发现C对象对A对象有依赖,此时Spring根据A对象的ID从singletonFactories中获取ObjectFactory接口实现类,调用其getObject()方法获取A对象,然后把A对象赋值给C对象的属性,C对象完成了创建过程,再把C对象赋值给B对象的属性,B对象完成了创建过程,再B对象赋值给A对象的属性,至此A对象也创建成功了。

            补充说明下,在调用ObjectFactory#getObject()获取A对象时,如果存在实例化敏感bean后处理器(SmartInstantiationAwareBeanPostProcessor)的话,会调用处理器的getEarlyBeanReference(Object bean, String beanName)方法,对此时尚是半成品的A对象执行后处理操作。

三. 属性bean基于setter的循环引用

       1). bean定义

 <!-- 基于setter的非单例循环引用 -->
       <bean id="circleA" class="entity.CircleA" scope="prototype">
             <property name="circleB" ref="circleB"/>
       </bean>
       <bean id="circleB" class="entity.CircleB" scope="prototype">
            <property name="circleC" ref="circleC"/>
       </bean>
       <bean id="circleC" class="entity.CircleC" scope="prototype">
            <property name="circleA" ref="circleA"/>
       </bean> 

      2). 执行结果

            执行失败,Spring会抛出异常

      3). 原因分析

            A对象实例化成功后,由于是非单例Bean,所以并不会被缓存起来,从而导致死循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值