【Spring淺析】一、 BeanFactory 有啥可說的?

阿全啊發表於2021-05-16

閱讀 Spring 原始碼,BeanFactory 是避不了的存在。而大家常見的使用場景,也是以下形式:

ConfigurableApplicationContext ctx = SpringApplication.run(xxx.class);
BeanFactory beanFactory = (BeanFactory) ctx;
beanFactory.getBean(xxx);

但 BeanFactory 可不是如此枯燥無味的。

前置知識

  1. Spring 的 IOC 特性的核心就是: Bean容器,也叫 Bean工廠 —— 儲存 bean 的工廠,負責 bean 的加入、例項化、初始化、監控與刪除操作。
  2. 我們知道,使用 Spring 前,我們需要在 XML 或 Annotation 中配置 bean,這樣 Spring 在啟動時,通過載入指定的配置檔案或配置類,就能夠往容器中存入相應的 bean 例項。而這些在配置檔案/類中的定義,稱為 Bean定義,在 Spring 中使用 BeanDefinition 進行封裝。
  3. Bean定義所在的配置檔案/配置類,我們稱為配置源,Spring 支援不同的配置源,有:XML、Annotation、Groovy 甚至 Property。
  4. 讀取不同的配置源,有不同的讀取器,如:讀取 XML 的有 XmlBeanDefinitionReader,讀取 Property 的有 PropertiesBeanDefinitionReader,讀取 Annotation 的有:AnnotatedBeanDefinitionReader。
  5. 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 來獲知例項範圍資訊,只能通過其方法:isSingletonisPrototype。沒錯,就是這麼純粹。
因為單例模型在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 的內定工廠,實現如下:

總結:

  1. 瞭解了 BeanFactory 的繼承體系之後,你能夠很快地回憶起相關的知識點,比如:

    • Bean的例項化過程,那一定是在 AutowireCapableBeanFactory 中,而且預設實現就是抽象類:AbstractAutowireCapableBeanFactory
    • Bean工廠具有層級概念,當前 Bean工廠獲取不到相應資訊時(Bean例項、Bean定義等),可上溯父工廠獲取;
  2. 如何較好地記憶 BeanFactory 相關知識點,可通過功能、命名等方面入手,如:
    從功能與命名入手

  3. 回顧原始碼,你會看到如下程式碼
    型別判斷

只有相應的介面實現,才具備對應的 API 進行設定與獲取。

以上是 Bean工廠的相關學習與總結,如果錯漏,歡迎指正。

相關文章