天天看點

【Java】Spring架構是如何解決Bean建立過程中的循環依賴問題的

作者:搬山道猿

引言

本文主要梳理了Spring架構Bean建立過程中應對循環依賴問題的相關源碼。我在手寫super-mini-webpack的時候也介紹過解決循環依賴的算法:Map+記憶化搜尋。可以猜測這段源碼也實作了這個算法,是以在看這段源碼的時候,我們可以先找到遞歸點,再去分析調用棧涉及的那些函數,順便找出其用到的Map資料結構。

三級緩存資料結構簡介

三級緩存資料結構定義和操作三級緩存的函數都位于:spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

java複制代碼	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
           

緩存先找到第一級,第一級沒有才查第二級,依此類推。singletonObjects, earlySingletonObjects, singletonFactories分别是1到3級。第一級緩存是bean名到成品bean的映射;第二級緩存是bean名到半成品bean的映射;第三級緩存是bean名到函數式接口的映射,作用為延遲調用函數。

java複制代碼@FunctionalInterface
public interface ObjectFactory<T> {

	/**
	 * Return an instance (possibly shared or independent)
	 * of the object managed by this factory.
	 * @return the resulting instance
	 * @throws BeansException in case of creation errors
	 */
	T getObject() throws BeansException;

}
           

第三級緩存的Value裡的函數的調用方式就是調用.getObject()。

為什麼需要2級緩存?因為要展現一個分層的思想,半成品bean原則上是不能暴露到外部的。TODO:外部是指?

為什麼需要3級緩存?如果所有bean都沒有代理對象就不需要第3級緩存。TODO:補充說明。

源碼閱讀

如何啟動調試?

建立一個普通的SpringBoot項目。然後可以嘗試通過xml配置檔案和注解等方式構造循環引用。後文稱為場景1和場景2。

場景1:通過xml配置檔案來構造循環引用

入口src\main\java\com\hans\bean_dependency_cycle\hans\HansApplication.java:

java複制代碼package com.hans.bean_dependency_cycle.hans;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class HansApplication {

	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("cycle.xml");
		A beanA = ac.getBean(A.class);
		System.out.println(beanA);
		System.out.println(beanA.getB());
		B beanB = ac.getBean(B.class);
		System.out.println(beanB);
		System.out.println(beanB.getA());
	}

}
           

src\main\resources\cycle.xml:

xml複制代碼<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
                        "
>
    <bean id="a" class="com.hans.bean_dependency_cycle.hans.A">
        <property name="b" ref="b"></property>
    </bean>
    <bean id="b" class="com.hans.bean_dependency_cycle.hans.B">
        <property name="a" ref="a"></property>
    </bean>
</beans>
           

A.java和B.java:

java複制代碼// A 和 B 不能都使用 lombok,否則無法列印。這裡選擇了 B 不使用 lombok
package com.hans.bean_dependency_cycle.hans;

import lombok.Data;

@Data
public class A {
    private B b;

    public String toString(A o) {
        return o.getClass().getName() + "@" +
                Integer.toHexString(System.identityHashCode(o));
    }

}

package com.hans.bean_dependency_cycle.hans;

public class B {
    private A a;

    public A getA() {
        return this.a;
    }

    public void setA(A a) {
        this.a = a;
    }

}
           

期望輸出:

less複制代碼A(b=com.hans.bean_dependency_cycle.hans.B@750e2b97)
com.hans.bean_dependency_cycle.hans.B@750e2b97
com.hans.bean_dependency_cycle.hans.B@750e2b97
A(b=com.hans.bean_dependency_cycle.hans.B@750e2b97)
           

場景1不需要配置spring.main.allow-circular-references為true也能得到期望輸出,TODO:原因未知。

場景2:通過注解+自動裝配屬性來構造循環引用:以@Controller為例

入口src\main\java\com\hans\bean_dependency_cycle\hans\AnotherEntry.java:

java複制代碼package com.hans.bean_dependency_cycle.hans;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class AnotherEntry {
    public static void main(String[] args) {
        ConfigurableApplicationContext cac = SpringApplication.run(AnotherEntry.class, args);
        ControllerA beanA = cac.getBean(ControllerA.class);
        System.out.println(beanA);
        System.out.println(beanA.getCb());
        ControllerB beanB = cac.getBean(ControllerB.class);
        System.out.println(beanB);
        System.out.println(beanB.getCa());
    }
}
           

根據參考連結1,接下來一定要記得修改application.yml:

yaml複制代碼spring:
  main:
    allow-circular-references: true
           

否則會報錯:

vbnet複制代碼***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  controllerA (field private com.hans.bean_dependency_cycle.hans.ControllerB com.hansn_dependency_cycle.hans.ControllerA.cb)
↑     ↓
|  controllerB (field private com.hans.bean_dependency_cycle.hans.ControllerA com.hansn_dependency_cycle.hans.ControllerB.ca)
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Up your application to remove the dependency cycle between beans. As a last resort, it me possible to break the cycle automatically by setting spring.main.allow-circular-refees to true.
           

兩個普通的Controller:

java複制代碼// src\main\java\com\hans\bean_dependency_cycle\hans\ControllerA.java
package com.hans.bean_dependency_cycle.hans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ControllerA {
    @Autowired
    private ControllerB cb;

    public ControllerB getCb() {
        return cb;
    }

    @RequestMapping("/controllerA")
    public String index() {
        return "hello controllerA!";
    }
}

// src\main\java\com\hans\bean_dependency_cycle\hans\ControllerB.java
package com.hans.bean_dependency_cycle.hans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ControllerB {
    @Autowired
    private ControllerA ca;

    public ControllerA getCa() {
        return ca;
    }

    @RequestMapping("/controllerB")
    public String index() {
        return "hello controllerB!";
    }
}
           

期望輸出:

kotlin複制代碼com.hans.bean_dependency_cycle.hans.ControllerA@39f82681
com.hans.bean_dependency_cycle.hans.ControllerB@4bd9e7fd
com.hans.bean_dependency_cycle.hans.ControllerB@4bd9e7fd
com.hans.bean_dependency_cycle.hans.ControllerA@39f82681
           

脈絡

根據參考連結2,有一個經驗:“do”開頭的方法名是真正含有大量邏輯的方法。參考連結2Java之父精選的脈絡函數如下:

  • getBean
  • doGetBean
  • createBean
  • doCreateBean
  • createBeanInstance
  • populateBean

場景1到遞歸點為止的調用鍊:preInstantiateSingletons > getBean > doGetBean > getSingleton函數有多個,其中調用了beforeSingletonCreation的函數調用singletonFactory.getObject()時才真正調用了createBean > createBean > doCreateBean > createBeanInstance, populateBean 都在 doCreateBean 的實作裡 > populateBean調用了applyPropertyValues > resolveValueIfNecessary > resolveReference > getBean。

場景2的調用鍊到populateBean開始和場景1的調用鍊岔開,兩個場景的差異放在後文《Controller和普通Bean解決循環依賴過程的相同點與不同點》分析。

場景1遞歸到A -> B -> A時的調用鍊:getBean > doGetBean > 未調用beforeSingletonCreation的getSingleton,操作第2級緩存後離開。

操作三級緩存的函數都位于https://github.com/spring-projects/spring-framework/blob/502997d8e986dcfde1f49b2b2f443a32b5488b13/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java:

  1. 放入第3級緩存的函數:doCreateBean調用的addSingletonFactory。
  2. 放入第2級緩存的函數:首先要知道getSingleton在DefaultSingletonBeanRegistry.java裡本質上的實作有兩個,一個public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)調用了beforeSingletonCreation并間接調用了createBean;另一個protected Object getSingleton(String beanName, boolean allowEarlyReference)則是操作了第2級緩存。
  3. 放入第1級緩存的函數:addSingleton。在間接調用了createBean函數的getSingleton處調用。

場景1的執行過程

真正意義上的入口:spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java的beanFactory.preInstantiateSingletons();。這裡的beanFactory就有singletonObjects那3級緩存的對象。于是跳到https://github.com/spring-projects/spring-framework/blob/bbde68c49e66c3c531920cb80a55742262507be7/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java:

java複制代碼	@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        // 觸發所有非延遲加載的(non-lazy)單例 bean 的初始化
		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			// 假如先周遊 A 再周遊 B 那麼周遊到 B 的時候,因為循環引用解決的關系,B 已經放到了第1級緩存,是以 doGetBean 的 getSingleton 可以直接從第1級緩存取到值,不用再走一遍 createBean 方法
            // 合并父類 BeanDefinition
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            // 非抽象、是單例、非懶加載
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // 如果實作了 FactoryBean 接口則是 FactoryBean
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 比如:FACTORY_BEAN_PREFIX + beanName = "&A"
					if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
						getBean(beanName);
					}
				}
				else {
                    // 不是 FactoryBean,隻是普通 Bean,則走這個分支
					getBean(beanName);
				}
			}
		}

        // 觸發所有 SmartInitializingSingleton 的後初始化回調
		// Trigger post-initialization callback for all applicable beans...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
				StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
						.tag("beanName", beanName);
				smartSingleton.afterSingletonsInstantiated();
				smartInitialize.end();
			}
		}
	}
           

順便看下isFactoryBean的實作https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java:

java複制代碼	@Override
	public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);
		Object beanInstance = getSingleton(beanName, false);
		if (beanInstance != null) {
			return (beanInstance instanceof FactoryBean);
		}
		// No singleton instance found -> check bean definition.
		if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory cbf) {
			// No bean definition found in this factory -> delegate to parent.
			return cbf.isFactoryBean(name);
		}
		return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
	}
           

getBean和isFactoryBean都位于https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java,getBean隻有1行

java複制代碼	@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}
           

doGetBean和getBean, isFactoryBean都在AbstractBeanFactory.java:

java複制代碼	/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @param typeCheckOnly whether the instance is obtained for a type check,
	 * not for actual use
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object beanInstance;

        // 提前檢查單例緩存中是否有手動注冊的單例對象,跟循環依賴有關。如果是初次進這裡,比如 controllerA 初次進這個方法,肯定是拿不到值的,就會是 null
		// 對于最簡單的循環依賴,controllerA -> controllerB -> controllerA 之後,需要進入 getSingleton 了,這裡的邏輯就是要從第3級緩存裡拿到工廠函數,調用後得到 controllerA 半成品,進而可以直接 return
		// Eagerly check singleton cache for manually registered singletons.
		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);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory abf) {
					return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
					.tag("beanName", name);
			try {
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
				if (mbd.isSingleton()) {
					// 傳回 beanName 的原始單例對象。如果尚未注冊,則使用 singletonFactory 建立并注冊一個對象
					sharedInstance = getSingleton(beanName, () -> {
						try {
							// 為給定的合并後的 BeanDefinition 和參數建立一個 bean 執行個體
							// createBean 真實執行時機是調用了 beforeSingletonCreation 方法的 getSingleton 方法執行 singletonObject = singletonFactory.getObject() 時
							// 首次運作到 beanName = "controllerA" 時 args = null
							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.
							// 顯式地從單例緩存中删除bean執行個體:因為這個執行個體可能是由建立過程急切地放在那裡的,放在那裡的目的是允許循環引用解析。
							// 還要删除所有被這個bean臨時引用的所有bean。如果找到相應的一次性bean執行個體,則委托給 destroyBean
							destroySingleton(beanName);
							throw ex;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}
				}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
				beanCreation.end();
			}
		}

		return adaptBeanInstance(name, beanInstance, requiredType);
	}
           

我們先隻關注createBean,是以需要關注getSingleton。注意:

getSingleton的函數在DefaultSingletonBeanRegistry.java裡本質上的實作有兩個,一個調用了beforeSingletonCreation并間接調用了createBean;另一個則是操作了第2級緩存。

是以目前我們需要關注的是調用了beforeSingletonCreation的getSingleton方法。路徑:https://github.com/spring-projects/spring-framework/blob/502997d8e986dcfde1f49b2b2f443a32b5488b13/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

java複制代碼	/**
	 * Return the (raw) singleton object registered under the given name,
	 * creating and registering a new one if none registered yet.
	 * @param beanName the name of the bean
	 * @param singletonFactory the ObjectFactory to lazily create the singleton
	 * with, if necessary
	 * @return the registered singleton object
	 */
	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				// 建立單例前的回調,預設實作為:将單例注冊為目前正在建立中,實作隻有3行,可以看下。
				beforeSingletonCreation(beanName);
				// flag 表示是否生成了新的單例對象
				boolean newSingleton = false;
				// flag 表示是否有抑制異常的記錄,true表示沒有
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					// 若沒有抑制異常記錄,則對抑制異常清單進行初始化
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 從單例工廠擷取對象。注意 singletonFactory 是本方法第二個參數,之前也介紹了
					// ObjectFactory 對象通過調 getObject 來正式執行函數,是以 createBean 在此時才真正執行
					singletonObject = singletonFactory.getObject();
					// 擷取後,就已經生成了新的單例對象,标記為 true
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					// 操作第1級緩存
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}
           

在調用singletonObject = singletonFactory.getObject();以間接調用createBean後,會調用addSingleton,将bean加入第1級緩存,這标志着bean變為成品。接下來我們看下addSingleton的代碼,它和getSingleton定義于同一個檔案。

java複制代碼	/**
	 * Add the given singleton object to the singleton cache of this factory.
	 * <p>To be called for eager registration of singletons.
	 * @param beanName the name of the bean
	 * @param singletonObject the singleton object
	 */
	protected void addSingleton(String beanName, Object singletonObject) {
		// addSingleton 在調用了 createBean 的 getSingleton 方法中被調用,标志着 bean 變為成品對象
		synchronized (this.singletonObjects) {
			// 第1級緩存添加,第2、3級緩存移除
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			// 添加到已注冊的單例集合。 registeredSingletons 為 Set<String>
			this.registeredSingletons.add(beanName);
		}
	}
           

接下來看createBean。https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

java複制代碼	/**
	 * Central method of this class: creates a bean instance,
	 * populates the bean instance, applies post-processors, etc.
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		// 首次看源碼,直接看 doCreateBean 調用處
		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// 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.
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			// 實際建立 bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}
           

createBean目前唯一值得關注的點就是調用了doCreateBean。doCreateBean做了幾件值得本文關注的事:

  • 調用了createBeanInstance完成bean的執行個體化。
  • 調用了addSingletonFactory,即加入了第3級緩存。
  • 調用了populateBean完成bean的屬性指派操作。

doCreateBean和createBean都定義在https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java。

java複制代碼	/**
	 * Actually create the specified bean. Pre-creation processing has already happened
	 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
	 * <p>Differentiates between default bean instantiation, use of a
	 * factory method, and autowiring a constructor.
	 * @param beanName the name of the bean
	 * @param mbd the merged bean definition for the bean
	 * @param args explicit arguments to use for constructor or factory method invocation
	 * @return a new instance of the bean
	 * @throws BeanCreationException if the bean could not be created
	 * @see #instantiateBean
	 * @see #instantiateUsingFactoryMethod
	 * @see #autowireConstructor
	 */
	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		// instanceWrapper 持有建立出的 bean 對象
		BeanWrapper instanceWrapper = null;
		// 擷取 factoryBean 執行個體緩存
		if (mbd.isSingleton()) {
			// 如果是單例對象,從 factoryBean 執行個體緩存中移除目前 bean 的資訊
			// 首次 controllerA 進來會進行移除操作,并且會調用 createBeanInstance
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 沒有就建立執行個體
		if (instanceWrapper == null) {
			// 根據執行 bean 使用對應的政策建立新的工廠執行個體,如:工廠方法、構造函數主動注入、簡單初始化
			// 第一次讀源碼時不需要點進去看 createBeanInstance 。下一個主幹方法 populateBean 是在本函數下文調用的
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		// 從包裝類中擷取原始 bean 。首次執行 controllerA 的時候, bean 就是普通的 controllerA 對象 ControllerA@100{cb=null}
		Object bean = instanceWrapper.getWrappedInstance();
		// 擷取具體 bean 對象的 Class 屬性
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			// 不等于 NullBean 類型時就修改目标類型
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.markAsPostProcessed();
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		// 判斷目前 bean 是否需要提前曝光,條件為:是單例 && 允許循環依賴 spring.main.allow-circular-references 配置為 true && 目前 bean 正在建立中
		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");
			}
			// 為避免後期循環依賴,可以在 bean 初始化完成前将建立執行個體的 ObjectFactory 加入工廠
			// 注意,這個方法會操作3級緩存的資料結構,尤其是第3級緩存。在 controllerA -> controllerB -> controllerA 的時候未調用 createBean 的 getSingleton 方法會真正調用這個匿名函數,進而調用 getEarlyBeanReference 進而操作第2級緩存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		// controllerA 首次執行到這裡時,exposedObject = ControllerA@100{cb=null}
		Object exposedObject = bean;
		try {
			// populateBean 是主幹方法,給剛剛執行個體化的 bean (半成品)填充屬性
			populateBean(beanName, mbd, instanceWrapper);
			// controllerA -> controllerB -> controllerA 之後,getSingleton 傳回 controllerA 後回到這裡, controllerB 的 ca 屬性就有值了
			// 随後遞歸傳回到這句話且調用棧隻有 controllerA 的時候,發現兩者都有值了
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
				throw bce;
			}
			else {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}
           

createBeanInstance的細節與本文主題無關,不關注。接下來我們看下addSingletonFactory的實作。addSingletonFactory位于https://github.com/spring-projects/spring-framework/blob/502997d8e986dcfde1f49b2b2f443a32b5488b13/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java,主要動作是操作第3級緩存。

java複制代碼	/**
	 * Add the given singleton factory for building the specified singleton
	 * if necessary.
	 * <p>To be called for eager registration of singletons, e.g. to be able to
	 * resolve circular references.
	 * @param beanName the name of the bean
	 * @param singletonFactory the factory for the singleton object
	 */
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			// controllerA 首次執行到這裡,第一級緩存肯定是查不到的
			if (!this.singletonObjects.containsKey(beanName)) {
				// 第3級緩存放入。再回顧一下, singletonFactory存的是 beanName 到一個延遲執行的函數的映射
				// controllerA 首次執行到這裡的時候, singletonFactory = () -> getEarlyBeanReference(beanName, mbd, bean)
				this.singletonFactories.put(beanName, singletonFactory);
				// 從早期單例對象的高速緩存(即第2級緩存)移除目前 beanName 對應的緩存對象
				this.earlySingletonObjects.remove(beanName);
				// 添加到已注冊的單例集合裡,和三級緩存無關。值得注意的是, A 首次加入三級緩存時,就是首次加入已注冊的單例集合
				this.registeredSingletons.add(beanName);
			}
		}
	}
           

接下來關注位于https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java的populateBean。populateBean方法有一個功能是給bean的屬性指派,包含了遞歸點。在建立Bean的源碼中,遞歸點指的是遞歸調用getBean方法。

java複制代碼	/**
	 * Populate the bean instance in the given BeanWrapper with the property values
	 * from the bean definition.
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @param bw the BeanWrapper with bean instance
	 */
	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		if (bw.getWrappedClass().isRecord()) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to a record");
			}
			else {
				// Skip property population phase for records since they are immutable.
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					return;
				}
			}
		}

		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}
		if (hasInstantiationAwareBeanPostProcessors()) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				// 對于 Controller Bean 自動裝配屬性産生循環依賴的場景,周遊到 AutowiredAnnotationBeanPostProcessor 時,這句話包含遞歸點
				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					return;
				}
				pvs = pvsToUse;
			}
		}

		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
		if (needsDepCheck) {
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}

		if (pvs != null) {
			// 對于普通 Bean 的場景,這句話包含遞歸點
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}
           

applyPropertyValues也位于https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java,主要功能為完成bean初始化。其中,Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue)包含了遞歸點,通過動調可以驗證bw.setPropertyValues(new MutablePropertyValues(deepCopy))真正完成了屬性指派工作。

java複制代碼	/**
	 * Apply the given property values, resolving any runtime references
	 * to other beans in this bean factory. Must use deep copy, so we
	 * don't permanently modify this property.
	 * @param beanName the bean name passed for better exception information
	 * @param mbd the merged bean definition
	 * @param bw the BeanWrapper wrapping the target object
	 * @param pvs the new property values
	 */
	protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		// applyPropertyValues 是真正完成指派操作的函數
		// 如果 pvs 沒有 PropertyValue,就直接結束
		if (pvs.isEmpty()) {
			return;
		}

		MutablePropertyValues mpvs = null;
		List<PropertyValue> original;

		if (pvs instanceof MutablePropertyValues _mpvs) {
			mpvs = _mpvs;
			if (mpvs.isConverted()) {
				// Shortcut: use the pre-converted values as-is.
				try {
					bw.setPropertyValues(mpvs);
					return;
				}
				catch (BeansException ex) {
					throw new BeanCreationException(
							mbd.getResourceDescription(), beanName, "Error setting property values", ex);
				}
			}
			// 擷取 mpvs 的 PropertyValue 清單
			original = mpvs.getPropertyValueList();
		}
		else {
			// 擷取 pvs 的 PropertyValue 對象數組并轉為清單
			original = Arrays.asList(pvs.getPropertyValues());
		}

		// 使用者自定義的類型轉換器,預設轉換器為 bean 的包裝類對象
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

		// Create a deep copy, resolving any references for values.
		List<PropertyValue> deepCopy = new ArrayList<>(original.size());
		// resolveNecessary flag 含義為是否還需要解析
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			// 屬性已解析過則加入 deepCopy
			if (pv.isConverted()) {
				deepCopy.add(pv);
			}
			else {
				String propertyName = pv.getName();
				// 擷取未經類型轉換的值
				Object originalValue = pv.getValue();
				if (originalValue == AutowiredPropertyMarker.INSTANCE) {
					Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
					// 如果 setter 方法為空
					if (writeMethod == null) {
						// 異常:自動裝配标記屬性沒有寫方法
						throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
					}
					originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
				}
				// 交由 valueResolver 根據 pv 解析出 originalValue 所封裝的對象。注意:這個函數包含遞歸點
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				// flag 含義為是否可轉換: propertyName 是 bw 中的可寫屬性 && propertyName 不是索引屬性或嵌套屬性
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					// 可轉換則将 resolvedValue 轉換為指定的目标屬性對象
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}
				// Possibly store converted value in merged bean definition,
				// in order to avoid re-conversion for every created bean instance.
				if (resolvedValue == originalValue) {
					if (convertible) {
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				}
				else if (convertible && originalValue instanceof TypedStringValue typedStringValue &&
						!typedStringValue.isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					pv.setConvertedValue(convertedValue);
					deepCopy.add(pv);
				}
				else {
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		try {
			// 完成屬性指派工作。咱們做個簡單的實驗,動調執行此句前後各點選調用棧看一次 populateBean 調用處的下一句的 exposedObject 或者 bean 變量,這就證明了 bean 的屬性指派确實是在這句話完成的
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
		}
	}
           

resolveValueIfNecessary位于https://github.com/spring-projects/spring-framework/blob/30d6ec3398ce41add7bc44d360b8fb86ac0264b1/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java。其實作也很長,但目前隻需要關注return resolveReference(argName, ref),因為resolveReference包含遞歸點。

java複制代碼	/**
	 * Given a PropertyValue, return a value, resolving any references to other
	 * beans in the factory if necessary. The value could be:
	 * <li>A BeanDefinition, which leads to the creation of a corresponding
	 * new bean instance. Singleton flags and names of such "inner beans"
	 * are always ignored: Inner beans are anonymous prototypes.
	 * <li>A RuntimeBeanReference, which must be resolved.
	 * <li>A ManagedList. This is a special collection that may contain
	 * RuntimeBeanReferences or Collections that will need to be resolved.
	 * <li>A ManagedSet. May also contain RuntimeBeanReferences or
	 * Collections that will need to be resolved.
	 * <li>A ManagedMap. In this case the value may be a RuntimeBeanReference
	 * or Collection that will need to be resolved.
	 * <li>An ordinary object or {@code null}, in which case it's left alone.
	 * @param argName the name of the argument that the value is defined for
	 * @param value the value object to resolve
	 * @return the resolved object
	 */
	@Nullable
	public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
		// We must check each value to see whether it requires a runtime reference
		// to another bean to be resolved.
		// RuntimeBeanReference:當屬性值對象是工廠中另一個 bean 的引用時,使用不可變的占位符類,在運作時進行解析
		if (value instanceof RuntimeBeanReference ref) {
			// 解析出對應 ref 所封裝的 Bean 的元資訊的 Bean 對象。Bean 的元資訊:Bean 名,Bean類型。注意,這個函數裡包含遞歸點
			return resolveReference(argName, ref);
		}
		else if (value instanceof RuntimeBeanNameReference ref) {
			String refName = ref.getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			if (!this.beanFactory.containsBean(refName)) {
				throw new BeanDefinitionStoreException(
						"Invalid bean name '" + refName + "' in bean reference for " + argName);
			}
			return refName;
		}
		else if (value instanceof BeanDefinitionHolder bdHolder) {
			// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
			return resolveInnerBean(bdHolder.getBeanName(), bdHolder.getBeanDefinition(),
					(name, mbd) -> resolveInnerBeanValue(argName, name, mbd));
		}
		else if (value instanceof BeanDefinition bd) {
			return resolveInnerBean(null, bd,
					(name, mbd) -> resolveInnerBeanValue(argName, name, mbd));
		}
		else if (value instanceof DependencyDescriptor dependencyDescriptor) {
			Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
			Object result = this.beanFactory.resolveDependency(
					dependencyDescriptor, this.beanName, autowiredBeanNames, this.typeConverter);
			for (String autowiredBeanName : autowiredBeanNames) {
				if (this.beanFactory.containsBean(autowiredBeanName)) {
					this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);
				}
			}
			return result;
		}
		else if (value instanceof ManagedArray managedArray) {
			// May need to resolve contained runtime references.
			Class<?> elementType = managedArray.resolvedElementType;
			if (elementType == null) {
				String elementTypeName = managedArray.getElementTypeName();
				if (StringUtils.hasText(elementTypeName)) {
					try {
						elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
						managedArray.resolvedElementType = elementType;
					}
					catch (Throwable ex) {
						// Improve the message by showing the context.
						throw new BeanCreationException(
								this.beanDefinition.getResourceDescription(), this.beanName,
								"Error resolving array type for " + argName, ex);
					}
				}
				else {
					elementType = Object.class;
				}
			}
			return resolveManagedArray(argName, (List<?>) value, elementType);
		}
		else if (value instanceof ManagedList<?> managedList) {
			// May need to resolve contained runtime references.
			return resolveManagedList(argName, managedList);
		}
		else if (value instanceof ManagedSet<?> managedSet) {
			// May need to resolve contained runtime references.
			return resolveManagedSet(argName, managedSet);
		}
		else if (value instanceof ManagedMap<?, ?> managedMap) {
			// May need to resolve contained runtime references.
			return resolveManagedMap(argName, managedMap);
		}
		else if (value instanceof ManagedProperties original) {
			// Properties original = managedProperties;
			Properties copy = new Properties();
			original.forEach((propKey, propValue) -> {
				if (propKey instanceof TypedStringValue typedStringValue) {
					propKey = evaluate(typedStringValue);
				}
				if (propValue instanceof TypedStringValue typedStringValue) {
					propValue = evaluate(typedStringValue);
				}
				if (propKey == null || propValue == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Error converting Properties key/value pair for " + argName + ": resolved to null");
				}
				copy.put(propKey, propValue);
			});
			return copy;
		}
		else if (value instanceof TypedStringValue typedStringValue) {
			// Convert value to target type here.
			Object valueObject = evaluate(typedStringValue);
			try {
				Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
				if (resolvedTargetType != null) {
					return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
				}
				else {
					return valueObject;
				}
			}
			catch (Throwable ex) {
				// Improve the message by showing the context.
				throw new BeanCreationException(
						this.beanDefinition.getResourceDescription(), this.beanName,
						"Error converting typed String value for " + argName, ex);
			}
		}
		else if (value instanceof NullBean) {
			return null;
		}
		else {
			return evaluate(value);
		}
	}
           

resolveReference和resolveValueIfNecessary都位于https://github.com/spring-projects/spring-framework/blob/30d6ec3398ce41add7bc44d360b8fb86ac0264b1/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java。其實作也很長,但目前隻需要關注bean = this.beanFactory.getBean(resolvedName),因為這就是遞歸點。

java複制代碼	/**
	 * Resolve a reference to another bean in the factory.
	 */
	@Nullable
	private Object resolveReference(Object argName, RuntimeBeanReference ref) {
		try {
			Object bean;
			Class<?> beanType = ref.getBeanType();
			// 如果引用來自父工廠
			if (ref.isToParent()) {
				// 擷取父工廠
				BeanFactory parent = this.beanFactory.getParentBeanFactory();
				if (parent == null) {
					// 沒有父工廠則報錯:在父工廠中無法解析對 Bean 的引用,因為父工廠就不存在
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Cannot resolve reference to bean " + ref +
									" in parent factory: no parent factory available");
				}
				if (beanType != null) {
					bean = parent.getBean(beanType);
				}
				else {
					bean = parent.getBean(String.valueOf(doEvaluate(ref.getBeanName())));
				}
			}
			else {
				String resolvedName;
				if (beanType != null) {
					// 解析與 beanType 唯一比對的 bean 執行個體,包括其 bean 名
					NamedBeanHolder<?> namedBean = this.beanFactory.resolveNamedBean(beanType);
					// 讓 bean 引用 namedBean 所封裝的 bean 對象
					bean = namedBean.getBeanInstance();
					resolvedName = namedBean.getBeanName();
				}
				else {
					resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
					// 擷取 ref 所包裝的 Bean 名對應的 Bean 對象
					// 注意,這就是遞歸點了
					bean = this.beanFactory.getBean(resolvedName);
				}
				// 注冊依賴關系到 Bean 工廠
				this.beanFactory.registerDependentBean(resolvedName, this.beanName);
			}
			if (bean instanceof NullBean) {
				bean = null;
			}
			return bean;
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					this.beanDefinition.getResourceDescription(), this.beanName,
					"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
		}
	}
           

B建立過程調用棧是完全一樣的,接下來我們假設現在走到了A -> B -> A,回到了doGetBean的Object sharedInstance = getSingleton(beanName);處。此時我們需要關注其實作了:

java複制代碼	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		// 該函數主要是調用放入第3級緩存的 getEarlyBeanReference 并放入第2級緩存
		// 首先從第1級緩存擷取 bean
		Object singletonObject = this.singletonObjects.get(beanName);
		// 第1級緩存沒有,并且已标記為建立中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			// 從第2級緩存擷取 bean.因為controllerA -> controllerB -> controllerA 的時候是建立中的 bean,隻放到了第3級緩存,是以是查不到的
			singletonObject = this.earlySingletonObjects.get(beanName);
			// controllerA -> controllerB -> controllerA 的時候進來, allowEarlyReference 肯定是 true
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					// 這段做二次确認的代碼讓我聯想到線程安全的單例模式的寫法
					// 從第1級緩存取
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						// 從第2級緩存取
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							// 從第3級緩存取。如果是 controllerA 初次進來,因為 not in creation 是以不會進這裡,就算進了這裡,因為第3級緩存取不到是以還是會直接 return。如果是 controllerA -> controllerB -> controllerA 則會操作第2級緩存
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								// 回顧一下,DefaultSingletonBeanRegistry.java 的 addSingletonFactory 函數在操作第3級緩存的時候,放入的匿名函數就是:
								// singletonFactory = () -> getEarlyBeanReference(beanName, mbd, bean) 是以 getEarlyBeanReference 傳回值會在此被放入第2級緩存
								singletonObject = singletonFactory.getObject();
								// 為什麼是放第2級緩存?因為 getBean 的遞歸還沒傳回
								// 放入第2級緩存後,三級緩存的就可以移除了
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}
           

A -> B -> A的遞歸傳回後,A, B兩個單例bean都已經是成品,beanName周遊到B的時候,進入getSingleton就能命中第1級緩存了,不用再走一遍createBean方法。

java複制代碼		for (String beanName : beanNames) {
			// 假如先周遊 A 再周遊 B 那麼周遊到 B 的時候,因為循環引用解決的關系,B 已經放到了第1級緩存,是以 doGetBean 的 getSingleton 可以直接從第1級緩存取到值,不用再走一遍 createBean 方法
            // 合并父類 BeanDefinition
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            // 非抽象、是單例、非懶加載
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // 如果實作了 FactoryBean 接口則是 FactoryBean
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 比如:FACTORY_BEAN_PREFIX + beanName = "&A"
					if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
						getBean(beanName);
					}
				}
				else {
                    // 不是 FactoryBean,隻是普通 Bean,則走這個分支
					getBean(beanName);
				}
			}
		}
           

至此,場景1的遞歸點和三級緩存的操作時機都已經清楚了。

@Controller+自動裝配屬性和普通Bean解決循環依賴過程的相同點與不同點

場景2的Controller使用了@Autowired注解來構造循環依賴。動調可知,這個場景并不是在populateBean的applyPropertyValues(beanName, mbd, bw, pvs);完成修改的,而是在populateBean的:

java複制代碼			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					return;
				}
				pvs = pvsToUse;
			}
           

這個循環裡完成屬性指派的。動調發現getBeanPostProcessorCache().instantiationAware有一個元素是AutowiredAnnotationBeanPostProcessor@133(所有元素分别是ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor@129, InfrastructureAdvisorAutoProxyCreator@130, PersistenceExceptionTranslationPostProcessor@131, CommonAnnotationBeanPostProcessor@132, AutowiredAnnotationBeanPostProcessor@133),周遊到這個元素時執行的操作完成了自動裝配屬性的指派。那我們跟進去看下。

AutowiredAnnotationBeanPostProcessor.postProcessProperties位于https://github.com/spring-projects/spring-framework/blob/6183f0684684912802021556dce916ba26228c26/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

java複制代碼	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}
           

顯然是在metadata.inject處完成自動裝配的。InjectionMetadata.inject位于https://github.com/spring-projects/spring-framework/blob/2f33e77ab49f136d83b6ebf5eeb72d200fe23c0b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java

java複制代碼	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			for (InjectedElement element : elementsToIterate) {
				element.inject(target, beanName, pvs);
			}
		}
	}
           

element.inject最終跳入的是https://github.com/spring-projects/spring-framework/blob/6183f0684684912802021556dce916ba26228c26/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java的protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable。AutowiredFieldElement, AutowiredMethodElement都有inject方法,顯然這個case裡我們調用的是AutowiredFieldElement的inject方法。

java複制代碼		@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 = resolveCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (BeansException ex) {
					// Unexpected target bean mismatch for cached argument -> re-resolve
					this.cached = false;
					logger.debug("Failed to resolve cached argument", ex);
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
           

顯然field.set(bean, value);最終完成了屬性的自動裝配。值得注意的是,動調看到resolveFieldValue擷取到的cb是已經裝配好的,這裡一定存在遞歸調用。我們用一個簡單的動态調試技巧來找到遞歸點:在執行到value = resolveFieldValue(field, bean, beanName)時,給doGetBean函數下一個臨時的斷點。得到的調用棧如下:

  1. AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.inject()
  2. AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.resolveFieldValue()的value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  3. DefaultListableBeanFactory.java的resolveDependency方法的result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
  4. DefaultListableBeanFactory.java的doResolveDependency方法的instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  5. https://github.com/spring-projects/spring-framework/blob/f1fe16e3cda66b164f77489f82287116477197bc/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java的resolveCandidate方法的return beanFactory.getBean(beanName);,這就是遞歸點了,beanName = controllerB。

總而言之,Controller的bean自動裝配屬性的場景和普通的bean的遞歸點不一樣,但對三級緩存的操作邏輯是完全一緻的。謎底已揭曉~

一些擴充結論

【1】為什麼Spring不能解決構造器的循環依賴?

在doCreateBean調用createBeanInstance時,一二三級緩存都沒有Bean的相關資訊,在執行個體化之後才調用addSingletonFactory放入第3級緩存中,是以當getBean的時候緩存不會命中,是以會抛出循環依賴的異常。

【2】為什麼多執行個體Bean不能解決循環依賴?

多執行個體Bean是每次建立都會調用doGetBean方法,mbd.isSingleton()是false,不走sharedInstance = getSingleton(beanName, () -> {這個分支,是以也不會使用三級緩存,不能解決循環依賴。

總結

  1. 根據常識猜測Spring建立Bean過程解決循環依賴的算法也是Map+記憶化搜尋。是以我們可以先找到遞歸點,再去分析調用棧涉及的那些函數,順便找出其用到的Map資料結構。
  2. 在不了解Spring架構的情況下可以用一個動态調試技巧快速找到遞歸點:在執行到某條順序靠後的語句時,給順序靠前的語句下一個斷點,若下斷成功,則說明找到了遞歸點。剩下的工作就是關注調用棧涉及的函數。