閱讀 Spring 原始碼,BeanFactory 是避不了的存在。而大家常見的使用場景,也是以下形式:
ConfigurableApplicationContext ctx = SpringApplication.run(xxx.class);
BeanFactory beanFactory = (BeanFactory) ctx;
beanFactory.getBean(xxx);
但 BeanFactory 可不是如此枯燥無味的。
前置知識
- Spring 的 IOC 特性的核心就是: Bean容器,也叫 Bean工廠 —— 儲存 bean 的工廠,負責 bean 的加入、例項化、初始化、監控與刪除操作。
- 我們知道,使用 Spring 前,我們需要在 XML 或 Annotation 中配置 bean,這樣 Spring 在啟動時,通過載入指定的配置檔案或配置類,就能夠往容器中存入相應的 bean 例項。而這些在配置檔案/類中的定義,稱為 Bean定義,在 Spring 中使用 BeanDefinition 進行封裝。
- Bean定義所在的配置檔案/配置類,我們稱為配置源,Spring 支援不同的配置源,有:XML、Annotation、Groovy 甚至 Property。
- 讀取不同的配置源,有不同的讀取器,如:讀取 XML 的有 XmlBeanDefinitionReader,讀取 Property 的有 PropertiesBeanDefinitionReader,讀取 Annotation 的有:AnnotatedBeanDefinitionReader。
- Annotation 是在 Java5 之後引入的,所以初期 Spring 以讀取指定配置源來收集 Bean定義;在 Spring 支援註解之後,配置源由配置檔案轉變為 Class。所以,AnnotatedBeanDefinitionReader 與 XmlBeanDefinitionReader 並沒有繼承自統一父類,但他們屬於相同的模組概念。
BeanFactory
在 Spring 中 BeanFactory 介面的定義非常簡單,主要有以下方法:
<T> T getBean(Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
// ...其他
只負責獲取、判斷操作。
然而,僅僅這些方法並不滿足多功能 BeanFactory 的需求,所以 BeanFactory 有以下三個不同特性的子類:
ListableBeanFactory
HierarchicalBeanFactory
AutowireCapableBeanFactory
- ListableBeanFactory:此子介面要求實現者具被列舉/索引/檢索功能,從而避免需要通過 beanName 一個個迭代進行查詢,我們很快就能想到 Map 結構。同時,此介面定義的方法,要求能夠關注到預載入的BeanDefinition(即 Bean 定義)。
String[] getBeanDefinitionNames();
boolean containsBeanDefinition(String beanName);
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType, boolean allowEagerInit);
- HierarchicalBeanFactory:此類見名知意,是個具有層級結構的 BeanFactory,它提供的方法,也只有以下兩個:獲取父工廠及判斷當前工廠是否包含指定 Bean。
BeanFactory getParentBeanFactory();
boolean containsLocalBean(String name);
- AutowireCapableBeanFactory:此類是 Spring 提供的,用以支援 Bean 自動裝配、注入、填充的工廠,也就是說 Bean 的例項化在此工廠中(
常見的 Bean 的例項化的過程
在此工廠中完成,定義了相關的 API)
<T> T createBean(Class<T> beanClass) throws BeansException; // 建立
void autowireBean(Object existingBean) throws BeansException; // 自動裝配
Object initializeBean(Object existingBean, String beanName) throws BeansException; // 初始化
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) BeansException; // 過程回撥
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException; // 過程回撥
void destroyBean(Object existingBean); // 銷燬
乍一看,發現 BeanFactory 豐富了很多,與 BeanDefinition 也有了關聯,可仔細一看,就會發現,這些介面定義的都是獲取或判斷方法,與 BeanFactory 類似;而且都是與 Bean 有緊密聯絡。在此之外,Spring 提供了 BeanFactory 的相關控制選項的配置介面。
Spring 首先提供了一個 HierarchicalBeanFactory 的子類:ConfigurableBeanFactory。它定義了 BeanFactory 的一些相關配置入口,同時也定義了 HierarchicalBeanFactory#getParentBeanFactory 的parent設定入口,總的來說,它是 BeanFactory 配置的相關類(定義了配置的設定與獲取),比如有:Scope 的註冊、型別轉換器列表的持有,後置處理器的新增等等,
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException; // 設定父工廠
void setBeanClassLoader(@Nullable ClassLoader beanClassLoader); // 設定 Bean 的 ClassLoader
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); // 新增 BeanPostProcessor
void registerScope(String scopeName, Scope scope); // 註冊 Scope
Scope getRegisteredScope(String scopeName); // 從 BeanFactory 獲取配置的 Scope 資訊
BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 獲取合併後的 BeanDefinition
工廠相關的配置有了。Spring 又在此基礎上,擴充套件了一個新的子工廠型別:ConfigurableListableBeanFactory,它的繼承關係如下:
此介面繼承了所有 BeanFactory 相關的主要介面型別,具備了上述提及的工廠的所有功能,現在這個型別是“有閒有錢”,我們來看看它定義了哪些新方法:
void ignoreDependencyInterface(Class<?> ifc); // 忽略依賴介面,如:Aware 類介面
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 獲取Bean定義(只獲取當前Bean內的定義,不考慮層級)
boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException; // 是否支援自動注入
void freezeConfiguration(); // 凍結配置,Bean定義不再支援修改(修改意味著要重新生成Bean例項)
void preInstantiateSingletons() throws BeansException; // 與例項化所有單例
因為繼承了自動配置工廠(AutowireCapableBeanFactory),因此具備自動裝配Bean能力。同時自己在此基礎上,定義了一層外部控制,如:isAutowireCandidate、freezeConfiguration,更重要的是:preInstantiateSingletons。
畢竟它現在掌握了資源:具備檢索Bean定義、具備自動裝配控制、具備Bean工廠配置,因此它有能力作為例項化單例的入口。
除了繼承主要的 Bean工廠型別,此介面還繼承了 SingletonBeanRegistry
。此介面與其他介面沒有任何關聯,卻與 Spring IOC 特性息息相關。
我們知道,Spring 容器中的 Bean 具備多種 Scope 範圍,我們常見的是 Singleton、Prototype。但通過以上工廠定義,我們看到的只有 getBean 操作,而沒有 getSingleton。也就是說:Spring 的頂層 BeanFactory 關注的的確只有 IOC(即控制反轉),而不關注容器內是單例還是原型。要想通過 BeanFactory 來獲知例項範圍資訊,只能通過其方法:isSingleton
,isPrototype
。沒錯,就是這麼純粹。
因為單例模型在Spring中也是舉足輕重,所以 Spring 劃分了單獨的概念:SingletonBeanRegistry
它的方法有:
void registerSingleton(String beanName, Object singletonObject); // 註冊單例(不一定要通過Bean定義構建)
Object getSingleton(String beanName); // 獲取單例
boolean containsSingleton(String beanName); // 判斷是否包含單例
String[] getSingletonNames(); // 獲取所有單例名稱
int getSingletonCount(); // 獲取單例數
具備了單例的註冊與獲取。
當然,除非明確指定 bean 非單例,否則,在 Spring 中預設解析 Bean定義後註冊到容器中,都是使用:registerSingleton
,即預設註冊單例型別。
以上結果,構建了一個功能豐富且完善的 Bean工廠,具有
- Bean工廠 配置
- BeanDefinition 索引獲取
- 觸發例項化 Bean
- Bean例項化與填充、初始化
- Bean構建時回撥
- Bean例項索引獲取
- Bean銷燬
- 上溯獲取父工廠
每個 Bean工廠都有相應的 Abstract實現,最終由子類:DefaultListableBeanFactory 完成統一大業,並被 ApplicationContext 使用
Bean 的構建基於 Bean定義,它從何而來?
Spring 為 BeanDefinition 定義了抽象 BeanDefinitionRegistry
,具有以下方法:
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
boolean containsBeanDefinition(String beanName);
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
具備 Bean定義的註冊、移除、獲取、判斷等,並由 DefaultListableBeanFactory 進行實現。所以 DefaultListableBeanFactory 作為 Spring 的核心類 ApplicationContext 的內定工廠,實現如下:
總結:
-
瞭解了 BeanFactory 的繼承體系之後,你能夠很快地回憶起相關的知識點,比如:
- Bean的例項化過程,那一定是在
AutowireCapableBeanFactory
中,而且預設實現就是抽象類:AbstractAutowireCapableBeanFactory
; - Bean工廠具有層級概念,當前 Bean工廠獲取不到相應資訊時(Bean例項、Bean定義等),可上溯父工廠獲取;
- Bean的例項化過程,那一定是在
-
如何較好地記憶 BeanFactory 相關知識點,可通過功能、命名等方面入手,如:
-
回顧原始碼,你會看到如下程式碼
只有相應的介面實現,才具備對應的 API 進行設定與獲取。
以上是 Bean工廠的相關學習與總結,如果錯漏,歡迎指正。