Spring原理解析-BeanFactory---容器加載過程解析

容器加載

DefaultListableBeanFactory類介紹

  DefaultListableBeanFactory類是BeanFactory的默認實現類,其間接實現BeanFactory接口、BeanDefinitionRegistry接口,同時依賴於BeanDefinition接口。其類圖如下:

DefaultListableBeanFactory類圖
如上圖所示,DefaultListableBeanFactory類間接實現BeanFactory接口和BeanDefinitionRegistry接口,其中BeanDefinitionRegistry接口又依賴於BeanDefinition接口。接下來將圍繞着張圖來講解BeanFactory容器的啓動過程。

BeanFactory接口介紹

  BeanFactory接口主要是用於創建bean的工廠接口,其定義的接口方法分爲以下幾個部分:獲取bean實例、判斷容器中是否包含某個bean、判斷實例是否爲單列模式或則原型模式、類型匹配、實例類型、獲取別名。具體接口內容如下:

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
	//獲取bean實例
    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
	//是否包含指定實例
    boolean containsBean(String var1);
	//是否爲單例模式
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
	//是否爲原型模式
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
	//類型匹配
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
	//返回實例類型
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
	//返回別名
    String[] getAliases(String var1);
}

BeanDefinition接口介紹

  根據接口名稱便可以猜出,該接口是跟bean定義相關。的確,該接口是用來管理每個bean的定義信息。在容器啓動的時候,spring會將配置文件中定以的信息或則使用註解(如@Componet @Service @Controller等)定義的bean封裝成一個個BeanDefinition對象,每個bean對應一個BeanDefinition對象,在對象實例化期間,使用bean對應的BeanDenifition對象通過反射生成實例對象。BeanDefinition接口方法用來管理bean的屬性信息
,如父類名、類名、作用域、懶加載等。以下代碼爲BeanDefinition接口源碼:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;
	//父類名
    void setParentName(String var1);

    String getParentName();
	//類名
    void setBeanClassName(String var1);

    String getBeanClassName();
	//作用域
    void setScope(String var1);

    String getScope();
	//懶加載
    void setLazyInit(boolean var1);

    boolean isLazyInit();
	//依賴
    void setDependsOn(String... var1);

    String[] getDependsOn();

    void setAutowireCandidate(boolean var1);

    boolean isAutowireCandidate();

    void setPrimary(boolean var1);

    boolean isPrimary();
	//工廠類名
    void setFactoryBeanName(String var1);

    String getFactoryBeanName();
	//工廠方法名
    void setFactoryMethodName(String var1);

    String getFactoryMethodName();

    ConstructorArgumentValues getConstructorArgumentValues();

    MutablePropertyValues getPropertyValues();

    boolean isSingleton();

    boolean isPrototype();

    boolean isAbstract();

    int getRole();

    String getDescription();

    String getResourceDescription();

    BeanDefinition getOriginatingBeanDefinition();
}

  可以發現,BeanDefinition接口中的屬性信息跟xml配置文件中的標籤中的屬性一一對應,也就是說一個BeanDefinition對應一個標籤信息。在容器啓動的時候,就會先將標籤信息解析封裝成BeanDefinition接口實現類對象。
  BeanDefinition有三個間接實現類,分別是RootBeanFactory、ChildBeanFactory和GenericBeanDefinition。其中RootBeanFactory是用來封裝父類定義信息(沒有顯示繼承其他類的類)、ChildBeanDefinition是用來封裝子類定義信息。

BeanDefinitionRegistry接口介紹

  同樣,根據接口名就可以看出,這個接口跟BeanDefinition對象的註冊有關,上一部分將了,在容器啓動階段會將標籤定義的bean信息解析封裝成BeanDefinition對象,以備後續實例化使用,但是這些BeanDefinition歸誰保存維護呢。沒錯,BeanDefinitionRegistry就是用來保存維護BeanDefinition對象的。

public interface BeanDefinitionRegistry extends AliasRegistry {
	//註冊BeanDefinition
    void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;
	//移除BeanDefinition
    void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
	//獲取BeanDefinition
    BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
	//是否包含指定BeanDefinition對象
    boolean containsBeanDefinition(String var1);
	//獲取所有BeanDefinition對象名稱
    String[] getBeanDefinitionNames();
	//獲取BeanDefinition對象數量
    int getBeanDefinitionCount();
	//BeanDefinition是否在使用
    boolean isBeanNameInUse(String var1);
}

如何解析xml文件

  到這裏,大家想必對BeanDefinition管理和BeanFactory工廠有一定 的瞭解了吧,但是大家是否會疑惑,spring是如何將x在這裏插入代碼片ml中的字符定義轉換成BeanDefinition對象的呢,那麼接下來我將帶着大家繼續探討(其實看到這裏,各位可以返現,我明沒有深入的去分析源碼,而是從原理層面上分析,當大家能夠掌握基本原理再去看源碼就應該簡單了)
  將解析之前我先上一段代碼,如下:

public class ClassA {
    
    public void say(){
        System.out.println("我是ClassA對象,我在說話");
    }
}

  這是一個普通的class類

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <bean id="classA" class="springBeanTest.ClassA"/>
</beans>

  這是spring的配置文件spring.xml

public class LifeCycleTest {
    @Test
    public void test1(){
        //創建bean的工廠類
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //創建一個xml文件的reader類,將bean工廠傳入reader中
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory);
        //加載xml未配置文件,並將配置文件解析成BeanDefinition對象保存在工廠類中
        reader.loadBeanDefinitions("classpath:/spring.xml");
        //從工廠類中獲取classA類的beanDefinition對象
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("classA");
        //打印
        System.out.println(beanDefinition.getBeanClassName());
        System.out.println(beanDefinition.isSingleton());
    }
}

  運行結果如下圖,可以看出,spring確實將xml配置文件解析成beanDefinition對象
運行結果

  1. 以baenFactory爲構造器參數,創建BeanDefinitionReader對象:

  AbstractBeanDefinitionReader抽象類構造器部分內容:

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		//將beanFactory傳給父類構造器
        super(registry);
       
    }

  父類構造器部分代碼如下:

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        //父類構造器將beanFactory複製給自己的依賴對象,這樣便爲BeanDefinitionReader對象設置好了BeanFactory實例,用於註冊beanDefinition。
        this.registry = registry;
        //初始化ResourceLoader
        if (this.registry instanceof ResourceLoader) {
            this.resourceLoader = (ResourceLoader)this.registry;
        } else {
        	//創建resourceLoader對象,類型爲DefaultResourceLoader
            this.resourceLoader = new PathMatchingResourcePatternResolver();
        }
    }
  1. 調用AbstrcatBeanDefinitionReader方法:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//獲取上一步初始化時創建的resourceLoader對象
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            //異常處理代碼
        } else {
            int loadCount;
            //接下來的代碼是根據loaction的格式創建不同的Resource對象。
            if (!(resourceLoader instanceof ResourcePatternResolver)) {
    		//省略部分代碼
            } else {
                try {
                //根絕loaction格式,創建像一面的Resource對象
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
               //從resource對象中加載beanDefinition對象
                    loadCount = this.loadBeanDefinitions(resources);
                   	//省略部分代碼
                    return loadCount;
                } catch (IOException var10) {
                    //省略部分代碼
                }
            }
        }
    }

  Resource接口屏蔽了不同資源的訪問差別,爲上層提供統一的資源訪問接口。

  1. 獲取到resource對象之後,spring會使用該對象創建一個EncodedResource對象,主要是方便編碼處理

  以下代碼是XmlBeanDefinitionReader類中的方法

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
    }
  1. 得到EncodedResource對象之後,調用以下方法進行xml文件解析,並封裝成beanDefinition對象,儲存在beanFactroy中。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
       //省去異常處理
        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if (!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
					//此處開始進行解析加載
                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
               //異常處理
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if (((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }
  1. doLoadBeanDefinitions方法源碼如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
        	//這裏將從resource中加載資源,並封裝成Document對象
            Document doc = this.doLoadDocument(inputSource, resource);
            //解析doucument對象,生成beanDefinition對象並註冊到BeanDefinitionRegistry中(BeanFactory接口的默認實現類同時也實現了BeanDefinitionRegistry接口,因此可以從BeanDefinitionRegistry獲取所有的beanDefinition對象)
            return this.registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException var4) {
            //連續一串的異常處理
        } 
    }
  1. registerBeanDefinitions方法,該方法的作用是使用documentReader對象讀取document對象中的內容,並解析生成beanDefinition對象,然後註冊到BeanDefinitionRegistry中去。詳解解析過程此處不再繼續深究。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//創建documentReader對象
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
       	//解析document對象,生成beanDefinition對象並註冊
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }
  1. 總結:從以上步驟可以發現,xml的具體解析工作並非由BeanDefinitionReader對象來做的,而是由DocumentReader對象來完成的。下面是一張XmlBeanDefinitionReader類的類圖,將根據這張類圖來梳理整個流程。
    在這裏插入圖片描述
      BeanDefinitionReader類更具配置文件路徑名格式,生成不同的Resource接口實現類,這樣做的目的是屏蔽對不同資源加載的差異性,獲得resource對象之後,BeanDefinitionReader使用resource對象,創建一個EncodedResource對象,方便對資源的編碼處理。然後BeanDefinitionReader在解析EncodedResource對象,生成Doucement對象,最後纔是使用BeanDefinitionReader內部的DoucementReader對象來解析Doucement,從而生成BeanDefinition對象,並註冊到BeanDefinitionRegistry中去。至此,完成了配置文件的解析工作。這裏說明一下AbstractBeanDefinitionReader的實現類不只XmlBeanDefinitionReader一個,還有PropertiesBeanDefinitionReader,不同的配置文件使用不同的實現類,同時也可以根據自己的需求自定義一個BeanDefinitionReader實現類,來完成配置文件的機械加載工作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章