成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Spring源碼一(容器的基本實(shí)現(xiàn)2)

yagami / 2795人閱讀

摘要:進(jìn)一步解析其他所有屬性并統(tǒng)一封裝至類(lèi)型的實(shí)例中。是一個(gè)接口,在中存在三種實(shí)現(xiàn)以及。通過(guò)將配置文件中配置信息轉(zhuǎn)換為容器的內(nèi)部表示,并將這些注冊(cè)到中。容器的就像是配置信息的內(nèi)存數(shù)據(jù)庫(kù),主要是以的形式保存。而代碼的作用就是實(shí)現(xiàn)此功能。

前言:繼續(xù)前一章。 一、porfile 屬性的使用

如果你使用過(guò)SpringBoot, 你一定會(huì)知道porfile配置所帶來(lái)的方便, 通過(guò)配置開(kāi)發(fā)環(huán)境還是生產(chǎn)環(huán)境, 我們可以十分方便的切換開(kāi)發(fā)環(huán)境,部署環(huán)境,更換不同的數(shù)據(jù)庫(kù)。 可能為了讓Java開(kāi)發(fā)者轉(zhuǎn)向SpringBoot開(kāi)發(fā), Spring在5.x之后停止了對(duì)這個(gè)屬性的支持。所以本文也就不再繼續(xù)描述這一屬性。

二、bean標(biāo)簽的解析及注冊(cè)
Spring中的標(biāo)簽分為默認(rèn)標(biāo)簽和自定義標(biāo)簽兩種,而這兩種標(biāo)簽的用法及解析方式存在著很大的不同,默認(rèn)標(biāo)簽是在parseDefaultElement中進(jìn)行的,函數(shù)中的功能一目了然,分別對(duì)4種標(biāo)簽(import, alias、bean、beans)做了不同的處理。
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);
    }
}

我們不得不承認(rèn),Spring5.x提倡我們更少的使用xml文件,而是更多的使用注解進(jìn)行配置,而且如果你經(jīng)常使用Springboot的話(huà),那么你肯定知道習(xí)慣優(yōu)于約定,并且springboot中只需要一個(gè)配置文件,雖然這有時(shí)根本無(wú)法滿(mǎn)足需求,這里不做關(guān)于springboot的更多的說(shuō)明。不過(guò)這并不影響Spring內(nèi)部的實(shí)現(xiàn),現(xiàn)在主要還是從xml文件分析一下bean標(biāo)簽的解析及注冊(cè)。

在4中標(biāo)簽的解析中,對(duì)bean標(biāo)簽的解析最為復(fù)雜也最為重要, 所以我們從這個(gè)標(biāo)簽進(jìn)行深入的分析。不過(guò)在這之前我還是要將之前怎么加載這個(gè)文件的部分進(jìn)行一下回憶

還記得上一部分,有一個(gè)這樣的方法:

/**
 * This implementation parses bean definitions according to the "spring-beans" XSD
 * (or DTD, historically).
 * 

Opens a DOM Document; then initializes the default settings * specified at the {@code } level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); }

如果確實(shí)對(duì)一步感興趣可以追溯下去,這樣就可以發(fā)現(xiàn)下面這段代碼:

/**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     */
    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);
        }
    }

這段代碼可能有點(diǎn)難以理解,不過(guò)當(dāng)知道了if(delegate.isDefaultNamespace(ele)) 這個(gè)方法的作用就知道了,這其實(shí)就是在對(duì)標(biāo)簽進(jìn)行一次處理而已, 是默認(rèn)標(biāo)簽的就交給默認(rèn)的處理方式,是自定義標(biāo)簽的話(huà)就換另一種處理方式。這就是這個(gè)方法中做的事了。

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

這里的Node節(jié)點(diǎn)定義了所有的Spring提供的默認(rèn)標(biāo)簽的解析結(jié)果。

parseDefaultElement(ele, delegate)這個(gè)方法又在做些什么呢?其實(shí)不過(guò)是對(duì)根級(jí)節(jié)點(diǎn)的標(biāo)簽進(jìn)行解析分類(lèi)而已,現(xiàn)在我們先分析一下bean標(biāo)簽, 所以現(xiàn)在只看針對(duì)于標(biāo)簽做了些什么。

else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    processBeanDefinition(ele, delegate);
}
進(jìn)入這個(gè)方法

/**
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 */
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));
    }
}
這里使用Spring源碼深入解析的一段:
三、解析BeanDefiniton

這個(gè)部分是Spring解析配置文件的最重要的部分, 根本就是解析標(biāo)簽并加載, 在使用Spring進(jìn)行配置的時(shí)候不難發(fā)現(xiàn), 我們有以下幾個(gè)重要的根級(jí)標(biāo)簽,bean, imort, alias, nested-beans, 下面的內(nèi)容就主要介紹一下bean標(biāo)簽的解析。上一個(gè)小結(jié)的結(jié)尾部分已經(jīng)涉及了這個(gè)的處理,繼續(xù)上面的內(nèi)容, 我們會(huì)發(fā)現(xiàn),實(shí)際上Spring首先是先通過(guò)“Bean定義解析委托”來(lái)獲得了一個(gè)BeanDefinitionHolder, 在上面的分析中,我們似乎只注意了Element,而忘記了委托的是什么時(shí)候出現(xiàn)的,事實(shí)上這個(gè)委托是在DefaultBeanDefinitionDocumentReader在這個(gè)類(lèi)中的時(shí)候就已經(jīng)創(chuàng)建了這個(gè)委托, 并且一直通過(guò)參數(shù)的方式保存著這個(gè)委托, 知道們希望獲得一個(gè)BeanDefinitionHolder的時(shí)候才真正的發(fā)揮作用,那么這個(gè)委托具體是什么呢?這個(gè)委托的作用是狀態(tài)的保存, 早在DefaultBeanDefinitionDocumentReader 這個(gè)類(lèi)中使用的時(shí)候就通過(guò)xml解析的上下文,保存了bean標(biāo)簽中的所有狀態(tài),這些狀態(tài)包括,


....
等等等……
那么BeanDefinitionHolder的作用又是什么呢? 通過(guò)這個(gè)holder, 可是實(shí)現(xiàn)注冊(cè)的功能這是一個(gè)十分重要的功能,后面會(huì)具體分析這個(gè)功能?,F(xiàn)在首先要看的是怎么獲得的這個(gè)holder呢:

/**
     * Parses the supplied {@code } element. May return {@code null}
     * if there were errors during parse. Errors are reported to the
     * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
     */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        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 (logger.isTraceEnabled()) {
                logger.trace("No XML "id" specified - using "" + beanName +
                        "" as bean name and " + aliases + " as aliases");
            }
        }

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

        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);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isTraceEnabled()) {
                        logger.trace("Neither XML "id" nor "name" specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }


此處引用Spring源碼解析中的一段內(nèi)容:

以上便是對(duì)默認(rèn)標(biāo)簽解析的全過(guò)程了。當(dāng)然,對(duì)Spring的解析猶如洋蔥剝皮一樣,一層一層的進(jìn)行,盡管現(xiàn)在只能看到對(duì)屬性id以及name的解析,但是很慶幸,思路我們已經(jīng)了解了。在開(kāi)始對(duì)屬性進(jìn)行全面分析之前, Spring在最外層做了一個(gè)當(dāng)前成的功能架構(gòu), 在當(dāng)前成完成的主要工作包括以下的內(nèi)容。
(1)提取元素中的id和name屬性。
(2)進(jìn)一步解析其他所有屬性并統(tǒng)一封裝至GenericBeanDefinition類(lèi)型的實(shí)例中。
(3)如果檢測(cè)到bean沒(méi)有指定beanName,那么使用默認(rèn)規(guī)則為此Bean生成beanName。
(4)將檢測(cè)到的信息封裝到BeanDefintionHolder的實(shí)例中。

繼續(xù)跟進(jìn)代碼:

/**
 * Parse the bean definition itself, without regard to name or aliases. May return
 * {@code null} if problems occurred during the parsing of the bean definition.
 */
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
        Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));

    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        parseMetaElements(ele, bd);
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

        parseConstructorArgElements(ele, bd);
        parsePropertyElements(ele, bd);
        parseQualifierElements(ele, bd);

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;
    }
    catch (ClassNotFoundException ex) {
        error("Bean class [" + className + "] not found", ele, ex);
    }
    catch (NoClassDefFoundError err) {
        error("Class that bean class [" + className + "] depends on not found", ele, err);
    }
    catch (Throwable ex) {
        error("Unexpected failure during bean definition parsing", ele, ex);
    }
    finally {
        this.parseState.pop();
    }

    return null;
}

通過(guò)對(duì)代碼的跟蹤,事實(shí)上,我們很容易發(fā)現(xiàn),這里的className就是從上一個(gè)方法中的通過(guò)解析別名得到beanName,在這里通過(guò)beanName又從已存的元素中獲取得到的。同樣的這個(gè)標(biāo)簽的父級(jí)元素parent也是這樣獲取得到。而接下來(lái)的操作也就是對(duì)各種屬性的具體的解析操作了,諸如:me他, lookup-method, replace-method, property, qualifier子元素等。

BeanDefinition是一個(gè)接口,在Spring中存在三種實(shí)現(xiàn):RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition。三種實(shí)現(xiàn)均繼承了AbstractBeanDefinition,其中BeanDefinition是配置文件元素標(biāo)簽在容器中的內(nèi)部表示形式。元素標(biāo)簽擁有class、scope、lazy-init等配置屬性,BeanDefinition則提供了相應(yīng)的beanClass、scope、lazyInit屬性,BeanDefinition和中的屬性是一一對(duì)應(yīng)的。其中RootBeanDefinition是最常用的實(shí)現(xiàn)類(lèi),它對(duì)應(yīng)一般性的元素標(biāo)簽,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置屬性定義類(lèi),是一站式服務(wù)類(lèi)。
在配置文件中可以定義父和子,父用RootBeanDefinition表示,而子用ChildBeanDefinition表示,而沒(méi)有父就使用RootBeanDefinition表示。AbstractBeanDefinition對(duì)兩者共同的類(lèi)信息進(jìn)行抽象。
Spring通過(guò)BeanDefinition將配置文件中配置信息轉(zhuǎn)換為容器的內(nèi)部表示,并將這些BeanDefinition注冊(cè)到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRestry就像是Spring配置信息的內(nèi)存數(shù)據(jù)庫(kù),主要是以map的形式保存。后續(xù)操作直接從BeanDefinitionRegistry中讀取配置信息。
BeanDefinition 及其實(shí)現(xiàn)類(lèi)

由此可知,要解析屬性首先要?jiǎng)?chuàng)建用于承載屬性的實(shí)例,也就是創(chuàng)建GenericBeanDefinition類(lèi)型的實(shí)例。而代碼createBeanDefinition(className, parent)的作用就是實(shí)現(xiàn)此功能。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72751.html

相關(guān)文章

  • Spring IOC 容器源碼分析系列文章導(dǎo)讀

    摘要:本文是容器源碼分析系列文章的第一篇文章,將會(huì)著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過(guò)這兩個(gè)別名獲取到這個(gè)實(shí)例,比如下面的測(cè)試代碼測(cè)試結(jié)果如下本小節(jié),我們來(lái)了解一下這個(gè)特性。 1. 簡(jiǎn)介 Spring 是一個(gè)輕量級(jí)的企業(yè)級(jí)應(yīng)用開(kāi)發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過(guò)十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...

    NSFish 評(píng)論0 收藏0
  • 起來(lái)讀Spring源碼吧(容器初始化

    摘要:對(duì)于開(kāi)發(fā)者來(lái)說(shuō),無(wú)疑是最常用也是最基礎(chǔ)的框架之一。概念上的東西還是要提一嘴的用容器來(lái)管理。和是容器的兩種表現(xiàn)形式。定義了簡(jiǎn)單容器的基本功能。抽象出一個(gè)資源類(lèi)來(lái)表示資源調(diào)用了忽略指定接口的自動(dòng)裝配功能委托解析資源。 對(duì)于Java開(kāi)發(fā)者來(lái)說(shuō),Spring無(wú)疑是最常用也是最基礎(chǔ)的框架之一。(此處省略1w字吹Spring)。相信很多同行跟我一樣,只是停留在會(huì)用的階段,比如用@Component...

    libxd 評(píng)論0 收藏0
  • Java深入-框架技巧

    摘要:從使用到原理學(xué)習(xí)線(xiàn)程池關(guān)于線(xiàn)程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開(kāi)發(fā)中,分散于應(yīng)用中多出的功能被稱(chēng)為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過(guò)的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛(ài)情萌芽的模樣…… Java 進(jìn)階面試問(wèn)題列表 -...

    chengtao1633 評(píng)論0 收藏0
  • Spring源碼容器基本實(shí)現(xiàn)1)

    摘要:下面跟蹤代碼到這個(gè)實(shí)現(xiàn)中看看是怎么做的在實(shí)例化的過(guò)程中,在構(gòu)造函數(shù)中調(diào)用了其超類(lèi)的構(gòu)造函數(shù),而在超類(lèi)中對(duì)其所處換環(huán)境進(jìn)行的判斷,所謂的環(huán)境呢,事實(shí)上指得就是是通過(guò),還是通過(guò)加載的上下文,這也就意味著不同方式加載可能存在某些不同。 前言 本文基于《Spring源碼深度解析》學(xué)習(xí), 《Spring源碼深度解析》講解的Spring版本低于Spring3.1,當(dāng)前閱讀的版本為Spring5.x...

    awokezhou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<