springboot bean的迴圈依賴實現 原始碼分析

wang03發表於2021-06-26

springboot bean的迴圈依賴實現 原始碼分析

本文基於springboot版本2.5.1

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

本文主要聚焦在迴圈依賴部分,主要用單例bean來進行講解,其他bean實現的流程不會過多涉及。

1、什麼叫迴圈依賴呢

簡單來說就是springboot容器中的多個bean,如A、B兩個bean,A有屬性B需要注入,B有屬性A需要注入,形成相互依賴的情況。

看下程式碼,就是類似下面這種情況

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

上面有兩個bean,分別是ServiceA,ServiceB。ServiceA中需要注入ServiceB的例項,ServiceB中需要注入ServiceA的例項,這就是一種典型的迴圈依賴,其他還有方法引數迴圈依賴的場景等等,但是它們的內部實現基本是一樣的。

2、具體出現迴圈依賴的程式碼邏輯

  1. 獲取bean的方法

    在springboot中預設的beanFactory是DefaultListableBeanFactory,在我們獲取bean物件的時候,如果bean物件存在就直接返回,如果不存在,就先建立bean物件再返回。

    我們先看下我們獲取bean的常用方法都有哪些

    public <T> T getBean(Class<T> requiredType) throws BeansException
    public Object getBean(String name) throws BeansException
    public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException
    public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
    public void preInstantiateSingletons() throws BeansException 
    

    常用的獲取bean的方法主要有上面幾個和它們的過載版本,對於第3行、第4行、第5行最終都會呼叫到第2行的方法來獲取bean。而它也會通過呼叫doGetBean(在AbstractBeanFactory這個類中)來獲取bean

    	public Object getBean(String name) throws BeansException {
    		return doGetBean(name, null, null, false);
    	}
    

    第1行的方法也會呼叫doGetBean來獲取bean

    	public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
    			throws BeansException {
    
    		return doGetBean(name, requiredType, args, false);
    	}
    

    所有最終獲取bean的方法都是

    	protected <T> T doGetBean(
    			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    			throws BeansException {
    

    這個方法,這個方法是protected的,是不對外提供的。所以我們不能直接呼叫它,只能通過上面提供的5個方法來獲取bean物件。

  2. 下面我們從doGetBean這裡來看下serviceA建立的過程

    	protected <T> T doGetBean(
    			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    			throws BeansException {
    			//如果bean之前存在,這裡返回的shareInstance就是非空,就會從後面的if分支中返回,如果bean之前不存在,就會執行後面的bean建立及注入屬性的過程
    			Object sharedInstance = getSingleton(beanName);
    			if (sharedInstance != null && args == null) {
    			......
    			//如果當前不只是檢查,而且是建立bean,這個引數就是false,在這裡就會做個bean建立的標記,把beanName 加到alreadyCreated裡面去
    			if (!typeCheckOnly) {
    				markBeanAsCreated(beanName);
    			}
    				//我們當前要建立的bean是單例的,就會走到這裡去,下面我們走到裡面的呼叫去看看
    				// Create bean instance.
    				if (mbd.isSingleton()) {
    					sharedInstance = getSingleton(beanName, () -> {
    						try {
    							return createBean(beanName, mbd, args);
    						}
    						catch (BeansException ex) {
    							// Explicitly remove instance from singleton cache: It might have been put there
    							// eagerly by the creation process, to allow for circular reference resolution.
    							// Also remove any beans that received a temporary reference to the bean.
    							destroySingleton(beanName);
    							throw ex;
    						}
    					});
    					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    				}
    
    		}
    
    	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    		Assert.notNull(beanName, "Bean name must not be null");
    		synchronized (this.singletonObjects) {
                    ......
                    //這裡會把當前bean的名字加入到當前正在建立的單例物件集合singletonsCurrentlyInCreation中
    				beforeSingletonCreation(beanName);
    				......
    				try {
                        //這裡就是呼叫上面的return createBean(beanName, mbd, args);這個方法,我們進這裡面去看看
    					singletonObject = singletonFactory.getObject();
    					newSingleton = true;
    				}
    				......
    			}
    			return singletonObject;
    		}
    	}
    
    
    	@Override
    	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    			throws BeanCreationException {
    		......
    		// Make sure bean class is actually resolved at this point, and
    		// clone the bean definition in case of a dynamically resolved Class
    		// which cannot be stored in the shared merged bean definition.
            //在這裡獲取要建立的bean的class物件
    		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    		......
    		try {
                //呼叫這裡來建立,我們再走到這裡面去看看
                //3個引數分別為
                //1、beanName  bean物件的名字
                //2、mbdToUseRootBeanDefinition物件,可以認為就是bean的後設資料資訊,包含bean的類物件,bean的類上註解,bean實際位置路徑等等
                //3、args  bean物件的構造方法的實參,這裡一般是空的
    			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    			if (logger.isTraceEnabled()) {
    				logger.trace("Finished creating instance of bean '" + beanName + "'");
    			}
    			return beanInstance;
    		}
    		......
    	}
    
    
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
          throws BeanCreationException {
    
       ......
       //真正建立bean物件是在這裡,這裡返回的instanceWrapper是bean物件的類例項的包裝物件BeanWrapper
       if (instanceWrapper == null) {
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
       //這裡的bean就是實際建立的bean物件的類例項
       Object bean = instanceWrapper.getWrappedInstance();
       Class<?> beanType = instanceWrapper.getWrappedClass();
       if (beanType != NullBean.class) {
          mbd.resolvedTargetType = beanType;
       }
    	......
       // Eagerly cache singletons to be able to resolve circular references
       // even when triggered by lifecycle interfaces like BeanFactoryAware.
       //看上面的註釋大概也能明白, 大概意思就是早期的單例快取,為了解決由 BeanFactoryAware等等觸發的迴圈依賴
       //mbd.isSingleton()  表示bean是單例的(這個是bean對應的類上的,預設就是單例),
       //this.allowCircularReferences 允許迴圈引用,這個是beanFactory的成員屬性,預設也是true
       //isSingletonCurrentlyInCreation(beanName) 表示是否在當前正在建立的bean集合中。beforeSingletonCreation(beanName);我們在前面執行過這句就加到正在建立的bean集合中了
       //這裡earlySingletonExposure 就是true了,會進到if分支中
       boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
             isSingletonCurrentlyInCreation(beanName));
       if (earlySingletonExposure) {
          if (logger.isTraceEnabled()) {
             logger.trace("Eagerly caching bean '" + beanName +
                   "' to allow for resolving potential circular references");
          }
          //這句主要是將將() -> getEarlyBeanReference(beanName, mbd, bean) 這個lambda表示式儲存到this.singletonFactories集合中
          addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
       }
    
       // Initialize the bean instance.
       Object exposedObject = bean;
       try {
          //在這裡就會進行屬性填充,完成成員注入等等,也就是在這裡serviceA這個bean會注入serviceB這個成員屬性,我們走進這個方法去看看
          populateBean(beanName, mbd, instanceWrapper);
          ......
       }
     	......
    
       return exposedObject;
    }
    
    	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    		......
    		if (hasInstAwareBpps) {
    			if (pvs == null) {
    				pvs = mbd.getPropertyValues();
    			}
    			//真正的屬性注入是在這裡完成的,aop也是在這裡來完成的。這裡是獲取beanFactory中的InstantiationAwareBeanPostProcessor對bean物件進行增強
    			//如果屬性注入用的是@Resource,就會用CommonAnnotationBeanPostProcessor來完成
    			//如果屬性注入用的是@Autowired,就會用AutowiredAnnotationBeanPostProcessor來完成
    			//如果是AOP 就會使用InfrastructureAdvisorAutoProxyCreator來生成對應的代理物件
    			//我們這裡使用的是@Autowired,所以會用AutowiredAnnotationBeanPostProcessor來完成注入。我們走到它的postProcessProperties的去看看
    			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
    				......
    	}
    
    	@Override
    	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
            //這裡主要是獲取bean的類屬性和方法上的org.springframework.beans.factory.annotation.Autowired,org.springframework.beans.factory.annotation.Value註解來進行注入
    		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    		try {
                //繼續進去看看
    			metadata.inject(bean, beanName, pvs);
    		}
    		......
    	}
    
    public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    		 ......
    		 //對每一個屬性分別進行注入,繼續進去
             element.inject(target, beanName, pvs);
          }
       }
    }
    
    
      		@Override
      		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
      			Field field = (Field) this.member;
      			Object value;
      			//如果之前快取過就從快取取,我們是第一次注入,所以之前沒有快取,不會走這個分支
      			if (this.cached) {
      				try {
      					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
      				}
      				catch (NoSuchBeanDefinitionException ex) {
      					// Unexpected removal of target bean for cached argument -> re-resolve
      					value = resolveFieldValue(field, bean, beanName);
      				}
      			}
      			else {
      				//會走這裡來解析欄位的值,再進去
      				value = resolveFieldValue(field, bean, beanName);
      			}
      			if (value != null) {
      				ReflectionUtils.makeAccessible(field);
      				field.set(bean, value);
      			}
      		}
    
    
    
    		@Nullable
    		private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
    			//建立欄位的包裝類DependencyDescriptor
    			DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    
    			try {
    				//呼叫這裡完成對應欄位值的查詢,再進去
    				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    			}
    			catch (BeansException ex) {
    				throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
    			}
    			synchronized (this) {
    				//獲取到值之後,進行快取
    				if (!this.cached) {
    						......
    					}
    					this.cachedFieldValue = cachedFieldValue;
    					this.cached = true;
    				}
    			}
    			return value;
    		}
    	}
    
    	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
    			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
    		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    		if (Optional.class == descriptor.getDependencyType()) {
    			return createOptionalDependency(descriptor, requestingBeanName);
    		}
    		else if (ObjectFactory.class == descriptor.getDependencyType() ||
    				ObjectProvider.class == descriptor.getDependencyType()) {
    			return new DependencyObjectProvider(descriptor, requestingBeanName);
    		}
    		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
    			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    		}
    		else {
    			//當前的類是一個普通的class,會走到這裡面,由於我們的bean沒有Lazy註解,所以這裡返回時null,走到下面的if分支
    			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    					descriptor, requestingBeanName);
    			if (result == null) {
    				//在這裡我們看下這裡的入參。
    				//descriptor是包含了需要注入的欄位的資訊。
    				//requestingBeanName是當前正在建立的bean的名字serviceA,
    				//autowiredBeanNames是當前需要注入的欄位的對應的bean的名字的集合,這裡只有serviceB
    				//typeConverter這個是進行注入時做型別轉換的,這裡我們可以不用關注這個
    				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    			}
    			return result;
    		}
    	}
    
    	@Nullable
    	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
    			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    			......
    			if (instanceCandidate instanceof Class) {
    				//又會呼叫到這裡,我們再進入到DependencyDescriptor的resolveCandidate去看看
                    //注意:這裡的autowiredBeanName是我們需要注入的屬性名這裡是serviceB
    				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
    			}
    			......
    	}
    
    
    	public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
    			throws BeansException {
    		//看到沒,到這裡就出現迴圈呼叫了,到這裡又會重新呼叫beanFactory.getBean("serviceB")去建立serviceB的bean物件,完成後注入到serivceA對應的Bean上的屬性上來,這時程式碼又會從本節開頭的位置開始執行,先建立serviceB物件例項,再去注入serviceB物件的serviceA屬性。
            //最終會執行到beanFactory.getBean("serviceA")這裡
    		return beanFactory.getBean(beanName);
    	}
    

    就是下面圖的樣子

3、解決迴圈依賴的程式碼實現

接著上面的beanFactory.getBean("serviceA")這行程式碼我們繼續往下看

這次又會走到這裡

	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
		//我們第二部分就是從這裡開始的,又走回來了,但這次又會有所不同
		String beanName = transformedBeanName(name);
		Object beanInstance;

		// Eagerly check singleton cache for manually registered singletons.
		//這次我們這裡返回的就不是空了,sharedInstance物件的值就是對應serviceA的bean物件了,這次就會從if分支中返回,而之前我們不會進這裡的if分支而是進入else分支導致後面出現了迴圈依賴的問題,這次我們進到這個方法看看
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
	@Nullable
	public Object getSingleton(String beanName) {
		//再點進去
		return getSingleton(beanName, true);
	}
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
         //這裡由於當前的serviceA bean還沒完成建立,所以這裡singletonObject返回的是空,
        //再看看 isSingletonCurrentlyInCreation(beanName)這裡,由於我們在建立serviceA過程中有這麼一句beforeSingletonCreation(beanName)(不清楚這句的搜尋下本文,上面就有講到),所有這個條件是true。這時我們就會進入if分支中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
           //由於我們是第一次進入這裡,所以this.earlySingletonObjects.get(beanName)返回的也是null
           //我們的入參 allowEarlyReference是true,會繼續進到這個if分支中
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
                    //這裡的singletonObject還是null,繼續進到if分支
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
                            //最終會走到這裡,在建立serviceA物件之後,屬性注入之前,執行了這句 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))(不清楚的搜尋下本文,上面有說到),所以這裡返回的singletonFactory是個lamdba表示式,getEarlyBeanReference(beanName, mbd, bean))附帶了3個引數,第一個beanName是serivceA,mdb是對應serviceA的附帶serviceA後設資料資訊的RootBeanDefinition物件,bean就是建立出來的serviceA物件
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
                                //這裡就會呼叫getEarlyBeanReference(beanName, mbd, bean)對serviceA物件進行一個getEarlyBeanReference增強後返回,返回後放置到earlySingletonObjects中,並從singletonFactories中刪除
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects中,並從.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

最終在serviceA 這個bean建立完成後,就會從singletonsCurrentlyInCreation移除掉

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
				......
				finally {
					//在這裡從singletonsCurrentlyInCreation中移除掉
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					//將serviceA bean物件新增到singletonObjects,registeredSingletons中
					//從singletonFactories,earlySingletonObjects中移除掉
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

所以整個獲取serviceA的流程就是這樣了,

1、首先去建立serviceA這個bean,

  • 由於它有個屬性serviceB,在建立完serviceA物件後,就會去進行serviceB的屬性注入,

  • 這時由於serviceB之前沒有生成,這時又會去建立serviceB這個bean,

  • 先建立serviceB物件,然後再進行serviceA這個屬性的注入,

  • 繼續去獲取serviceA這個bean,第二次進入獲取serviceA的流程,這時從之前快取的lambda表示式中獲取到之前建立的serviceA的引用返回。

2、總結下關鍵的程式碼點

  • 建立bean物件之前呼叫beforeSingletonCreation(beanName)將bean物件名字新增到singletonsCurrentlyInCreation集合中
  • 建立bean物件對應的類例項後呼叫addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));新增到singletonFactories中
  • 在迴圈依賴中第二次呼叫到建立bean物件時,呼叫getSingleton(beanName, true)時,從singletonFactories中返回對應的早期bean物件的引用

相關文章