Spring源码分析(一)
????? Spring提供四种自动装配类型:
byName-试图在容器中寻找和需要自动装配的属性名相同的Bean(或ID)。byType-试图在容器中寻找一个与需要装配的属性类型相同的Bean。constructor-试图在容器中查找与需要自动装配的Bean的构造函数参数一致的一个或多个Bean。autodetect-首先尝试使用constructor来自动装配,然后使用byType方式。????? BeanDefinitionParserDelegate是Bean的加载类。
?
/* * Copyright 2002-2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.beans.factory.xml;import java.util.ArrayList;import java.util.Arrays;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Properties;import java.util.Set;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.w3c.dom.Element;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.springframework.beans.BeanMetadataAttribute;import org.springframework.beans.BeanMetadataAttributeAccessor;import org.springframework.beans.PropertyValue;import org.springframework.beans.factory.BeanDefinitionStoreException;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.BeanDefinitionHolder;import org.springframework.beans.factory.config.ConstructorArgumentValues;import org.springframework.beans.factory.config.RuntimeBeanNameReference;import org.springframework.beans.factory.config.RuntimeBeanReference;import org.springframework.beans.factory.config.TypedStringValue;import org.springframework.beans.factory.parsing.BeanEntry;import org.springframework.beans.factory.parsing.ConstructorArgumentEntry;import org.springframework.beans.factory.parsing.ParseState;import org.springframework.beans.factory.parsing.PropertyEntry;import org.springframework.beans.factory.parsing.QualifierEntry;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.AutowireCandidateQualifier;import org.springframework.beans.factory.support.BeanDefinitionDefaults;import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;import org.springframework.beans.factory.support.LookupOverride;import org.springframework.beans.factory.support.ManagedList;import org.springframework.beans.factory.support.ManagedMap;import org.springframework.beans.factory.support.ManagedProperties;import org.springframework.beans.factory.support.ManagedSet;import org.springframework.beans.factory.support.MethodOverrides;import org.springframework.beans.factory.support.ReplaceOverride;import org.springframework.util.Assert;import org.springframework.util.ClassUtils;import org.springframework.util.CollectionUtils;import org.springframework.util.ObjectUtils;import org.springframework.util.PatternMatchUtils;import org.springframework.util.StringUtils;import org.springframework.util.xml.DomUtils;/** * Stateful delegate class used to parse XML bean definitions. * Intended for use by both the main parser and any extension * {@link BeanDefinitionParser BeanDefinitionParsers} * or {@link BeanDefinitionDecorator BeanDefinitionDecorators}. * * @author Rob Harrop * @author Juergen Hoeller * @author Rod Johnson * @author Mark Fisher * @since 2.0 * @see ParserContext * @see DefaultBeanDefinitionDocumentReader */public class BeanDefinitionParserDelegate {public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";public static final String BEAN_NAME_DELIMITERS = ",; ";/** * Value of a T/F attribute that represents true. * Anything else represents false. Case seNsItive. */public static final String TRUE_VALUE = "true";public static final String DEFAULT_VALUE = "default";public static final String DESCRIPTION_ELEMENT = "description";public static final String AUTOWIRE_BY_NAME_VALUE = "byName";public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";public static final String NAME_ATTRIBUTE = "name";public static final String BEAN_ELEMENT = "bean";public static final String META_ELEMENT = "meta";public static final String ID_ATTRIBUTE = "id";public static final String PARENT_ATTRIBUTE = "parent";public static final String CLASS_ATTRIBUTE = "class";public static final String ABSTRACT_ATTRIBUTE = "abstract";public static final String SCOPE_ATTRIBUTE = "scope";public static final String SINGLETON_ATTRIBUTE = "singleton";public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";public static final String AUTOWIRE_ATTRIBUTE = "autowire";public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";public static final String PRIMARY_ATTRIBUTE = "primary";public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";public static final String INIT_METHOD_ATTRIBUTE = "init-method";public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";public static final String INDEX_ATTRIBUTE = "index";public static final String TYPE_ATTRIBUTE = "type";public static final String VALUE_TYPE_ATTRIBUTE = "value-type";public static final String KEY_TYPE_ATTRIBUTE = "key-type";public static final String PROPERTY_ELEMENT = "property";public static final String REF_ATTRIBUTE = "ref";public static final String VALUE_ATTRIBUTE = "value";public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";public static final String REPLACED_METHOD_ELEMENT = "replaced-method";public static final String REPLACER_ATTRIBUTE = "replacer";public static final String ARG_TYPE_ELEMENT = "arg-type";public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";public static final String REF_ELEMENT = "ref";public static final String IDREF_ELEMENT = "idref";public static final String BEAN_REF_ATTRIBUTE = "bean";public static final String LOCAL_REF_ATTRIBUTE = "local";public static final String PARENT_REF_ATTRIBUTE = "parent";public static final String VALUE_ELEMENT = "value";public static final String NULL_ELEMENT = "null";public static final String LIST_ELEMENT = "list";public static final String SET_ELEMENT = "set";public static final String MAP_ELEMENT = "map";public static final String ENTRY_ELEMENT = "entry";public static final String KEY_ELEMENT = "key";public static final String KEY_ATTRIBUTE = "key";public static final String KEY_REF_ATTRIBUTE = "key-ref";public static final String VALUE_REF_ATTRIBUTE = "value-ref";public static final String PROPS_ELEMENT = "props";public static final String PROP_ELEMENT = "prop";public static final String MERGE_ATTRIBUTE = "merge";public static final String QUALIFIER_ELEMENT = "qualifier";public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";protected final Log logger = LogFactory.getLog(getClass());private final XmlReaderContext readerContext;private DocumentDefaultsDefinition defaults;private ParseState parseState = new ParseState();/** * Stores all used bean names so we can enforce uniqueness on a per file basis. */private final Set usedNames = new HashSet();/** * Create a new BeanDefinitionParserDelegate associated with the * supplied {@link XmlReaderContext}. */public BeanDefinitionParserDelegate(XmlReaderContext readerContext) {Assert.notNull(readerContext, "XmlReaderContext must not be null");this.readerContext = readerContext;}/** * Get the {@link XmlReaderContext} associated with this helper instance. */public final XmlReaderContext getReaderContext() {return this.readerContext;}/** * Invoke the {@link org.springframework.beans.factory.parsing.SourceExtractor} to pull the * source metadata from the supplied {@link Element}. */protected Object extractSource(Element ele) {return this.readerContext.extractSource(ele);}/** * Report an error with the given message for the given source element. */protected void error(String message, Element source) {this.readerContext.error(message, source, this.parseState.snapshot());}/** * Report an error with the given message for the given source element. */protected void error(String message, Element source, Throwable cause) {this.readerContext.error(message, source, this.parseState.snapshot(), cause);}/** * Initialize the default lazy-init, autowire, dependency check settings, * init-method, destroy-method and merge settings. * @see #getDefaults() */public void initDefaults(Element root) {DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE));defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE));defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE));defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));}if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));}if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));}defaults.setSource(this.readerContext.extractSource(root));this.defaults = defaults;this.readerContext.fireDefaultsRegistered(defaults);}/** * Return the defaults definition object, or <code>null</code> if the * defaults have been initialized yet. */public DocumentDefaultsDefinition getDefaults() {return this.defaults;}/** * Return the default settings for bean definitions as indicated within * the attributes of the top-level <code><beans/></code> element. */public BeanDefinitionDefaults getBeanDefinitionDefaults() {BeanDefinitionDefaults bdd = new BeanDefinitionDefaults();if (this.defaults != null) {bdd.setLazyInit("TRUE".equalsIgnoreCase(this.defaults.getLazyInit()));bdd.setDependencyCheck(this.getDependencyCheck(DEFAULT_VALUE));bdd.setAutowireMode(this.getAutowireMode(DEFAULT_VALUE));bdd.setInitMethodName(this.defaults.getInitMethod());bdd.setDestroyMethodName(this.defaults.getDestroyMethod());}return bdd;}/** * Return any patterns provided in the 'default-autowire-candidates' * attribute of the top-level <code><beans/></code> element. */public String[] getAutowireCandidatePatterns() {String candidatePattern = this.defaults.getAutowireCandidates();return candidatePattern == null ? null : StringUtils.commaDelimitedListToStringArray(candidatePattern);}/** * Parses the supplied <code><bean></code> element. May return <code>null</code> * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {return parseBeanDefinitionElement(ele, null);}/** * Parses the supplied <code><bean></code> element. May return <code>null</code> * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {String id = ele.getAttribute(ID_ATTRIBUTE);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);List aliases = new ArrayList();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}String beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = (String) aliases.remove(0);if (logger.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {beanName = this.readerContext.generateBeanName(beanDefinition);// Register an alias for the plain bean class name, if still possible,// if the generator returned the class name plus a suffix.// This is expected for Spring 1.2/2.0 backwards compatibility.String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {this.readerContext.getRegistry().registerAlias(beanName, beanClassName);}}if (logger.isDebugEnabled()) {logger.debug("Neither XML 'id' nor 'name' specified - " +"using generated bean name [" + beanName + "]");}}catch (BeanDefinitionStoreException ex) {error(ex.getMessage(), ele);return null;}}String[] aliasesArray = StringUtils.toStringArray(aliases);return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}return null;}private void checkNameUniqueness(String beanName, List aliases, Element beanElement) {String foundName = null;if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {foundName = beanName;}if (foundName == null) {foundName = (String) CollectionUtils.findFirstMatch(this.usedNames, aliases);}if (foundName != null) {error("Bean name '" + foundName + "' is already used in this file.", beanElement);}this.usedNames.add(beanName);this.usedNames.addAll(aliases);}/** * Parse the bean definition itself, without regard to name or aliases. May return * <code>null</code> if problems occured during the parse of the bean definition. */public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}try {this.parseState.push(new BeanEntry(beanName));AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, this.readerContext.getBeanClassLoader());if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {// Spring 2.0 "scope" attributebd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {error("Specify either 'scope' or 'singleton', not both", ele);}}else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {// Spring 1.x "singleton" attributebd.setSingleton(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)));}else if (containingBean != null) {// Take default from containing bean in case of an inner bean definition.bd.setScope(containingBean.getScope());}if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));}String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);if (DEFAULT_VALUE.equals(lazyInit) && bd.isSingleton()) {// Just apply default to singletons, as lazy-init has no meaning for prototypes.lazyInit = this.defaults.getLazyInit();}bd.setLazyInit(TRUE_VALUE.equals(lazyInit));String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);bd.setAutowireMode(getAutowireMode(autowire));String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);bd.setDependencyCheck(getDependencyCheck(dependencyCheck));if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));}String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);if ("".equals(autowireCandidate) || DEFAULT_VALUE.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_VALUE.equals(autowireCandidate));}if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));}if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);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_ATTRIBUTE)) {String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);if (!"".equals(destroyMethodName)) {bd.setDestroyMethodName(destroyMethodName);}}else {if (this.defaults.getDestroyMethod() != null) {bd.setDestroyMethodName(this.defaults.getDestroyMethod());bd.setEnforceDestroyMethod(false);}}if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));}if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));}parseMetaElements(ele, bd);parseLookupOverrideSubElements(ele, bd.getMethodOverrides());parseReplacedMethodSubElements(ele, bd.getMethodOverrides());parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(ele, bd);bd.setResourceDescription(this.readerContext.getResource().getDescription());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;}public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {NodeList nl = ele.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element && DomUtils.nodeNameEquals(node, META_ELEMENT)) {Element metaElement = (Element) node;String key = metaElement.getAttribute(KEY_ATTRIBUTE);String value = metaElement.getAttribute(VALUE_ATTRIBUTE);BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);attribute.setSource(extractSource(metaElement));attributeAccessor.addMetadataAttribute(attribute);}}}public int getAutowireMode(String attValue) {String att = attValue;if (DEFAULT_VALUE.equals(att)) {att = this.defaults.getAutowire();}int autowire = AbstractBeanDefinition.AUTOWIRE_NO;if (AUTOWIRE_BY_NAME_VALUE.equals(att)) {autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME;}else if (AUTOWIRE_BY_TYPE_VALUE.equals(att)) {autowire = AbstractBeanDefinition.AUTOWIRE_BY_TYPE;}else if (AUTOWIRE_CONSTRUCTOR_VALUE.equals(att)) {autowire = AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR;}else if (AUTOWIRE_AUTODETECT_VALUE.equals(att)) {autowire = AbstractBeanDefinition.AUTOWIRE_AUTODETECT;}// Else leave default value.return autowire;}public int getDependencyCheck(String attValue) {String att = attValue;if (DEFAULT_VALUE.equals(att)) {att = this.defaults.getDependencyCheck();}int dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE;if (DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE.equals(att)) {dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_ALL;}else if (DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE.equals(att)) {dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE;}else if (DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE.equals(att)) {dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS;}// Else leave default value.return dependencyCheckCode;}/** * 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 (node instanceof Element && DomUtils.nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {parseConstructorArgElement((Element) node, bd);}}}/** * 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);if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {parsePropertyElement((Element) node, bd);}}}/** * Parse qualifier sub-elements of the given bean element. */public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element && DomUtils.nodeNameEquals(node, QUALIFIER_ELEMENT)) {parseQualifierElement((Element) node, bd);}}}/** * Parse lookup-override sub-elements of the given bean element. */public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element && DomUtils.nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {Element ele = (Element) node;String methodName = ele.getAttribute(NAME_ATTRIBUTE);String beanRef = ele.getAttribute(BEAN_ELEMENT);LookupOverride override = new LookupOverride(methodName, beanRef);override.setSource(extractSource(ele));overrides.addOverride(override);}}}/** * Parse replaced-method sub-elements of the given bean element. */public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element && DomUtils.nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {Element replacedMethodEle = (Element) node;String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);// Look for arg-type match elements.List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);for (Iterator it = argTypeEles.iterator(); it.hasNext();) {Element argTypeEle = (Element) it.next();replaceOverride.addTypeIdentifier(argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE));}replaceOverride.setSource(extractSource(replacedMethodEle));overrides.addOverride(replaceOverride);}}}/** * Parse a constructor-arg element. */public void parseConstructorArgElement(Element ele, BeanDefinition bd) {String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);String typeAttr = ele.getAttribute(TYPE_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));Object value = parsePropertyValue(ele, bd, null);ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);if (StringUtils.hasLength(typeAttr)) {valueHolder.setType(typeAttr);}valueHolder.setSource(extractSource(ele));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);}valueHolder.setSource(extractSource(ele));bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);}finally {this.parseState.pop();}}}/** * Parse a property element. */public void parsePropertyElement(Element ele, BeanDefinition bd) {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 {if (bd.getPropertyValues().contains(propertyName)) {error("Multiple 'property' definitions for property '" + propertyName + "'", ele);return;}Object val = parsePropertyValue(ele, bd, propertyName);PropertyValue pv = new PropertyValue(propertyName, val);parseMetaElements(ele, pv);pv.setSource(extractSource(ele));bd.getPropertyValues().addPropertyValue(pv);}finally {this.parseState.pop();}}/** * Parse a qualifier element. */public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {String typeName = ele.getAttribute(TYPE_ATTRIBUTE);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));String value = ele.getAttribute(VALUE_ATTRIBUTE);if (StringUtils.hasLength(value)) {qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);}NodeList nl = ele.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element && DomUtils.nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {Element attributeEle = (Element) node;String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);attribute.setSource(extractSource(attributeEle));qualifier.addMetadataAttribute(attribute);}else {error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);return;}}}bd.addQualifier(qualifier);}finally {this.parseState.pop();}}/** * Get the value of a property element. May be a list etc. * Also used for constructor arguments, "propertyName" being null in this case. */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.NodeList nl = ele.getChildNodes();Element subElement = null;for (int i = 0; i < nl.getLength(); i++) {if (nl.item(i) instanceof Element) {Element candidateEle = (Element) nl.item(i);if (DomUtils.nodeNameEquals(candidateEle, DESCRIPTION_ELEMENT) ||DomUtils.nodeNameEquals(candidateEle, META_ELEMENT)) {// Keep going: we don't use these values for now.}else {// Child element is what we're looking for.if (subElement != null) {error(elementName + " must not contain more than one sub-element", ele);}else {subElement = candidateEle;}}}}boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);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_ATTRIBUTE);if (!StringUtils.hasText(refName)) {error(elementName + " contains empty 'ref' attribute", ele);}RuntimeBeanReference ref = new RuntimeBeanReference(refName);ref.setSource(extractSource(ele));return ref;}else if (hasValueAttribute) {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;}}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 defaultTypeClassName the default type (class name) for any * <code><value></code> tag that might be created */public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultTypeClassName) {if (!isDefaultNamespace(ele.getNamespaceURI())) {return parseNestedCustomElement(ele, bd);}else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, bd);if (bdHolder != null) {bdHolder = decorateBeanDefinitionIfRequired(ele, bdHolder);}return bdHolder;}else if (DomUtils.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;}else if (DomUtils.nodeNameEquals(ele, IDREF_ELEMENT)) {// A generic reference to any name of any bean.String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);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)) {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;}else if (DomUtils.nodeNameEquals(ele, VALUE_ELEMENT)) {// It's a literal value.String value = DomUtils.getTextValue(ele);String typeClassName = ele.getAttribute(TYPE_ATTRIBUTE);if (!StringUtils.hasText(typeClassName)) {typeClassName = defaultTypeClassName;}try {return buildTypedStringValue(value, typeClassName, ele);}catch (ClassNotFoundException ex) {error("Type class [" + typeClassName + "] not found for <value> element", ele, ex);return value;}}else if (DomUtils.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 (DomUtils.nodeNameEquals(ele, LIST_ELEMENT)) {return parseListElement(ele, bd);}else if (DomUtils.nodeNameEquals(ele, SET_ELEMENT)) {return parseSetElement(ele, bd);}else if (DomUtils.nodeNameEquals(ele, MAP_ELEMENT)) {return parseMapElement(ele, bd);}else if (DomUtils.nodeNameEquals(ele, PROPS_ELEMENT)) {return parsePropsElement(ele);}error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);return null;}/** * Build a typed String value Object for the given raw value. * @see org.springframework.beans.factory.config.TypedStringValue */protected Object buildTypedStringValue(String value, String targetTypeName, Element ele)throws ClassNotFoundException {ClassLoader classLoader = this.readerContext.getBeanClassLoader();TypedStringValue typedValue = null;if (!StringUtils.hasText(targetTypeName)) {typedValue = new TypedStringValue(value);}else if (classLoader != null) {Class targetType = ClassUtils.forName(targetTypeName, classLoader);typedValue = new TypedStringValue(value, targetType);}else {typedValue = new TypedStringValue(value, targetTypeName);}typedValue.setSource(extractSource(ele));return typedValue;}/** * Parse a list element. */public List parseListElement(Element collectionEle, BeanDefinition bd) {String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);NodeList nl = collectionEle.getChildNodes();ManagedList list = new ManagedList(nl.getLength());list.setSource(extractSource(collectionEle));list.setMergeEnabled(parseMergeAttribute(collectionEle));for (int i = 0; i < nl.getLength(); i++) {if (nl.item(i) instanceof Element) {Element ele = (Element) nl.item(i);list.add(parsePropertySubElement(ele, bd, defaultTypeClassName));}}return list;}/** * Parse a set element. */public Set parseSetElement(Element collectionEle, BeanDefinition bd) {String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);NodeList nl = collectionEle.getChildNodes();ManagedSet set = new ManagedSet(nl.getLength());set.setSource(extractSource(collectionEle));set.setMergeEnabled(parseMergeAttribute(collectionEle));for (int i = 0; i < nl.getLength(); i++) {if (nl.item(i) instanceof Element) {Element ele = (Element) nl.item(i);set.add(parsePropertySubElement(ele, bd, defaultTypeClassName));}}return set;}/** * Parse a map element. */public Map parseMapElement(Element mapEle, BeanDefinition bd) {String defaultKeyTypeClassName = mapEle.getAttribute(KEY_TYPE_ATTRIBUTE);String defaultValueTypeClassName = mapEle.getAttribute(VALUE_TYPE_ATTRIBUTE);List entryEles = DomUtils.getChildElementsByTagName(mapEle, ENTRY_ELEMENT);ManagedMap map = new ManagedMap(entryEles.size());map.setMergeEnabled(parseMergeAttribute(mapEle));map.setSource(extractSource(mapEle));for (Iterator it = entryEles.iterator(); it.hasNext();) {Element entryEle = (Element) it.next();// Should only have one value child element: ref, value, list, etc.// Optionally, there might be a key child element.NodeList entrySubNodes = entryEle.getChildNodes();Element keyEle = null;Element valueEle = null;for (int j = 0; j < entrySubNodes.getLength(); j++) {if (entrySubNodes.item(j) instanceof Element) {Element candidateEle = (Element) entrySubNodes.item(j);if (DomUtils.nodeNameEquals(candidateEle, KEY_ELEMENT)) {if (keyEle != null) {error("<entry> element is only allowed to contain one <key> sub-element", entryEle);}else {keyEle = candidateEle;}}else {// Child element is what we're looking for.if (valueEle != null) {error("<entry> element must not contain more than one value sub-element", entryEle);}else {valueEle = candidateEle;}}}}// Extract key from attribute or sub-element.Object key = null;boolean hasKeyAttribute = entryEle.hasAttribute(KEY_ATTRIBUTE);boolean hasKeyRefAttribute = entryEle.hasAttribute(KEY_REF_ATTRIBUTE);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 = buildTypedStringValueForMap(entryEle.getAttribute(KEY_ATTRIBUTE), defaultKeyTypeClassName, entryEle);}else if (hasKeyRefAttribute) {String refName = entryEle.getAttribute(KEY_REF_ATTRIBUTE);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 = parseKeyElement(keyEle, bd, defaultKeyTypeClassName);}else {error("<entry> element must specify a key", entryEle);}// Extract value from attribute or sub-element.Object value = null;boolean hasValueAttribute = entryEle.hasAttribute(VALUE_ATTRIBUTE);boolean hasValueRefAttribute = entryEle.hasAttribute(VALUE_REF_ATTRIBUTE);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);}if (hasValueAttribute) {value = buildTypedStringValueForMap(entryEle.getAttribute(VALUE_ATTRIBUTE), defaultValueTypeClassName, entryEle);}else if (hasValueRefAttribute) {String refName = entryEle.getAttribute(VALUE_REF_ATTRIBUTE);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) {value = parsePropertySubElement(valueEle, bd, defaultValueTypeClassName);}else {error("<entry> element must specify a value", entryEle);}// Add final key and value to the Map.map.put(key, value);}return map;}/** * Build a typed String value Object for the given raw value. * @see org.springframework.beans.factory.config.TypedStringValue */protected final Object buildTypedStringValueForMap(String value, String defaultTypeClassName, Element entryEle) {try {return buildTypedStringValue(value, defaultTypeClassName, entryEle);}catch (ClassNotFoundException ex) {error("Type class [" + defaultTypeClassName + "] not found for Map key/value type", entryEle, ex);return value;}}/** * Parse a key sub-element of a map element. */public Object parseKeyElement(Element keyEle, BeanDefinition bd, String defaultKeyTypeClassName) {NodeList nl = keyEle.getChildNodes();Element subElement = null;for (int i = 0; i < nl.getLength(); i++) {if (nl.item(i) instanceof Element) {Element candidateEle = (Element) nl.item(i);// Child element is what we're looking for.if (subElement != null) {error("<key> element must not contain more than one value sub-element", keyEle);}else {subElement = candidateEle;}}}return parsePropertySubElement(subElement, bd, defaultKeyTypeClassName);}/** * Parse a props element. */public Properties parsePropsElement(Element propsEle) {ManagedProperties props = new ManagedProperties();props.setSource(extractSource(propsEle));props.setMergeEnabled(parseMergeAttribute(propsEle));List propEles = DomUtils.getChildElementsByTagName(propsEle, PROP_ELEMENT);for (Iterator it = propEles.iterator(); it.hasNext();) {Element propEle = (Element) it.next();String key = propEle.getAttribute(KEY_ATTRIBUTE);// Trim the text value to avoid unwanted whitespace// caused by typical XML formatting.String value = DomUtils.getTextValue(propEle).trim();TypedStringValue keyHolder = new TypedStringValue(key);keyHolder.setSource(extractSource(propEle));TypedStringValue valueHolder = new TypedStringValue(value);valueHolder.setSource(extractSource(propEle));props.put(keyHolder, valueHolder);}return props;}/** * Parse the merge attribute of a collection element, if any. */public boolean parseMergeAttribute(Element collectionElement) {String value = collectionElement.getAttribute(MERGE_ATTRIBUTE);if (DEFAULT_VALUE.equals(value)) {value = this.defaults.getMerge();}return TRUE_VALUE.equals(value);}public BeanDefinition parseCustomElement(Element ele) {return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {String namespaceUri = ele.getNamespaceURI();NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {BeanDefinitionHolder finalDefinition = definitionHolder;// Decorate based on custom attributes first.NamedNodeMap attributes = ele.getAttributes();for (int i = 0; i < attributes.getLength(); i++) {Node node = attributes.item(i);finalDefinition = decorateIfRequired(node, finalDefinition);}// Decorate based on custom nested elements.NodeList children = ele.getChildNodes();for (int i = 0; i < children.getLength(); i++) {Node node = children.item(i);if (node.getNodeType() == Node.ELEMENT_NODE) {finalDefinition = decorateIfRequired(node, finalDefinition);}}return finalDefinition;}private BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDefinition) {String namespaceUri = node.getNamespaceURI();if (!isDefaultNamespace(namespaceUri)) {NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler != null) {return handler.decorate(node, originalDefinition, new ParserContext(this.readerContext, this));}else {// A custom namespace, not to be handled by Spring - maybe "xml:...".if (logger.isDebugEnabled()) {logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");}}}return originalDefinition;}public boolean isDefaultNamespace(String namespaceUri) {return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));}private BeanDefinitionHolder parseNestedCustomElement(Element ele, BeanDefinition containingBd) {BeanDefinition innerDefinition = parseCustomElement(ele, containingBd);if (innerDefinition == null) {error("Incorrect usage of element '" + ele.getNodeName() + "' in a nested manner. " +"This tag cannot be used nested inside <property>.", ele);return null;}String id = ele.getNodeName() + BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR +ObjectUtils.getIdentityHexString(innerDefinition);if (logger.isDebugEnabled()) {logger.debug("Using generated bean name [" + id +"] for nested custom element '" + ele.getNodeName() + "'");}return new BeanDefinitionHolder(innerDefinition, id);}}?