spring的核心容器包括:core、beans、context、express language四個模塊。所以對於一個簡單的spring工程,最基本的就是依賴以下三個jar包即可:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.8.RELEASE</version> </dependency>
暫時先不考慮express language模塊。
通過xml文件創建一個spring bean的大概過程:
- 主要考慮以下情況:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myTestBean" class="chapter02.MyTestBean"/> </beans>
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
注:beanFactoryTest.xml指上述xml文件;chapter02.MyTestBean是一個簡單的bean(包含一個私有屬性,get,set方法)
- 關於new ClassPathResource("beanFactoryTest.xml")
- ClassPathResource類是Resource接口的一個實現類
- Resource接口繼承了InputStreamSource接口
- InputStreamSource接口只有一個方法定義:InputStream getInputStream() throws IOException;
- 關於new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))
- 首先對傳入的resource參數做封裝
- 其次通過SAX讀取XML文件的方式來准備InputSource對象
- 最后將准備好的數據通過參數傳入真正的核心處理部分:doLoadBeanDefinitions
- 對於真正的核心處理部分:doLoadBeanDefinitions的處理過程,如下:
- 獲取對XML文件的驗證模式:DTD、XSD
- 加載XML文件,並得到對應的Document。通過SAX解析XML文檔的步驟如下:
- 首先創建DocumentBuilderFactory
- 通過DocumentBuilderFactory創建DocumentBuilder
- 使用DocumentBuilder解析InputSource來返回Document對象
- 根據返回的Document注冊Bean信息
- 對於根據返回的Document注冊Bean信息的處理過程,如下:
- BeanDefinitionDocumentReader接口的實現類DefaultBeanDefinitionDocumentReader會獲取到document對象的root節點
- 通過root節點獲取所有的bean節點:一種是默認的bean節點:使用parseDefaultElement()方法進行處理;一種是自定義的bean節點:使用delegate.parseCustomElement()方法進行處理,其中delegate是根據自定義節點的命名空間獲取到的自定義的節點處理器;以下只討論對於第一種情況即:對於默認的bean節點的處理。
- 對於默認的bean節點有四種標簽:import、alias、bean、beans,我們這里主要討論最常用也最復雜的bean標簽:
- 對於最常用也最復雜的默認節點中的bean標簽的處理過程,如下:
- 首先委托BeanDefinitionDelegate類的parseBeanDefinitionElement()方法進行元素解析,返回BeanDefinitionHolder類型的實例bdHolder
- 提取元素中的id以及name屬性
- 進一步解析其他所有屬性以及子元素,並統一封裝至GenericBeanDefinition類型的實例中;注:BeanDefinition存在三個實現類:RootBeanDefinition(父節點)、ChildBeanDefinition(子節點)、GenericBeanDefinition(Xml屬性對應的java容器)
- 解析各種屬性:scope屬性、singleton屬性、abstract屬性、lazy-init屬性、autowire屬性、dependency-check屬性、depends-on屬性、autowire-candidate屬性、primary屬性、init-method屬性、destory-method屬性、factory-method屬性、factory-bean屬性
- 解析各種子元素:
- 解析元數據:parseMetaElements
- 解析lookup-method子元素:parseLookupOverrideSubElements
- 解析replaced-method子元素:parseReplacedMethodSubElements
- 解析構造函數參數:parseConstructorArgElements
- 解析property子元素:parsePropertyElements
- 解析qualifier子元素:parseQualifierElements
- 如果檢測到bean沒有指定beanName,那么使用默認規則為此bean生產beanName
- 將獲取到的信息封裝到BeanDefinitionHolder的實例中
- 當返回的bdHolder下存在自定義屬性時,還需要對自定義標簽進行解析。例如以下包含mybean:user自定義屬性時:
<bean id="test" class="test.Myclass"> <mybean:user username="aaa"/> </bean>
需要用到delegate.decorateBeanDefinitionIfRequired()進行自定義標簽的處理,處理過程如下:
- 遍歷所有的屬性以及所有的子元素
- 根據節點獲取標簽的命名空間
- 根據命名空間判斷是否是默認標簽。(這里只對非默認標簽進行處理,因為對於默認標簽在之前已經處理過了)
- 根據命名空間找到對應的處理器
- 使用相應的處理器進行修飾處理
- 對解析完成后的bdHolder進行注冊:包括通過beanName的注冊以及alias(別名)的注冊
- 通過beanName注冊BeanDefinition,關鍵方法:this.beanDefinitionMap.put(beanName, beanDefinition),主要步驟如下:
- 對AbstractBeanDefinition的methodOvirrides屬性進行校驗
- 如果beanName已經注冊過:如果用戶設置了不允許重復注冊,則拋異常,否則直接覆蓋,重新注冊
- 加入beanDfinitionMap
- 清除解析之前留下的對應beanName的緩存
- 通過alias(別名)注冊BeanDefinition,關鍵方法:this.aliasMap.put(alias, beanName),主要步驟如下:
- alias與beanName相同,則不需要處理,並刪除掉原有alias
- 若alias存在,根據用戶是否設置允許別名覆蓋來進行相應的處理
- alias循環檢查。例如:當存在A(key)-->B(value)時,如果還存在B(key)-->A(value)或者B(key)-->C(value)、C(key)-->A(value)時,稱為出現了循環。此時則會拋出異常
- 注冊alias:this.aliasMap.put(alias, beanName);
- 通過beanName注冊BeanDefinition,關鍵方法:this.beanDefinitionMap.put(beanName, beanDefinition),主要步驟如下:
- 最后發出bean已注冊完成的響應事件,通知相關的監聽器
- 首先委托BeanDefinitionDelegate類的parseBeanDefinitionElement()方法進行元素解析,返回BeanDefinitionHolder類型的實例bdHolder
