3.6 spring-construction-arg 子元素的使用与解析
对于构造函数子元素是非常常用的. 相信大家也一定不陌生,
举个小例子:
public class Animal {
public String type;
public int age;
/**
* @param type
* @param age
*/
public Animal(String type, int age) {
super();
this.type = type;
this.age = age;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Animal [type=" + type + ", age=" + age + "]";
}
}
xml如下:
<?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-2.5.xsd"> <bean id="animal" class="test.constructor.Animal">
<constructor-arg index="0" value="cat" type="String"></constructor-arg>
<constructor-arg value="100" ></constructor-arg>
</bean>
</beans>
测试类如下:
public class Main {
public static String XML_PATH = "test\\constructor\\applicationContxt.xml";
public static void main(String[] args) {
try {
Resource resource = new ClassPathResource(XML_PATH);
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
Animal bean = (Animal) beanFactory.getBean("animal");
System.out.println(bean);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
对于construction-arg 子元素的解析,Spring 是通过 BeanDefinitionParserDelegate. parseConstructorArgElements(Element beanEle, BeanDefinition bd); 方法来实现的,
具体代码如下,
/**
* 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
parseConstructorArgElement((Element) node, bd);
}
}
}
追踪下去如下:
/**
* 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 {
// 对于有Index 的参数放这里
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));
// 对于没Index参数的放这里
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
上面一段代码虽然长,但涉及的逻辑其实并不复杂.
1.首先是提取 constructor-arg 上的必要属性,(index,name,type);
如果是指定了index属性的
1,解析constructor-arg 的子元素
2,使用 ConstructorArgumentValues.ValueHolder来封装解析出来的数据,
3,将 index,type,name 一起封装在当前beandefinition 的 IndexedArgumentValues 属性当中
如果没有指定index属性的
1,解析constructor-arg 的子元素
2,使用 ConstructorArgumentValues.ValueHolder来封装解析出来的数据,
3,将 index,type,name 一起封装在当前beandefinition 的 GenericArgumentValues 属性当中
这里可以看对于是否指定index 参数的,Spring的处理流程是不同的,关键在于保存的位置不同
那么了解这个过程之后,我们尝试进一步了解解析构造函数配置中解析子元素的过程,进入 parsePropertyValue
代码如下:
/**
* Get the value of a property element. May be a list etc. Also used for constructor
* arguments, "propertyName" being null in this case.
*/
/**
* @param ele
* @param bd
* @param propertyName
* @return
*/
public Object parsePropertyValue(Element ele, BeanDefinition bd, 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,值,列表等。
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 对应的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);
if ((hasRefAttribute && hasValueAttribute)
|| ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
/*
* 1.不能同时有ref 又有 value 2.不能存在ref 或者 value 又有子元素
*/
error(elementName
+ " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element",
ele);
} if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
// ref 属性的处理 , 使用RuntimeBeanReference封装对应的ref名称
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) {
// 解析子元素
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
// 对于没有ref 也没有子元素的,Spring只好丢出异常
error(elementName + " must specify a ref or value", ele);
return null;
}
}
从上面的代码上看,对构造函数中属性的解析,经历了以下几个过程:
1. 略过description 和 meta
2. 提取 ref ,value 属性,并验证其合法性,
3. ref 属性的处理,使用 RuntimeBeanReference 封装
4. Value 属性的处理 , 使用TypedStringValue封装
5. 子属性的处理 如
<constructor-arg>
<map>
<entry key = "key" value = "value"></entry>
</map>
</constructor-arg>
而对于子元素,则交给 parsePropertySubElement 方法来实现对各种子元素进行分类处理
代码如下:
public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
继续如下:
/**
* Parse a value, ref or collection sub-element of a property or constructor-arg
* element.
*
* @param ele subelement of property element; we don't know which yet
* @param defaultValueType the default type (class name) for any {@code <value>}
* tag that might be created
*/
public Object parsePropertySubElement(Element ele, BeanDefinition bd,
String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in the same XML file.
// 解析本地资源
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
// 解析父类资源
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element",
ele);
return null;
}
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
// 对idref 的处理
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
// 对value 的处理
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
// 对 null 的处理
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
// 对array 的处理
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
// 对list 的处理
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
// 对set 的处理
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
// 对map 的处理
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
// 对props 的处理
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
可以看到,上面方法中实现了所有可支持的子类的分类处理,到这里,我们已经大致清楚构造函数的处理流程了,至于在深入的解析,这里不做解析.
3.6 spring-construction-arg 子元素的使用与解析的更多相关文章
- 3.5 spring-replaced-method 子元素的使用与解析
1.replaced-method 子元素 方法替换: 可以在运行时用新的方法替换现有的方法,与之前的 look-up不同的是replace-method 不但可以动态地替换返回的实体bean,而且可 ...
- 3.4 spring- lookup-method 子元素的使用与解析
1. lookup-method的应用: 1.1 子元素lookup-method 似乎不是很常用,但是在某些时候他的确是非常有用的属性,通常我们称它为 "获取器注入" . 引用 ...
- 3.8 spring-qualifier 子元素的使用与解析
对于 qualifier 子元素,我们接触的更多的是注解形式,在使用Spring 自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个.当找不到一个匹配的 Bean 时, S ...
- 3.7 spring-property 子元素的使用与解析
1.0 Property子元素的使用 property 子元素是再常用不过的了, 在看Spring源码之前,我们先看看它的使用方法, 1. 实例类如下: public class Animal { p ...
- 3.3 spring-meta子元素的使用与解析
1. meta元素的使用 在解析元数据的分析之前,我们先回顾一下 meta属性的使用: <bean id="car" class="test.CarFactoryB ...
- spring源码学习之默认标签的解析(一)
继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...
- spring bean属性及子元素使用总结
spring bean属性及子元素使用总结 2016-08-03 00:00 97人阅读 评论(0) 收藏 举报 分类: Spring&SpringMVC(17) 版权声明:本文为博主原创 ...
- JAVA读取XML文件并解析获取元素、属性值、子元素信息
JAVA读取XML文件并解析获取元素.属性值.子元素信息 关键字 XML读取 InputStream DocumentBuilderFactory Element Node 前言 最 ...
- spring xml配置文件根元素(文件头文件)说明
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
随机推荐
- 关于css雪碧图sprite
天气转凉了,又开始贪恋暖暖的被窝了. 早上不想起床的时候在被窝里看了有关于雪碧图的视频. 1.使用场景 V导航条,登录框img标签多次载入,性能低 X大图banner按需加载,如果做成雪碧图一次加载就 ...
- css3 边框记
css3 边框 border属性在css1中就已经定义了,使用它可以设置元素的边框风格,边框颜色以及边框粗细. border-width:设置元素边框的粗细. border-color:设置元素边框的 ...
- HTML5와 CSS3 적용기
HTML5의 DTD 선언 <!DOCTYPE html> HTML5의 인코딩 선언 <meta charset="utf-8"> 그리고나서는 새로 ...
- es6模板字符串 问题记录
自古无图无真相,望各位博主在条件允许的情况下,配张图片吧! 界面是用join拼接的,当循环td的时候会产生一个空串,界面就会出现一个逗号, 虽然功能块算实现了,不过始终美中不足,然后想到的办法是替换所 ...
- sql中在查询时两列合并为一列
select a + ' '+b as ab from tableName 如果是有整型的(例,b字段为int类型): select a +' '+Convert(VARCHAR(50),b) a ...
- opencv for android sample导入有误
我们下载好opencv for android 后导入eclipse的时候发现人脸检测还有一个sample项目会有小叉,但是好像没有文件有问题.这时我们该怎么办呢? 在window中: 我们右键选择p ...
- 牛客_Java_值传递(拷贝)不该表原来变量+传引用的话会一起改变
总结: 许多编程语言都有2种方法将参数传递给方法------按值传递和按引用传递. 与其他语言不同,Java不允许程序员选择按值传递还是按引用传递各个参数,基本类型(byte--short--int ...
- Android Timer用法
Android考虑到线程安全问题,不允许在线程中执行UI线程,在Android中,有一个类:android.os.Handler,这个可以实现各处线程间的消息传递.先看段代码,这个实例化了一个Hand ...
- 微软 Visual Studio 14 CTP2 发布
对于在微软阵营下进行工作的团队来说,拥有最新版本的 Visual Studio 是提高效率最佳的选择,没有之一. 在本文中,我们就上个月发布的 Visual Studio "14" ...
- 基于MRG_MyISAM引擎的Mysql分表
正常情况下的分表,都是直接创建多个相同结构的表,比如table_1.table_2...最近碰到一个特殊需求,需要创建一个主表,所有分表的数据增删改查,全部自动实时更新到主表,这个时候可以使用MRG_ ...