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物件的引用