Spring源碼解析 - BeanFactory介面體系解讀

来源:http://www.cnblogs.com/leftthen/archive/2016/03/10/5261837.html
-Advertisement-
Play Games

BeanFactory是Spring IOC實現的基礎,這邊定義了一系列的介面,我們通過這些介面的學習,可以大致瞭解BeanFactory體系各介面如何分工合作.為閱讀具體實現打下基礎.


不知道為什麼看著Spring的源碼,感觸最深的是Spring對概念的抽象,所以我就先學介面了.

 

BeanFactory是Spring IOC實現的基礎,這邊定義了一系列的介面,我們通過這些介面的學習,可以大致瞭解BeanFactory體系各介面如何分工合作.

為學習具體實現打下基礎.畢竟這邊邏輯複雜,涉及的概念很多.

BeanFactory 是Spring bean容器的根介面.提供獲取bean,是否包含bean,是否單例與原型,獲取bean類型,bean 別名的api.

-- AutowireCapableBeanFactory 添加集成其他框架功能.如果集成WebWork則可以使用Spring對Actions等進行管理.

-- HierarchicalBeanFactory 提供父容器的訪問功能

-- -- ConfigurableBeanFactory 如名,提供factory的配置功能,眼花繚亂好多api

-- -- -- ConfigurableListableBeanFactory 集大成者,提供解析,修改bean定義,並與初始化單例.

-- ListableBeanFactory 提供容器內bean實例的枚舉功能.這邊不會考慮父容器內的實例.

 

看到這邊,我們是不是想起了設計模式原則里的介面隔離原則

 Interface Segregation Principle(ISP):客戶端不應該依賴它不需要的介面;類間的依賴關係應該建立在最小的介面上

對這個有興趣的話,找度娘或者看看這個設計模式六大原則(4):介面隔離原則

 

這邊清晰地定義瞭如下的體系:

  根介面BeanFactory(基礎容器)

  第二層: 第三方集成,繼承體系,遍歷bean

  第三層: 配置功能

  第四層: 配置+迭代

  

接下來具體分析下各個介面吧(順便做目錄):

1. BeanFactory

2. AutowireCapableBeanFactory

3. HierarchicalBeanFactory

4. ListableBeanFactory

5. ConfigurableBeanFactory

6. ConfigableListableBeanFactory

 

1. BeanFactory

BeanFactory是Spring bean容器的根介面.

每個bean都是通過string類型bean name進行標識.這邊提供了設計模式單例,原型的替代實現.

  如果bean name配置為單例,應用內只會獲取到一個實例.如果配置為原型,那麼可以實例化好後填充屬性(基於用戶的配置).

BeanFactory作為應用集中配置管理的地方,極大簡便應用開發,這樣開發人員可以集中與業務.

 

BeanFactory需要管理bean的生命周期,比如初始化時需要按順序實現如下介面:

  1. BeanNameAware's {@code setBeanName}
  2. BeanClassLoaderAware's {@code setBeanClassLoader}
  3. BeanFactoryAware's {@code setBeanFactory}
  4. ResourceLoaderAware's {@code setResourceLoader}僅對application context有效
  5. ApplicationEventPublisherAware's {@code setApplicationEventPublisher}僅對application context有效
  6. MessageSourceAware's {@code setMessageSource}僅對application context有效
  7. ApplicationContextAware's {@code setApplicationContext}僅對application context有效
  8. ServletContextAware's {@code setServletContext}僅對application context有效
  9. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
  10. InitializingBean's {@code afterPropertiesSet}
  11. a custom init-method definition xml中配置的init-method
  12. {@code postProcessAfterInitialization} methods of BeanPostProcessors

還有關閉容器的介面:

  1. DisposableBean's {@code destroy}
  2. a custom destroy-method definition xml配置中的destroy-method

 

介面里定義了一個變數String FACTORY_BEAN_PREFIX = "&";

  這是用來區分是獲取FactoryBean還是FactoryBean的createBean創建的實例.如果&開始則獲取FactoryBean;否則獲取createBean創建的實例.

我們來看下定義的方法:

  a, 獲取bean,這邊可以實現單例,原型

    Object getBean(String name) throws BeansException; 可以用別名查找哦

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException; 這邊的類型可以是介面或者子類,但不能是null

    Object getBean(String name, Object... args) throws BeansException;

  b, 判斷是否包含bean.陷阱出現:這邊不管類是否抽象類,懶載入,是否在容器範圍內,只要符合都返回true,所以這邊true,不一定能從getBean獲取實例

    boolean containsBean(String name);

  c, 單例,原型,bean類型的判斷

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;

  d, 獲取bean 的類型,別名

    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    String[] getAliases(String name);

 

2. AutowireCapableBeanFactory

在BeanFactory基礎上實現對已存在實例的管理.

可以使用這個介面集成其它框架,捆綁並填充並不由Spring管理生命周期並已存在的實例.像集成WebWork的Actions 和Tapestry Page就很實用.

一般應用開發者不會使用這個介面,所以像ApplicationContext這樣的外觀實現類不會實現這個介面,如果真手癢癢可以通過ApplicationContext的getAutowireCapableBeanFactory介面獲取. 

 

這邊定義了5種自動裝配策略:不註入AUTOWIRE_NO,使用bean name策略裝配AUTOWIRE_BY_NAME,使用類型裝配策略AUTOWIRE_BY_TYPE,使用構造器裝配策略AUTOWIRE_CONSTRUCTOR,自動裝配策略AUTOWIRE_AUTODETECT

  這邊的自動策略是先嘗試構造器,然後才是byType.這邊應該是跟xml配置文件中的裝配策略對應.

繼續看定義的api:

  a, 創建和填充外部bean實例的典型方法

    <T> T createBean(Class<T> beanClass) throws BeansException;

    void autowireBean(Object existingBean) throws BeansException; // 使用autowireBeanProperties裝配屬性

    Object configureBean(Object existingBean, String beanName) throws BeansException; // 自動裝配屬性,填充屬性值,使用諸如setBeanName,setBeanFactory這樣的工廠回調填充屬性,最好還要調用post processor

    Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException;

  b, 在bean的生命周期進行細粒度控制的專門方法

    Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; // 會執行bean完整的初始化,包括BeanPostProcessors和initializeBean

    Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

    void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;

    void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;

    Object initializeBean(Object existingBean, String beanName) throws BeansException;

    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;

    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;

    Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException;

 

3. HierarchicalBeanFactory

 提供父容器的訪問功能.至於父容器的設置,需要找ConfigurableBeanFactory的setParentBeanFactory(介面把設置跟獲取給拆開了!).

這邊可說的不多,直接上api:

  a, 獲取父容器 bean factory

    BeanFactory getParentBeanFactory();

  b, 判斷當前容器是否保護bean

    boolean containsLocalBean(String name);

 

4. ListableBeanFactory

獲取bean時,Spring 鼓勵使用這個介面定義的api. 還有個Beanfactory方便使用.其他的4個介面都是不鼓勵使用的.

提供容器中bean迭代的功能,不再需要一個個bean地查找.比如可以一次獲取全部的bean(太暴力了),根據類型獲取bean.在看SpringMVC時,掃描包路徑下的具體實現策略就是使用的這種方式(那邊使用的是BeanFactoryUtils封裝的api).

 如果同時實現了HierarchicalBeanFactory,返回值不會考慮父類BeanFactory,只考慮當前factory定義的類.當然也可以使用BeanFactoryUtils輔助類來查找祖先工廠中的類. 

 這個介面中的方法只會考慮本factory定義的bean.這些方法會忽略ConfigurableBeanFactory的registerSingleton註冊的單例bean(getBeanNamesOfType和getBeansOfType是例外,一樣會考慮手動註冊的單例).當然BeanFactory的getBean一樣可以透明訪問這些特殊bean.當然在典型情況下,所有的bean都是由external bean定義,所以應用不需要顧慮這些差別.

註意:getBeanDefinitionCount和containsBeanDefinition的實現方法因為效率比較低,還是少用為好.

繼續上api吧

  a, 暴力獲取全部bean的屬性:

    boolean containsBeanDefinition(String beanName);  //是否包含bean

    int getBeanDefinitionCount(); // 當前factory中定義的bean數量

    String[] getBeanDefinitionNames(); // 獲取當前工廠中定義的所有bean 的name

  b, 根據bean 的類型獲取bean

    這邊的方法僅檢查頂級bean.它不會檢查嵌套的bean.FactoryBean創建的bean會匹配為FactoryBean而不是原始類型.

    一樣不會考慮父factory中的bean,非要用可以通過BeanFactoryUtils中的beanNamesForTypeIncludingAncestors.
    其他方式註冊的單例這邊會納入判斷.
    這個版本的getBeanNamesForType會匹配所有類型的bean,包括單例,原型,FactoryBean.返回的bean names會根據backend 配置的進行排序.

    String[] getBeanNamesForType(Class<?> type); // 獲取給定類型的bean names(包括子類),通過bean 定義或者FactoryBean的getObjectType判斷.

    String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);

    <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException; // 如果保護懶載入的類,FactoryBean初始化的類和工廠方法初始化的類會被初始化.就是說執行這個方法會執行對應的初始化.

    <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;

  c, 查找使用註解的類

    Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;

  d, 查找一個類上的註解,如果找不到,父類,介面使用註解也算.

    <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);

 

5. ConfigurableBeanFactory

定義BeanFactory的配置.

這邊定義了太多太多的api,比如類載入器,類型轉化,屬性編輯器,BeanPostProcessor,作用域,bean定義,處理bean依賴關係,合併其他ConfigurableBeanFactory,bean如何銷毀.

 

定義了兩個作用域: 單例和原型.可以通過registerScope來添加.

  SCOPE_SINGLETON,SCOPE_PROTOTYPE

這邊定義了好多好多的api,所以我們這邊只講業務,具體的api看文末的附錄吧:

  a, 父容器設置.而且一旦設置了就不讓修改

  b, 類載入器設置與獲取.預設使用當前線程中的類載入器

  c, 為了類型匹配,搞個臨時類載入器.好在一般情況為null,使用上面定義的標準載入器  

  d, 是否需要緩存bean metadata,比如bean difinition 和 解析好的classes.預設開啟緩存

  e, 定義用於解析bean definition的表達式解析器

  f, 類型轉化器

  g, 屬性編輯器

  h, BeanFactory用來轉換bean屬性值或者參數值的自定義轉換器

  i,string值解析器(想起mvc中的ArgumentResolver了)

  j,大boss BeanPostProcessor用於增強bean初始化功能 

  k,作用域定義

  l,訪問許可權控制

  m, 合併其他ConfigurableBeanFactory的配置,包括上面說到的BeanPostProcessor,作用域等

  n, bean定義處理

  o, bean創建狀態控制.在解決迴圈依賴時有使用

  p, 處理bean依賴問題

  q, bean生命周期管理-- 銷毀bean

 

 

 

6. ConfigableListableBeanFactory

 提供bean definition的解析,註冊功能,再對單例來個預載入(解決迴圈依賴問題). 

貌似我們一般開發就會直接定義這麼個介面了事.而不是像Spring這樣先根據使用情況細分那麼多,到這邊再合併

 

  a, 設置忽略的依賴關係,註冊找到的特殊依賴

    void ignoreDependencyType(Class<?> type); // 忽略類型

    void ignoreDependencyInterface(Class<?> ifc); // 忽略介面

    void registerResolvableDependency(Class<?> dependencyType, Object autowiredValue);

    boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException;

  b, 獲取bean定義 (可以訪問屬性值跟構造方法的參數值)

    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

  c, 鎖定配置信息.在調用refresh時會使用到.

    void freezeConfiguration();

    boolean isConfigurationFrozen();

  d, 預載入不是懶載入的單例.用於解決迴圈依賴問題

    void preInstantiateSingletons() throws BeansException;

 

 

附錄--ConfigureableBeanFactory中定義的api:

  a, 父容器設置.而且一旦設置了就不讓修改

    void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;

  b, 類載入器設置與獲取.預設使用當前線程中的類載入器

    void setBeanClassLoader(ClassLoader beanClassLoader);

    ClassLoader getBeanClassLoader();

  c, 為了類型匹配,搞個臨時類載入器.好在一般情況為null,使用上面定義的標準載入器  

    void setTempClassLoader(ClassLoader tempClassLoader);

    ClassLoader getTempClassLoader();

  d, 是否需要緩存bean metadata,比如bean difinition 和 解析好的classes.預設開啟緩存

    void setCacheBeanMetadata(boolean cacheBeanMetadata);

    boolean isCacheBeanMetadata();

  e, 定義用於解析bean definition的表達式解析器

    void setBeanExpressionResolver(BeanExpressionResolver resolver);

    BeanExpressionResolver getBeanExpressionResolver();

  f, 類型轉化器

    void setConversionService(ConversionService conversionService);

    ConversionService getConversionService();

  g, 屬性編輯器

    void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);

    void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);

    void copyRegisteredEditorsTo(PropertyEditorRegistry registry);

  h, BeanFactory用來轉換bean屬性值或者參數值的自定義轉換器

    void setTypeConverter(TypeConverter typeConverter);

    TypeConverter getTypeConverter();

  i,string值解析器(想起mvc中的ArgumentResolver了)

    void addEmbeddedValueResolver(StringValueResolver valueResolver);

    String resolveEmbeddedValue(String value);

  j,大boss BeanPostProcessor用於增強bean初始化功能

    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

    int getBeanPostProcessorCount();    

  k,作用域定義

    void registerScope(String scopeName, Scope scope);

    String[] getRegisteredScopeNames();

    Scope getRegisteredScope(String scopeName);

  l,訪問許可權控制

    AccessControlContext getAccessControlContext();

  m, 合併其他ConfigurableBeanFactory的配置,包括上面說到的BeanPostProcessor,作用域等

    void copyConfigurationFrom(ConfigurableBeanFactory otherFactory);

  n, bean定義處理

    void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException; // 註冊別名

    void resolveAliases(StringValueResolver valueResolver);

    BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 合併bean定義,包括父容器的

    boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException; // 是否是FactoryBean類型

  o, bean創建狀態控制.在解決迴圈依賴時有使用

    void setCurrentlyInCreation(String beanName, boolean inCreation);

    boolean isCurrentlyInCreation(String beanName);

  p, 處理bean依賴問題

    void registerDependentBean(String beanName, String dependentBeanName);

    String[] getDependentBeans(String beanName);

    String[] getDependenciesForBean(String beanName);

  q, bean生命周期管理-- 銷毀bean

    void destroyBean(String beanName, Object beanInstance);

    void destroyScopedBean(String beanName);

    void destroySingletons();

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Ext.Net是一個對ExtJS進行封裝了的.net控制項庫,可以在ASP.NET WebForm和MVC中使用。從今天開始記錄我的學習筆記,這是第一篇,今天學習瞭如何在WebForm中使用Ext.Net控制項庫。 下載Ext.Net 首先要去Ext.Net網站上下載Ext.Net,我先學習的是WebF
  • SSRS 報表 如何匿名查看 昨晚一直研究怎麼能匿名訪問報表然後給客戶看呢? 研究了好幾種辦法 我試過的分為三種,其中推薦我認為相對可控一點。 1.修改SSRS配置文件來禁止他驗證登陸用戶許可權 操作過的文章:SSRS匿名登錄 可以完全匿名訪問,因為我們系統是涉及到客戶要自己做報表的,所以這裡屏蔽了權
  • 關於C# DataTable 的一些操作 經常操作DATATABLE 對於一些不需要再通過sql 來重覆操作的 可以通過操作datatable來達到同樣的效果 方法一: 也是廣為人知的一種: YourDataTable.Columns.Remove("列名"); 但是這種情況只適合於去掉很少列的情況
  • SSRS 報表 如何加參數 連接上以後出現一個問題 就是給報表加上參數以後報表不斷刷新,跟上次那個報表刷新是同樣的問題。那麼下麵我們來解決一下。 1. 這是給報表添加預設參數進入頁面後就不斷的刷新刷新。 ReportParameter para = new ReportParameter("Repo
  • 背水一戰 Windows 10 之 UI: UI 設計概述, 啟動屏幕(閃屏), 屏幕方向
  • 出處:http://www.cnblogs.com/Interkey/ 偶然看到一個可以自刪除的程式,於是瞭解下如何實現。然後整理如下: 思路: 在.NET程式中,因為運行中的程式是受系統保護的,不能自己刪除自身的,所以自刪除的思路: 在關閉本程式之前啟動新的進程打開另一個程式,調用這個程式來刪除原
  • Insus.NET近段時間應朋友的要求,寫一個GridView多層嵌套和摺疊與展開。這個功能的GridView多層嵌套沒有問題,因為已經做了無限次數,但是摺疊與展開的功能,卻花上不少時間(網上找資料),雖找到資料可參考,還是瞭解明它,並修改適合自己程式使用。效果如下: 站點中多個頁面使用,因此Ins
  • 高德地圖api解釋的實在是太爛了,一度想用系統獲取location,添加button實現定位功能,奈何高德地圖右上角又有定位按鈕,於是乎高德地圖定位研究如下: package com.example.basicmap; import java.util.List; import android.ap
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...