1.3淺談Spring(IOC容器的實現)

這一節咱們來討論IOC容器到底作了什麼。java

仍是借用以前的那段代碼spring

ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml"); Car car =app.getBean(Car.class); System.out.println(car.getBrand()+","+car.getDesc()); 

這裏ClassPathXmlApplicationContext是如何加載beans.xml的呢?數組

它到底作了哪些事情?網絡

1.資源定位 2.資源加載 3.資源註冊數據結構

再次以前,補充一點:SpringIOC容器管理了咱們定義的各類Bean對象及其相互的關係,Bean對象在Spring實現中是以BeanDefinition(Bean定義資源文件中配置的POJO對象在Spring IoC容器中的映射)來描述的。app

 

IOC初始化this

如今咱們經過源碼來分析並驗證:編碼

首先進入它的構造方法,這個構造方法調用其餘的構造方法spa

沒錯,就是這個方法啦,參數跟上圖同樣。這裏有三個方法:3d

super();

setConfigLocation();

refresh();

來看一下這三個方法:

一、資源定義

①super();//資源加載器的配置

執行的是AbstractApplicationContext的構造方法

這裏的this,就是ClassPathXmlApplicationContext,它間接的繼承自AbstractApplication Context,而AbstractApplicationContext又繼承了DefaultResourceLoader,故它自己就是個ResourceLoader

②setConfigLocation()//資源定位

處理文件路徑爲一個字符串的狀況

處理多個多個資源文件字符串數組

StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS) //String CONFIG_LOCATION_DELIMITERS = ",; \t\n"; 

這個方法是用來解析咱們給的location,由於在建立ClassPathXmlApplicationContext時,咱們能夠傳入多個Xml的配置,並用分號隔開。如:

ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml;spring.xml");

這時候就須要來解析這個String了,固然這個String有多種寫法,不止用分號這麼簡單。因此須要來解析一下。

this.configLocations[i] = resolvePath(locations[i]).trim(); 

這個resovlePath是用來解析一些佔位符的,因爲這些文件多是 classpath , filesystem ,或者是 URL 網絡資源, servletContext 等。因此可能會存在佔位符。

具體的解析過程在PropertyPlaceholderHelper的parseStringValue中。

二、資源加載

AbstractApplicationContext的refresh()是一個模版方法,它就是對IOC容器進行初始化而且對資源進行載入。其中obtainFreshBeanFactory()就是對"Bean"資源加載的關鍵。

startupShutdownMonitor用於刷新和銷燬的同步標記

①prepareRefresh()//準備刷新

準備此上下文以進行刷新,設置其啓動日期和活動標誌以及執行屬性源的任何初始化。

②obtainFreshBeanFactory()//資源加載

先看refreshBeanFactory();//刷新BeanFactory

判斷是否存在BeanFacotry(即IOC容器),若是存在就銷燬,由於IOC容器是單例的。只能存在一個。

這裏createBeanFactory建立的是一個默認的DefaultListableBeanFactory

customizeBeanFactory();//初始化工廠參數

自定義此上下文使用的內部bean工廠。 爲每次refresh()嘗試調用。默認實現應用此上下文的「allowBeanDefinitionOverriding」和「allowCircularReferences」設置(若是已指定), 能夠在子類中重寫以自定義任何DefaultListableBeanFactory的設置。

loadBeanDefinitions();//加載BeanDefinitions

這裏調用的是AbstractXmlApplicationContext的loadBeanDefinitions方法

這裏傳入了RourceLoader的資源加載器爲ClassPathXmlApplicationContext

真正執行是重寫的方法

繼續執行重寫方法

真正執行的重寫方法:

圖-X

這裏首先獲取getResourceLoader便是獲取ClassPathXmlApplication,以前說過它繼承自AbstractApplicationContext,而AbstractApplicationContext又繼承了DefaultResource Loader

IOC容器是如何取到資源的呢?

這裏的getResourceLoader();//取資源加載器

//獲取資源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

這裏locationPattern用來區分是以多資源文件的形式,仍是單資源文件的形式(就像Spring跟SpringMVC同時使用時的狀況).

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

這裏走else分支的else分支(獲取單個資源)

return new Resource[] {getResourceLoader().getResource(locationPattern)};

getResourceLoader()即獲取資源加載器,這裏的就是ClassPathXmlApplicationContext,

getResource()//獲取資源

這裏仍然走的是else分支,執行getResourceByPath(location);區分是從 本地,仍是URL,仍是其餘的方式來配置的。

 

最終執行ClassPathResource的構造方法

最終獲得的resource以下圖:

在獲取到了Resource以後,咱們繼續回到圖-X,看它接下來的操做

int loadCount = loadBeanDefinitions(resources); 

最終執行XmlBeanDefinitionReader的loadBeanDefinitions方法:

/**
*從指定的XML文件加載bean定義。
* @param encodedResource XML文件的資源描述符,
*容許指定用於解析文件的編碼
* @return找到的bean定義數
* @throws BeanDefinitionStoreException在加載或解析錯誤的狀況下
*/

三、資源註冊

在註冊以前須要將讀取到的resource轉換成BeanDefinitions

這裏獲取資源的輸入流,並執行真正執行的方法doLoadBeanDefinitions()

/**
*實際上從指定的XML文件加載bean定義。
* @param inputSource要讀取的SAX InputSource
* @param資源XML文件的資源描述符
* @return找到的bean定義數
* @throws BeanDefinitionStoreException在加載或解析錯誤的狀況下
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/

這裏doLoadDocument(inputSource,resource)將資源文件轉換成DOM對象

 

具體轉換過程這裏就不進行細看了,主要使用JAXP。

轉換成DOM以後須要作的就是將DOM轉換成Spring可以識別的數據結構BeanDefinitions

這裏的BeanDefinitionDocumentReader用於解析實際的DOM文檔。

這裏執行DefaultBeanDefinitionDocumentReader的registerBeanDefinitions

doRegisterBeanDefinitions();是真正的註冊beanDefinitions的方法

這裏有一個用來幫助解析的類:

BeanDefinitionParserDelegate

官方給的解釋是:用於解析XML bean定義的有狀態委託類。 旨在供主解析器和任何擴展BeanDefinitionParsers或BeanDefinitionDecorators使用。

以後是對Spring命名空間的一些檢測。

最後纔是真正的解析BeanDefinitions——parseBeanDefinitions

/** *解析文檔中根級別的元素: *「import」,「alias」,「bean」。 * @param root文檔的DOM根元素 */ 

這裏補充一點XML的知識

xml文檔 —————-> Document對象 表明整個xml文檔
節點 —————>Node對象 父類
標籤節點 —————> Element對象 子類
屬性節點 —————> Attribute對象 子類
文本節點 —————>Text對象 子類
NodeList ---------->節點列表集合(Node的集合)

先看這個默認命名空間的解析方法:

parseDefaultElement();

這裏分別對<import> <alias> <bean><beans> 用各自的方法進行解析

這裏的順序也是比較講究

先查看是否有<import>標籤,若是有能夠先引入其餘資源文件到IOC容器中。

而後查看<alias>標籤,若是有先引入別名到IOC容器中(後面會根據是否有id將別名賦值給bean)

而後查看<bean>標籤

最後查看<beans>,可能<beans>裏引入了其餘<bean>,故在最後

這裏咱們查看對於 <bean>標籤的解析:

processBeanDefinition();

/**
*處理給定的bean元素,解析beanDefinition
*並在註冊表中註冊。
*/

首先解析BeanDefinition的基本屬性:

BeanDefinitionParserDelegate裏的parseBeanDefinitionElement();

相關文章
相關標籤/搜索