@Autowired为何能解决循环依赖

文章目录

@Autowired的what&how

在spring框架下,我们可以通过@Autowired注解对属性或者方法参数进行标注,当spring ioc容器初始化时,会帮我们从容器中拿到对应的实例进行注入

什么是循环依赖

假如现在有两个Bean如下所示

public class BeanA {
    @Autowired
    private BeanB beanB;
}

public class BeanB {
@Autowired
private BeanA beanA;
}

    然后我们通过annotationConfigApplicationContext#register将两个bean的信息注入到容器中,最后通过refresh进行容器到初始化操作

    public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
            annotationConfigApplicationContext.register(Bean1.class);
            annotationConfigApplicationContext.register(Bean2.class);
            annotationConfigApplicationContext.refresh();
        }
    
     
     

      可以看到A跟B互相依赖,试着想象:当容器先初始化beanA时,必然要对属性beanB进行赋值,这个时候容器中还没有beanB,那么势必会触发beanB的初始化流程,而beanB初始化的完成也需要对属性beanA赋值,但beanA还未初始化完成,这里就产生了所谓的循环依赖。

      spring如何解决循环依赖

      这里有一个很关键的属性:

      public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
      	/** Cache of singleton factories: bean name to ObjectFactory. */
      	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
      }
      
       
       

        key是beanName,value是一个对象工厂,我们点进去看一下

        public interface ObjectFactory<T> {
        
        T <span class="token function">getObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> BeansException<span class="token punctuation">;</span>
        

        }

          其实这里的getObject()就是最终解决循环依赖所调用的方法。
          那么程序是怎样执行到这的呢?
          我们先从bean的创建入手
          如果容器还未实例化bean,那么就会走到这里

          protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
          			throws BeanCreationException {
          		BeanWrapper instanceWrapper = null;
          		if (instanceWrapper == null) {
          			//实例化bean,如果@Autowired加在构造方法上,
          			//那么就会在这里完成注入
          			//因为下面的回调还未注册,所以这里无法解决循环依赖
          			instanceWrapper = createBeanInstance(beanName, mbd, args);
          		}
          
          	<span class="token keyword">final</span> Object bean <span class="token operator">=</span> instanceWrapper<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          	
          	<span class="token keyword">boolean</span> earlySingletonExposure <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>allowCircularReferences <span class="token operator">&amp;&amp;</span>
          			<span class="token function">isSingletonCurrentlyInCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          	<span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonExposure<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          		<span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          			logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Eagerly caching bean '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span>
          					<span class="token string">"' to allow for resolving potential circular references"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          		<span class="token punctuation">}</span>
          		<span class="token comment">//往单例工厂(之前说的singletonFactories)中添加一个</span>
          		<span class="token comment">//ObjectFactory的匿名实现作为回调,</span>
          		<span class="token function">addSingletonFactory</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">&gt;</span> <span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          		
          		<span class="token comment">//属性赋值,处理@Autowired(非构造方法)</span>
          		<span class="token function">populateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> instanceWrapper<span class="token punctuation">)</span><span class="token punctuation">;</span>
          	<span class="token punctuation">}</span>
          

            这里我们发现,在实例化bean跟对属性赋值之间有一个addSingletonFactory的操作,作用是注册一个可以获取当前正在创建的bean的一个回调

            	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            		synchronized (this.singletonObjects) {
            			if (!this.singletonObjects.containsKey(beanName)) {
            				this.singletonFactories.put(beanName, singletonFactory);
            			}
            		}
            	}
            
             
             

              进入回调,发现回调默认返回的就是bean本身

              	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
              		Object exposedObject = bean;
              		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
              			for (BeanPostProcessor bp : getBeanPostProcessors()) {
              				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
              					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
              					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
              				}
              			}
              		}
              		return exposedObject;
              	}
              
              <span class="token keyword">default</span> Object <span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span>Object bean<span class="token punctuation">,</span> String beanName<span class="token punctuation">)</span> <span class="token keyword">throws</span> BeansException <span class="token punctuation">{<!-- --></span>
              	<span class="token comment">//	返回bean本身</span>
              	<span class="token keyword">return</span> bean<span class="token punctuation">;</span>
              <span class="token punctuation">}</span>
              
              • ok,这里得出一个结论,即使bean未初始化完成,spring也提供了方法来获取这个bean的实例。
                如果应用到我们上面的栗子中来就是:
                1. beanA实例化完成
                2. 添加获取beanA的回调到singletonFactories
                3. 调用populateBean,处理@Autowired,注入beanB

                因为beanB还未创建,那么势必会进入创建beanB的流程,当beanB也走到populateBean时,也需要完成beanA的注入,这时就会尝试从beanFactory中获取beanA,这里最终会进到
                AbstractBeanFactory的doGetBean中

                	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
                
                	<span class="token keyword">final</span> String beanName <span class="token operator">=</span> <span class="token function">transformedBeanName</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
                	Object bean<span class="token punctuation">;</span>
                
                	<span class="token comment">// Eagerly check singleton cache for manually registered singletons.</span>
                	Object sharedInstance <span class="token operator">=</span> <span class="token function">getSingleton</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                

                这里很关键,我们进入getSingleton(beanName)

                	public Object getSingleton(String beanName) {
                		return getSingleton(beanName, true);
                	}
                
                <span class="token keyword">protected</span> Object <span class="token function">getSingleton</span><span class="token punctuation">(</span>String beanName<span class="token punctuation">,</span> <span class="token keyword">boolean</span> allowEarlyReference<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
                	Object singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
                	<span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> null <span class="token operator">&amp;&amp;</span> <span class="token function">isSingletonCurrentlyInCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
                		<span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
                			singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlySingletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
                			<span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> null <span class="token operator">&amp;&amp;</span> allowEarlyReference<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
                				<span class="token comment">//拿到之前注册的单例工厂对象</span>
                				ObjectFactory<span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">&gt;</span> singletonFactory <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonFactories<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
                				<span class="token keyword">if</span> <span class="token punctuation">(</span>singletonFactory <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
                				    <span class="token comment">//调用之前注册的回调</span>
                					singletonObject <span class="token operator">=</span> singletonFactory<span class="token punctuation">.</span><span class="token function">getObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                					<span class="token keyword">this</span><span class="token punctuation">.</span>earlySingletonObjects<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> singletonObject<span class="token punctuation">)</span><span class="token punctuation">;</span>
                					<span class="token keyword">this</span><span class="token punctuation">.</span>singletonFactories<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
                				<span class="token punctuation">}</span>
                			<span class="token punctuation">}</span>
                		<span class="token punctuation">}</span>
                	<span class="token punctuation">}</span>
                	<span class="token keyword">return</span> singletonObject<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                

                  当beanB走到这里时通过beanA的beanName获取beanA,首先会尝试从singletonObjects中获取,这里肯定获取不到,因为singletonObjects的put操作是在bean初始化完成之后。所以只能通过调用之前注册的回调singletonFactory.getObject()来获取beanA。
                  那么到此beanA注入到beanB的顺利完成,当beanB初始化完成之后,其实beanA的getBean()也就返回了beanB的引用,到此beanA也可以顺利完成依赖注入。

                  评论 1
                  添加红包

                  请填写红包祝福语或标题

                  红包个数最小为10个

                  红包金额最低5元

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

                  抵扣说明:

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

                  余额充值