上一篇文章提到過Spring中的標(biāo)簽包括默認(rèn)標(biāo)簽和自定義標(biāo)簽,而兩種標(biāo)簽的用法以及解析方式存在著很大的不同,先說說默認(rèn)標(biāo)簽的解析。
默認(rèn)標(biāo)簽的解析是在parseDefaultElement
函數(shù)中進(jìn)行的,分別對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);
}
}
進(jìn)入processBeanDefinition(ele, delegate)
方法:
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));
}
}
大致邏輯如下:
BeanDefinitionParserDelegate
的parseBeanDefinitionElement
方法進(jìn)行元素解析,返回bhHolder
。這個(gè)bdHolder
實(shí)例已經(jīng)包含我們配置文件中配置的各種屬性了,例如class
、name
、id
、alias
等。bdHolder
不為空的情況下,若存在默認(rèn)標(biāo)簽的子節(jié)點(diǎn)下再有自定義屬性,還需要再次對自定義標(biāo)簽進(jìn)行解析。bdHolder
進(jìn)行注冊,同樣,注冊操作委托給了BeanDefinitionReaderUtils
的registerBeanDefinition
方法。bean
就加載完成了。我們進(jìn)入到BeanDefinitionDelegate
類的parseBeanDefinitionElement
方法。
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} 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) {
// 解析id屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
// name屬性也是alias的一種
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);
}
// 對標(biāo)簽其他屬性進(jìn)行解析,如class、parent等
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果不存在beanName那么根據(jù)Spring提供的命名規(guī)則為當(dāng)前bean生成
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);
// 封裝進(jìn)BeanDefinitionHolder中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
parseBeanDefinitionElement
的主要工作包括如下內(nèi)容:
id
以及name
屬性。GenericBeanDefinition
類型的實(shí)例中。bean
沒有指定beanName
,就用默認(rèn)規(guī)則為此bean
生成beanName
BeanDefinitionHolder
的實(shí)例中。進(jìn)一步查看步驟2中對標(biāo)簽其他屬性的解析過程。
@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)) {
// 解析class屬性
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
// 解析parent屬性
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 創(chuàng)建用于承載屬性的AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析默認(rèn)bean的各種屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元數(shù)據(jù)
parseMetaElements(ele, bd);
// 解析lookup-method屬性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method屬性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析構(gòu)造函數(shù)參數(shù)
parseConstructorArgElements(ele, bd);
// 解析property子元素
parsePropertyElements(ele, bd);
// 解析qualifier子元素
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;
}
舉個(gè)Spring構(gòu)造函數(shù)配置的例子:
<bean id="helloBean" class="com.HelloBean">
<constructor-arg index="0">
<value>mutianjie</value>
</constructor-arg>
<constructor-arg index="1">
<value>你好</value>
</constructor-arg>
</bean>
上面的配置實(shí)現(xiàn)的功能是對HelloBean自動尋找對應(yīng)的構(gòu)造函數(shù),并在初始化的時(shí)候?qū)⒃O(shè)置的參數(shù)傳入進(jìn)去。對于constructor-arg
子元素的解析,Spring是通過parseConstructorArgElements
函數(shù)來實(shí)現(xiàn)的,具體代碼如下:
/**
* Parse constructor-arg sub-elements of the given bean element.
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
parseConstructorArgElement((Element) node, bd);
}
}
}
在循環(huán)中,parseConstructorArgElements
函數(shù)遍歷了所有的<constructor-arg>
標(biāo)簽,提取所有的子元素。具體的解析放在了另一個(gè)函數(shù)parseConstructorArgElement
中,該函數(shù)的具體代碼如下:
/**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取index屬性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取type屬性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 解析ele對應(yīng)的屬性元素
Object value = parsePropertyValue(ele, bd, null);
// 封裝解析出來的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
// 不允許重復(fù)指定相同的參數(shù)
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
// 沒有index屬性則自動尋找
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
根據(jù)以上代碼總結(jié)出來的流程為:
如果配置中指定了index
屬性,那么操作步驟如下:
parsePropertyValue
方法解析constructor-arg
的子元素ConstructArgumentValues.ValueHolder
類型來封裝解析出來的元素type
、name
和index
屬性一并封裝在ConstructArgumentValues.ValueHolder
中,并添加至當(dāng)前BeanDefinition
的constructorArgumentValues
的indexedArgumentValue
中。如果配置中未指定index
屬性,前兩步操作與指定index
屬性的做法相同,但是第三步中,valueHolder
會添加至當(dāng)前BeanDefinition
的constructorArgumentValues
的genericArgumentValue
中。
在構(gòu)造函數(shù)解析中,解析constructor-arg
的子元素使用的是parsePropertyValue
方法,它是用來解析某一個(gè)具體的構(gòu)造函數(shù)參數(shù)的。這個(gè)方法的代碼如下:
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 一個(gè)屬性只能對應(yīng)一種類型,如 ref, value, list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); 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;
}
}
}
// 解析constructor-arg的ref屬性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 解析constructor-arg的value屬性
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);
}
if (hasRefAttribute) {
// ref屬性的處理,使用RuntimeBeanReference封裝對應(yīng)的ref名稱
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
// value屬性的處理,使用TypedStringValue封裝
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
// map、list等子元素的處理,如
/**
* <constructor-arg>
* <map>
* <entry key="key" value="value"/>
* </map>
* </constructor-arg>
*/
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
值得注意的是,除了ref
和value
兩種子元素之外,還有嵌套子元素如map
、list
、array
等。這類嵌套子元素的處理在parsePropertySubElement
方法中完成,對嵌套子元素進(jìn)行分類處理。
parsePropertyElement
函數(shù)完成了對property
屬性的提取。property
使用方式如下:
<bean id="test" class="test.TestClass">
<property name="testStr" value="aaa"/>
</bean>
<!--或者-->
<bean id="a">
<property name="p">
<list>
<value>aa</value>
<value>bb</value>
</list>
</property>
</bean>