上篇部落格( 【死磕 Spring】—– IOC 之解析 bean 标簽:meta、lookup-method、replace-method
)分析了 meta 、 lookup-method、replace-method 三個子元素,這篇部落格分析 constructor-arg 、property、qualifier 三個子元素。
constructor-arg 子元素
舉個小栗子:
-
public class StudentService {
-
private String name;
-
private Integer age;
-
private BookService bookService;
-
StudentService(String name, Integer age, BookService bookService){
-
this.name = name;
-
this.age = age;
-
this.bookService = bookService;
-
}
-
}
-
<bean id="bookService" class="org.springframework.core.service.BookService"/>
-
<bean id="studentService" class="org.springframework.core.service.StudentService">
-
<constructor-arg index="0" value="chenssy"/>
-
<constructor-arg name="age" value="100"/>
-
<constructor-arg name="bookService" ref="bookService"/>
-
</bean>
StudentService 定義一個構造函數,配置檔案中使用 constructor-arg 元素對其配置,該元素可以實作對 StudentService 自動尋找對應的構造函數,并在初始化的時候将值當做參數進行設定。
parseConstructorArgElements()
方法完成 constructor-arg 子元素的解析。
-
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);
-
}
-
}
-
}
周遊所有子元素,如果為 constructor-arg 則調用
parseConstructorArgElement()
進行解析。
-
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
-
// 提取 index、type、name 屬性值
-
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
-
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
-
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
-
// 如果有index
-
if (StringUtils.hasLength(indexAttr)) {
-
try {
-
int index = Integer.parseInt(indexAttr);
-
if (index < 0) {
-
error("'index' cannot be lower than 0", ele);
-
}
-
else {
-
try {
-
// 構造一個 ConstructorArgumentEntry 并将其加入到 ParseState 中
-
this.parseState.push(new ConstructorArgumentEntry(index));
-
// 解析 ele 對應屬性元素
-
Object value = parsePropertyValue(ele, bd, null);
-
// 根據解析的屬性元素構造一個 valueHolder 對象
-
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 {
-
// 加入到 indexedArgumentValues 中國
-
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 {
-
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、type、name 三個屬性值,然後根據是否存在 index 來區分。其實兩者邏輯都差不多,總共分為如下幾個步驟(以有 index 為例):
- 構造 ConstructorArgumentEntry 對象并将其加入到 ParseState 隊列中。ConstructorArgumentEntry 表示構造函數的參數。
- 調用
解析 constructor-arg 子元素,傳回結果值parsePropertyValue()
- 根據解析的結果值構造
執行個體對象ConstructorArgumentValues.ValueHolder
- 将 type、name 封裝到
中,然後将 ValueHolder 執行個體對象添加到 indexedArgumentValues 中。ConstructorArgumentValues.ValueHolder
無 index 的處理邏輯差不多,隻有幾點不同:構造 ConstructorArgumentEntry 對象時是調用無參構造函數;最後是将 ValueHolder 執行個體添加到 genericArgumentValues 中。
parsePropertyValue()
對子元素進一步解析。
-
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
-
String elementName = (propertyName != null) ?
-
"<property> element for property '" + propertyName + "'" :
-
"<constructor-arg> element";
-
NodeList nl = ele.getChildNodes();
-
Element subElement = null;
-
for (int i = 0; i < nl.getLength(); i++) {
-
Node node = nl.item(i);
-
// meta 、description 不處理
-
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);
-
// constructor-arg 子元素有兩種情況不存在
-
// 1. 即存在 ref 又存在 value
-
// 2. 存在 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);
-
}
-
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);
-
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.
-
error(elementName + " must specify a ref or value", ele);
-
return null;
-
}
-
}
- 提取 constructor-arg 子元素的 ref 和 value 的屬性值,對其進行判斷,以下兩種情況是不允許存在的
- ref 和 value 屬性同時存在
- 存在 ref 或者 value 且又有子元素
- 若存在 ref 屬性,則擷取其值并将其封裝進 RuntimeBeanReference 執行個體對象中
- 若存在 value 屬性,則擷取其值并将其封裝進 TypedStringValue 執行個體對象中
- 如果子元素不為空,則調用
進行子元素進一步處理parsePropertySubElement()
對于 constructor-arg 子元素的嵌套子元素,需要調用
parsePropertySubElement()
進一步處理。
-
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
-
return parsePropertySubElement(ele, bd, null);
-
}
-
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable 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 a parent context.
-
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
-
toParent = true;
-
if (!StringUtils.hasLength(refName)) {
-
error("'bean' 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;
-
}
-
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
-
return parseIdRefElement(ele);
-
}
-
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
-
return parseValueElement(ele, defaultValueType);
-
}
-
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;
-
}
-
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
-
return parseArrayElement(ele, bd);
-
}
-
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
-
return parseListElement(ele, bd);
-
}
-
else if (nodeNameEquals(ele, SET_ELEMENT)) {
-
return parseSetElement(ele, bd);
-
}
-
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
-
return parseMapElement(ele, bd);
-
}
-
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
-
return parsePropsElement(ele);
-
}
-
else {
-
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
-
return null;
-
}
-
}
上面對各個子類進行分類處理,詳細情況如果各位有興趣可以移步源碼進行深一步的探究。
property 子元素
我們一般使用如下方式來使用 property 子元素。
-
<bean id="studentService" class="org.springframework.core.service.StudentService">
-
<property name="name" value="chenssy"/>
-
<property name="age" value="18"/>
-
</bean>
對于 property 子元素的解析,Spring 調用
parsePropertyElements()
。如下:
-
public void parsePropertyElements(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, PROPERTY_ELEMENT)) {
-
parsePropertyElement((Element) node, bd);
-
}
-
}
-
}
和 constructor-arg 子元素差不多,同樣是提取所有的 property 的子元素,然後調用
parsePropertyElement()
進行分析。
-
public void parsePropertyElement(Element ele, BeanDefinition bd) {
-
// 擷取 name 屬性
-
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
-
if (!StringUtils.hasLength(propertyName)) {
-
error("Tag 'property' must have a 'name' attribute", ele);
-
return;
-
}
-
this.parseState.push(new PropertyEntry(propertyName));
-
try {
-
// 如果存在相同的 name
-
if (bd.getPropertyValues().contains(propertyName)) {
-
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
-
return;
-
}
-
// 解析屬性值
-
Object val = parsePropertyValue(ele, bd, propertyName);
-
// 根據解析的屬性值構造 PropertyValue 執行個體對象
-
PropertyValue pv = new PropertyValue(propertyName, val);
-
parseMetaElements(ele, pv);
-
pv.setSource(extractSource(ele));
-
// 添加到 MutablePropertyValues 中
-
bd.getPropertyValues().addPropertyValue(pv);
-
}
-
finally {
-
this.parseState.pop();
-
}
-
}
與解析 constructor-arg 子元素步驟差不多。調用
parsePropertyValue()
解析子元素屬性值,然後根據該值構造 PropertyValue 執行個體對象并将其添加到 BeanDefinition 中的 MutablePropertyValues 中。
原文釋出時間為:2018-09-26
本文作者:Java技術驿站
本文來自雲栖社群合作夥伴“
Java技術驿站”,了解相關資訊可以關注“
”。