1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Spring IoC 容器初始化(2)

        共 4236字,需瀏覽 9分鐘

         ·

        2021-02-06 18:37

        前情回顧

        前文「Spring IoC 容器初始化」 以 IoC 容器中的 ClassPathXmlApplicationContext 為例進(jìn)行了深入分析。

        Spring 從我們的配置文件(即 application-ioc.xml)中讀取到 Bean 的原始信息,將其解析為 Document 對象。由于 DOM 解析只是充當(dāng)了工具(語法解析),不必舍本逐末,這里不再深入分析。

        本文繼續(xù)分析 Spring 如何從 Document 進(jìn)行語義解析和注冊 BeanDefinition。

        BeanDefinition 解析和注冊

        前文提到,Spring 從 Document 解析和注冊 BeanDefinition 是在 XmlBeanDefinitionReader#registerBeanDefinitions 方法中實現(xiàn)的,繼續(xù)跟進(jìn)這個方法:

        public?class?XmlBeanDefinitionReader?extends?AbstractBeanDefinitionReader?{
        ????//?...

        ????public?int?registerBeanDefinitions(Document?doc,?Resource?resource)?throws?BeanDefinitionStoreException?{
        ??????//?創(chuàng)建?BeanDefinitionDocumentReader?對象
        ??????BeanDefinitionDocumentReader?documentReader?=?createBeanDefinitionDocumentReader();
        ??????int?countBefore?=?getRegistry().getBeanDefinitionCount();
        ??????//?將?Document?中的?Bean?信息注冊到容器
        ??????documentReader.registerBeanDefinitions(doc,?createReaderContext(resource));
        ??????return?getRegistry().getBeanDefinitionCount()?-?countBefore;
        ????}

        ????//?...
        }

        該方法主要做了兩件事:

        1. 創(chuàng)建 BeanDefinitionDocumentReader 對象
        2. 注冊 Document 中的 Bean 信息

        這里又有個 BeanDefinitionDocumentReader,聯(lián)系前文的 BeanDefinitionReader、ResourceLoader 等,這就是面向?qū)ο缶幊蹋∣OP)思想的一種典型實現(xiàn),值得細(xì)品。

        第二步實際調(diào)用了 DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions 方法:

        public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
        ????//?...

        ????protected?void?doRegisterBeanDefinitions(Element?root)?{
        ??????BeanDefinitionParserDelegate?parent?=?this.delegate;
        ??????//?創(chuàng)建解析?BeanDefinition?的代理對象?BeanDefinitionParserDelegate
        ??????this.delegate?=?createDelegate(getReaderContext(),?root,?parent);

        ??????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.isDebugEnabled())?{
        ??????logger.debug("Skipped?XML?bean?definition?file?due?to?specified?profiles?["?+?profileSpec?+
        ????????"]?not?matching:?"?+?getReaderContext().getResource());
        ????????????}
        ????????????return;
        ??????????}
        ????????}
        ??????}

        ??????//?解析?XML?配置文件前做的事情
        ??????preProcessXml(root);
        ??????//?解析?XML?配置文件
        ??????parseBeanDefinitions(root,?this.delegate);
        ??????//?解析?XML?配置文件后做的事情
        ??????postProcessXml(root);

        ??????this.delegate?=?parent;
        ???}

        ???//?...
        }

        該方法創(chuàng)建了一個 BeanDefinitionParserDelegate 對象,看它的名字可知,它是用來解析 BeanDefinition 的代理對象。

        在 createDelegate 方法中,還對創(chuàng)建的 BeanDefinitionParserDelegate 進(jìn)行了初始化,主要是保存了 標(biāo)簽的一些默認(rèn)配置,比如常見的 default-lazy-initdefault-autowire、default-init-method 等。

        真正對 XML 配置文件進(jìn)行語義解析的是 parseBeanDefinitions 方法。而且在該方法的前后,Spring 還留出了兩個方法 preProcessXml 和 postProcessXml。這兩個方法都是空的,用于自定義擴(kuò)展。

        一個框架或軟件之所以做得好,除了本身確實好用,「擴(kuò)展性」也是很重要的一方面。

        想起了 Tomcat 有很多參數(shù)可配置,JVM 的參數(shù)也有一大堆……

        下面研究 parseBeanDefinitions 方法是如何解析 Bean 定義的。

        public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
        ????//?...

        ????protected?void?parseBeanDefinitions(Element?root,?BeanDefinitionParserDelegate?delegate)?{
        ??????//?解析?Spring?默認(rèn)名稱空間
        ??????if?(delegate.isDefaultNamespace(root))?{
        ????????NodeList?nl?=?root.getChildNodes();
        ????????for?(int?i?=?0;?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);
        ??????}
        ???}

        ???//?...
        }

        Spring 默認(rèn)的名稱空間是什么呢?

        還記得前面的配置文件 application-ioc.xml 嗎?如下:


        <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="person"?class="com.jaxer.doc.ioc.Person">
        ????????<property?name="pet"?ref="dog"/>
        ????bean>

        ????<bean?id="dog"?class="com.jaxer.doc.ioc.Dog">
        ????????<property?name="owner"?ref="person"/>
        ????????<property?name="age"?value="1"/>
        ????bean>

        beans>

        標(biāo)簽中的第一行 xmlns 就是它的名稱空間,也就是:http://www.springframework.org/schema/beans

        這里暫且跳過 Spring 如何解析自定義標(biāo)簽的 parseCustomElement 方法。先來分析 Spring 如何解析默認(rèn)名稱空間,即 parseDefaultElement 方法:

        public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
        ????//?...

        ????//?解析?Spring?默認(rèn)名稱空間的配置
        ????private?void?parseDefaultElement(Element?ele,?BeanDefinitionParserDelegate?delegate)?{
        ??????//?解析?import?標(biāo)簽
        ??????if?(delegate.nodeNameEquals(ele,?IMPORT_ELEMENT))?{
        ????????importBeanDefinitionResource(ele);
        ??????}
        ??????//?解析?alias?標(biāo)簽
        ??????else?if?(delegate.nodeNameEquals(ele,?ALIAS_ELEMENT))?{
        ????????processAliasRegistration(ele);
        ??????}
        ??????//?解析?bean?標(biāo)簽
        ??????else?if?(delegate.nodeNameEquals(ele,?BEAN_ELEMENT))?{
        ????????processBeanDefinition(ele,?delegate);
        ??????}
        ??????//?解析?beans?標(biāo)簽
        ??????else?if?(delegate.nodeNameEquals(ele,?NESTED_BEANS_ELEMENT))?{
        ????????//?recurse
        ????????doRegisterBeanDefinitions(ele);
        ??????}
        ???}

        ???//?...
        }

        這里又分為四個部分,分別解析 import、alias、bean 和 beans 標(biāo)簽。

        其實 標(biāo)簽是可以嵌套的,只是很少用到。若嵌套使用了 標(biāo)簽,會繼續(xù)遞歸去解析。

        下面以 標(biāo)簽為例,繼續(xù)跟進(jìn):

        public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{

        ????protected?void?processBeanDefinition(Element?ele,?BeanDefinitionParserDelegate?delegate)?{
        ??????//?通過?BeanDefinitionParserDelegate?解析出定義的?Bean?信息,并封裝為?BeanDefinitionHolder?對象
        ??????BeanDefinitionHolder?bdHolder?=?delegate.parseBeanDefinitionElement(ele);
        ??????if?(bdHolder?!=?null)?{
        ????????bdHolder?=?delegate.decorateBeanDefinitionIfRequired(ele,?bdHolder);
        ????????try?{
        ??????????//?將?BeanDefinition?注冊到注冊中心
        ??????????BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,?getReaderContext().getRegistry());
        ????????}
        ????????//?catch
        ????????getReaderContext().fireComponentRegistered(new?BeanComponentDefinition(bdHolder));
        ??????}
        ????}

        }

        processBeanDefinition 方法主要做了兩件事:

        1. 對 Document 對象中的 Bean 定義進(jìn)行語義解析,并封裝為 BeanDefinitionHolder。BeanDefinitionHolder 持有了 BeanDefinition,并且保存了后者的別名等信息。
        2. 將 BeanDefinition 注冊到注冊中心,這里的注冊中心其實就是 IoC 容器,也就是 DefaultListableBeanFactory。

        代碼走到這里,還沒看到 Spring 如何解析我們定義的 標(biāo)簽,以及 標(biāo)簽內(nèi)部的 、 等標(biāo)簽……藏得夠深啊!別急,馬上就到了!

        public?class?BeanDefinitionParserDelegate?{
        ??@Nullable
        ??public?BeanDefinitionHolder?parseBeanDefinitionElement(Element?ele)?{
        ????return?parseBeanDefinitionElement(ele,?null);
        ??}

        ??@Nullable
        ??public?BeanDefinitionHolder?parseBeanDefinitionElement(Element?ele,?@Nullable?BeanDefinition?containingBean)?{
        ????//?讀取?id、name?屬性
        ????String?id?=?ele.getAttribute(ID_ATTRIBUTE);
        ????String?nameAttr?=?ele.getAttribute(NAME_ATTRIBUTE);

        ????//?讀取別名?alias
        ????List?aliases?=?new?ArrayList<>();
        ????if?(StringUtils.hasLength(nameAttr))?{
        ??????String[]?nameArr?=?StringUtils.tokenizeToStringArray(nameAttr,?MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        ??????aliases.addAll(Arrays.asList(nameArr));
        ????}

        ????String?beanName?=?id;
        ????if?(!StringUtils.hasText(beanName)?&&?!aliases.isEmpty())?{
        ??????beanName?=?aliases.remove(0);
        ????}

        ????if?(containingBean?==?null)?{
        ??????checkNameUniqueness(beanName,?aliases,?ele);
        ????}

        ????//?解析??標(biāo)簽定義的各個屬性
        ????AbstractBeanDefinition?beanDefinition?=?parseBeanDefinitionElement(ele,?beanName,?containingBean);
        ????if?(beanDefinition?!=?null)?{
        ??????if?(!StringUtils.hasText(beanName))?{
        ????????try?{
        ??????????if?(containingBean?!=?null)?{
        ????????????beanName?=?BeanDefinitionReaderUtils.generateBeanName(
        ????????beanDefinition,?this.readerContext.getRegistry(),?true);
        ??????????}
        ??????????else?{
        ????????????beanName?=?this.readerContext.generateBeanName(beanDefinition);
        ??????
        ????????????String?beanClassName?=?beanDefinition.getBeanClassName();
        ????????????if?(beanClassName?!=?null?&&
        ????????beanName.startsWith(beanClassName)?&&?beanName.length()?>?beanClassName.length()?&&
        ????????!this.readerContext.getRegistry().isBeanNameInUse(beanClassName))?{
        ??????????????aliases.add(beanClassName);
        ????????????}
        ????????? }
        ?????? }
        ?????? catch?(Exception?ex)?{
        ???????? error(ex.getMessage(),?ele);
        ???????? return?null;
        ?????? }
        ???? }
        ???? //?封裝為持有?BeanDefinition?對象的?BeanDefinitionHolder
        ???? String[]?aliasesArray?=?StringUtils.toStringArray(aliases);
        ???? return?new?BeanDefinitionHolder(beanDefinition,?beanName,?aliasesArray);
        ?? }

        ?? return?null;
        ??}
        }

        parseBeanDefinitionElement 方法主要做了三件事:

        1. 從 Document 讀取 標(biāo)簽中的 id 和 name 屬性
        2. 將 Document 中的 Bean 定義轉(zhuǎn)換為 BeanDefinition(實現(xiàn)類為 GenericBeanDefinition)
        3. 將 BeanDefinition 封裝為 BeanDefinitionHolder 并返回

        拿到 BeanDefinitionHolder 后,Spring 會將其注冊到注冊中心。

        到這里我們還是沒看到 Spring 是如何解析 標(biāo)簽內(nèi)部的標(biāo)簽的,其實它們是在 parseBeanDefinitionElement 方法中實現(xiàn)的。

        留到后面再分析吧,本文先到這里,實在是太枯燥了??

        小結(jié)

        本文沿著上篇文章繼續(xù)跟進(jìn),好像沒什么實質(zhì)的東西……算了,就當(dāng)承前啟后。

        欲知后事如何,且聽下回分解。

        瀏覽 66
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产又大又粗又爽视频 | 91成人免费电影片 | 开心成人激情 | aaa精品 | AAA片免费看 | 国产无套粉嫩内谢在线观看 | 肉囊拍打发出黏腻水声 | 亚洲天堂成人网站 | 麻豆国产露脸在线观看 | 少妇又色又爽 |