概述
在使用ResourceLoader建立Resource對象一節中,我們探讨了Spring如何正确的查找我們指定的配置檔案并為配置檔案生成Resource對象。
在使用DocumentLoader建立Document對象一節中,我們又已經解析了Spring通過xerces如何把Resource對象中的XML内容轉換成Document對象。
在淺析Spring4使用XmlBeanDefinitionReader解析xml配置一文中,我們知道XmlBeanDefinitionReader使用BeanDefinitionDocumentReader接口的預設實作DefaultBeanDefinitionDocumentReader把Document對象中包含的配置資訊轉換成BeanDefinition對象并把它注冊到BeanDefintionRegistry對象中。在DefaultBeanDefinitionDocumentReader的實作中,它的責任是周遊xml根節點下的子節點,并把處理bean标簽和自定義命名空間的标簽(比如aop:,context:,p:等)的細節委托給BeanDefinitionParserDelegate對象,BeanDefinitionParserDelegate才是真正解析配置檔案的地方。
下面我們就開始探讨BeanDefinitionParserDelegate解析bean标簽的過程。
約定:為了提高本文的可讀性,我把spring源碼中定義的常量替換為對應的字面值。
(1)建立BeanDefinitionParserDelegate 對象
在DefaultBeanDefinitionDocumentReader中,調用createDelegate方法根據beans标簽來建立和初始化BeanDefinitionParserDelegate 對象,見下面createDelegate方法的源碼。
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
// 建立delegate對象
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
// 初始化delegate對象
delegate.initDefaults(root, parentDelegate);
return delegate;
}
createDelegate方法中的root參數為xml配置中<beans>标簽,它首先執行個體化一個BeanDefinitionParserDelegate對象,然後調用這個對象的initDefaults方法來初始化預設值,這個方法的代碼如下。
public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
// 計算預設值
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
this.readerContext.fireDefaultsRegistered(this.defaults);
}
protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) {
String lazyInit = root.getAttribute("default-lazy-init");
if ("default".equals(lazyInit)) {
// 繼承外層<beans>标簽的屬性值,否則為false字元串
lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : "false");
}
defaults.setLazyInit(lazyInit);
String merge = root.getAttribute("default-merge");
if ("default".equals(merge)) {
// 繼承外層<beans>标簽的屬性值,否則為false字元串
merge = (parentDefaults != null ? parentDefaults.getMerge() : "false");
}
defaults.setMerge(merge);
String autowire = root.getAttribute("default-autowire");
if ("default".equals(autowire)) {
// 繼承外層<beans>标簽的屬性值,否則為no字元串
autowire = (parentDefaults != null ? parentDefaults.getAutowire() : "no");
}
defaults.setAutowire(autowire);
defaults.setDependencyCheck(root.getAttribute("default-dependency-check"));
if (root.hasAttribute("default-autowire-candidates")) {
defaults.setAutowireCandidates(root.getAttribute("default-autowire-candidates"));
} else if (parentDefaults != null) {
// 繼承外層<beans>标簽的屬性值
defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
}
if (root.hasAttribute("default-init-method")) {
defaults.setInitMethod(root.getAttribute("default-init-method"));
} else if (parentDefaults != null) {
// 繼承外層<beans>标簽的屬性值
defaults.setInitMethod(parentDefaults.getInitMethod());
}
if (root.hasAttribute("default-destroy-method")) {
defaults.setDestroyMethod(root.getAttribute("default-destroy-method"));
} else if (parentDefaults != null) {
// 繼承外層<beans>标簽的屬性值
defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
}
defaults.setSource(this.readerContext.extractSource(root));
}
populateDefaults方法擷取<beans>标簽的屬性值,如果beans标簽上沒有指定屬性值并且外層還有beans标簽時,将沿用外層beans标簽的。
(2)使用BeanDefinitionParserDelegate 對象處理<bean>标簽
我們從DefaultBeanDefinitionDocumentReader使用BeanDefinitionParserDelegate解析<bean>标簽的方法processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)開始,代碼如下。
/**
* 解析bean節點,并注冊BeanDefinition對象
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 建立BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 裝飾BeanDefinition
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注冊已經建立好的BeanDefintion
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 發送BeanDefinition注冊事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
processBeanDefinition方法的ele參數為<bean>标簽,這段代碼分成三步。第一步,根據傳入的Element對象(bean标簽的)調用代理對象的parseBeanDefinitionElement(Element ele)方法建立BeanDefinitionHolder 對象,這個對象持有建立好的BeanDefinition對象、bean的id和bean的别名。
第二步,調用代理對象的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder)來對BeanDefinition對象再加工,主要是解析<bean>标簽中自定義屬性和自定義标簽。
第三步,調用工具類BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,這個方法用于注冊建立好的BeanDefinition。
第三步已經在淺析Spring4使用XmlBeanDefinitionReader解析xml配置部分探讨了。這裡我們深入的探讨前兩步。
1. 建立BeanDefinitionHolder 對象
執行BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele)方法,代碼如下。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 擷取id屬性
String id = ele.getAttribute("id");
// 擷取name屬性
String nameAttr = ele.getAttribute("name");
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 使用第一個alias作為id
beanName = aliases.remove();
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 檢查id和别名是否已經被使用了,如果已經被其他bean占用,則會抛出異常
checkNameUniqueness(beanName, aliases, ele);
}
// 建立BeanDefinition對象
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
// 配置中沒有指定id屬性
try {
if (containingBean != null) {
// bean是另一個bean的内部bean
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 為一個頂層bean生成id
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("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對象
// 此對象持有生成的BeanDefinition對象和id以及别名清單
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
這部分代碼主要是檢查使用者給的bean id是否已經被占用、為沒有id屬性值的bean建立id值以及調用parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)方法來解析Element對象并建立BeanDefinition對象,最後建立一個BeanDefinitionHolder對象來封裝BeanDefinition對象、bean id和bean别名。
parseBeanDefinitionElement方法是解析<bean>節點的主要方法,在這裡我們重點探讨它。下面是BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)方法代碼。
/**
* 建立BeanDefinition對象
**/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 讀取<bean>節點的class屬性
if (ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
}
try {
String parent = null;
// 讀取<bean>節點的parent屬性
if (ele.hasAttribute("parent")) {
parent = ele.getAttribute("parent");
}
// 建立一個GenericBeanDefinition對象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析<bean>節點的屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 擷取<desription>節點的值
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
// 解析<meta>節點
parseMetaElements(ele, bd);
// 解析<lookup-method>節點
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析<replaced-method>節點
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析<constructor-arg>節點
parseConstructorArgElements(ele, bd);
// 解析<property>節點
parsePropertyElements(ele, bd);
// 解析<qualifier>節點
parseQualifierElements(ele, bd);
// 讓BeanDefinition持有目前Resource對象
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;
}
這段代碼就是根據bean标簽的内容來執行個體化并初始化BeanDefinition對象。
parseBeanDefinitionElement方法制定了<bean>的解析流程,即BeanDefinition對象的建立和初始化流程,這個流程在parseBeanDefinitionElement方法中已經非常明确,主要有8步(除去<description>标簽),這裡我就不畫流程圖了,下面我們直接來探讨這個流程。
1.1 執行個體化BeanDefinition對象
parseBeanDefinitionElement方法調用BeanDefinitionParserDelegate的createBeanDefinition方法來建立BeanDefinition對象,這個方法的源碼如下。
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
createBeanDefinition方法通過調用工具類BeanDefinitionReaderUtils的createBeanDefinition靜态方法來建立BeanDefinition對象,代碼如下。
public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
// 使用ClassLoader加載Class對象
bd.setBeanClass(ClassUtils.forName(className, classLoader));
} else {
// 設定bean對于的class全名稱
bd.setBeanClassName(className);
}
}
return bd;
}
BeanDefinitionReaderUtils的createBeanDefinition方法建立一個GenericBeanDefinition對象,設定此對象的parent名稱,以及對應的Class對象或者class全名稱。
1.2 解析<bean>标簽屬性
在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseBeanDefinitionAttributes方法處理<bean>标簽的屬性,parseBeanDefinitionAttributes方法的代碼如下。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
BeanDefinition containingBean, AbstractBeanDefinition bd) {
// spring從4.0開始不再使用singleton屬性
if (ele.hasAttribute("singleton")) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
} else if (ele.hasAttribute("scope")) {
// 設定作用域
bd.setScope(ele.getAttribute("scope"));
} else if (containingBean != null) {
// 嵌套bean,則使用外面那個bean的作用域
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute("abstract")) {
bd.setAbstract("true".equals(ele.getAttribute("abstract")));
}
String lazyInit = ele.getAttribute("lazy-init");
if ("default".equals(lazyInit)) {
// 預設為false字元串
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit("true".equals(lazyInit));
String autowire = ele.getAttribute("autowire");
bd.setAutowireMode(getAutowireMode(autowire));
String dependencyCheck = ele.getAttribute("dependency-check");
bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
if (ele.hasAttribute("depends-on")) {
String dependsOn = ele.getAttribute("depends-on");
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, ",; "));
}
String autowireCandidate = ele.getAttribute("autowire-candidate");
if ("".equals(autowireCandidate) || "default".equals(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
} else {
bd.setAutowireCandidate("true".equals(autowireCandidate));
}
if (ele.hasAttribute("primary")) {
bd.setPrimary("true".equals(ele.getAttribute("primary")));
}
if (ele.hasAttribute("init-method")) {
String initMethodName = ele.getAttribute("init-method");
if (!"".equals(initMethodName)) {
bd.setInitMethodName(initMethodName);
}
} else {
if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
}
if (ele.hasAttribute("destroy-method")) {
String destroyMethodName = ele.getAttribute("destroy-method");
bd.setDestroyMethodName(destroyMethodName);
} else {
if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
}
if (ele.hasAttribute("factory-method")) {
bd.setFactoryMethodName(ele.getAttribute("factory-method"));
}
if (ele.hasAttribute("factory-bean")) {
bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
}
return bd;
}
public int getAutowireMode(String attValue) {
String att = attValue;
if("default".equals(attValue)) {
// 根據<beans>标簽的default-autowire屬性值确定
att = this.defaults.getAutowire();
}
// 不啟用自動裝配
int autowire = ;
if("byName".equals(att)) {
// 查找與屬性名稱相同的bean,并注入
autowire = ;
} else if("byType".equals(att)) {
// 通過屬性的類型查找JavaBean依賴的對象并為其注入
autowire = ;
} else if("constructor".equals(att)) {
// 同byType一樣是通過類型查找依賴對象
// 與byType的差別:它不是使用Seter方法注入,而是使用構造子注入
autowire = ;
} else if("autodetect".equals(att)) {
// 在byType和constructor之間自動的選擇注入方式
autowire = ;
}
return autowire;
}
public int getDependencyCheck(String attValue) {
String att = attValue;
if ("default".equals(att)) {
// 根據<beans>标簽的default-dependency-check屬性值确定
att = this.defaults.getDependencyCheck();
}
if ("all".equals(att)) {
// 檢查所有屬性
return ;
} else if ("objects".equals(att)) {
// 檢查對象的關聯關系
return ;
} else if ("simple".equals(att)) {
// 檢查原始類型和String類型的屬性
return ;
} else {
// 不檢查依賴
return ;
}
}
parseBeanDefinitionAttributes方法主要是解析bean标簽上的屬性,如果bean标簽上有些屬性沒有設定,則将使用在<beans>标簽上對應的屬性值。
1.3 解析<meta>标簽
在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseMetaElements方法處理<bean>标簽的子标簽<meta>,parseMetaElements方法的源碼如下。
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, "meta")) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute("key");
String value = metaElement.getAttribute("value");
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
parseMetaElements方法掃描bean标簽下的meta标簽,并擷取meta标簽上的key和value屬性值。這裡要說明兩個方法,其一是isCandidateElement方法,它是判斷目前節點是否需要要被處理的節點,代碼如下。
/**
* 判斷節點是否是待處理節點
**/
private boolean isCandidateElement(Node node) {
return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode())));
}
具有以下條件中一條的節點是待處理節點。
a. 節點是Element對象,并且在預設命名空間中。
b. 節點是Element對象,且它和它的父節點都不在預設命名空間中。
其二是nodeNameEquals方法,它判斷節點的名稱是否為指定的字元串,代碼如下。
/**
* 判斷節點名稱
**/
public boolean nodeNameEquals(Node node, String desiredName) {
return desiredName.equals(node.getNodeName()) || desiredName.equals(getLocalName(node));
}
isCandidateElement和nodeNameEquals方法我們還會在下面的探讨中見到,是以這裡對他們都認識一下。
1.4 解析<lookup-method>标簽
<lookup-method>标簽用于一個無狀态bean引用一個有狀态bean,也可以這樣說一個作用域廣的bean引用作用域小的bean,比如singleton bean應用一個prototype bean、sesstion bean、request bean時。
在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseLookupOverrideSubElements方法處理<bean>标簽的子标簽<lookup-method>,parseLookupOverrideSubElements方法的源碼如下。
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, "lookup-method")) {
Element ele = (Element) node;
// 方法名稱
String methodName = ele.getAttribute("name");
// 一個bean名稱,表示方法需要傳回這個名稱的bean
String beanRef = ele.getAttribute("bean");
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
1.5 解析<replaced-method>标簽
<replaced-method>用于修改非final bean中某個非private且非final方法的實作。一般的,如果一個方法不是隻有包範圍内通路的的,那麼可以直接通過繼承來重寫方法。但是如果要重寫的方法隻有包範圍内才可以通路的,那麼使用replaced-method方法是一個非常不錯的選擇。
<replaced-method>的作用是重寫bean中的某個方法。在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseLookupOverrideSubElements方法處理<bean>标簽的子标簽<replaced-method>,parseReplacedMethodSubElements方法的源碼如下。
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, "replaced-method")) {
Element replacedMethodEle = (Element) node;
// 将被替換的方法名稱
String name = replacedMethodEle.getAttribute("name");
// 一個實作了MethodReplacer接口的bean名稱
String callback = replacedMethodEle.getAttribute("replacer");
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// 搜尋<replaced-method>的<arg-type>子标簽
// <arg-type>标簽用于指定方法的參數類型
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, "arg-type");
for (Element argTypeEle : argTypeEles) {
// 擷取<arg-type>的值,這個值表示參數類型的全名稱
String match = argTypeEle.getAttribute("match");
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}
1.6 解析<constructor-arg>标簽
<constructor-arg>用于指定使用帶參構造器來執行個體化bean的場景。
在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseConstructorArgElements方法處理<bean>标簽的子标簽<constructor-arg>,parseConstructorArgElements方法的源碼如下。
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, "constructor-arg")) {
parseConstructorArgElement((Element) node, bd);
}
}
}
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 擷取參數索引
String indexAttr = ele.getAttribute("index");
// 擷取參數類型字元串
String typeAttr = ele.getAttribute("type");
// 擷取參數名稱
String nameAttr = ele.getAttribute("name");
if (StringUtils.hasLength(indexAttr)) {
// 配置了index屬性
try {
int index = Integer.parseInt(indexAttr);
if (index < ) {
error("'index' cannot be lower than 0", ele);
} else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 擷取參數值
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();
}
}
}
parseConstructorArgElement方法最重要的是通過調用BeanDefinitionParserDelegate的parsePropertyValue方法來擷取參數值,parsePropertyValue方法的代碼如下。
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
// 除了<description>和<meta>标簽外,<property>和<constructor-arg>隻允許存在一個用于擷取屬性或參數值的标簽,
// 比如ref, value, list, props, set, array, map, bean等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, "description") &&
!nodeNameEquals(node, "meta")) {
// 檢查是否有多個值子标簽
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
} else {
subElement = (Element) node;
}
}
}
boolean hasRefAttribute = ele.hasAttribute("ref");
boolean hasValueAttribute = ele.hasAttribute("value");
// 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);
}
if (hasRefAttribute) {
String refName = ele.getAttribute("ref");
// ref屬性不能為空
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
// 建立并傳回RuntimeBeanReference對象
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
} else if (hasValueAttribute) {
// 建立并傳回TypedStringValue對象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value"));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
} else {
// 參數和屬性必須要有值
error(elementName + " must specify a ref or value", ele);
return null;
}
}
parsePropertyValue方法主要是處理給定節點元素上的ref和value屬性值,如果沒有ref或者value屬性,則調用BeanDefinitionParserDelegate的parsePropertySubElement方法從<property>、<constroctor-arg>标簽的非meta非description子标簽中擷取值。下面是parsePropertySubElement方法的源代碼。
public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
if (!isDefaultNamespace(ele)) {
// 處理非預設命名空間中的<property>子标簽
return parseNestedCustomElement(ele, bd);
} else if (nodeNameEquals(ele, "bean")) {
// 處理<property>下的bean标簽
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
} else if (nodeNameEquals(ele, "ref")) {
// 處理<ref>标簽
String refName = ele.getAttribute("bean");
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in the same XML file.
// 一個bean名稱,這個bean來自同一個xml檔案
refName = ele.getAttribute("local");
if (!StringUtils.hasLength(refName)) {
// 一個bean名稱,這個bean來自于父類
refName = ele.getAttribute("parent");
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;
} else if (nodeNameEquals(ele, "idref")) {
// 處理<idref>标簽
return parseIdRefElement(ele);
} else if (nodeNameEquals(ele, "value")) {
// 處理<value>标簽
return parseValueElement(ele, defaultValueType);
} else if (nodeNameEquals(ele, "null")) {
// 傳回<null>标簽代表的null值,并用TypedStringValue對象來包裝
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
} else if (nodeNameEquals(ele, "array")) {
// 處理<array>标簽
return parseArrayElement(ele, bd);
} else if (nodeNameEquals(ele, "list")) {
// 處理<list>标簽
return parseListElement(ele, bd);
} else if (nodeNameEquals(ele, "set")) {
// 處理<set>标簽
return parseSetElement(ele, bd);
} else if (nodeNameEquals(ele, "map")) {
// 處理<map>标簽
return parseMapElement(ele, bd);
} else if (nodeNameEquals(ele, "props")) {
// 處理<props>标簽
return parsePropsElement(ele);
} else {
// 值标簽必須要提供一個值
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
parsePropertySubElement方法處理<property>和<constructor-arg>标簽下的bean,ref, idref, value, null, array, list, set, map, props以及其他命名空間中的标簽,并傳回這些标簽所代表的值。其中bean、ref和null标簽的處理已經這個方法中展現了,下面我們來探讨剩下7個标簽如何處理的(這節不會讨論其他命名空間的标簽如何處理)。
(1) 處理<idref>标簽
<idref>标簽不常用,一般隻用于注入容器中某個bean的名稱或者id。它和<property>的value屬性以及<value>标簽值一樣,隻是<idref>的bean、local屬性值必須為存在于容器中bean的id或name屬性值一緻,否則會抛異常。
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseIdRefElement方法來處理<idref>标簽,parseIdRefElement方法代碼如下。
public Object parseIdRefElement(Element ele) {
// 擷取bean屬性值
String refName = ele.getAttribute("bean");
if (!StringUtils.hasLength(refName)) {
// 擷取local屬性值,該值與同一個xml檔案中某個bean的名稱相同
refName = ele.getAttribute("local");
if (!StringUtils.hasLength(refName)) {
error("Either 'bean' or 'local' is required for <idref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<idref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanNameReference ref = new RuntimeBeanNameReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
(2) 處理<value>标簽
<value>标簽用于指定一個字面值,可以是基本類型,字元串。
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseValueElement方法來處理<value>标簽,parseValueElement方法代碼如下。
public Object parseValueElement(Element ele, String defaultTypeName) {
// 擷取<value>标簽的字面值
String value = DomUtils.getTextValue(ele);
// 擷取type屬性,表示屬性值的類型的全名稱
String specifiedTypeName = ele.getAttribute("type");
String typeName = specifiedTypeName;
if (!StringUtils.hasText(typeName)) {
typeName = defaultTypeName;
}
try {
// 根據value值和值類型建立TypedStringValue對象
TypedStringValue typedValue = buildTypedStringValue(value, typeName);
typedValue.setSource(extractSource(ele));
typedValue.setSpecifiedTypeName(specifiedTypeName);
return typedValue;
} catch (ClassNotFoundException ex) {
error("Type class [" + typeName + "] not found for <value> element", ele, ex);
return value;
}
}
parseValueElement方法擷取<value>标簽的字面值和type屬性後通過buildTypedStringValue方法建立TypedStringValue 對象,buildTypedStringValue方法的代碼如下。
protected TypedStringValue buildTypedStringValue(String value, String targetTypeName)
throws ClassNotFoundException {
ClassLoader classLoader = this.readerContext.getBeanClassLoader();
TypedStringValue typedValue;
if (!StringUtils.hasText(targetTypeName)) {
typedValue = new TypedStringValue(value);
} else if (classLoader != null) {
// 通過ClassLoader對象來加載一個Class對象
Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader);
typedValue = new TypedStringValue(value, targetType);
} else {
typedValue = new TypedStringValue(value, targetTypeName);
}
return typedValue;
}
(3) 處理<array>标簽
<array>标簽用于指定一個數組對象。
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseArrayElement方法來處理<array>标簽,parseArrayElement方法代碼如下。
public Object parseArrayElement(Element arrayEle, BeanDefinition bd) {
// 擷取元素的value-type屬性值,它表示元素的類型
String elementType = arrayEle.getAttribute("value-type");
NodeList nl = arrayEle.getChildNodes();
ManagedArray target = new ManagedArray(elementType, nl.getLength());
target.setSource(extractSource(arrayEle));
target.setElementTypeName(elementType);
// 擷取并設定merge屬性值
target.setMergeEnabled(parseMergeAttribute(arrayEle));
// 處理子标簽
parseCollectionElements(nl, target, bd, elementType);
return target;
}
/**
* 擷取元素的merge屬性值
**/
public boolean parseMergeAttribute(Element collectionElement) {
String value = collectionElement.getAttribute("merge");
if ("default".equals(value)) {
value = this.defaults.getMerge();
}
return "true".equals(value);
}
parseArrayElement方法主要是擷取<array>标簽中的value-type屬性值和merge屬性值來建立一個實作了Collection接口的ManagedArray對象,然後調用BeanDefinitionParserDelegate中處理集合元素子标簽的parseCollectionElements方法,這個方法的源碼如下。
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
for (int i = ; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
if (node instanceof Element && !nodeNameEquals(node, "description")) {
// 遞歸調用parsePropertySubElement方法來解析子節點。
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
parseCollectionElements方法周遊集合标簽(array、set和list)下的子标簽,并遞歸調用處理<property>、<constructor-arg>标簽的子标簽的parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType)方法。
(4) 處理<list>标簽
<list>标簽用于指定一個List對象
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseListElement方法來處理<list>标簽,parseListElement方法代碼如下。
public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) {
// 擷取元素的value-type屬性值,它表示元素的類型
String defaultElementType = collectionEle.getAttribute("value-type");
NodeList nl = collectionEle.getChildNodes();
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
// 擷取并設定merge屬性值
target.setMergeEnabled(parseMergeAttribute(collectionEle));
// 處理子标簽
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
parseListElement方法主要是擷取<list>标簽中的value-type屬性值和merge屬性值來建立一個實作了Collection接口的ManagedList對象,然後調用BeanDefinitionParserDelegate中處理集合元素子标簽的parseCollectionElements方法。
(5) 處理<set>标簽
<set>标簽用于指定一個Set對象。
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseSetElement方法來處理<set>标簽,parseSetElement方法代碼如下。
public Set<Object> parseSetElement(Element collectionEle, BeanDefinition bd) {
// 擷取元素的value-type屬性值,它表示元素的類型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
NodeList nl = collectionEle.getChildNodes();
ManagedSet<Object> target = new ManagedSet<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
// 擷取并設定merge屬性值
target.setMergeEnabled(parseMergeAttribute(collectionEle));
// 處理子标簽
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
parseSetElement方法主要是擷取<set>标簽中的value-type屬性值和merge屬性值來建立一個實作了Collection接口的ManagedSet對象,然後調用BeanDefinitionParserDelegate中處理集合元素子标簽的parseCollectionElements方法。
(6) 處理<map>标簽
<map>标簽用于指定一個Map對象。
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseMapElement方法來處理<map>标簽,parseMapElement方法代碼如下。
public Map<Object, Object> parseMapElement(Element mapEle, BeanDefinition bd) {
// 擷取Map中key值的類型
String defaultKeyType = mapEle.getAttribute("key-type");
// 擷取Map中value值得類型
String defaultValueType = mapEle.getAttribute("value-type");
// 擷取<map>标簽下的所有<entry>子标簽
List<Element> entryEles = DomUtils.getChildElementsByTagName(mapEle, "entry");
ManagedMap<Object, Object> map = new ManagedMap<Object, Object>(entryEles.size());
map.setSource(extractSource(mapEle));
map.setKeyTypeName(defaultKeyType);
map.setValueTypeName(defaultValueType);
// 擷取并設定merge屬性值
map.setMergeEnabled(parseMergeAttribute(mapEle));
for (Element entryEle : entryEles) {
NodeList entrySubNodes = entryEle.getChildNodes();
Element keyEle = null;
Element valueEle = null;
// 擷取<entry>标簽下的<key>和值标簽
for (int j = ; j < entrySubNodes.getLength(); j++) {
Node node = entrySubNodes.item(j);
if (node instanceof Element) {
Element candidateEle = (Element) node;
if (nodeNameEquals(candidateEle, "key")) {
// 擷取key标簽
if (keyEle != null) {
error("<entry> element is only allowed to contain one <key> sub-element", entryEle);
} else {
keyEle = candidateEle;
}
} else {
// 擷取值标簽
if (nodeNameEquals(candidateEle, "description")) {
// 忽略<description>
} else if (valueEle != null) {
error("<entry> element must not contain more than one value sub-element", entryEle);
} else {
valueEle = candidateEle;
}
}
}
}
// 從<entry>的屬性key或者key-ref或者子标簽<key>擷取Map中元素的key值
Object key = null;
boolean hasKeyAttribute = entryEle.hasAttribute("key");
boolean hasKeyRefAttribute = entryEle.hasAttribute("key-ref");
// 相同意義的标簽和屬性不能共存
if ((hasKeyAttribute && hasKeyRefAttribute) ||
((hasKeyAttribute || hasKeyRefAttribute)) && keyEle != null) {
error("<entry> element is only allowed to contain either " +
"a 'key' attribute OR a 'key-ref' attribute OR a <key> sub-element", entryEle);
}
if (hasKeyAttribute) {
// 處理屬性key值
key = buildTypedStringValueForMap(entryEle.getAttribute("key"), defaultKeyType, entryEle);
} else if (hasKeyRefAttribute) {
// 處理屬性key-ref值
String refName = entryEle.getAttribute("key-ref");
if (!StringUtils.hasText(refName)) {
error("<entry> element contains empty 'key-ref' attribute", entryEle);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(entryEle));
key = ref;
} else if (keyEle != null) {
// 處理<key>标簽
key = parseKeyElement(keyEle, bd, defaultKeyType);
} else {
error("<entry> element must specify a key", entryEle);
}
// 從<entry>的屬性value或者value-ref或者子值标簽(value,array,bean,map等)擷取Map中元素的value值
Object value = null;
boolean hasValueAttribute = entryEle.hasAttribute("value");
boolean hasValueRefAttribute = entryEle.hasAttribute("value-ref");
boolean hasValueTypeAttribute = entryEle.hasAttribute("value-type");
// 相同意義的标簽和屬性不能共存
// value、value-ref、值子标簽不能同時存在
if ((hasValueAttribute && hasValueRefAttribute) ||
((hasValueAttribute || hasValueRefAttribute)) && valueEle != null) {
error("<entry> element is only allowed to contain either " +
"'value' attribute OR 'value-ref' attribute OR <value> sub-element", entryEle);
}
// 有value-type屬性值,則必須有value屬性值,且不能有value-ref值和子值标簽
if ((hasValueTypeAttribute && hasValueRefAttribute) ||
(hasValueTypeAttribute && !hasValueAttribute) ||
(hasValueTypeAttribute && valueEle != null)) {
error("<entry> element is only allowed to contain a 'value-type' " +
"attribute when it has a 'value' attribute", entryEle);
}
if (hasValueAttribute) {
// 處理value和value-type屬性值
String valueType = entryEle.getAttribute("value-type");
if (!StringUtils.hasText(valueType)) {
valueType = defaultValueType;
}
value = buildTypedStringValueForMap(entryEle.getAttribute("value"), valueType, entryEle);
} else if (hasValueRefAttribute) {
// 處理value-ref屬性值
String refName = entryEle.getAttribute("value-ref");
if (!StringUtils.hasText(refName)) {
error("<entry> element contains empty 'value-ref' attribute", entryEle);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(entryEle));
value = ref;
} else if (valueEle != null) {
// 處理值标簽,遞歸調用parsePropertySubElement方法
value = parsePropertySubElement(valueEle, bd, defaultValueType);
} else {
error("<entry> element must specify a value", entryEle);
}
map.put(key, value);
}
return map;
}
/**
* 處理<entry>上的屬性key或value值。傳回一個TypedStringValue對象
**/
protected final Object buildTypedStringValueForMap(String value, String defaultTypeName, Element entryEle) {
try {
// 建立TypedStringValue對象
TypedStringValue typedValue = buildTypedStringValue(value, defaultTypeName);
typedValue.setSource(extractSource(entryEle));
return typedValue;
} catch (ClassNotFoundException ex) {
error("Type class [" + defaultTypeName + "] not found for Map key/value type", entryEle, ex);
return value;
}
}
/**
* 處理entry标簽下的key标簽
**/
protected Object parseKeyElement(Element keyEle, BeanDefinition bd, String defaultKeyTypeName) {
NodeList nl = keyEle.getChildNodes();
Element subElement = null;
// 周遊<key>标簽下的子标簽
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
if (subElement != null) {
error("<key> element must not contain more than one value sub-element", keyEle);
} else {
subElement = (Element) node;
}
}
}
return parsePropertySubElement(subElement, bd, defaultKeyTypeName);
}
(7) 處理<props>标簽
<props>标簽用于指定一個Property對象。
parsePropertySubElement方法調用BeanDefinitionParserDelegate的parsePropsElement方法來處理<props>标簽,parsePropsElement方法代碼如下。
public Properties parsePropsElement(Element propsEle) {
ManagedProperties props = new ManagedProperties();
props.setSource(extractSource(propsEle));
props.setMergeEnabled(parseMergeAttribute(propsEle));
// 擷取并周遊所有的<prop>标簽
List<Element> propEles = DomUtils.getChildElementsByTagName(propsEle, "prop");
for (Element propEle : propEles) {
// 擷取<prop>标簽的key屬性值
String key = propEle.getAttribute("key");
// 以<prop>标簽的字面值為value值
String value = DomUtils.getTextValue(propEle).trim();
// 用key值建立TypedStringValue對象
TypedStringValue keyHolder = new TypedStringValue(key);
keyHolder.setSource(extractSource(propEle));
// 用value值建立TypedStringValue
TypedStringValue valueHolder = new TypedStringValue(value);
valueHolder.setSource(extractSource(propEle));
props.put(keyHolder, valueHolder);
}
return props;
}
parsePropsElement方法周遊并擷取<props>标簽下的所有<prop>标簽的key值和字面值。
1.7 解析<property>标簽
在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parsePropertyElements方法處理<bean>标簽的子标簽<property>,parsePropertyElements方法的源碼如下。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 查找<property>标簽
if (isCandidateElement(node) && nodeNameEquals(node, "property")) {
parsePropertyElement((Element) node, bd);
}
}
}
parsePropertyElements方法主要是搜尋bean标簽下的<property>标簽,每找到一個<property>标簽就調用parsePropertyElement方法來處理此标簽,parsePropertyElement方法代碼如下。
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 擷取name屬性值,這值必須與bean中的屬性名稱相同
String propertyName = ele.getAttribute("name");
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
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));
// 儲存PropertyValue對象
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
和處理<constructor-arg>标簽一樣,parsePropertyElement方法也是調用BeanDefinitionParserDelegate的parsePropertyValue方法來擷取對應的值。
1.8 解析<qualifier>标簽
在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseQualifierElements方法處理<bean>标簽的子标簽<qualifier>,parseQualifierElements方法的源碼如下。
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, "qualifier")) {
parseQualifierElement((Element) node, bd);
}
}
}
parseQualifierElements方法主要是搜尋并處理<bean>标簽下所有的<qualifier>标簽,沒搜尋到一個<qualifier>标簽,就會調用BeanDefinitionParserDelegate的parseQualifierElement方法來處理,parseQualifierElement方法的源代碼如下。
/**
* 向BeanDefinition對象中添加AutowireCandidateQualifier對象
**/
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
String typeName = ele.getAttribute("type");
// <qualifier>标簽必須要有type屬性
if (!StringUtils.hasLength(typeName)) {
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
this.parseState.push(new QualifierEntry(typeName));
try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
// 擷取<qualifier>的value屬性
String value = ele.getAttribute("value");
if (StringUtils.hasLength(value)) {
qualifier.setAttribute("value", value);
}
NodeList nl = ele.getChildNodes();
// 搜尋<attribute>标簽
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, "attribute")) {
Element attributeEle = (Element) node;
// 擷取<attribute>标簽的key屬性值
String attributeName = attributeEle.getAttribute("key");
// 擷取<attribute>标簽的value屬性值
String attributeValue = attributeEle.getAttribute("value");
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
} else {
// 定義了<attribute>标簽就必須要提供key和value屬性值
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
bd.addQualifier(qualifier);
} finally {
this.parseState.pop();
}
}
<qualifier>标簽用于給bean建立限制辨別符,其中type屬性為一個注解類的全名稱,spring預設為org.springframework.beans.factory.annotation.Qualifier;value值為bean的辨別符,可為空;<attribute>标簽的key和value分别表示注解對象的方法名和方法傳回值。
<qualifier>作用是差別相同類型的不同bean。個人覺得,它的好處是可以指定一個注解來代表一個bean,以免在代碼中hard-code bean的名稱,但所指定的注解必須要用@Qualifier注解标注。
qualifier的替代方案:我們可以用id屬性值或者name屬性值來替代<qualifier>标簽。同樣可以自定義一個注解類來代表一個bean,隻是此時的@Qualifier的value方法的傳回值必須與id屬性或者name屬性對應。本人覺得如果id屬性值或者name屬性值不會經常改變時,這比使用<qualifier>标簽更友善。
2. 裝飾BeanDefinition對象
DefaultBeanDefinitionDocumentReader對象的processBeanDefinition方法在調用BeanDefinitionParserDelegate對象的parseBeanDefinitionElement方法解析<bean>标簽并獲得持有BeanDefintion對象的BeanDefinitionHolder對象後,繼續調用BeanDefinitionParserDelegate對象的decorateBeanDefinitionIfRequired來對剛獲得BeanDefintion對象做進一步的加工處理。
這一部分的加工處理主要是處理<bean>标簽中非預設命名空間中的屬性或者子标簽,比如p:命名空間修飾的屬性。我們來看看BeanDefinitionParserDelegate的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder)方法代碼,如下。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
}
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = definitionHolder;
// 首先根據自定義屬性裝飾BeanDefinition
// 比如http://www.springframework.org/schema/p命名空間的屬性
NamedNodeMap attributes = ele.getAttributes();
for (int i = ; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// 根據嵌套的自定義标簽元素裝飾BeanDefinition
NodeList children = ele.getChildNodes();
for (int i = ; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
這段代碼的責任是周遊标簽的屬性和子節點并調用decorateIfRequired(Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd)方法來處理屬性和<bean>的子标簽,詳見以下代碼。
/**
* 處理自定義命名空間标簽來裝飾BeanDefinition對象
**/
private BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(node);
if (!isDefaultNamespace(namespaceUri)) {
// 根據節點所在的命名空間,擷取NamespaceHandler對象
// 比如http://www.springframework.org/schema/p命名空間的為SimplePropertyNamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
// 執行裝飾BeanDefinition對象
return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
} else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
} else {
// 節點為自定義命名空間的,但沒有指定NamespaceHandler
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
decorateIfRequired方法隻處理非預設命名空間的屬性和标簽,是以它首先會檢查節點是否為預設明見中的,是預設空間的則直接傳回,如果不是則調用NamespaceHandlerResolver對象(預設為DefaultNamespaceHandlerResolver)來獲得節點所在命名空間的處理器NamespaceHandler對象,然後調用NamespaceHandler對象的decorate方法,并傳回一個BeanDefinitionHolder 對象,它可以是新建立的,也可以是裝飾前的那個。
總結
(1) 可以通過給<beans>指定屬性值來全局性的設定<bean>标簽對應的屬性值,比如定義全局的初始化方法,則可以在<beans>定義default-init-method屬性值。
(2)<property>和<constructor-arg>标簽使用同一套屬性名和子标簽。