Spring IOC源碼學習總結

最近在學習Spring IOC基本源碼,瞭解整個的流程。代碼太繞了,看的時候簡單記錄了下,現在分享下~

1、首先Spring簡單總結

Spring提供了很多輕量級應用開發實踐的工具集合,這些工具集以接口、抽象類、或工具類的形式存在於Spring中。通過使用這些工具集,可以實現應用程序與各種開源技術及框架間的友好整合。比如有關jdbc封裝的數據訪問工具Spring JDBC,有關編寫單元測試的spring test包以及spring-mock,有關訪問動態腳本語言的Spring Script,另外還有發送郵件的工具Spring Mail、日程及任務處理工具Spring scheduling等。 可以這麼說,大多數企業級應用開發中經常涉及到的一些通用的問題,都可以通過Spring提供的一些實用工具包輕鬆解決
依賴注入的三種方式:
(1)接口注入
(2)Construct注入
(3)Setter注入
控制反轉(IoC)與依賴注入(DI)是同一個概念,
引入IOC的目的:
(1)脫開、降低類之間的耦合;
(2)倡導面向接口編程、實施依賴倒換原則; 
(3)提高系統可插入、可測試、可修改等特性。
具體做法:
(1)將bean之間的依賴關係儘可能地抓換爲關聯關係;
(2)將對具體類的關聯儘可能地轉換爲對Java interface的關聯,而不是與具體的服務對象相關聯;
(3)Bean實例具體關聯相關Java interface的哪個實現類的實例,在配置信息的元數據中描述;
(4)由IoC組件(或稱容器)根據配置信息,實例化具體bean類、將bean之間的依賴關係注入進來。

2、簡單步驟

1、資源定位,即首先要找到applicationContext.xml文件
2、BeanDefinition的載入,把XML文件中的數據統一加載到BeanDefinition中,方便以後的處理
3、向IOC容器中注入BeanDefinition數據
4、將BeanDefinition中的數據進行依賴注入(反射)

3、XmlBeanFactory

  •  public class XmlBeanFactory extends DefaultListableBeanFactory{  
  •      private final XmlBeanDefinitionReader reader;  

  •      public XmlBeanFactory(Resource resource)throws BeansException{  
  •         this(resource, null);  
  •      }          
  •      public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)  
  •      throws BeansException{  
  •          super(parentBeanFactory);  
  •          this.reader = new XmlBeanDefinitionReader(this);  
  •          this.reader.loadBeanDefinitions(resource);  
  •     }  
  •  }

//根據Xml配置文件創建Resource資源對象,該對象中包含了BeanDefinition的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");

/創建DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();

//創建XmlBeanDefinitionReader讀取器,用於載入BeanDefinition。之所以需要BeanFactory作爲參數,是因爲會將讀取的信息回調配置給factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);

//XmlBeanDefinitionReader執行載入BeanDefinition的方法,最後會完成Bean的載入和註冊。完成後Bean就成功的放置到IOC容器當中,以後我們就可以從中取得Bean來使用
reader.loadBeanDefinitions(resource);

4、FileSystemXmlApplicationContext IOC容器初始化

基本步驟:

  1. 資源定位,用字符串或者數組配置BeanDefinition文件(確定文件地址)
  2. 設置資源加載器resourceLoader,AbstractApplicationContext(繼承了DefaultResourceLoader)
  3. 開始初始化IOC容器
  4. 初始化的入口在容器實現中的 refresh()調用來完成,實際調用的AbstractRefreshableApplicationContext的refreshBeanFactory()方法
  5. 已有BeanFacotry(ioc容器)則銷燬,關閉,重新創建ioc容器
  6. 容器定製化(啓動參數,開啓註解等),載入Bean定義(BeanDefinition)
  7. 對 bean 定義載入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致過程如下
  8. 通過 ResourceLoader 來完成資源文件位置的定位,DefaultResourceLoader 是默認的實現,同時上下文本身就給出了 ResourceLoader 的實現。
  9. 可以從類路徑,文件系統, URL 等方式來定位資源位置,並抽象成相應的Resource 。
  10. 如果是 XmlBeanFactory作爲 IOC 容器,那麼需要爲它指定 bean 定義的資源,也就是說 bean 定義文件是通過抽象成 Resource 來被 IOC 容器處理的。
  11. 容器通過 BeanDefinitionReader來完成定義信息的解析和 Bean 信息的註冊,往往使用XmlBeanDefinitionReader 來解析 bean 的 xml 定義文件(Resource),轉換成Document對象。
  12. 至此,IOC加載讀入了資源文件,並轉換成了document對象。下面解析Document對象爲Bean,並註冊到容器中。
  13. 實際的處理過程是委託給 BeanDefinitionParserDelegate 來完成的,它定義了Spring BeanDefinition XML文件的各種元素,以及解析各種元素的方法。
  14. 如果document對象使用了Spring默認的XML命名空間,則按照spring的解析規則,解析<import>導入元素,解析<Alias>別名元素以及普通的<Bean>元素。否則使用用戶自定義的解析規則。
  15. 將Document對象以及Resource文件轉換爲Spring IoC所識別的數據結構——BeanDefinition,它是Bean定義資源文件中配置的POJO對象在Spring IoC容器中的映射。
  16. 容器解析得到 BeanDefinition 以後,需要把它在 IOC 容器中註冊,這由 IOC 實現 BeanDefinitionRegistry 接口來實現。
  17. 註冊過程就是在 IOC 容器內部維護的一個HashMap 來保存得到的 BeanDefinition。這個 HashMap 是 IoC 容器持有 bean 信息的場所,以後對 bean 的操作都是圍繞這個HashMap 來實現的。註冊過程中需要線程同步,註冊後重載BeanDefinition的緩存。
  18. 註冊完成,IoC容器就建立了整個Bean的配置信息,這些BeanDefinition信息已經可以使用,並且可以被檢索,IoC容器的作用就是對這些註冊的BeanDefinition進行處理和維護。

詳細步驟:

1、ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
2、設置資源加載器和資源定位
  • 父類容器的構造方法AbstractApplicationContext(ApplicationContext parent)爲容器設置好Bean資源加載器 PathMatchingResourcePatternResolver
  • setConfigLocations(configLocations)方法設置Bean定義資源文件的定位路徑。既可以使用一個字符串來配置多個Spring Bean定義資源文件,也可以使用字符串數組
  • refresh(); 載入Bean定義(見3)
3、AbstractApplicationContext的refresh函數載入Bean定義 refresh() throws BeansException, IllegalStateException
  • synchronized (this.startupShutdownMonitor)
  • 調用容器準備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識 prepareRefresh();
  • 告訴子類啓動refreshBeanFactory()方法,啓動Bean定義資源文件的載入(見4)
  • 爲BeanFactory配置容器特性,例如類加載器、事件處理器等 prepareBeanFactory(beanFactory);
  • 爲BeanFactory初始化信息源,件傳播器等等
refresh()方法的作用是:在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。refresh的作用類似於對IoC容器的重啓,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。refresh()方法主要爲IoC容器Bean的生命週期管理提供條件,Spring IoC容器載入Bean定義資源文件從其子類容器refreshBeanFactory()方法啓動
4、AbstractApplicationContext子類的refreshBeanFactory()方法
  • 先判斷BeanFactory是否存在,如果存在則先銷燬beans並關閉beanFactory
  • 接着創建DefaultListableBeanFactory,完成定製化(啓動參數,開啓註解等)
  • 委派子類AbstractXmlApplicationContext調用loadBeanDefinitions(beanFactory)裝載bean 定義
5、其子類AbstractXmlApplicationContext對loadBeanDefinitions方法的實現
  • 用beanFactory創建了XmlBeanDefinitionReader
  • 將自身(XmlBeanDefinitionReader )設置爲Reader 的ResourceLoader
  • 用getConfigLocations方法獲得資源位置location
  • 用Reader調用XmlBeanDefinitionReader 父類的loadBeanDefinitions方法,參數爲location (見6)
6、XmlBeanDefinitionReader 父類AbstractBeanDefinitionReader的loadBeanDefinitions(location)方法
  • 獲得ResourceLoader(即5中的XmlBeanDefinitionReader)
  • 用ResourceLoader調用父類DefaultResourceLoader方法getResources(location)獲得BeanDefinitionResources(或者數組resources) (見7)
  • 委派子類XmlBeanDefinitionReader調用loadBeanDefinitions(resources)(見8)
7、XmlBeanDefinitionReader父類DefaultResourceLoader方法getResources(location)
  • ClassPathResource
  • UrlResource
  • ResourceByPath
8、XmlBeanDefinitionReader調用loadBeanDefinitions(resources)
  • 對XML進行特殊編碼處理,並調用重載方法loadBeanDefinitions(new EncodedResource(resource))
  • 將encodedResource轉換爲XML的解析源InputSource
  • 調用doLoadBeanDefinitions(inputSource, encodedResource.getResource())完成了
    • 將XML文件轉換爲DOM對象Document(見9)
    • 啓動對Bean定義解析registerBeanDefinitions(doc, resource)(見10)
9、DocumentLoader將Bean定義資源轉換爲Document對象
  • 創建文件解析器工廠DocumentBuilderFactory
  • 創建文檔解析器DocumentBuilder(factory, entityResolver, errorHandler)
  • 解析Spring的Bean定義資源返回Document對象 builder.parse(inputSource)
至此Spring IoC容器根據定位的Bean定義資源文件,將其加載讀入並轉換成爲Document對象過程完成。接下來我們要繼續分析如何將Document對象解析爲Spring IoC管理的Bean對象並將其註冊到容器中的。
10、XmlBeanDefinitionReader解析載入的Bean定義資源文件registerBeanDefinitions(Document doc, Resource resource) 即按照Spring的Bean語義要求將Bean定義資源解析並轉換爲容器內部數據結構
  • 創建BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析
  • 解析過程入口,委派實現類DefaultBeanDefinitionDocumentReader完成registerBeanDefinitions(doc, createReaderContext(resource)) (見11)
  • 統計解析的Bean數量 getRegistry().getBeanDefinitionCount() - countBefore
11、DefaultBeanDefinitionDocumentReader對Bean定義的Document對象解析(根據Spring DTD對Bean的定義規則解析)registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
  • 獲取Document的根元素doc.getDocumentElement();
  • 調用方法createHelper(readerContext, root)獲得BeanDefinitionParserDelegate,定義了Spring Bean定義XML文件的各種元素
  • 解析Bean定義之前,進行自定義的解析 preProcessXml(root);
  • 調用方法parseBeanDefinitions(root, delegate);從Document的根元素開始解析Bean定義
  • 在解析Bean定義之後,進行自定義的解析 postProcessXml(root);
方法 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate),從Document的根元素開始解析Bean定義
  • Bean定義的Document對象使用了Spring默認的XML命名空間,遍歷Document對象根元素的所有子節點,如果是XML元素節點,調用parseDefaultElement(ele, delegate);否則則使用用戶自定義的解析規則解析元素節點
  • 根節點沒有使用Spring默認的命名空間,則使用用戶自定義的解析規則解析Document根節點
方法 parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate),使用Spring的Bean規則解析Document元素節點
  • 如果元素節點是<Import>導入元素,進行導入解析 importBeanDefinitionResource(ele)
  • 如果元素節點是<Alias>別名元素,進行別名解析 processAliasRegistration(ele);
  • 元素節點既不是導入元素,也不是別名元素,即普通的<Bean>元素,按照Spring的Bean規則解析元素 processBeanDefinition(ele, delegate);(見12)
方法 importBeanDefinitionResource(Element ele),解析<Import>導入元素,從給定的導入路徑加載Bean定義資源到Spring IoC容器中
  • 獲取給定的導入元素的location屬性 ele.getAttribute(RESOURCE_ATTRIBUTE);
  • 使用系統變量值解析location屬性值 location = SystemPropertyUtils.resolvePlaceholders(location);
  • 導入元素的location是否是絕對路徑
  • 絕對路徑 使用資源讀入器加載給定路徑的Bean定義資源 getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
  • 相對路徑 將給定導入元素的location封裝爲相對路徑資源 Resource relativeResource =
  • getReaderContext().getResource().createRelative(location);
  • 相對路徑資源存在 使用資源讀入器加載Bean定義資源,不存在 根據Spring IoC容器資源讀入器的基本路徑加載
  • 在解析完<Import>元素之後,發送容器導入其他資源處理完成事件
方法 processAliasRegistration(Element ele) 解析<Alias>別名元素,爲Bean向Spring IoC容器註冊別名
  • 獲取<Alias>別名元素中name的屬性值 String name = ele.getAttribute(NAME_ATTRIBUTE);
  • 獲取<Alias>別名元素中alias的屬性值 String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
  • 都不爲空時,向容器的資源讀入器註冊別名 getReaderContext().getRegistry().registerAlias(name, alias);
  • 在解析完<Alias>元素之後,發送容器別名處理完成事件
方法 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate),解析Bean定義資源Document對象的普通元素
  • delegate.parseBeanDefinitionElement(ele);對Document對象中<Bean>元素的解析,獲得對BeanDefinition的封裝類,BeanDefinitionHolder(見12-15)
  • 向Spring IoC容器註冊解析得到的Bean定義 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); (見16)
  • 完成向Spring IoC容器註冊後發送註冊事件,getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
12、BeanDefinitionParserDelegate解析Bean定義資源文件中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name和別名屬性 parseBeanDefinitionElement(Element ele)
  • 獲取<Bean>元素中的id屬性值、name、alias,將所有name屬性值存放到別名中
  • 如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
  • 詳細對<Bean>元素中配置的Bean定義解析beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  • 如果beanDefinition 中不包含beanName(即沒有id、別名、name),且沒有子<Bean>,需要生成一個唯一的baenName並註冊,有子<Bean>則使用別名+類名後綴作爲baenName並註冊。
方法 parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean)詳細對<Bean>元素中配置的Bean定義除id、name和別名以外的其他屬性數據進行解析
  • 獲得class名字,parent屬性(如果有)
  • 根據<Bean>元素配置的class名稱和parent屬性值創建BeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  • 解析一些屬性,如配置的單態(singleton)屬性,解析description信息,meta(元信息)等
  • 解析<Bean>元素的<property>設置(見13)
注意:在解析<Bean>元素過程中沒有創建和實例化Bean對象,只是創建了Bean對象的定義類
BeanDefinition,將<Bean>元素中的配置信息設置到BeanDefinition中作爲記錄,當依賴注入時才使用這些記錄信息創建和實例化具體的Bean對象。
13、BeanDefinitionParserDelegate解析<property>元素 parsePropertyElements(Element beanEle, BeanDefinition bd)
  • 獲取<Bean>元素中所有的子元素,如果子元素是<property>,則調用方法 解析<property>子元素
  • 通過一個parseState隊列存儲所有property鍵值對,this.parseState.push(new PropertyEntry(propertyName));
  • 獲取<property>元素的名字,如果一個Bean中已經有同名的property存在,則不進行解析,直接返回。即如果在同一個Bean中配置同名的property,則只有第一個起作用。
  • 解析獲取property的值,方法 Object val = parsePropertyValue(ele, bd, propertyName);
  • 根據property的名字和值創建property實例
方法 解析獲取property值 Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName)
  • 獲取<property>,只能是其中一種類型:ref,value,list等
  • 如果屬性是ref,創建一個ref的數據對象RuntimeBeanReference,封裝了ref信息。一個指向運行時所依賴對象的引用RuntimeBeanReference ref = new RuntimeBeanReference(refName);並調用ref.setSource(extractSource(ele));與當前的property對象關聯。
  • 如果屬性是value,創建一個value的數據對象TypedStringValue,封裝了value信息。TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele));與當前的property對象關聯
  • 如果當前<property>元素還有子元素,解析<property>的子元素(見14)
14、解析<property>元素的子元素 BeanDefinitionParserDelegate類中的
Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType)
  • 如果<property>沒有使用Spring默認的命名空間,則使用用戶自定義的規則解析
  • 如果子元素是bean,則使用解析<Bean>元素的方法解析
  • 如果子元素是ref,ref中只能有以下3個屬性:bean、local、parent。bean引用其他解析的Bean的名稱可以不再同一個Spring配置文件中;local屬性值,引用同一個Xml文件中配置;parent屬性值,引用父級容器中的Bean。創建ref類型數據,與當前的property對象關聯
  • 如果子元素是value、null、array、list……,使用相應的方法解析,生成對應的數據對象,比如ManagedList、ManagedArray、ManagedSet等,這些Managed類是Spring對象BeanDefiniton的數據封裝(見15)
15、解析<list>子元素 parseListElement(Element collectionEle, BeanDefinition bd)
  • 獲取數據類型
  • 獲取所有子節點 nl
  • 新建ManagedList<Object> target,設置類型
  • 具體的<list>元素解析 parseCollectionElements(nl, target, bd, defaultElementType); <array>、<list>和<set>都使用該方法解析
經過對Spring Bean定義資源文件轉換的Document對象中的元素層層解析,Spring IoC現在已經將XML形式定義的Bean定義資源文件轉換爲Spring IoC所識別的數據結構——BeanDefinition,它是Bean定義資源文件中配置的POJO對象在Spring IoC容器中的映射。但是最爲重要的依賴注入還沒有發生,即向容 器註冊BeanDefinition信息。
16、解析過後的BeanDefinition在IoC容器中的註冊 registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  • 獲取解析的BeanDefinition的名稱
  • 向IoC容器註冊BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())(見17)
  • 如果解析的BeanDefinition有別名,向容器爲其註冊別名
17、DefaultListableBeanFactory向IoC容器註冊解析後的BeanDefinition registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException
  • 使用一個HashMap的集合對象存放IoC容器中註冊解析的BeanDefinition
  • 校驗解析的BeanDefiniton,失敗拋出異常
  • 註冊的過程中需要線程同步 synchronized (this.beanDefinitionMap)
  • 檢查是否有同名的BeanDefinition已經在IoC容器中註冊,已經註冊不允許覆蓋就拋出異常,允許覆蓋則覆蓋。IoC容器中沒有已經註冊同名的Bean,按正常註冊流程註冊。
  • 註冊後重置所有已經註冊過的BeanDefinition的緩存
至此,Bean定義資源文件中配置的Bean被解析過後,已經註冊到IoC容器中,被容器管理起來,真正完成了IoC容器初始化所做的全部工作。
現在IoC容器中已經建立了整個Bean的配置信息,這些BeanDefinition信息已經可以使用,並且可以被檢索,IoC容器的作用就是對這些註冊的Bean定義信息進行處理和維護。接下來進行IOC的依賴注入。


源碼在這裏閱讀的:http://www.itnose.net/detail/6669144.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章