3.7 spring-property 子元素的使用与解析
1.0 Property子元素的使用
property 子元素是再常用不过的了, 在看Spring源码之前,我们先看看它的使用方法,
1. 实例类如下:
1 public class Animal { 2 3 public String type; 4 5 public Set<Integer> age; 6 7 private Map<String, Integer> sell; 8 9 public Animal() { 10 11 } 12 13 /** 14 * @return the type 15 */ 16 public String getType() { 17 return type; 18 } 19 20 /** 21 * @param type the type to set 22 */ 23 public void setType(String type) { 24 this.type = type; 25 } 26 27 /** 28 * @return the age 29 */ 30 public Set<Integer> getAge() { 31 return age; 32 } 33 34 /** 35 * @param age the age to set 36 */ 37 public void setAge(Set<Integer> age) { 38 this.age = age; 39 } 40 41 /** 42 * @return the sell 43 */ 44 public Map<String, Integer> getSell() { 45 return sell; 46 } 47 48 /** 49 * @param sell the sell to set 50 */ 51 public void setSell(Map<String, Integer> sell) { 52 this.sell = sell; 53 } 54 55 /* 56 * (non-Javadoc) 57 * 58 * @see java.lang.Object#toString() 59 */ 60 @Override 61 public String toString() { 62 return "Animal [type=" + type + ", age=" + age + ", sell=" + sell + "]"; 63 } 64 65 }
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.property.Animal"> <property name="type" value="cat"></property> <property name="age"> <set> <value>1</value> <value>2</value> <value>3</value> </set> </property> <property name="sell"> <map> <entry key="blue" value="111"></entry> <entry key="red" value="22"></entry> </map> </property> </bean> </beans>
测试类如下:
1 public class Main { 2 3 public static String XML_PATH = "test\\property\\applicationContxt.xml"; 4 5 public static void main(String[] args) { 6 try { 7 Resource resource = new ClassPathResource(XML_PATH); 8 XmlBeanFactory beanFactory = new XmlBeanFactory(resource); 9 Animal bean = (Animal) beanFactory.getBean("animal"); 10 System.out.println(bean); 11 } 12 catch (Exception e) { 13 e.printStackTrace(); 14 } 15 } 16 }
控制台输出的结果为
Animal [type=cat, age=[1, 2, 3], sell={blue=111, red=22}]
2.0 Spring具体的解析过程为:
2.1
1 /** 2 * Parse property sub-elements of the given bean element. 3 */ 4 public void parsePropertyElements(Element beanEle, BeanDefinition bd) { 5 NodeList nl = beanEle.getChildNodes(); 6 for (int i = 0; i < nl.getLength(); i++) { 7 Node node = nl.item(i); 8 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { 9 // 这里进去 10 parsePropertyElement((Element) node, bd); 11 } 12 } 13 }
2.2
1 /** 2 * Parse a property element. 3 */ 4 public void parsePropertyElement(Element ele, BeanDefinition bd) { 5 // 获取配置文件中name 的值 6 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); 7 if (!StringUtils.hasLength(propertyName)) { 8 error("Tag 'property' must have a 'name' attribute", ele); 9 return; 10 } 11 this.parseState.push(new PropertyEntry(propertyName)); 12 try { 13 // 不容许多次对同一属性配置 14 if (bd.getPropertyValues().contains(propertyName)) { 15 error("Multiple 'property' definitions for property '" + propertyName 16 + "'", ele); 17 return; 18 } 19 Object val = parsePropertyValue(ele, bd, propertyName); 20 PropertyValue pv = new PropertyValue(propertyName, val); 21 parseMetaElements(ele, pv); 22 pv.setSource(extractSource(ele)); 23 bd.getPropertyValues().addPropertyValue(pv); 24 } 25 finally { 26 this.parseState.pop(); 27 } 28 }
2.3 然后又回到parsePropertyValue 方法了
1 public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { 2 String elementName = (propertyName != null) ? "<property> element for property '" 3 + propertyName + "'" : "<constructor-arg> element"; 4 5 // Should only have one child element: ref, value, list, etc. 6 // 应该只有一个子元素:REF,值,列表等。 7 NodeList nl = ele.getChildNodes(); 8 Element subElement = null; 9 for (int i = 0; i < nl.getLength(); i++) { 10 Node node = nl.item(i); 11 // 对应的description 或者meta不处理 12 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) 13 && !nodeNameEquals(node, META_ELEMENT)) { 14 // Child element is what we're looking for. 15 if (subElement != null) { 16 error(elementName + " must not contain more than one sub-element", 17 ele); 18 } 19 else { 20 subElement = (Element) node; 21 } 22 } 23 } 24 // 解析 ref 25 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); 26 // 解析 value 27 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); 28 if ((hasRefAttribute && hasValueAttribute) 29 || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { 30 /* 31 * 1.不能同时有ref 又有 value 2.不能存在ref 或者 value 又有子元素 32 */ 33 error(elementName 34 + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", 35 ele); 36 } 37 38 if (hasRefAttribute) { 39 String refName = ele.getAttribute(REF_ATTRIBUTE); 40 if (!StringUtils.hasText(refName)) { 41 error(elementName + " contains empty 'ref' attribute", ele); 42 } 43 // ref 属性的处理 , 使用RuntimeBeanReference封装对应的ref名称 44 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 45 ref.setSource(extractSource(ele)); 46 return ref; 47 } 48 else if (hasValueAttribute) { 49 // Value 属性的处理 , 使用TypedStringValue封装对应的 50 TypedStringValue valueHolder = new TypedStringValue( 51 ele.getAttribute(VALUE_ATTRIBUTE)); 52 valueHolder.setSource(extractSource(ele)); 53 return valueHolder; 54 } 55 else if (subElement != null) { 56 // 解析子元素 57 return parsePropertySubElement(subElement, bd); 58 } 59 else { 60 // Neither child element nor "ref" or "value" attribute found. 61 // 对于没有ref 也没有子元素的,Spring只好丢出异常 62 error(elementName + " must specify a ref or value", ele); 63 return null; 64 } 65 }
这里之前构造函数的解析那里已经讲得很详细了,这里不多做解释,不同的是返回值使用PropertyValue 封装,并且记录在BeanDefinition 的 propertyValues属性当中.