當前位置:
首頁 > 教育 > Spring源碼閱讀——Bean的載入和獲取過程

Spring源碼閱讀——Bean的載入和獲取過程

我們經常使用Spring,並且也都了解其大概原理。我想我們一定會對Spring源碼的解讀有迫切的渴望。

我也如此。所以,我打算閱讀一下Spring的源碼。再此之前,我也為此準備了很多。包括,去複習熟練java反射,理解常用的設計模式。當然,這些複習筆記也會在今後的複習中順便記錄在我的csdn博客。(當然,可能寫的不好,也可能理解不正確(可以一起交流嘛)。但是樂於分享總歸是好的。)

首先看下spring的各個組件。



可以看到,在Core Container(核心容器)中包含有Core、Beans、Context和Spring Expression Language.Core和Beans模塊是Spring框架的基礎部分,提供IoC控制反轉和依賴注入的特性。

Core模塊主要包含著Spring框架基本的核心工具類,供其它組件使用。

Beans模塊是所有應用都要用到的,它包含訪問配置文件、創建和管理bean以及ioc、依賴注入。

Context模塊構建於Core和Beans之上。是spring的上下文環境,為Spring核心提供了大量的擴展,天津挨了對國際化、事件傳播、資源載入等支持。ApplicationContext介面是Context模塊的關鍵。

Spring Expression Language為Spring提供了一個強大的表達式語言用於在運行時查詢草操縱對象 。

現在我們已經了解了Spring的基礎組件,我們現在就在代碼中跟蹤一下Spring Bean的創建和獲取過程。

beans.xml

<?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:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="hello" class="bean.HelloSpring" lazy-init="false"></bean>

</beans>

HelloSpring.java

package bean;

/**
* Created by yuyufeng on 2016/11/17.
*/
public class HelloSpring {
private String name;

public HelloSpring() {
System.out.println("##HelloSpring.HelloSpring初始化……………………………………");
}

public HelloSpring(String name) {
this.name = name;
}

public void sayHello(String something){
System.out.println("hello"+something);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "HelloSpring{" +
"name="" + name + """ +
"}";
}
}

BeanFactoryTest.java

package spring.ioc;

import bean.HelloSpring;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

/**
* Created by yuyufeng on 2016/11/18.
* Spring中Bean的載入過程
*/
public class BeanFactoryTest {
public static void main(String[] args) {
//spring如何初始化有兩種方式 beanFactory applicationContext
Resource resource = new ClassPathResource("spring/ioc/beans.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloSpring helloSpring = (HelloSpring) beanFactory.getBean("hello");
helloSpring.sayHello("張三");
}
}

先從表面上可以看到 bean的載入可大致可以分為:從xml讀取bean的信息載入到Spring容器中,通過xml配置的id從Spring容器反射得到這個類的實例對象。

現在,我們進行詳細分析

1.Resource resource = new ClassPathResource("spring/ioc/beans.xml");

我們通過Sring Core模塊的工具從本地獲得了xml資源,並生成Resource對象。這一過程就不詳細跟進了。

2.通過XmlBeanFactory來創建BeanFactory對象。

直接debug進入

3.首先我們會跟進 DefaultSingletonBeanRegistry 其中有靜態對象需要實例化。至於為什麼會跟進這個類,我們來看下類的繼承關係就知道了(為什麼會先實例其中的靜態類,可以複習以下java對象的實例順序)

4.接著會進入DefaultListableBeanFactory創建裡面的靜態對象實例以及執行裡面的靜態模塊

5.通過類載入器注入DefaultListableBeanFactory對象

然後又實例化了一個存放DefaultListableBeanFactory的map

6.接著再執行XmlBeanFactory的構造方法,其中把配置文件Resource賦值給了resource

7.執行this.reader.loadBeanDefinitions(resource); //可以看到這個步驟是要把resource載入到容器中去了。這裡是整個資源載入進入的切入點。

8.接著再跟進,直到進入public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException ;方法


9.對導入資源再進行一定包裝處理後進入doLoadBeanDefinitions(inputSource, encodedResource.getResource()); //對於encode我們是比較熟悉的 肯定是處理編碼相關的


10.現在已經進入到了XmlBeanDefinitionReader.java,

再包裝處理(畢竟xml文件規則什麼的驗證啊 獲取比較麻煩,不知道你暈了沒有)

xml還是包裝成了Document委託給DocumentLoader去處理執行



11.現在又進入public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException ;

程序結果以上的處理,已經獲取了xml文檔的document,已經可以準備提取註冊bean了。


12.經過documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

最終我們獲取到了root,protected void doRegisterBeanDefinitions(Element root)這個方法,開始真正的解析已經處理過的資源。


13.解析完成後就是註冊了,

debug到如下代碼


14.可以看到在這裡,把bean存到了beanDefinitionMap中,

對於beanDefinitionMap是什麼,就是存在內存中的map,bean就存在裡面供外部獲取。


跟蹤了這麼多的源代碼,肯定有點亂。做下總結吧。

Spring中bean的載入過程

1.獲取配置文件資源

2.對獲取的xml資源進行一定的處理檢驗

3.處理包裝資源

4.解析處理包裝過後的資源

5.載入提取bean並註冊(添加到beanDefinitionMap中)

至於bean的獲取,那就比上面的簡單多了。

斷點進入AbstractBeanFactory

入口

final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

進入之後

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

再進入

return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));

我們發現又進入了

DefaultListableBeanFactory.java,是不是有種熟悉的感覺。

當你看到這條語句,你就豁然開朗了,

BeanDefinition bd = this.beanDefinitionMap.get(beanName);

就是之前載入bean放入到的map嗎?

其實整個過程還是比較容易理解的,就是裡面的包裝解析很複雜

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!

TAG: |