[spring源碼學習]二、IOC源碼——配置文件讀取


一、環境准備

  對於學習源碼來講,拿到一大堆的代碼,腦袋里肯定是嗡嗡的,所以從代碼實例進行跟蹤調試未嘗不是一種好的辦法,此處,我們准備了一個小例子:

package com.zjl;

public class Person {
    private String name;

    public String getName() {
        return name;
    }

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

    public void sayHello(){
        System.out.println("hello "+this.name);
    }
}

  bean的定義:

    <bean id="person" class="com.zjl.Person">
        <property name="name" value="zhangsan"></property>
    </bean>  

  從很久以前,spring的第一個例子慣性的我們都是用XmlBeanFactory來進行,測試代碼如下:

        XmlBeanFactory xmlBeanFactory=new XmlBeanFactory(new ClassPathResource("bean.xml"));
        Person person=(Person)xmlBeanFactory.getBean("person");
        person.sayHello();

  不過,很可惜,這個類在后來的版本中被刪除了,不過沒關系,他的源碼很簡單:

public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


    /**
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    /**
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }

}

  我們可以看到,只是他繼承了DefaultListableBeanFactory ,並持有一個XmlBeanDefinitionReader的引用,然后調用了this.reader.loadBeanDefinitions(resource)方法。所以我們很容易根據這個源碼將程序改造一下

        DefaultListableBeanFactory beanFacory=new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFacory);
        reader.loadBeanDefinitions(new ClassPathResource("bean.xml"));
        Person person=(Person)beanFacory.getBean("person");
        person.sayHello();

 二、源碼解析

  結合上一節的內容,在本章節,我們只是詳細看下針對xml配置的bean的一個加載過程,並不會調用getBean方法具體生成bean的對象,入口程序我們鎖定為reader.loadBeanDefinitions(new ClassPathResource("bean.xml"))

  1、對resource進行了再次包裝,添加編碼和字符集

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

  2、讀取配置文件,將inputStream轉化為sax的inputSource,並設置字符集

try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
         //入口程序
return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } }

  3、進入doLoadBeanDefinitions方法,將inputSource轉化為Dom解析的Document

            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);

  4、創建dom的讀取方法DefaultBeanDefinitionDocumentReader的實例,,並獲取bean的數量,使用registerBeanDefinitions加載bean,解析完成后,返回此次加載bean的數量,注:此處對resource再次做了封裝,疑似注入一些監聽函數,但是暫時不管他們

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

  5、進入DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法,獲取到配置文件的根節點beans,針對根節點開始遍歷

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

  6、創建了一個delegate,並判斷節點是否為NameSpaceURI-http://www.springframework.org/schema/beans

    public boolean isDefaultNamespace(Node node) {
        return isDefaultNamespace(getNamespaceURI(node));
    }

  7、如果是默認的,獲取profile屬性,此處資料上可以配置開發、生產或者測試環境,暫不深究

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

  8、繼續解析,在真正解析前后,可以使用pre和post方法對xml進行處理,此處均為空,只看parseBeanDefinitions方法即可

        preProcessXml(root);
     //入口 parseBeanDefinitions(root,
this.delegate); postProcessXml(root);

  9、此處再次判斷,root是否為默認的namespace,如果是,獲取所有子節點,進行遍歷子節點,並解析。如果根節點或者子節點不是默認的namespace,將使用delegate.parseCustomElement進行解析,由此可以看出,spring為我們預留了一個自定義配置文件解析的入口(此部分作為下一個章節重點分析)

  此次我們只有默認的namespace的應用,所以進行解析他的Element的子節點,我們知道唯一一個子節點為“bean”

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
              //入口 parseDefaultElement(ele, delegate); }
else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }

10、對子節點的解析分為4中情況,分別為import,alias,bean,beans,其中

  beans:之前處理過,再次解析子節點即可

  import:表示引入另外的配置文件資源進行解析,此處預留其他文件解析入口

  alias:支持此格式<alias name="doSpring" alias="do"/>,是對一個bean起別名

  bean:是我們目前解析的重點,進入下一步解析

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      //入口 processBeanDefinition(ele, delegate); }
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }

11、根據節點,創建一個BeanDefinitionHolder,

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     //入口 BeanDefinitionHolder bdHolder
= delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

12、我們具體看下他的源碼,可以知道它是根據Element的id,name等兩個屬性進行生成,如果有id,id作為beanName,如果id不存在,name的第一個作為beanName,其他作為別名,此處不再貼出,然后根據name和元素,創建一個AbstractBeanDefinition 

AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

13、 分別獲得className、parent、descriptioString className = null;        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();

        }

        try {
            String parent = null;
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
       //14、初始化GenericBeanDefinition AbstractBeanDefinition bd
= createBeanDefinition(className, parent);        //15、設置bean的其他屬性,包括單例,抽象,延遲加載、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method、factory-bean等屬性,
此部分的特點是有一個默認值,
但也通過系統默認設置 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));        //解析子標簽中的meta標簽 parseMetaElements(ele, bd);
       //解析look-up標簽 parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
       //解析replaced-method標簽 parseReplacedMethodSubElements(ele, bd.getMethodOverrides());        //constructor-arg標簽,為了便於構造函數注入 parseConstructorArgElements(ele, bd);
      //解析Property標簽,根據value或者ref標簽分別注入TypedStringValue或RuntimeBeanReference到PropertyValue中 parsePropertyElements(ele, bd);
       //解析qualifier parseQualifierElements(ele, bd); bd.setResource(
this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; }

14、與13一起,根據parentName和className創建GenericBeanDefinition的實例,此處classLoader不為null時候,似乎客戶設置復雜的數據格式,暫時跳過

    public static AbstractBeanDefinition createBeanDefinition(
            String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

15、如果有其他標簽,仍按照此方法繼續處理。到此部分,我們已經解析完了配置文件中的所有標簽

16、根據別名,beanDefinition beanName創建BeanDefinitionHolder

String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);

17、對holder進行進一步裝飾,主要裝飾[autowire="default", autowire-candidate="default", class="com.zjl.Person", id="person", lazy-init="default"]等

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.將最終裝飾后的BeanDefinition注入registry,
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

18、調用registerBeanDefinition,注入bean時,先做一個父類檢測((AbstractBeanDefinition) beanDefinition).validate()

主要檢測是否在配置文件中設置了factoryMethodName,

    public void validate() throws BeanDefinitionValidationException {
        if (!getMethodOverrides().isEmpty() && getFactoryMethodName() != null) {
            throw new BeanDefinitionValidationException(
                    "Cannot combine static factory method with method overrides: " +
                    "the static factory method must create the instance");
        }

        if (hasBeanClass()) {
            prepareMethodOverrides();
        }
    }

19、將beanDefinition放入map,beanName放入list,將alias也放到map

this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);

20、觸發注入的事件,此處觸發了第四部分注入的監聽事件,由於創建時候使用的XmlBeanDefinitionReader的new XmlReaderContext(resource, this.problemReporter, this.eventListener,this.sourceExtractor, this, getNamespaceHandlerResolver());方法中的EmptyReaderEventListener方法均為空,所以不做任何事情,此處我們也知道了,可以在自定義reader的子類中注入新的監聽類,可以得到其他通知

// Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

三、總結

1、到此為止,整個ioc的配置文件讀取和解析過程以及完成,此處只有一個bean,如果有多個,會從根繼續讀取下一個子節點進行解析。,最終我們在DefaultListableBeanFactory里得到了bean的map,beanName的list,alias的map等,通過這些容器就可以得到配置文件中bean的內容,但如何在調用getBean的時候獲得到需要的bean,需要我們在以后的學習中進一步閱讀源碼

2、在源碼中我們可以看到除了自己配置的一些簡單信息外,spring還有大量的默認配置和全局配置,這些配置的不同,是spring的各種功能的入口和配置,需要我們進行關注

3、學習過程中,還有很多暫時不明白的功能,只能先跳過

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM