spring容器标签解析之property
2019-06-19 本文已影响0人
会上树的程序猿
上节我们了解了spring对constructor-arg字标签的解析过程,可能在实际的开发中这种使用的场景很少,但至少我们知道了是如何处理的过程,接下来我们来了解下spring对property字标签的解析过程,首先我们来看这段代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.sgcc.bean.Student">
<property name="name" value="无敌"/>
<property name="age" value="20"/>
</bean>
</beans>
这段代码很简单,其中我们定义的Student的属性在配置文件中是用<property>来包裹,这里不多说了,直接来看解析的过程:
/**
* Parse property sub-elements of the given bean element.
*/
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取所有的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//元素是property的
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
//真正的解析方法
parsePropertyElement((Element) node, bd);
}
}
}
/**
* 解析property的过程
* @param ele property元素
* @param bd
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取name属性
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
//name的属性为null时,报以下错误
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
//1.通过获取到的name属性构建PropertyEntry实例对象
//1.1.调用ParseState#push方法将实例保存
this.parseState.push(new PropertyEntry(propertyName));
try {
//不允许对同一个property的属性进行多次操作
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析value属性
Object val = parsePropertyValue(ele, bd, propertyName);
//通过解析之后的value的属性值去构建PropertyValue实例
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
//封装PropertyValue属性
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
//删除PropertyEntry实例
this.parseState.pop();
}
}
上述是spring解析property的过程,简单的分析总结下每一步的过程:
- 首先是获取所有的所有的子元素
NodeList nl = beanEle.getChildNodes();
- 遍历获取property元素
- 调用parsePropertyElement方法进行真正的解析过程
- 首先获取name属性
public static final String NAME_ATTRIBUTE = "name";
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
- 通过name属性构建PropertyEntry实体对象,接着调用ParseState#push保存该实例
PropertyEntry.java
private final String name;
/**
* Creates a new instance of the {@link PropertyEntry} class.
* @param name the name of the JavaBean property represented by this instance
* @throws IllegalArgumentException if the supplied {@code name} is {@code null}
* or consists wholly of whitespace
*/
public PropertyEntry(String name) {
if (!StringUtils.hasText(name)) {
throw new IllegalArgumentException("Invalid property name '" + name + "'.");
}
this.name = name;
}
第 2步:
this.parseState.push(new PropertyEntry(propertyName));
ParserState.java
/**
* Add a new {@link Entry} to the {@link LinkedList}.
*/
public void push(Entry entry) {
this.state.push(entry);
}
- 解析value属性
/**
* Get the value of a property element. May be a list etc.
* Also used for constructor arguments, "propertyName" being null in this case.
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
//一个属性只能对应一种类型:ref,value list.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果node是description和meta元素不做处理
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属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//判断是否有value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//如果同时有ref和value属性或者是有ref属性或value属性,且又有子元素,那么就报错
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) {
//拿到ref属性
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//通过ref属性来构建一个RuntimeBeanReference实例对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//属性封装source
ref.setSource(extractSource(ele));
return ref;
}
//有value属性
else if (hasValueAttribute) {
//通过获取到的value属性来构建一个TypedStringValue实例对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//source属性的封装
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//有子元素
else if (subElement != null) {
//解析子元素
return parsePropertySubElement(subElement, bd);
}
//既没有ref or value or 子元素
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
- 通过解析之后的value的属性值去构建PropertyValue实例
PropertyValue pv = new PropertyValue(propertyName, val);
- 封装PropertyValue属性
pv.setSource(extractSource(ele));
MutablePropertyValues.java
private final List<PropertyValue> propertyValueList;
第2步:
/**
* Add a PropertyValue object, replacing any existing one for the
* corresponding property or getting merged with it (if applicable).
* @param pv the PropertyValue object to add
* @return this in order to allow for adding multiple property values in a chain
*/
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
for (int i = 0; i < this.propertyValueList.size(); i++) {
PropertyValue currentPv = this.propertyValueList.get(i);
//如果相等,合并
if (currentPv.getName().equals(pv.getName())) {
pv = mergeIfRequired(pv, currentPv);
//覆盖当前的值
setPropertyValueAt(pv, i);
return this;
}
}
//将新的值保存
this.propertyValueList.add(pv);
return this;
}
第3步:合并过程
/**
* Merges the value of the supplied 'new' {@link PropertyValue} with that of
* the current {@link PropertyValue} if merging is supported and enabled.
* @see Mergeable
*/
private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
//获取value属性的值
Object value = newPv.getValue();
if (value instanceof Mergeable) {
Mergeable mergeable = (Mergeable) value;
//判断value是否启动了合并功能,是的话返回true反之false
if (mergeable.isMergeEnabled()) {
//合并
Object merged = mergeable.merge(currentPv.getValue());
//利用合并后的merged来构建PropertyValue实例对象
return new PropertyValue(newPv.getName(), merged);
}
}
return newPv;
}
第4步:Mergeable接口
public interface Mergeable {
/**是否为当前实例开启合并*/
boolean isMergeEnabled();
/**
* Merge the current value set with that of the supplied object.
* <p>The supplied object is considered the parent, and values in
* the callee's value set must override those of the supplied object.
* @param parent the object to merge with
* @return the result of the merge operation
* @throws IllegalArgumentException if the supplied parent is {@code null}
* @throws IllegalStateException if merging is not enabled for this instance
* (i.e. {@code mergeEnabled} equals {@code false}).
*/
Object merge(@Nullable Object parent);
- 删除PropertyEntry实例
''''''
ParserState.java
/**
* Internal {@link LinkedList} storage.
*/
private final LinkedList<Entry> state;
第1步:调用ParserState#pop方法
this.parseState.pop();
/**
* Remove an {@link Entry} from the {@link LinkedList}.
*/
public void pop() {
this.state.pop();
}
上述就是整个property标签解析的过程