摘要:進(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
摘要:本文是容器源碼分析系列文章的第一篇文章,將會(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)非常成熟了...
摘要:對(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...
摘要:從使用到原理學(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)題列表 -...
摘要:下面跟蹤代碼到這個(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...
閱讀 1276·2021-11-24 09:39
閱讀 1533·2021-09-07 09:59
閱讀 3490·2019-08-30 15:54
閱讀 2486·2019-08-30 11:00
閱讀 2678·2019-08-29 15:06
閱讀 2169·2019-08-26 13:52
閱讀 438·2019-08-26 13:24
閱讀 2505·2019-08-26 12:20