對於Java開發者來講,Spring無疑是最經常使用也是最基礎的框架之一。(此處省略1w字吹Spring)。相信不少同行跟我同樣,只是停留在會用的階段,好比用@Component寫一個組件、用@Autowired注入其餘組件等等,可是不知道爲何能夠這麼作,Spring是怎麼實現的。爲了瞭解這些,我閱讀了《Spring源碼深度解析》,這本書講的很詳細,可是由於步驟多而複雜容易記混,我就作了一下梳理,先呈現大體流程,但對每一個步驟進行詳細描述。數組
概念上的東西仍是要提一嘴的:
Spring用IoC容器來管理Bean。
BeanFactory和ApplicationContext是SpringIoC容器的兩種表現形式。
BeanFactory定義了簡單IoC容器的基本功能。
ApplicationContext實現了BeanFactory,且經過繼承MessageSource、ResourceLoader、ApplicationEventPublisher接口,添加了許多高級容器的特性。框架
這裏以XmlBeanFactory爲表明,看容器是怎麼工做的。
新建一個XmlBeanFactory很簡單,只須要你有一個符合格式的xml文件,裏面用<bean>標籤設置你但願被容器加載的Bean:函數
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("test.xml"))
新建了一個ClassPathResource資源對象做爲參數傳入XmlBeanFactory的構造函數。
點進XmlBeanFactory類,XmlBeanFactory會先調用父類構造器,一直跟蹤到AbstractAutowireCapableBeanFactory,會看到調用了三次ignoreDependencyInterface用來忽略給定接口的自動裝配功能(後面會提到);再調用this.reader.loadBeanDefinitions(Resource),用本身持有的XmlBeanDefinitionReader解析傳入的資源。因此最宏觀的三個步驟:
一、新建了一個ClassPathResource資源。抽象出一個資源類來表示資源;
二、調用了ignoreDependencyInterface忽略指定接口的自動裝配功能;
三、委託XmlBeanDefinitionReader解析資源。
重點確定在第三步了,點進XmlBeanDefinitionReader,到loadBeanDefinitions(EncodedResource),先從
Resource獲取InputStream構形成InputSource,做爲參數調用doLoadBeanDefinitions(InputSource, Resource)開始真正的解析。因此第三步下面是兩個小步驟:
3.一、從Resource獲取輸入流;
3.二、調用doLoadBeanDefinitions繼續解析。
再看doLoadBeanDefinitions方法,主要是兩個方法,doLoadDocument(inputSource, resource)解析輸入流返回一個Document對象;registerBeanDefinitions(doc, resource)繼續解析Document返回解析的Bean數量,因此3.2下面是兩個步驟:
3.2.一、將資源解析成Document對象(這個步驟這邊就不展開了,有興自究);
3.2.二、解析Document,提取註冊Bean。
來到registerBeanDefinitions方法,這裏建立了一個BeanDefinitionDocumentReader對象負責具體解析(是否是以爲又冒出了不認識的類,框架就是這樣,遵循單一職責的原則,把一個集中的邏輯放到其餘類中處理),它調用doRegisterBeanDefinitions(doc.getDocumentElement()),提取Document的root做爲參數繼續解析,先查找解析profile屬性,將表示環境的屬性註冊到Environment;而後遍歷root每一個子節點,若是是默認標籤,調用parseDefaultElement進行解析;若是是自定義標籤,就調用delegate.parseCustomElement。因此3.2.2下面是這幾個步驟:
3.2.2.一、建立BeanDefinitionDocumentReader委託解析對象;
3.2.2.二、解析profile屬性到Environment;
3.2.2.三、遍歷子節點,繼續解析默認標籤和自定義標籤。
咱們這邊主要分析默認標籤的解析,自定義的有興自究。首先根據標籤類型選擇不一樣的處理方法,類型分別是import、alias、bean和beans。重點確定是對bean標籤的解析,進入processBeanDefinition方法,咱們看到裏面先委託BeanDefinitionParserDelegate解析出一個持有bean信息的BeanDefinitionHolder;若是BeanDefinitionHolder不爲空且子節點下存在自定義標籤,再解析它們;而後對解析完成後的BeanDefinitionHolder進行註冊,註冊過程很簡單就是將BeanDefinitionHolder持有的beanName和BeanDefinition的鍵值對、beanName和每一個alias別名的鍵值對保存在容器中;最後發出bean已註冊完成的事件通知,因此這裏分爲4步:
3.2.2.3.一、委託BeanDefinitionParserDelegate解析返回BeanDefinitionHolder;
3.2.2.3.二、解析存在的自定義標籤;
3.2.2.3.三、解析完成後註冊;
3.2.2.3.四、發出響應事件。
到了這裏終於開始具體的解析,過程其實就是先解析出beanName、alias別名,而後把其他各類標籤,如class、scope、lazy-init等屬性解析成用於屬性承載的BeanDefinition對象的成員變量,最後將beanName、alias數組和BeanDefinition封裝成BeanDefinitionHolder對象返回。具體各類屬性的功能和規則這邊就不展開了,有興自究。
對於import、alias和beans標籤,簡述一下:alias的解析和bean中的alias解析差很少,也是將每個別名與beanName以map形式保存;impot能夠導入其餘配置文件,解析過程就是找到那個文件而後遞歸進行解析;beans就是把多個bean標籤包起來,而後遍歷解析每個bean標籤。this
畫一個流程圖做爲總結:spa