Spring 是如何解析 標簽的?
前情回顧
上回「Spring IoC 容器初始化(2)」說到了 Spring 如何解析我們定義的
本文繼續(xù)跟進。
嗯,還是要耐著點性子,最好寫個 demo 打斷點跟蹤一下,這樣理解起來才更深刻。
如何解析 的內(nèi)容?
繼續(xù)看代碼:
public?class?BeanDefinitionParserDelegate?{
????public?AbstractBeanDefinition?parseBeanDefinitionElement(
??? Element?ele,?String?beanName,?@Nullable?BeanDefinition?containingBean)?{
?? String?className?=?null;
?? //?讀取??標簽的?class?屬性
?? if?(ele.hasAttribute(CLASS_ATTRIBUTE))?{
??? className?=?ele.getAttribute(CLASS_ATTRIBUTE).trim();
?? }
?? String?parent?=?null;
?? //?讀取??標簽的?parent?屬性
?? if?(ele.hasAttribute(PARENT_ATTRIBUTE))?{
??? parent?=?ele.getAttribute(PARENT_ATTRIBUTE);
?? }
?? try?{
??? //?創(chuàng)建?BeanDefinition?對象(GenericBeanDefinition)
??? AbstractBeanDefinition?bd?=?createBeanDefinition(className,?parent);
??? //?解析?scope、lazy-init、autowire?等屬性
??? parseBeanDefinitionAttributes(ele,?beanName,?containingBean,?bd);
??? bd.setDescription(DomUtils.getChildElementValueByTagName(ele,?DESCRIPTION_ELEMENT));
??? //?解析?meta?標簽
??? parseMetaElements(ele,?bd);
????????????
??? //?解析?lookup-method?標簽
??? parseLookupOverrideSubElements(ele,?bd.getMethodOverrides());
??? //?解析?replace-method?標簽
??? parseReplacedMethodSubElements(ele,?bd.getMethodOverrides());
??? //?解析?constructor-arg?標簽
? ?? parseConstructorArgElements(ele,?bd);
?? ? //?解析?property?標簽
??? parsePropertyElements(ele,?bd);
????????????
??? //?解析?qualifier?標簽
??? parseQualifierElements(ele,?bd);
??? bd.setResource(this.readerContext.getResource());
? ?? bd.setSource(extractSource(ele));
?? ? return?bd;
?? }
?? //?catch?...
? }
}
這里才是真正解析
該方法內(nèi)部調(diào)用了一個個方法去解析不同的標簽。這里我們只跟進常見的 property 如何解析,其他方法大體也都差不多,有興趣可以自行研究。
parsePropertyElements 方法代碼如下:
public?class?BeanDefinitionParserDelegate?{
? //?解析?property?標簽
? public?void?parsePropertyElements(Element?beanEle,?BeanDefinition?bd)?{
?? NodeList?nl?=?beanEle.getChildNodes();
?? for?(int?i?=?0;?i???? Node?node?=?nl.item(i);
??? //?篩選??標簽
??? if?(isCandidateElement(node)?&&?nodeNameEquals(node,?PROPERTY_ELEMENT))?{
???? parsePropertyElement((Element)?node,?bd);
??? }
?? }
? }
? public?void?parsePropertyElement(Element?ele,?BeanDefinition?bd)?{
?? //?property?標簽的?name?屬性
?? String?propertyName?=?ele.getAttribute(NAME_ATTRIBUTE);
?? if?(!StringUtils.hasLength(propertyName))?{
??? //?error
??? return;
?? }
?? this.parseState.push(new?PropertyEntry(propertyName));
?? try?{
??? if?(bd.getPropertyValues().contains(propertyName))?{
???? //?error
???? return;
??? }
??? //?這里解析得到的是?RuntimeBeanReference?或者?TypedStringValue
??? Object?val?=?parsePropertyValue(ele,?bd,?propertyName);
??? PropertyValue?pv?=?new?PropertyValue(propertyName,?val);
??? parseMetaElements(ele,?pv);
??? pv.setSource(extractSource(ele));
??? //?將解析到的值添加到?BeanDefinition?的屬性列表
??? bd.getPropertyValues().addPropertyValue(pv);
?? }
?? finally?{
??? this.parseState.pop();
?? }
? }????
}
這個方法主要做了什么呢?
遍歷節(jié)點并找到 property 標簽 解析 property 標簽的 name 屬性,將它對應的值封裝為 RuntimeBeanReference 類型或者 TypedStringValue 類型(其中前者對應 ref 屬性,后者對應 value 屬性,可參考前文 application-ioc.xml 文件),然后再封裝為 PropertyValue 類型,并保存到 BeanDefinition 的屬性列表中。
解析 ref 和 value 的過程如下:
public?class?BeanDefinitionParserDelegate?{
public?Object?parsePropertyValue(Element?ele,?BeanDefinition?bd,?@Nullable?String?propertyName)?{
?? String?elementName?=?(propertyName?!=?null??
???? "?element?for?property?'" ?+?propertyName?+?"'"?:
???? "?element" );
?? //?Should?only?have?one?child?element:?ref,?value,?list,?etc.
NodeList?nl?=?ele.getChildNodes();
?? Element?subElement?=?null;
?? for?(int?i?=?0;?i? ??Node?node?=?nl.item(i);
??? if?(node?instanceof?Element?&&?!nodeNameEquals(node,?DESCRIPTION_ELEMENT)?&&
????? !nodeNameEquals(node,?META_ELEMENT))?{
???? //?Child?element?is?what?we're?looking?for.
???? if?(subElement?!=?null)?{
????? error(elementName?+?"?must?not?contain?more?than?one?sub-element",?ele);
???? }
???? else?{
????? subElement?=?(Element)?node;
???? }
??? }
?? }
????//?ref?和?value?屬性,二者不能并存
?? boolean?hasRefAttribute?=?ele.hasAttribute(REF_ATTRIBUTE);
?? boolean?hasValueAttribute?=?ele.hasAttribute(VALUE_ATTRIBUTE);
?? if?((hasRefAttribute?&&?hasValueAttribute)?||
???? ((hasRefAttribute?||?hasValueAttribute)?&&?subElement?!=?null))?{
??? error(elementName?+
????? "?is?only?allowed?to?contain?either?'ref'?attribute?OR?'value'?attribute?OR?sub-element",?ele);
?? }
????//?ref?屬性
?? if?(hasRefAttribute)?{
??? String?refName?=?ele.getAttribute(REF_ATTRIBUTE);
??? if?(!StringUtils.hasText(refName))?{
???? error(elementName?+?"?contains?empty?'ref'?attribute",?ele);
??? }
??????//?封裝為?RuntimeBeanReference?類型
??? RuntimeBeanReference?ref?=?new?RuntimeBeanReference(refName);
??? ref.setSource(extractSource(ele));
??? return?ref;
?? }
????//?value?屬性
?? else?if?(hasValueAttribute)?{
???? //?封裝為?TypedStringValue?類型
??? TypedStringValue?valueHolder?=?new?TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
??? valueHolder.setSource(extractSource(ele));
??? return?valueHolder;
?? }
????//?若還有子元素,繼續(xù)解析
?? else?if?(subElement?!=?null)?{
??????//?這里包含了?property?標簽的子標簽,例如?list、map、set?等
??? return?parsePropertySubElement(subElement,?bd);
?? }
?? else?{
??? error(elementName?+?"?must?specify?a?ref?or?value",?ele);
??? return?null;
?? }
? }
}
property 標簽的解析算是相對復雜的,其他標簽(meta、constructor-arg 等)的解析過程大體是類似的,不再一一分析。
經(jīng)過 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法的解析和封裝后,就得到了保存我們自定義 bean 信息的 BeanDefinition,即 GenericBeanDefinition。Spring 又把 BeanDefinition 和別名信息封裝成了 BeanDefinitionHolder:
public?class?BeanDefinitionParserDelegate?{
??public?BeanDefinitionHolder?parseBeanDefinitionElement(Element?ele,?@Nullable?BeanDefinition?containingBean)?{
?? String?id?=?ele.getAttribute(ID_ATTRIBUTE);
?? String?nameAttr?=?ele.getAttribute(NAME_ATTRIBUTE);
?? //?...
????//?解析后得到的?BeanDefinition
?? AbstractBeanDefinition?beanDefinition?=?parseBeanDefinitionElement(ele,?beanName,?containingBean);
?? if?(beanDefinition?!=?null)?{
??? //?...
??? String[]?aliasesArray?=?StringUtils.toStringArray(aliases);
??? return?new?BeanDefinitionHolder(beanDefinition,?beanName,?aliasesArray);
?? }
?? return?null;
? }
}
此外,在向 IoC 容器注冊之前,還有一個 decorateBeanDefinitionIfRequired 方法,它主要是用來處理默認名稱空間(即 http://www.springframework.org/schema/beans)之外的 bean 定義,比如
接下來就是將 BeanDefinition 注冊到 IoC 容器:
public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
//?...
????
? protected?void?processBeanDefinition(Element?ele,?BeanDefinitionParserDelegate?delegate)?{
?? //?解析后的?BeanDefinition?封裝成的?BeanDefinitionHolder
?? BeanDefinitionHolder?bdHolder?=?delegate.parseBeanDefinitionElement(ele);
?? if?(bdHolder?!=?null)?{
??? bdHolder?=?delegate.decorateBeanDefinitionIfRequired(ele,?bdHolder);
??? try?{
???? //?Register?the?final?decorated?instance.
????????//?注冊?BeanDefinition
???? 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));
?? }
?}
}
public?abstract?class?BeanDefinitionReaderUtils?{
??//?...
??public?static?void?registerBeanDefinition(
?? BeanDefinitionHolder?definitionHolder,?BeanDefinitionRegistry?registry)
??? throws?BeanDefinitionStoreException?{
?? //?Register?bean?definition?under?primary?name.
?? String?beanName?=?definitionHolder.getBeanName();
?? registry.registerBeanDefinition(beanName,?definitionHolder.getBeanDefinition());
?? //?Register?aliases?for?bean?name,?if?any.
?? String[]?aliases?=?definitionHolder.getAliases();
?? if?(aliases?!=?null)?{
??? for?(String?alias?:?aliases)?{
???? registry.registerAlias(beanName,?alias);
??? }
?? }
? }
}
IoC 容器是哪個?如何注冊呢?
前文提到過,Spring 默認的 IoC 容器是 DefaultListableBeanFactory,來看下它的繼承結(jié)構(gòu):

可以看到 DefaultListableBeanFactory 實現(xiàn)了 BeanDefinitionRegistry 接口。
所謂的“注冊”到 IoC 容器,其實就是把 BeanDefinition 保存到了 DefaultListableBeanFactory ?持有的一個 Map 中,如下:
public?class?DefaultListableBeanFactory?extends?AbstractAutowireCapableBeanFactory
??implements?ConfigurableListableBeanFactory,?BeanDefinitionRegistry,?Serializable?{
??//?...
??public?void?registerBeanDefinition(String?beanName,?BeanDefinition?beanDefinition)
??? throws?BeanDefinitionStoreException?{
?? Assert.hasText(beanName,?"Bean?name?must?not?be?empty");
?? Assert.notNull(beanDefinition,?"BeanDefinition?must?not?be?null");
?? if?(beanDefinition?instanceof?AbstractBeanDefinition)?{
??? try?{
???? ((AbstractBeanDefinition)?beanDefinition).validate();
??? }
??? catch?(BeanDefinitionValidationException?ex)?{
???? throw?new?BeanDefinitionStoreException(beanDefinition.getResourceDescription(),?beanName,
?????? "Validation?of?bean?definition?failed",?ex);
??? }
?? }
?? //?獲取已存在的?BeanDefinition
?? BeanDefinition?existingDefinition?=?this.beanDefinitionMap.get(beanName);
?? if?(existingDefinition?!=?null)?{
??? if?(!isAllowBeanDefinitionOverriding())?{
???? throw?new?BeanDefinitionOverrideException(beanName,?beanDefinition,?existingDefinition);
??? }
??????//?這幾個異常信息是不是有點眼熟?
??? else?if?(existingDefinition.getRole()????? //?e.g.?was?ROLE_APPLICATION,?now?overriding?with?ROLE_SUPPORT?or?ROLE_INFRASTRUCTURE
???? if?(logger.isInfoEnabled())?{
????? logger.info("Overriding?user-defined?bean?definition?for?bean?'"?+?beanName?+
??????? "'?with?a?framework-generated?bean?definition:?replacing?["?+
??????? existingDefinition?+?"]?with?["?+?beanDefinition?+?"]");
???? }
??? }
??? else?if?(!beanDefinition.equals(existingDefinition))?{
???? if?(logger.isDebugEnabled())?{
????? logger.debug("Overriding?bean?definition?for?bean?'"?+?beanName?+
??????? "'?with?a?different?definition:?replacing?["?+?existingDefinition?+
??????? "]?with?["?+?beanDefinition?+?"]");
???? }
??? }
??? else?{
???? if?(logger.isTraceEnabled())?{
????? logger.trace("Overriding?bean?definition?for?bean?'"?+?beanName?+
??????? "'?with?an?equivalent?definition:?replacing?["?+?existingDefinition?+
??????? "]?with?["?+?beanDefinition?+?"]");
???? }
??? }
??? this.beanDefinitionMap.put(beanName,?beanDefinition);
?? }
?? else?{
??? if?(hasBeanCreationStarted())?{
???? //?Cannot?modify?startup-time?collection?elements?anymore?(for?stable?iteration)
???? synchronized?(this.beanDefinitionMap)?{
????? this.beanDefinitionMap.put(beanName,?beanDefinition);
????? List?updatedDefinitions?=?new?ArrayList<>(this.beanDefinitionNames.size()?+?1);
????? updatedDefinitions.addAll(this.beanDefinitionNames);
????? updatedDefinitions.add(beanName);
????? this.beanDefinitionNames?=?updatedDefinitions;
????? removeManualSingletonName(beanName);
???? }
??? }
??? else?{
?????? //?注冊到?Map?中
???? //?Still?in?startup?registration?phase
???? this.beanDefinitionMap.put(beanName,?beanDefinition);
???? this.beanDefinitionNames.add(beanName);
???? removeManualSingletonName(beanName);
??? }
??? this.frozenBeanDefinitionNames?=?null;
?? }
if?(existingDefinition?!=?null?||?containsSingleton(beanName))?{
??? resetBeanDefinition(beanName);
?? }
?? else?if?(isConfigurationFrozen())?{
??? clearByTypeCache();
?? }
? }
}
上面幾個異常信息是不是有點眼熟?
這個 beanDefinitionMap 是個什么呢?它就是個 Map:
/**?Map?of?bean?definition?objects,?keyed?by?bean?name.?*/
private?final?Map?beanDefinitionMap?=?new?ConcurrentHashMap<>(256);
小結(jié)
到這里,Spring 已經(jīng)從我們定義的 application-ioc.xml 文件中讀取和解析到了
為了有個整體的把握,這里把主要流程梳理成了一個思維導圖:

其實前面幾篇文章主要是第一個步驟,也就是「初始化 BeanFactory,注冊 Bean 定義」,而且只是沿著一條主線走下來的,其它細節(jié)部分有興趣的小伙伴可以自行研究。
IoC 容器已經(jīng)建立,而且 BeanDefinition 也放進去了,如何從容器拿到我們想要的對象呢?
欲知后事如何,且聽下回分解~
咱也來體驗一下這個名片
