那些你應該掌握的 Spring 原理

參考文章

《Spring In Action, 2nd.Ed.》
Difference between beanfactory and-applicationcont
Spring Framework Annotations
The IoC container
BeanFactoryPostProcessor and BeanPostProcessor in lifecycle events

前言

spring 目前所代表的已經不是一個框架, 或者多個框架, 而是 java 企業級應用中一整套體系化的解決方案。
這一點從 spring 官方的宣傳圖上就可以看出來。
在這裏插入圖片描述
對於普通 java 開發者而言, 應用 spring 已經是再熟悉不過的事情了, 本文旨在梳理一些 spring 應用級別之外, 應該被進階開發者所掌握的原理性知識。

由於 spring 的內容已經涵蓋了方方面面, 本文僅會涉及 spring 中最核心, 最值得普通開發者掌握的一些原理性知識。

Spring 的基本思想:控制反轉/依賴注入(IoC/DI)

首先明確一下, 控制反轉(IoC, Inversion of Control) 和 依賴注入(DI, Dependency Injection) 是一個概念的兩種稱呼, 新手同學不必糾結, 後文可能會經常交叉使用這兩個不同稱呼。

在展開學習 SpringFramework 的實現原理之前,值得思考的是, 所謂的依賴注入(DI)到底有什麼優點, 以致於被如此廣泛的使用。

以下這段話引自 《Spring In Action, 2nd.Ed.》作者 Craig Walls , 他同樣是 Pivotal 公司的開發人員( 不瞭解 Pivotal 公司的童鞋需要知道: Spring 以及衍生框架、Redis 存儲、消息隊列框架 RabbitMQ 等都出自 Pivotal 公司)

“Any nontrivial application is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). When applying DI, the objects are given their dependencies at creation time by some external entity that coordinates each object in the system. In other words, dependencies are injected into objects.”

任何正式的應用都是由多個類相互協作, 各自實現不同的業務邏輯完成的。 傳統上,每一個對象都是自行負責獲取它所依賴對象引用的。 但是應用了 DI(Dependency Injection)後, 每個對象所依賴的對象不再由其自身負責獲取, 而是統一由一個外部實體在創建這些需要相互協作的對象時提供。

上面這段話, 有些抽象, 通過代碼和圖片更加形象地表述一下

應用 控制翻轉 / 依賴注入 (IoC/DI)之前

public class A {
    public void doSomethingByA(){
        // 實現一些 A 邏輯

        // 需要 B 類執行一些 B 邏輯
        // new B 也可以換做是 ActionFacotry.createB() 之類的工廠方法
        // 這裏的關鍵是, 由 A 關心怎麼獲得 b 的引用
        B b = new B();

        // 執行 B 邏輯
        b.doSomethingB();
    }
}

由於在類 A 中直接編寫了構造 B(或者是獲取 B 的應用的代碼), A 與 B 類變成了一種緊耦合的協作方式, 下圖可以形象的表達這種耦合關係。
在這裏插入圖片描述
這種耦合意味着, 如果我們希望更換 A 類獲取的類 B 引用, 不得不更改 A 類的代碼 ( 圖中可以形象理解爲, 要把卡住紅色部分的藍色部分砸開 )。

有同學可能會在這裏指出, 如果使用工廠模式 BFacotry 用於創建類型爲 B 的實例, 那麼不需要修改類 A 也可以更換類 B 的引用。

但實際上, 這並沒有太大區別, 只是把 A與 B 的耦合替換成了 A 與 BFactory 的耦合而已, 會進一步引入 BFactory 的引用如何獲取的問題

應用 控制翻轉 / 依賴注入 (IoC/DI)之前, 代碼是這項協作的 。

應用 控制翻轉 / 依賴注入 (IoC/DI)之後

public class A {
	@Autowired
	private B b;
    public void doSomethingByA(){
        // 實現一些 A 邏輯
        
        // 執行 B 邏輯
        b.doSomethingB();
    }
}

由於 A 類的代碼中沒有編寫任何獲取 B 類引用的代碼, 使得 A 與 B 的耦合關係變成了如下的鬆耦合狀態
在這裏插入圖片描述

這樣當我們想要替換 B 的引用時, 我們就完全無需改動 A 的代碼。

鬆耦合的好處

這個地方可能還會有同學進一步產生疑問, 爲什麼總是要假設改變 B 的引用, 實際開發中,相互依賴的類在很多情況下, 並不需要動態修改啊。

這裏的答案是 測試需求

想象一下, 你要編寫一段針對某個類某個方法進行單元測試, 由於這個方法的正常執行會依賴很多其他的類, 於是在構造測試環境時, 需要 mock 很多用於測試的假的對象。

如果你編寫的代碼使用了 IoC 容器, 想要爲待測試的組件提供一個虛假的 mock 引用就變成了很容易的事。 反之, 如果沒有使用 IoC 容器, 那麼在不改動代碼的情況下, 替換待測試類的依賴組件就變成了一個很難實現的目標。

除去測試需求, 即便你沒有靈活替換某個類引用的需求, 將對象之間的引用關係維護放在一個統一的組件中管理, 也可以使得系統組件中錯綜複雜的關係變得有跡可循

IoC 就沒有副作用嗎

有, 就像前面提到的, 必要的耦合關係是不能被消除的, 只是被從一種耦合替換爲另外一種耦合。使用 IoC 容器後, 由於將系統中組件依賴關係都交給了容器去維護, 整個系統就與容器耦合了起來。
在這裏插入圖片描述
也就是說, 如果你的系統有一天想要替換框架, 這就變得很困難, 當然這種需求很少會有, 所以 Spring IoC 框架已經近乎成爲了 java 世界中企業應用的標準框架。

Spring 的核心: Bean 容器

根據 spring-framework 官方文檔給出的定義, 由 Spring IoC 容器實例化, 組裝, 管理的對象被統稱爲 Bean。 所以後文我們都會用 Bean 來描述被 Spring 所創建以及管理的對象。
在這裏插入圖片描述
Spring IoC 實現的最基本的功能是對象的的創建與注入, 所以從最原始的抽象層面考慮, 至少存在如下 3 個步驟。

  1. 獲取 Bean 的定義信息

  2. 根據 Bean 的定義信息創建 Bean

  3. 在需要時刻, 將創建好的 Bean_A 注入 Bean_B

所以, 如果要理解 Spring, 應該從源碼角度其理解 Spring 是如何實現這三個基本步驟的。

獲取 Bean 的描述信息:BeanDefinition

Xml 形式的 Bean 描述

Spring 框架剛創建時, 是通過 xml 文件描述一個 Bean 的。例如下圖
在這裏插入圖片描述

即描述了一個 class 爲 MyTestBean 的對象, 其 id 被定義爲 myTestBean

public class MyTestBean {
	private String testStr = "testStr";
	
	public String getTestStr(){
			return testStr;
	}
	public void setTestStr(String testStr){
			this.testStr = testStr;
	}
	
}

然後可以通過聲明 XmlBeanFacotry 的方式來解析 xml, 加載對應的 bean

public class TestXmlBeanFactory{
	@Test
	public void testLoadBeanFromXml(String testStr){
		BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))
		MyTestBean bean = (MyTestBean) bf.getBean("myTestBean");
		assertEquals("testStr",bean.getTestStr());
	}
	
}

Annotation 形式的 Bean 描述

自從 Java 5.0 開始提供 Annotation 的語法支持後, Spring Framework 從 Release 2.5 版本開始, 也支持了通過 Annotation 來定義 Bean。由於 Annotation 的便捷性, Xml 形式的 bean 配置文件越來越少, 以至當下最爲流行的 Spring Boot 通常使用純註解的方式完成 Bean 的描述

@Component
public class Customer {
    private Person person;
    @Autowired
    public Customer (Person person) {          
      this.person=person;
    }
}

@Component 註釋的類, 只要在 @ComponentScan 所配置的掃描路徑下, 就會被 Spring 自動加入 bean 的管理範圍中。 被 @Autowired 修飾的域或構造器, 可以自動根據需要構造器的參數類型, 找到合適的 bean 予以注入。

兩類 IoC 容器: BeanFactory vs ApplicationContext

對於 Bean 的管理, Spring 事實上提供了兩類容器接口:

  1. BeanFactory
  2. ApplicationContext

這裏引用一個 StackOverFlow 上發現的清晰的圖, 可以較爲清晰的體現兩類容器接口的差別

Feature BeanFactory ApplicationContext
Annotation support No Yes
BeanPostProcessor Registration Manual Automatic
implementation XmlBeanFactory(Deprecated Since 3.1)
–> XmlBeanDeifinitionReader + DefaultListBeanFactory
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
WebXmlApplicationContext
internationalization No Yes
Enterprise services No Yes
ApplicationEvent publication No Yes

從上面的對比表格中, 我們可以發現, ApplicationContext 的功能比 BeanFactory 更爲全面。 從接口關係上來說, 也確實是這樣, ApplicationContext 繼承了 BeanFactory( 注意這裏是接口的繼承)。

Tips: BeanFactory 和 ApplicationContext 的區分關鍵點並不在於是否支持 Annotation。
因爲 BeanFactory 接口本身並沒有定義任何與 Xml 或者 Annotation 有關的方法。
只是 Spring恰好沒有提供一個只實現了 BeanFactory 接口的類去支持
Anotation 形式的 Bean 定義讀取。

兩類容器的接口關係及相關實現類的 UML 圖如下(注意圖中並未對 Interface 和 Class 進行圖形上的區分標註)。
在這裏插入圖片描述上圖的容器實現類主要區別如下:

  • XmlBeanFactory 從 Xml 配置文件中獲取 Bean 的描述信息, 該類已經從 Spring 3.1 之後被標註爲 @Deprecated , 相對應的, 鼓勵使用 XmlBeanDefinitionReaderDefaultListableBeanfacotory 來完成原本 XmlBeanFactory 所實現的功能。
  • FileSystemXmlApplicationContext 從特定完整路徑的 Xml 配置文件獲取 Bean 描述信息
  • ClassPathXmlApplicationContext 從類路徑中的Xml 配置文件獲取 Bean 描述信息
  • AnnotationConfigApplicationContext 從註解類型的配置中獲取 Bean 的描述信息
  • AnnotationConfigWebApplicationContextXmlWebApplicationContext 顧名思義, 分別從 Annotation 和 Xml 中獲取 Bean 的描述信息, 另外, 這種容器專門用於爲 Web 類型的應用提供 Bean 的管理

只是兩種不同的描述格式

對於 Spring 容器來說, 無論是通過 xml 獲取 Bean 的定義, 還是通過 annotation 獲取 bean 的定義, 只是兩種不同的信息獲取渠道而已, 前者通過解析 xml 文件內容完成, 後者通過掃描 java class 文件, 利用 jvm 提供的反射功能, 獲取 annotation 信息 。

Spring 具體解析 xml 或着 class 文件的過程不必細究, 需要了解的是,這些 Bean 的描述信息, 最終會被存儲在一個 BeanDefination 的實例中, spring 容器後續會以此爲基礎來創建以及管理 Bean

BeanDefinition

正如前文所描述, 通過 xml 和 annotation 獲取到 Bean 的描述信息後, 肯定需要將其統一存儲和管理起來。 在 Spring 框架代碼中, Bean 的描述信息的最終存儲形式即爲 BeanDefinition

BeanDefinition 是一個接口, 在 Spring 中有三種實現:

  1. RootBeanDefinition
  2. ChildBeanDefinition
  3. GenericBeanDefinition

這三種實現,均繼承自 AbstractBeanDefinition。

在這裏插入圖片描述
BeanDefinition 接口中包含了所有 Bean 描述信息的獲取方法, 例如:

  • getBeanClassName()
  • isLazyInit()
  • getScope()

其中, RootBeanDefinition 是最常用的實現類, 它對應於 Xml 文檔中的 標籤 或者 Annotation 中的 @Component, @Bean 等類型註解所描述的 Bean 。

ChildBeanDefinition 的出現來源於 Xml 文檔中提供的 Bean 繼承功能, 這項功能在 Annotation 中並沒有對應實現, 原因是使用 Annotation 描述 Bean 時, java 語言本身就足以提供 xml 文檔通過 Bean 繼承實現的功能。

GenericBeanDefinition 則主要用於註冊用戶可見的 BeanDefinition, 例如, 用戶會通過 post-processor 來修改 BeanDefinition 的屬性。

BeanDefinition 的管理

明確了 BeanDefinition 的概念以後, 就可以思考 Spring 如何管理 BeanDefinition 了, 整體首先肯定要劃分爲如下步驟:

  1. 從 Xml 或 Annotation 中讀取 Bean 的原始描述信息, 實例化爲 BeanDefinition 接口實現類的實例
  2. 將衆多 BeanDefinition 的信息統一存儲到一個數據結構中, 最容易想到的是一個 Map 中
  3. 根據需求, 在特定的時刻獲取 BeanDefinition , 完成 Bean 的創建與管理

BeanDefinition 的讀取與註冊

由於 Annotation 是現在最爲廣泛使用的 Bean 定義來源,以 Spring Boot 框架爲例學習, BeanDefiniton 的讀取與註冊過程。

Spring Boot 典型的包含 main 方法的啓動類代碼如下:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}    

對於 SpringApplication 的 run 方法中的源碼做刪減之後, 可以得到如下的核心步驟

		// 注意下面代碼在源碼基礎上刪減了一些次要步驟, 例如 environment , applicatioNArguments 的的構建操作等
		ConfigurableApplicationContext context = null;
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
					
			context = createApplicationContext();
			
			refreshContext(context);
			
			afterRefresh(context, applicationArguments);
		}

上面的源碼完成了

  • 應用啓動時監聽器 SpringApplicationRunListener 獲取註冊
  • ApplicationContext 的預處理( prepare)
  • ApplicationContext 的創建 ( create )
  • ApplicationContext 的刷新(refresh)

(拓展內容開始)

在執行 prepareContext 方法時, 會廣播一個 ApplicationEnvironmentPrepared類型的 event, 觸發監聽了該事件類型的 ApplicationListener 的 onApplicationEvent(ApplicationEnvironmentPreparedEvent event) 方法執行。 如果使用了 SpringCloud, 此時會觸發BootstrapApplicationListener 的 onApplicationEvent 方法, 其中包含一段如下代碼

		ConfigurableApplicationContext context = null;
		... 其他代碼...
		if (context == null) {
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
		}
		... 其他代碼...

這裏的 bootstrapServiceContext 方法內部還會再次調用 SpringApplication.run() 的方法, 創建一個 contextId 爲 “bootstrap” 的 context, 該 context 會成爲主應用 ApplicationContext 的 parent 。

更詳細的信息可以參見: Spring Cloud Context: Application Context Services, 這裏爲保持文章主線清晰, 不做展開。

拓展內容結束


回到 SpringApplication.run() 的主流程中, 如果啓動的是一個典型的 web 應用, 那麼 createApplicationContext() 會返回一個 AnnotationConfigServletWebServerApplicationContext 的實例。

在這裏觀察下 ApplicationContext 中 beanFacotory 中的變量 beanDefinitionMap
在這裏插入圖片描述
可以看到, 在剛剛執行完 createApplicationContext() 方法後, 獲得的 context 實例中的 beanDefinitionMap 變量中已經產生了 6 個鍵值對。

這說明在這個步驟中, 已經發生了 beanDefinition 的獲取。 於是查看一下 createApplicationContext 的內容

... 其他代碼 ...
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

... 其他代碼 ...
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			... 其他代碼 ...
		}
		// 下面是實例化操作
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

可以看到, createApplicationContext() 方法就是簡單的根據應用類型, 實例化了特定的 ApplicationContext 類, 在筆者測試的樣例中, 由於測試的是 Servlet 類型的應用, 這裏實例化的類就是 AnnotationConfigServletWebServerApplicationContext

進一步查看 BeanUtils.instantiateClass(contextClass) 的方法, 可以看到實例化操作是通過調用 context 類的無參數構造器(constructor) 完成的。

public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
		... 其他代碼 ...
		try {
			Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
					KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
			return instantiateClass(ctor);
		}
		... 其他代碼 ...
	}

進一步查看 AnnotationConfigServletWebServerApplicationContext 無參構造函數內容

public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

構造函數初始化了 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner 兩個變量, 從字面上可以看出來, 這兩個類分別負責了 BeanDeifinition 的讀取和掃描。

當然, 到這裏爲止, 依然沒有發生任何 beanDefinition 的獲取, 我們需要繼續追蹤, 先前的 6 個 beanDefinition 是在哪裏獲取的。

步進到 AnnotatedBeanDefinitionReader 的構造函數中

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

注意到一個可疑關鍵步驟 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

這一步驟按照方法名的字面含義, 註冊了註解形式配置(AnnotationConfig)的處理器。

步進到該函數中可以看到如下形式的代碼

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		
		... 其他代碼 ...
		
		// 下面的這段代碼直接根據固定的類, 構造了 RootBeanDefinition, 添加了到了 registry 中, 剛剛提到的 6 個beanDefinition 都是以下面這種方式完成註冊的
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		
		... 其他代碼(省略了 5 個和上面類似的註冊步驟) ...

		return beanDefs;
	}
	
... 其他代碼 ...
private static BeanDefinitionHolder registerPostProcessor(
			BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

		definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		// 下面這個操作最終會觸發 beanDefinitionMap.put(beanName, beanDefinition);
		registry.registerBeanDefinition(beanName, definition);
		return new BeanDefinitionHolder(definition, beanName);
	}	

小結一下, 至此我們已經搞清楚, 在 SpringApplication.run 方法中的 createApplicationContext 執行後, 最先被放置到 context 中的 6 個 beanDefinition 的來源。同時也發現了 AnnotationConfigServletWebServerApplicationContext 在構造的時候, 會分別初始化一個 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner

public ConfigurableApplicationContext run(String... args) {

		... 其他代碼 ...
		
		try {
			context = createApplicationContext();
			// 在上面這一步驟執行完後, 已經有 6 個最爲基礎的 beanDeifinition 被默認加載到 context 中: 
			/* 1. internalConfigurationAnnotationProcessor
					--> 對應類 ConfigurationClassPostProcessor 
			   2. internalAutowiredAnnotationProcessor
					--> 對應類 AutowiredAnnotationBeanPostProcessor
			   3. internalRequiredAnnotationProcessor
			   		--> 對應類 RequiredAnnotationBeanPostProcessor
			   4. internalCommonAnnotationProcessor
			   		--> 對應類 CommonAnnotationBeanPostProcessor
			   5. internalEventListenerProcessor
			   		--> 對應類 EventListenerMethodProcessor
			   6. internalEventListenerFactory
			   		--> 對應類 DefaultEventListenerFactory
			 */
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);		
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			
		... 其他代碼 ...
	
		}
		return context;
	}

可以注意到被默認預先加載的 6 個 beanDefinition 中, 前 4 個基本都是某一種 AnnotationProcessor 。 從類名上我們可以預判, 這些 beanDefinition 得到實例化之後, 會被用於處理各類註解。 我們需要進一步探索, 各種註解的讀取時機, 以及這些 Processor 的調用時機。

繼續順着 run 方法閱讀源碼, createApplicationContext 後的下一個關鍵步驟是 refreshContext(context) , 順着該方法可以跟進到 AbstractApplicationContext 中的 refresh 方法, 看到如下代碼

			// (1) Prepare this context for refreshing.
			prepareRefresh();

			// (2) Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// (3)Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// (4)Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// (5)Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// (6)Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// (7)Initialize message source for this context.
				initMessageSource();

				// (8)Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// (9)Initialize other special beans in specific context subclasses.
				onRefresh();

				// (10)Check for listener beans and register them.
				registerListeners();

				// (11)Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// (12)Last step: publish corresponding event.
				finishRefresh();
			}

從上述代碼可以看到, refresh context 的過程被清晰地拆解爲 12 個步驟, 每個步驟都配了清晰的註釋。根據註釋, 我們初步可以得到如下重要信息

  • 步驟 (2) 獲取了一個 beanFactory , 默認情況會返回的實例類型是 DefaultListableBeanFactory
  • 步驟 (11) 通過獲取到的 beanFactory , 實例化所有非延遲加載的 bean 到 context 中

注意, DefaultListableBeanFactory 是Spring 註冊、加載 Bean 的默認核心實現類。


(DefaultListableBeanFactory 的一些細節介紹, 可以粗略閱讀,熟悉一些接口名和類名)
在這裏插入圖片描述
從上面這張圖可以清晰地看到, DefaultListableBeanFacotry 是如何通過一層層的繼承, 來實現衆多接口類的。 DefaultListableBeanFacotry 幾乎實現了所有 Bean 管理所需要的接口, 所以後續 Spring 容器的原理多半都需要圍繞其展開。

DefaultListableBeanFactory 的接口功能簡要概括如下:

  • AliasRegistry
    • 定義對 Bean 別名的增刪改等操作
  • BeanDefinitionRegistry
    • 定義對 BeanDefinition 的增刪改操作
  • SingletonBeanRegistry
    • 定義對單例 Bean 的註冊獲取操作
  • BeanFacotry
    • 主要定義了對 Bean 以及 Bean 各種屬性的獲取操作
  • ListableBeanFactory
    • 繼承了 BeanFactory , 在其基礎上增加了批量獲取 多個bean 的方法
      • 例如: <T> Map<String, T> getBeansOfType(@Nullable Class<T> type)
  • HierarchicalBeanFactory
    • 繼承了 BeanFactory, 在其基礎上, 增加了 getParentBeanFactory() 這個方法, 實現了該接口的 BeanFacotry 就就擁有了層級關係
  • ConfigurableBeanFactory
    • 繼承了 HierarchicalBeanFactory, 提供 BeanFacotry 的多種 set 方法, 用於配置 BeanFacotroy, 其中有一個方法和 HierarchicalBeanFactory有關
    • void setParentBeanFactory(BeanFactory parentBeanFactory)
  • AutowireCapableBeanFactory
    • 顧名思義, 提供了非常核心的 Bean 創建以及 autowire 的功能,將 bean_A 自動注入到 bean_B 中
  • ConfigurableListableBeanFactory
    • 同時繼承了 ConfigurableBeanFactory, ListableBeanFactory, AuwowireCapableBeanFacotry
    • 增加了 ignoreDependencyForType(Class<?> type) 方法, 主要用於, 在自動注入時, 忽略特定的類型屬性注入
    • 增加了 void ignoreDependencyInterface(Class<?> ifc) 方法, 主要用於, 在自動注入時, 忽略特定的接口類型屬性注入
    • 上面兩種 ignore 方法的應用場景比較小衆, 暫時不作深入

(DefaultListableBeanFactory 的細節介紹結束)


回到主流程中, 在 AbstractApplicationContext.refresh() 方法的 12 步驟中, 有趣的是我們好像並不能看出來, 大量的 beanDefinition 是哪一步讀取解析的, 這需要進一步探索。

通過打斷點觀察可以發現,進入 refresh 方法時, context 中的 beanFactory 內變量 beanDeifinitionMap 包含 10 個元素(有 4 個是在 prepareContext 加載的)。 逐行執行後可以發現, 調用 invokeBeanFactoryPostProcessors(beanFactory); 後 ,beanDefinitionMap 中元素的數量明顯增多, 在筆者的測試樣例中, beanDeifinitionMap 中的元素增加到了 20 個, 說明在 invokeBeanFactoryPostProcessors 這一步驟裏, 涉及到 beanDefinition 的獲取。

	/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	... 其他代碼 ...
	}

從該方法的註釋就可以看到, 該方法負責實例化調用之前註冊好的 BeanFacotryPostProcessor 類型的 bean 。 這裏回顧一下之前在 createApplicationContext 中預先加載的 6 個 beanDeifinition, 看看這些 bean 中有沒有實現 BeanFactoryPostProcessor 接口。

挨個查看後, 可以得到如下信息

  • bean 名稱: internalConfigurationAnnotationProcessor
    • 對應類: ConfigurationClassPostProcessor
    • 實現接口: BeanFacotryPostProcessor
  • bean 名稱: internalAutowiredAnnotationProcessor
    • 對應類: AutowiredAnnotationBeanPostProcessor
    • 實現接口: BeanPostProcessor
  • bean 名稱: internalRequiredAnnotationProcessor
    • 對應類 RequiredAnnotationBeanPostProcessor
    • 實現接口: BeanPostProcessor
  • bean 名稱: internalCommonAnnotationProcessor
    • 對應類 CommonAnnotationBeanPostProcessor
    • 實現接口: BeanPostProcessor
  • bean 名稱: internalEventListenerProcessor
    • 對應類 EventListenerMethodProcessor
    • 實現接口: SmartInitializingSingleton
  • bean 名稱: internalEventListenerFactory
    • 對應類 DefaultEventListenerFactory
    • 實現接口: EventListenerFactory

(補充知識點開始)

BeanFacotryPostProcessor 和 BeanPostProcessor 的區別, 有必要在文章進一步深入之前明確一下,下圖是引自 StackOverflow 一個回答。
在這裏插入圖片描述
可以注意到區別如下:

  • BeanFacotryPostProcessor 的調用時機是所有的 BeanDefinition 都被加載到容器後,此時還沒有任何一個 Bean 被實例化。 BeanFacotryPostProcessor 可以用於修改 BeanDefinition 。
  • BeanPostProcessor 的調用時機是 Bean 被實例化之後, 它可以用於修改已經實例化後的 Bean

(補充知識結束)


注意到 internalConfigurationAnnotationProcessor 這個 bean 實現了 BeanFacotryPostProcessor , 可以在 invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) 中得到調用

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	
	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	... 其他代碼略 ...
	}

invokeBeanFactoryPostProcessors 的函數內容很長, 這裏不作展開, 如果感興趣自行閱讀一下可以發現核心內容就分兩個階段, 區分優先級順序分別調用 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 而已。

  • 階段一: 調用 BeanDefinitionRegistryPostProcessor
    • BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor 接口,由於它會在其他 BeanFacotryPostProcessor 執行前先被調用, 可以用於在 BeanDeifinition Registry ( Bean 定義的註冊表) 中進一步添加一些 beanDefinition 。
    • BeanDefinitionRegistryPostProcessor 的執行順序分優先級, 實現了 PriorityOrdered 接口的先被執行。 接着是實現了 Ordered 接口, 最後是普通的
  • 階段二: 調用 BeanFacotoryPostProcessor
    • BeanFactoryPostProcessor 的執行順序也分優先級, 實現了 PriorityOrdered 接口的先被執行。 接着是實現了 Ordered 接口, 最後是普通的

先前註冊的 ConfigurationClassPostProcessor 就實現了 BeanDefinitionRegistryPostProcessor 和 PriorityOrdered 接口, 可以得到優先地調用, 觸發該類的 postProcessBeanDefinitionRegistry 方法執行

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		... 其他代碼 ...
		this.registriesPostProcessed.add(registryId);
		processConfigBeanDefinitions(registry);
	}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}

		// Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// Detect any custom bean name generation strategy supplied through the enclosing application context
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}	

for (String beanName : candidateNames) 這一行打斷點查看一下
在這裏插入圖片描述
可以看到 candidateNames 的變量中有一個 BeanDefiniton 的名稱爲 apiApplication , 在筆者的項目中, 這個名稱就是 SpringBoot 應用的 main 方法入口類, 這個 BeanDefinition 是在 prepareContext 步驟中添加到容器中的。 此入口類上有 SpringBoot 應用的核心註釋 @SpringBootApplication

@SpringBootApplication(exclude= {UserDetailsServiceAutoConfiguration.class}
public class ApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }
}

查看一下@SpringBootApplicaion 這一註解的內容, 可看到如下內容
在這裏插入圖片描述
進一步查看 @SpringBootApplicaion 註解內容
在這裏插入圖片描述
發現 @SpringBootApplicaion 這一註解的效果之一就等同於添加了 @Configuration

所以添加了 @SpringBootApplicaion 註解的 main 方法主類, 會被添加到 configCandidates 變量中, 最終通過 parser.pase(candidates) 得到解析。

查看一下 ConfigurationClassParser 這個類的註釋解析
在這裏插入圖片描述
可以看到, ConfigurationClassParse 的作用是解析被 @Configuration 修飾的配置類的結構, 解析一個 ConfigurationClass, 最終可能會被構造出任意數量的 ConfigurationClass 的實例, 這是因爲 ConfigurationClass 可以被 @Import 註解或是 @ImportSelector 註解修飾, 這兩個註解可相當於文件引用, 可以將其他 ConfigurationClass 的內容導入到被修飾的 ConfigurationClass 中

緊接着閱讀一下 parse(Set<BeanDefinitionHolder> configCandidates) 方法, 稍微跟進一下代碼, 很快進入關鍵方法 doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

在上面的代碼中, 我們終於看到了非常熟悉的 @ComponentScan, @Import, @ImportResource 註釋的處理。 以 @ComponentScan 爲入手點, 跟進到 componentScanParser.parse( ... ) 方法中, 可以看到關鍵代碼 scanner.doScan(…) 的調用



public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
		... 其他代碼 ...
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			// 下面的 findCandidateComponents(basePackage), 將classpath 下的 basePackage 路徑下的的 class 文件, 都讀取解析成了 BeanDeifinition 的形式
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// 這裏會將解析出來的  BeanDefinition 註冊到容器中
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

至此, 我們終於梳理出來 SpringBoot 應用在零 xml 配置文件的形式下, 解析獲取 beanDefinition 的基本步驟脈絡。

下面通過一張圖把上面的文章中, 獲取到的最核心信息總結一下。
在這裏插入圖片描述

創建 Bean (實例化 Bean)

上文梳理了 beanDeifinition 的獲取流程, 現在可以學習 bean 的創建流程, 回到 AbstractApplicationContext 類的 refresh() 方法

			// (1) Prepare this context for refreshing.
			prepareRefresh();

			// (2) Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// (3)Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// (4)Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// (5)Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// (6)Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// (7)Initialize message source for this context.
				initMessageSource();

				// (8)Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// (9)Initialize other special beans in specific context subclasses.
				onRefresh();

				// (10)Check for listener beans and register them.
				registerListeners();

				// (11)Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// (12)Last step: publish corresponding event.
				finishRefresh();
			}

根據註釋可以看到, 步驟 7 、8、9、11 涉及到 bean 的初始化(也就是實例化操作), 7、8、9 屬於特殊的個別 bean 初始化,11 則完成了步驟 7、8 、9 沒有涉及到的其他非懶加載的單例 bean。 具體邏輯跟進代碼看下

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		... 其他代碼 ...
		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
}

@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isDebugEnabled()) {
			logger.debug("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.
		// 這裏對 DefaultListBeanFactory 中已經註冊的 beanDefinitionNames 進行了拷貝
		// 然後遍歷處理, 目的是確保在遍歷的過程中,還能向原始的 beanDeifintionNames 中註冊新的 beanDefnition, 同時不影響遍歷過程
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// 這裏開始出發所有非延遲加載的單例 bean 初始化
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 這裏判斷, beanDefinition 是非抽象的, 單例的, 非延遲加載的, 才嘗試進一步處理
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				// 如果是工廠 bean
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						// 查看SmartFacotryBean 這個接口, 可以看到這是一個主要用於 spring 內部的接口, 基本上不需要spring 框架使用者關心, 這裏不做深究 
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) 
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
						// 如果最終判斷了 factoryBean 是屬於 eagerInit (迫切需要初始化的) 類型
						// 在這裏就通過 getBean 方法進行初始化
							getBean(beanName);
						}
					}
				}
				else {
				// 非工廠類型的, 單例非懶加載 bean 這裏直接初始化
					getBean(beanName);
				}
			}
		}

		// Trigger post-initialization callback for all applicable beans...
		// 觸發在需要在初始化後, 對所有 bean (適用的 bean ) 執行的回調函數邏輯
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

從上面的代碼邏輯中, 我們可以看出, beanDefinition 的初始化是通過 DefaultListableBeanFactory 的 getBean( beanName) 完成的。 於是跟進這個方法, 追蹤到 AbstractBeanFactory 中定義的 doGetBean(name, null, null, false);

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

/**
	 * 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(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException 

doGetBean 是 bean 創建的核心方法, 這個方法的代碼比較多, 這裏拆分開來一點點學習

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		// 首先看這裏的 transformedBeanName 方法作用
		final String beanName = transformedBeanName(name);
		Object bean;

根據 transformedBeanName 的註釋,

/**
	 * Return the actual bean name, stripping out the factory dereference
	 * prefix (if any, also stripping repeated factory prefixes if found).
	 * @param name the name of the bean
	 * @return the transformed name
	 * @see BeanFactory#FACTORY_BEAN_PREFIX
	 */
	 public static String transformedBeanName(String name)

可以看出該方法的作用是, 將傳入的 name 參數轉換爲實際的最終 beanName , 有如下轉換情況

  • 轉換前的 beanName 可能是一個包含 facotory bean 前綴的名稱, 該方法會去除該前綴。
  • 轉換前的 beanName 可能是一個包含 factory bean 前綴的別名(alias), 該方法會去除前綴, 並將別名轉換爲實際名稱
  • 轉換前的 beanName 可能是一個 bean 的別名, 該方法將別名轉換爲實際名稱

bean 的別名機制很好理解, 但是 factory bean 的特殊前綴是一個知識點。


知識點: Spring 中的 FactoryBean ( 注意與 BeanFactory 進行區分)

在 Spring bean 中, 有兩類 bean, 一類是普通的 bean, 另一類是 facotory bean。 factory bean 與普通 bean 的區別是:

  • 普通 bean (ordinary bean): 我們通常直接從 spring 容器中獲取定義的 bean, 注入需要依賴它的對象
  • 工廠 bean (factory bean):是自己本身可以生產 bean 的一類工廠 bean。 我們往往最終注入的往往不是定義的工廠 bean 本身, 而是它所生產的對象。 但是, 它所生產的對象是不在 Spring 容器的管理範圍之內的。

舉個例子, 我們構建一個可以生產 Tool 的 ToolFactory

public class Tool {
 
    private int id;
 
    // standard constructors, getters and setters
}
public class ToolFactory implements FactoryBean<Tool> {
 
    private int factoryId;
    private int toolId;
 
    @Override
    public Tool getObject() throws Exception {
        return new Tool(toolId);
    }
 
    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }
 
    @Override
    public boolean isSingleton() {
        return false;
    }
 
    // standard setters and getters
}

在使用註解的場景下, 我們可以通過如下方式配置 bean

@Configuration
public class FactoryBeanAppConfig {
  
    @Bean(name = "toolFactory")
    public ToolFactory toolFactory() {
        ToolFactory factory = new ToolFactory();
        factory.setFactoryId(7070);
        factory.setToolId(2);
        return factory;
    }
 
    @Bean
    public Tool tool() throws Exception {
        return toolFactory().getObject();
    }
}

下面看,怎麼注入工廠bean。 需要注意默認情況下, 直接

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FactoryBeanAppConfig.class)
public class FactoryBeanJavaConfigTest {
 
    @Autowired
    private Tool tool;
  
    @Resource(name = "&tool")
    private ToolFactory toolFactory;
 
    @Test
    public void testConstructWorkerByJava() {
        assertThat(tool.getId(), equalTo(2));
        assertThat(toolFactory.getFactoryId(), equalTo(7070));
    }
}


從上面這個使用流程可以看到, 最終需要注入 Tool 實例的場景, 是通過 toolFacotry.getObject() 這一方法獲得的。

當我們需要獲取生產 “Tool”這一類型的工廠bean本身時, 在注入域上有一個特殊的名稱寫法,@Resource(name = "&tool")

這裏的 “&” 符號, 即是前文提到的 transformed 方法會處理的一種情況

知識點結束


回到前文正在閱讀的 AbstractBeanFactory 中的 doGetBean 方法源碼

		// transformedBeanName 方法作用已經基本理解
		final String beanName = transformedBeanName(name);
	    // 從這裏繼續看後續的邏輯
		Object bean;
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			... 此處省略一些日誌邏輯...
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}else{
			// Check if bean definition exists in this factory.
			// 檢查需要獲取的 bean 是否需要從 parent bean factory 中獲取
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)){
				... 此處省略嘗試從 parent bean facotry 獲取 bean 的邏輯
				... 如果成功從 parent bean facotry 獲取到到對應的 bean, 會直接 return
			}
			// 如果當前獲取的 bean 並不僅僅是用於類型檢查, 而是真的需要使用,此時就已經可以將這個 bean 標識爲已創建了, 具體是向一個名稱爲 alreadyCreated 的 set 中添加該 beanName
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			// 程序運行到這裏就說明, 該 bean 既不在已創建的bean緩存中, 也不能從 parent bean factory 中獲取, 應該由當前的 beanFactory 創建
			// 下面是一個大的 try catch 塊, 包含了一個 bean 的在 AbstractBeanFactory 的正式創建流程
			try {
				// 1. 獲取 beanDeifinition, 做一些檢查工作
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				// 2. 根據 beanDefinition 獲取該 bean 的全部依賴 bean, 挨個予以創建或獲取
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
					    // 在這裏檢查是否存在循環依賴
					    // 注意: isDependent 這個方法是在檢查名稱爲 dep 是否依賴於 beanName, 參數的順序上有點反直覺
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 將這個依賴的 dep 添加到一個 map 中,將來在銷燬 beanName 的時候, 要先把所有的 dep 銷燬了
						registerDependentBean(dep, beanName);
						// 執行 dep 的獲取/創建邏輯
						// 注意這裏調用的還是 getBean 方法, 實質上是發生了遞歸調用
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
				// 3. 執行到這裏時, beanName 所有依賴的 bean,包括傳遞依賴, 肯定已經被創建了
				//    可以正式執行 beanName 的創建邏輯了
				// Create bean instance.
				if (mbd.isSingleton()) {// 如果待創建的是 單例Bean
					// 注意這裏第二個參數的寫法是 lamda 語法對於傳統匿名內部類的表達, 這裏構造的其實是 ObjectFactory 的匿名內部類
					// getSingleton 方法中還是會嘗試先從已經存在的 singltonBeans 中獲取
					// 找不到時, 再進行創建, 最終調用的方法就是 createBean 這個方法
					// createBean 方法的默認實現來自於 AbstractAutowireCapableBeanFactory
					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;
						}
					});
					// 上面已經完成了 bean 的創建, 下面這個方法實際是在處理 factoryBean 的情況
					// 如果正在構造的是一個factory bean, 那麼最終返回給外部的應該是這個 factory bean
					// 生產的實例對象 object 而不是這個 bean 本身
					// 如果正在構造的是一個普通的 bean, 那這個方法的最終就會直接返回 sharedInstance 本身
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {// 如果待創建的是原型 bean
				    // 原型 bean 的創建過程就不包含從已有的 bean 中進行尋找的過程了
				    // 因爲原型 bean 的設計目的就是獲取的時候即創建新實例
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {// 如果待創建的既不是單例bean, 也不是原型 bean
				      // 那麼根據 beanDefinition 中的 scope 屬性決定如何創建這個 bean		
					String scopeName = mbd.getScope();
					final 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);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			
		}
		// 前面的代碼已經完成了 bean 的獲取/創建工作, 這裏做一些後置的requiredType檢查
		// 在必要的時候對 bean 做一下類型轉換, 確保最終返回的 bean 是調用方所期待的 bean
		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}

上面的這段代碼雖然很長, 但是通過逐步註釋分解, 並忽略掉異常處理分支以後, 我們可以總結出它如下的幾個步驟。
在這裏插入圖片描述
至此我們就理清創建 bean (實例化 bean)的基本流程, 更加細節的實例化流程(如何根據 beanDefinition 找到 class 文件, 實例化出一個對象, 完成初始化等)的默認實現由 AbstractAutowireCapableBeanFactory.createBean(..) 完成, 這裏不做更詳細的展開, 感興趣的讀者可以自行研究

總結

本文主要解析瞭如下的知識點:

  1. Spring 容器的 IoC 思想爲何被廣泛使用
  2. Spring 容器的核心 Bean, BeanDefinition, BeanFactory, ApplicationContext 的概念
  3. Spring 容器如何加載 BeanDefinition, 如何根據 BeanDefinition 實例化 Bean (以 SpringBoot 應用爲例)

本文附帶衍生介紹瞭如下知識點:
4. Spring Cloud 框架中會創建的 bootstrapServiceContext
5. FactoryBean 的概念(注意不要與 BeanFactory 混淆)

掌握了以上知識有什麼用:

  1. 使用 Spring 框架的功能, 最後都會落腳於一個個負責不同功能的 Bean , 掌握了 Bean 在 Spring 中最基礎的管理方式, 有助於我們在遇到問時(例如無法正常 autowire 一個 bean 時), 進行有效地排查。
  2. 可以進一步去學習 Spring 框架的延伸框架 Spring Cloud 等流行框架的啓動及運行原理, 有能力對此類開箱即用的框架在必要時進行調試, 擴展乃至修改。

下一篇文章, 筆者會開始整理 Spring Cloud 體系中, 一個極大提升了開發效率的框架 OpenFeign 的基本工作原理, 其中必然會涉及到很多這篇文章所介紹的基本概念,歡迎感興趣的讀者屆時關注閱讀

發佈了51 篇原創文章 · 獲贊 264 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章