分享

Spring默认标签的解析

 python_lover 2022-12-04 发布于北京

上一篇文章提到过Spring中的标签包括默认标签自定义标签,而两种标签的用法以及解析方式存在着很大的不同,先说说默认标签的解析。
默认标签的解析是在parseDefaultElement函数中进行的,分别对importaliasbeanbeans做了不同的处理。

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);
    }
}

1 bean标签的解析和注册

进入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));
    }
}

大致逻辑如下:

  1. 委托BeanDefinitionParserDelegateparseBeanDefinitionElement方法进行元素解析,返回bhHolder。这个bdHolder实例已经包含我们配置文件中配置的各种属性了,例如classnameidalias等。
  2. 当返回的bdHolder不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
  3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtilsregisterBeanDefinition方法。
  4. 最后发出响应事件,通知相关的监听器,这个bean就加载完成了。

1.1 解析BeanDefinition

我们进入到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);
		}

    // 对标签其他属性进行解析,如class、parent等
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
          // 如果不存在beanName那么根据Spring提供的命名规则为当前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);
      // 封装进BeanDefinitionHolder中
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}

parseBeanDefinitionElement的主要工作包括如下内容:

  1. 提取元素中的id以及name属性。
  2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。
  3. 如果检测到bean没有指定beanName,就用默认规则为此bean生成beanName
  4. 将获取到的信息封装到BeanDefinitionHolder的实例中。

进一步查看步骤2中对标签其他属性的解析过程。

@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 {
      // 创建用于承载属性的AbstractBeanDefinition
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

      // 解析默认bean的各种属性
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

      // 解析元数据
			parseMetaElements(ele, bd);
      // 解析lookup-method属性
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // 解析replaced-method属性
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

      // 解析构造函数参数
			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;
	}

1.1.1 解析子元素constructor-arg

举个Spring构造函数配置的例子:

<bean id="helloBean" class="com.HelloBean">
  <constructor-arg index="0">
    <value>mutianjie</value>
  </constructor-arg>
  <constructor-arg index="1">
  	<value>你好</value>
  </constructor-arg>
</bean>

上面的配置实现的功能是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。对于constructor-arg子元素的解析,Spring是通过parseConstructorArgElements函数来实现的,具体代码如下:

/**
	 * 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);
			}
		}
	}

在循环中,parseConstructorArgElements函数遍历了所有的<constructor-arg>标签,提取所有的子元素。具体的解析放在了另一个函数parseConstructorArgElement中,该函数的具体代码如下:

	/**
	 * 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对应的属性元素
						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)) {
              // 不允许重复指定相同的参数
							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();
			}
		}
	}

根据以上代码总结出来的流程为:

如果配置中指定了index属性,那么操作步骤如下:

  1. 使用parsePropertyValue方法解析constructor-arg的子元素
  2. 使用ConstructArgumentValues.ValueHolder类型来封装解析出来的元素
  3. typenameindex属性一并封装在ConstructArgumentValues.ValueHolder中,并添加至当前BeanDefinitionconstructorArgumentValuesindexedArgumentValue中。

如果配置中未指定index属性,前两步操作与指定index属性的做法相同,但是第三步中,valueHolder会添加至当前BeanDefinitionconstructorArgumentValuesgenericArgumentValue中。

在构造函数解析中,解析constructor-arg的子元素使用的是parsePropertyValue方法,它是用来解析某一个具体的构造函数参数的。这个方法的代码如下:

@Nullable
	public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
		String elementName = (propertyName != null ?
				"<property> element for property '" + propertyName + "'" :
				"<constructor-arg> element");

		// 一个属性只能对应一种类型,如 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封装对应的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;
		}
	}

值得注意的是,除了refvalue两种子元素之外,还有嵌套子元素如maplistarray等。这类嵌套子元素的处理在parsePropertySubElement方法中完成,对嵌套子元素进行分类处理。

1.1.2 解析子元素property

parsePropertyElement函数完成了对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>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多