【Spring源码分析系列】结构组成和容器的基本实现

beans包的层级结构

src/main/java:用于展现Spring的主要逻辑

src/main/resources:用于存放系统的配置文件

src/test/java:用于对主要逻辑单元进行测试

src/test/resources:用于存放配置测试用的文件

核心类介绍:

1、DefaultListableBeanFactory

XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个Bean加载的核心部分,是Spring注册及加载bean的默认实现,而对于XmlBeanFactory与DefaultListableBeanFactory不同的地方其实是在XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取,DefaultListableBeanFactory继承了AbstraceAutowireCapableBeanFactory并实现了ConfigurableListableBeanFactory以及BeanDefinitionRegisty接口。

  • AliasRegistry:定义对alias的简单增删改等操作
  • SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现
  • SingletonBeanRegistry:定义对单例的注册及获取
  • BeanFactory:定义获取Bean及Bean的各种属性
  • DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry各函数的实现
  • HierarchicalBeanFactory:继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的支持
  • BeanDefinitionRegistry:定义对BeanDefinition的各种增删改的操作
  • FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上周增加了对FactoryBean的特殊处理功能
  • ConfigurableBeanFactory:提供了配置Factory的各种方法
  • ListableBeanFactory:根据各种条件获取bean的配置清单
  • AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能
  • AutowireCapableBeanFactory:提供创建bean、自动注入。初始化以及应用bean的后续处理器
  • AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
  • ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等
  • DefaultListableBeanFactory:Bean注册后的处理

XmlBeanFactory对DefaultListableBeanFactory类进行了扩展,主要用于从XML文档中读取BeanDefinition,对于DefaultListableBeanFactory都是使用从父类DefaultListableBeanFactory继承的方法去实现,而唯独与父类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性。在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。

2、XmlBeanDefinitionReader

XML配置文件的读取是Spring中重要的功能,因为Spring的大部分功能都是以配置作为切入点的,从XmlBeanDefinitionReader梳理一下资源文件读取、解析及注册的大致脉络

  • ResourceLoader:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
  • BeanDefinitionReader:主要定义资源文件读取并转换为BeanDefinition的各个功能
  • EnvironmrntCapable:定义获取Environment方法
  • DocumentLoader,定义从资源文件加载到转换为Document方法
  • AbstractBeanDefinitionReader:对EnvironmentCapable、BeanDefinitionReader类定义的功能进行实现
  • BeanDefinitionDocumentReader:定义读取Document并注册BeanDefiition功能
  • BeanDefinitionParserDelegate定义解析Element的各种方法

XmlBeanDefninitionReader中主要包含:

1)通过继承自AbstractBeanDefinitionReader中的方法,来使用ResourceLoader将资源文件路径转换为对应的Resource文件

 1 /**
 2      * Load bean definitions from the specified resource location.
 3      * <p>The location can also be a location pattern, provided that the
 4      * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
 5      * @param location the resource location, to be loaded with the ResourceLoader
 6      * (or ResourcePatternResolver) of this bean definition reader
 7      * @param actualResources a Set to be filled with the actual Resource objects
 8      * that have been resolved during the loading process. May be {@code null}
 9      * to indicate that the caller is not interested in those Resource objects.
10      * @return the number of bean definitions found
11      * @throws BeanDefinitionStoreException in case of loading or parsing errors
12      * @see #getResourceLoader()
13      * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
14      * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
15      */
16     public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
17         ResourceLoader resourceLoader = getResourceLoader();
18         if (resourceLoader == null) {
19             throw new BeanDefinitionStoreException(
20                     "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
21         }
22 
23         if (resourceLoader instanceof ResourcePatternResolver) {
24             // Resource pattern matching available.
25             try {
26                 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
27                 int loadCount = loadBeanDefinitions(resources);
28                 if (actualResources != null) {
29                     for (Resource resource : resources) {
30                         actualResources.add(resource);
31                     }
32                 }
33                 if (logger.isDebugEnabled()) {
34                     logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
35                 }
36                 return loadCount;
37             }
38             catch (IOException ex) {
39                 throw new BeanDefinitionStoreException(
40                         "Could not resolve bean definition resource pattern [" + location + "]", ex);
41             }
42         }
43         else {
44             // Can only load single resources by absolute URL.
45             Resource resource = resourceLoader.getResource(location);
46             int loadCount = loadBeanDefinitions(resource);
47             if (actualResources != null) {
48                 actualResources.add(resource);
49             }
50             if (logger.isDebugEnabled()) {
51                 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
52             }
53             return loadCount;
54         }
55     }

2)通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件

 1 /**
 2      * Actually load the specified document using the configured DocumentLoader.
 3      * @param inputSource the SAX InputSource to read from
 4      * @param resource the resource descriptor for the XML file
 5      * @return the DOM Document
 6      * @throws Exception when thrown from the DocumentLoader
 7      * @see #setDocumentLoader
 8      * @see DocumentLoader#loadDocument
 9      */
10     protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
11         return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
12                 getValidationModeForResource(resource), isNamespaceAware());
13     }

3)通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionDelegate对Element进行解析

   1 /*
   2  * Copyright 2002-2017 the original author or authors.
   3  *
   4  * Licensed under the Apache License, Version 2.0 (the "License");
   5  * you may not use this file except in compliance with the License.
   6  * You may obtain a copy of the License at
   7  *
   8  *      http://www.apache.org/licenses/LICENSE-2.0
   9  *
  10  * Unless required by applicable law or agreed to in writing, software
  11  * distributed under the License is distributed on an "AS IS" BASIS,
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  * See the License for the specific language governing permissions and
  14  * limitations under the License.
  15  */
  16 
  17 package org.springframework.beans.factory.xml;
  18 
  19 import java.util.ArrayList;
  20 import java.util.Arrays;
  21 import java.util.Collection;
  22 import java.util.HashSet;
  23 import java.util.List;
  24 import java.util.Map;
  25 import java.util.Properties;
  26 import java.util.Set;
  27 
  28 import org.apache.commons.logging.Log;
  29 import org.apache.commons.logging.LogFactory;
  30 import org.w3c.dom.Element;
  31 import org.w3c.dom.NamedNodeMap;
  32 import org.w3c.dom.Node;
  33 import org.w3c.dom.NodeList;
  34 
  35 import org.springframework.beans.BeanMetadataAttribute;
  36 import org.springframework.beans.BeanMetadataAttributeAccessor;
  37 import org.springframework.beans.PropertyValue;
  38 import org.springframework.beans.factory.config.BeanDefinition;
  39 import org.springframework.beans.factory.config.BeanDefinitionHolder;
  40 import org.springframework.beans.factory.config.ConstructorArgumentValues;
  41 import org.springframework.beans.factory.config.RuntimeBeanNameReference;
  42 import org.springframework.beans.factory.config.RuntimeBeanReference;
  43 import org.springframework.beans.factory.config.TypedStringValue;
  44 import org.springframework.beans.factory.parsing.BeanEntry;
  45 import org.springframework.beans.factory.parsing.ConstructorArgumentEntry;
  46 import org.springframework.beans.factory.parsing.ParseState;
  47 import org.springframework.beans.factory.parsing.PropertyEntry;
  48 import org.springframework.beans.factory.parsing.QualifierEntry;
  49 import org.springframework.beans.factory.support.AbstractBeanDefinition;
  50 import org.springframework.beans.factory.support.AutowireCandidateQualifier;
  51 import org.springframework.beans.factory.support.BeanDefinitionDefaults;
  52 import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
  53 import org.springframework.beans.factory.support.LookupOverride;
  54 import org.springframework.beans.factory.support.ManagedArray;
  55 import org.springframework.beans.factory.support.ManagedList;
  56 import org.springframework.beans.factory.support.ManagedMap;
  57 import org.springframework.beans.factory.support.ManagedProperties;
  58 import org.springframework.beans.factory.support.ManagedSet;
  59 import org.springframework.beans.factory.support.MethodOverrides;
  60 import org.springframework.beans.factory.support.ReplaceOverride;
  61 import org.springframework.core.env.Environment;
  62 import org.springframework.util.Assert;
  63 import org.springframework.util.ClassUtils;
  64 import org.springframework.util.CollectionUtils;
  65 import org.springframework.util.ObjectUtils;
  66 import org.springframework.util.PatternMatchUtils;
  67 import org.springframework.util.StringUtils;
  68 import org.springframework.util.xml.DomUtils;
  69 
  70 /**
  71  * Stateful delegate class used to parse XML bean definitions.
  72  * Intended for use by both the main parser and any extension
  73  * {@link BeanDefinitionParser BeanDefinitionParsers} or
  74  * {@link BeanDefinitionDecorator BeanDefinitionDecorators}.
  75  *
  76  * @author Rob Harrop
  77  * @author Juergen Hoeller
  78  * @author Rod Johnson
  79  * @author Mark Fisher
  80  * @author Gary Russell
  81  * @since 2.0
  82  * @see ParserContext
  83  * @see DefaultBeanDefinitionDocumentReader
  84  */
  85 public class BeanDefinitionParserDelegate {
  86 
  87     public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
  88 
  89     public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
  90 
  91     /**
  92      * Value of a T/F attribute that represents true.
  93      * Anything else represents false. Case seNsItive.
  94      */
  95     public static final String TRUE_VALUE = "true";
  96 
  97     public static final String FALSE_VALUE = "false";
  98 
  99     public static final String DEFAULT_VALUE = "default";
 100 
 101     public static final String DESCRIPTION_ELEMENT = "description";
 102 
 103     public static final String AUTOWIRE_NO_VALUE = "no";
 104 
 105     public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
 106 
 107     public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
 108 
 109     public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
 110 
 111     public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
 112 
 113     public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";
 114 
 115     public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";
 116 
 117     public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";
 118 
 119     public static final String NAME_ATTRIBUTE = "name";
 120 
 121     public static final String BEAN_ELEMENT = "bean";
 122 
 123     public static final String META_ELEMENT = "meta";
 124 
 125     public static final String ID_ATTRIBUTE = "id";
 126 
 127     public static final String PARENT_ATTRIBUTE = "parent";
 128 
 129     public static final String CLASS_ATTRIBUTE = "class";
 130 
 131     public static final String ABSTRACT_ATTRIBUTE = "abstract";
 132 
 133     public static final String SCOPE_ATTRIBUTE = "scope";
 134 
 135     private static final String SINGLETON_ATTRIBUTE = "singleton";
 136 
 137     public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
 138 
 139     public static final String AUTOWIRE_ATTRIBUTE = "autowire";
 140 
 141     public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
 142 
 143     public static final String PRIMARY_ATTRIBUTE = "primary";
 144 
 145     public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";
 146 
 147     public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
 148 
 149     public static final String INIT_METHOD_ATTRIBUTE = "init-method";
 150 
 151     public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
 152 
 153     public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
 154 
 155     public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
 156 
 157     public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
 158 
 159     public static final String INDEX_ATTRIBUTE = "index";
 160 
 161     public static final String TYPE_ATTRIBUTE = "type";
 162 
 163     public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
 164 
 165     public static final String KEY_TYPE_ATTRIBUTE = "key-type";
 166 
 167     public static final String PROPERTY_ELEMENT = "property";
 168 
 169     public static final String REF_ATTRIBUTE = "ref";
 170 
 171     public static final String VALUE_ATTRIBUTE = "value";
 172 
 173     public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
 174 
 175     public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
 176 
 177     public static final String REPLACER_ATTRIBUTE = "replacer";
 178 
 179     public static final String ARG_TYPE_ELEMENT = "arg-type";
 180 
 181     public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
 182 
 183     public static final String REF_ELEMENT = "ref";
 184 
 185     public static final String IDREF_ELEMENT = "idref";
 186 
 187     public static final String BEAN_REF_ATTRIBUTE = "bean";
 188 
 189     public static final String LOCAL_REF_ATTRIBUTE = "local";
 190 
 191     public static final String PARENT_REF_ATTRIBUTE = "parent";
 192 
 193     public static final String VALUE_ELEMENT = "value";
 194 
 195     public static final String NULL_ELEMENT = "null";
 196 
 197     public static final String ARRAY_ELEMENT = "array";
 198 
 199     public static final String LIST_ELEMENT = "list";
 200 
 201     public static final String SET_ELEMENT = "set";
 202 
 203     public static final String MAP_ELEMENT = "map";
 204 
 205     public static final String ENTRY_ELEMENT = "entry";
 206 
 207     public static final String KEY_ELEMENT = "key";
 208 
 209     public static final String KEY_ATTRIBUTE = "key";
 210 
 211     public static final String KEY_REF_ATTRIBUTE = "key-ref";
 212 
 213     public static final String VALUE_REF_ATTRIBUTE = "value-ref";
 214 
 215     public static final String PROPS_ELEMENT = "props";
 216 
 217     public static final String PROP_ELEMENT = "prop";
 218 
 219     public static final String MERGE_ATTRIBUTE = "merge";
 220 
 221     public static final String QUALIFIER_ELEMENT = "qualifier";
 222 
 223     public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
 224 
 225     public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
 226 
 227     public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
 228 
 229     public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
 230 
 231     public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";
 232 
 233     public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
 234 
 235     public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
 236 
 237     public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";
 238 
 239 
 240     protected final Log logger = LogFactory.getLog(getClass());
 241 
 242     private final XmlReaderContext readerContext;
 243 
 244     private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
 245 
 246     private final ParseState parseState = new ParseState();
 247 
 248     /**
 249      * Stores all used bean names so we can enforce uniqueness on a per
 250      * beans-element basis. Duplicate bean ids/names may not exist within the
 251      * same level of beans element nesting, but may be duplicated across levels.
 252      */
 253     private final Set<String> usedNames = new HashSet<String>();
 254 
 255 
 256     /**
 257      * Create a new BeanDefinitionParserDelegate associated with the supplied
 258      * {@link XmlReaderContext}.
 259      */
 260     public BeanDefinitionParserDelegate(XmlReaderContext readerContext) {
 261         Assert.notNull(readerContext, "XmlReaderContext must not be null");
 262         this.readerContext = readerContext;
 263     }
 264 
 265 
 266     /**
 267      * Get the {@link XmlReaderContext} associated with this helper instance.
 268      */
 269     public final XmlReaderContext getReaderContext() {
 270         return this.readerContext;
 271     }
 272 
 273     /**
 274      * Get the {@link Environment} associated with this helper instance.
 275      * @deprecated in favor of {@link XmlReaderContext#getEnvironment()}
 276      */
 277     @Deprecated
 278     public final Environment getEnvironment() {
 279         return this.readerContext.getEnvironment();
 280     }
 281 
 282     /**
 283      * Invoke the {@link org.springframework.beans.factory.parsing.SourceExtractor} to pull the
 284      * source metadata from the supplied {@link Element}.
 285      */
 286     protected Object extractSource(Element ele) {
 287         return this.readerContext.extractSource(ele);
 288     }
 289 
 290     /**
 291      * Report an error with the given message for the given source element.
 292      */
 293     protected void error(String message, Node source) {
 294         this.readerContext.error(message, source, this.parseState.snapshot());
 295     }
 296 
 297     /**
 298      * Report an error with the given message for the given source element.
 299      */
 300     protected void error(String message, Element source) {
 301         this.readerContext.error(message, source, this.parseState.snapshot());
 302     }
 303 
 304     /**
 305      * Report an error with the given message for the given source element.
 306      */
 307     protected void error(String message, Element source, Throwable cause) {
 308         this.readerContext.error(message, source, this.parseState.snapshot(), cause);
 309     }
 310 
 311 
 312     /**
 313      * Initialize the default settings assuming a {@code null} parent delegate.
 314      */
 315     public void initDefaults(Element root) {
 316         initDefaults(root, null);
 317     }
 318 
 319     /**
 320      * Initialize the default lazy-init, autowire, dependency check settings,
 321      * init-method, destroy-method and merge settings. Support nested 'beans'
 322      * element use cases by falling back to the given parent in case the
 323      * defaults are not explicitly set locally.
 324      * @see #populateDefaults(DocumentDefaultsDefinition, DocumentDefaultsDefinition, org.w3c.dom.Element)
 325      * @see #getDefaults()
 326      */
 327     public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
 328         populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
 329         this.readerContext.fireDefaultsRegistered(this.defaults);
 330     }
 331 
 332     /**
 333      * Populate the given DocumentDefaultsDefinition instance with the default lazy-init,
 334      * autowire, dependency check settings, init-method, destroy-method and merge settings.
 335      * Support nested 'beans' element use cases by falling back to <literal>parentDefaults</literal>
 336      * in case the defaults are not explicitly set locally.
 337      * @param defaults the defaults to populate
 338      * @param parentDefaults the parent BeanDefinitionParserDelegate (if any) defaults to fall back to
 339      * @param root the root element of the current bean definition document (or nested beans element)
 340      */
 341     protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) {
 342         String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
 343         if (DEFAULT_VALUE.equals(lazyInit)) {
 344             // Potentially inherited from outer <beans> sections, otherwise falling back to false.
 345             lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
 346         }
 347         defaults.setLazyInit(lazyInit);
 348 
 349         String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
 350         if (DEFAULT_VALUE.equals(merge)) {
 351             // Potentially inherited from outer <beans> sections, otherwise falling back to false.
 352             merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
 353         }
 354         defaults.setMerge(merge);
 355 
 356         String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
 357         if (DEFAULT_VALUE.equals(autowire)) {
 358             // Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
 359             autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
 360         }
 361         defaults.setAutowire(autowire);
 362 
 363         // Don't fall back to parentDefaults for dependency-check as it's no longer supported in
 364         // <beans> as of 3.0. Therefore, no nested <beans> would ever need to fall back to it.
 365         defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));
 366 
 367         if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
 368             defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
 369         }
 370         else if (parentDefaults != null) {
 371             defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
 372         }
 373 
 374         if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
 375             defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
 376         }
 377         else if (parentDefaults != null) {
 378             defaults.setInitMethod(parentDefaults.getInitMethod());
 379         }
 380 
 381         if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
 382             defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
 383         }
 384         else if (parentDefaults != null) {
 385             defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
 386         }
 387 
 388         defaults.setSource(this.readerContext.extractSource(root));
 389     }
 390 
 391     /**
 392      * Return the defaults definition object, or {@code null} if the
 393      * defaults have been initialized yet.
 394      */
 395     public DocumentDefaultsDefinition getDefaults() {
 396         return this.defaults;
 397     }
 398 
 399     /**
 400      * Return the default settings for bean definitions as indicated within
 401      * the attributes of the top-level {@code <beans/>} element.
 402      */
 403     public BeanDefinitionDefaults getBeanDefinitionDefaults() {
 404         BeanDefinitionDefaults bdd = new BeanDefinitionDefaults();
 405         bdd.setLazyInit("TRUE".equalsIgnoreCase(this.defaults.getLazyInit()));
 406         bdd.setDependencyCheck(this.getDependencyCheck(DEFAULT_VALUE));
 407         bdd.setAutowireMode(this.getAutowireMode(DEFAULT_VALUE));
 408         bdd.setInitMethodName(this.defaults.getInitMethod());
 409         bdd.setDestroyMethodName(this.defaults.getDestroyMethod());
 410         return bdd;
 411     }
 412 
 413     /**
 414      * Return any patterns provided in the 'default-autowire-candidates'
 415      * attribute of the top-level {@code <beans/>} element.
 416      */
 417     public String[] getAutowireCandidatePatterns() {
 418         String candidatePattern = this.defaults.getAutowireCandidates();
 419         return (candidatePattern != null ? StringUtils.commaDelimitedListToStringArray(candidatePattern) : null);
 420     }
 421 
 422 
 423     /**
 424      * Parses the supplied {@code <bean>} element. May return {@code null}
 425      * if there were errors during parse. Errors are reported to the
 426      * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
 427      */
 428     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
 429         return parseBeanDefinitionElement(ele, null);
 430     }
 431 
 432     /**
 433      * Parses the supplied {@code <bean>} element. May return {@code null}
 434      * if there were errors during parse. Errors are reported to the
 435      * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
 436      */
 437     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
 438         String id = ele.getAttribute(ID_ATTRIBUTE);
 439         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
 440 
 441         List<String> aliases = new ArrayList<String>();
 442         if (StringUtils.hasLength(nameAttr)) {
 443             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
 444             aliases.addAll(Arrays.asList(nameArr));
 445         }
 446 
 447         String beanName = id;
 448         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
 449             beanName = aliases.remove(0);
 450             if (logger.isDebugEnabled()) {
 451                 logger.debug("No XML 'id' specified - using '" + beanName +
 452                         "' as bean name and " + aliases + " as aliases");
 453             }
 454         }
 455 
 456         if (containingBean == null) {
 457             checkNameUniqueness(beanName, aliases, ele);
 458         }
 459 
 460         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
 461         if (beanDefinition != null) {
 462             if (!StringUtils.hasText(beanName)) {
 463                 try {
 464                     if (containingBean != null) {
 465                         beanName = BeanDefinitionReaderUtils.generateBeanName(
 466                                 beanDefinition, this.readerContext.getRegistry(), true);
 467                     }
 468                     else {
 469                         beanName = this.readerContext.generateBeanName(beanDefinition);
 470                         // Register an alias for the plain bean class name, if still possible,
 471                         // if the generator returned the class name plus a suffix.
 472                         // This is expected for Spring 1.2/2.0 backwards compatibility.
 473                         String beanClassName = beanDefinition.getBeanClassName();
 474                         if (beanClassName != null &&
 475                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
 476                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
 477                             aliases.add(beanClassName);
 478                         }
 479                     }
 480                     if (logger.isDebugEnabled()) {
 481                         logger.debug("Neither XML 'id' nor 'name' specified - " +
 482                                 "using generated bean name [" + beanName + "]");
 483                     }
 484                 }
 485                 catch (Exception ex) {
 486                     error(ex.getMessage(), ele);
 487                     return null;
 488                 }
 489             }
 490             String[] aliasesArray = StringUtils.toStringArray(aliases);
 491             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
 492         }
 493 
 494         return null;
 495     }
 496 
 497     /**
 498      * Validate that the specified bean name and aliases have not been used already
 499      * within the current level of beans element nesting.
 500      */
 501     protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
 502         String foundName = null;
 503 
 504         if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
 505             foundName = beanName;
 506         }
 507         if (foundName == null) {
 508             foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
 509         }
 510         if (foundName != null) {
 511             error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
 512         }
 513 
 514         this.usedNames.add(beanName);
 515         this.usedNames.addAll(aliases);
 516     }
 517 
 518     /**
 519      * Parse the bean definition itself, without regard to name or aliases. May return
 520      * {@code null} if problems occurred during the parsing of the bean definition.
 521      */
 522     public AbstractBeanDefinition parseBeanDefinitionElement(
 523             Element ele, String beanName, BeanDefinition containingBean) {
 524 
 525         this.parseState.push(new BeanEntry(beanName));
 526 
 527         String className = null;
 528         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
 529             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
 530         }
 531 
 532         try {
 533             String parent = null;
 534             if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
 535                 parent = ele.getAttribute(PARENT_ATTRIBUTE);
 536             }
 537             AbstractBeanDefinition bd = createBeanDefinition(className, parent);
 538 
 539             parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
 540             bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
 541 
 542             parseMetaElements(ele, bd);
 543             parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
 544             parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
 545 
 546             parseConstructorArgElements(ele, bd);
 547             parsePropertyElements(ele, bd);
 548             parseQualifierElements(ele, bd);
 549 
 550             bd.setResource(this.readerContext.getResource());
 551             bd.setSource(extractSource(ele));
 552 
 553             return bd;
 554         }
 555         catch (ClassNotFoundException ex) {
 556             error("Bean class [" + className + "] not found", ele, ex);
 557         }
 558         catch (NoClassDefFoundError err) {
 559             error("Class that bean class [" + className + "] depends on not found", ele, err);
 560         }
 561         catch (Throwable ex) {
 562             error("Unexpected failure during bean definition parsing", ele, ex);
 563         }
 564         finally {
 565             this.parseState.pop();
 566         }
 567 
 568         return null;
 569     }
 570 
 571     /**
 572      * Apply the attributes of the given bean element to the given bean * definition.
 573      * @param ele bean declaration element
 574      * @param beanName bean name
 575      * @param containingBean containing bean definition
 576      * @return a bean definition initialized according to the bean element attributes
 577      */
 578     public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
 579             BeanDefinition containingBean, AbstractBeanDefinition bd) {
 580 
 581         if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
 582             error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
 583         }
 584         else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
 585             bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
 586         }
 587         else if (containingBean != null) {
 588             // Take default from containing bean in case of an inner bean definition.
 589             bd.setScope(containingBean.getScope());
 590         }
 591 
 592         if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
 593             bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
 594         }
 595 
 596         String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
 597         if (DEFAULT_VALUE.equals(lazyInit)) {
 598             lazyInit = this.defaults.getLazyInit();
 599         }
 600         bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
 601 
 602         String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
 603         bd.setAutowireMode(getAutowireMode(autowire));
 604 
 605         String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
 606         bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
 607 
 608         if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
 609             String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
 610             bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
 611         }
 612 
 613         String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
 614         if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
 615             String candidatePattern = this.defaults.getAutowireCandidates();
 616             if (candidatePattern != null) {
 617                 String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
 618                 bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
 619             }
 620         }
 621         else {
 622             bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
 623         }
 624 
 625         if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
 626             bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
 627         }
 628 
 629         if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
 630             String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
 631             if (!"".equals(initMethodName)) {
 632                 bd.setInitMethodName(initMethodName);
 633             }
 634         }
 635         else {
 636             if (this.defaults.getInitMethod() != null) {
 637                 bd.setInitMethodName(this.defaults.getInitMethod());
 638                 bd.setEnforceInitMethod(false);
 639             }
 640         }
 641 
 642         if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
 643             String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
 644             bd.setDestroyMethodName(destroyMethodName);
 645         }
 646         else {
 647             if (this.defaults.getDestroyMethod() != null) {
 648                 bd.setDestroyMethodName(this.defaults.getDestroyMethod());
 649                 bd.setEnforceDestroyMethod(false);
 650             }
 651         }
 652 
 653         if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
 654             bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
 655         }
 656         if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
 657             bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
 658         }
 659 
 660         return bd;
 661     }
 662 
 663     /**
 664      * Create a bean definition for the given class name and parent name.
 665      * @param className the name of the bean class
 666      * @param parentName the name of the bean's parent bean
 667      * @return the newly created bean definition
 668      * @throws ClassNotFoundException if bean class resolution was attempted but failed
 669      */
 670     protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
 671             throws ClassNotFoundException {
 672 
 673         return BeanDefinitionReaderUtils.createBeanDefinition(
 674                 parentName, className, this.readerContext.getBeanClassLoader());
 675     }
 676 
 677     public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
 678         NodeList nl = ele.getChildNodes();
 679         for (int i = 0; i < nl.getLength(); i++) {
 680             Node node = nl.item(i);
 681             if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
 682                 Element metaElement = (Element) node;
 683                 String key = metaElement.getAttribute(KEY_ATTRIBUTE);
 684                 String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
 685                 BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
 686                 attribute.setSource(extractSource(metaElement));
 687                 attributeAccessor.addMetadataAttribute(attribute);
 688             }
 689         }
 690     }
 691 
 692     @SuppressWarnings("deprecation")
 693     public int getAutowireMode(String attValue) {
 694         String att = attValue;
 695         if (DEFAULT_VALUE.equals(att)) {
 696             att = this.defaults.getAutowire();
 697         }
 698         int autowire = AbstractBeanDefinition.AUTOWIRE_NO;
 699         if (AUTOWIRE_BY_NAME_VALUE.equals(att)) {
 700             autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME;
 701         }
 702         else if (AUTOWIRE_BY_TYPE_VALUE.equals(att)) {
 703             autowire = AbstractBeanDefinition.AUTOWIRE_BY_TYPE;
 704         }
 705         else if (AUTOWIRE_CONSTRUCTOR_VALUE.equals(att)) {
 706             autowire = AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR;
 707         }
 708         else if (AUTOWIRE_AUTODETECT_VALUE.equals(att)) {
 709             autowire = AbstractBeanDefinition.AUTOWIRE_AUTODETECT;
 710         }
 711         // Else leave default value.
 712         return autowire;
 713     }
 714 
 715     public int getDependencyCheck(String attValue) {
 716         String att = attValue;
 717         if (DEFAULT_VALUE.equals(att)) {
 718             att = this.defaults.getDependencyCheck();
 719         }
 720         if (DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE.equals(att)) {
 721             return AbstractBeanDefinition.DEPENDENCY_CHECK_ALL;
 722         }
 723         else if (DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE.equals(att)) {
 724             return AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS;
 725         }
 726         else if (DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE.equals(att)) {
 727             return AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE;
 728         }
 729         else {
 730             return AbstractBeanDefinition.DEPENDENCY_CHECK_NONE;
 731         }
 732     }
 733 
 734     /**
 735      * Parse constructor-arg sub-elements of the given bean element.
 736      */
 737     public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
 738         NodeList nl = beanEle.getChildNodes();
 739         for (int i = 0; i < nl.getLength(); i++) {
 740             Node node = nl.item(i);
 741             if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
 742                 parseConstructorArgElement((Element) node, bd);
 743             }
 744         }
 745     }
 746 
 747     /**
 748      * Parse property sub-elements of the given bean element.
 749      */
 750     public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
 751         NodeList nl = beanEle.getChildNodes();
 752         for (int i = 0; i < nl.getLength(); i++) {
 753             Node node = nl.item(i);
 754             if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
 755                 parsePropertyElement((Element) node, bd);
 756             }
 757         }
 758     }
 759 
 760     /**
 761      * Parse qualifier sub-elements of the given bean element.
 762      */
 763     public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
 764         NodeList nl = beanEle.getChildNodes();
 765         for (int i = 0; i < nl.getLength(); i++) {
 766             Node node = nl.item(i);
 767             if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
 768                 parseQualifierElement((Element) node, bd);
 769             }
 770         }
 771     }
 772 
 773     /**
 774      * Parse lookup-override sub-elements of the given bean element.
 775      */
 776     public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
 777         NodeList nl = beanEle.getChildNodes();
 778         for (int i = 0; i < nl.getLength(); i++) {
 779             Node node = nl.item(i);
 780             if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
 781                 Element ele = (Element) node;
 782                 String methodName = ele.getAttribute(NAME_ATTRIBUTE);
 783                 String beanRef = ele.getAttribute(BEAN_ELEMENT);
 784                 LookupOverride override = new LookupOverride(methodName, beanRef);
 785                 override.setSource(extractSource(ele));
 786                 overrides.addOverride(override);
 787             }
 788         }
 789     }
 790 
 791     /**
 792      * Parse replaced-method sub-elements of the given bean element.
 793      */
 794     public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
 795         NodeList nl = beanEle.getChildNodes();
 796         for (int i = 0; i < nl.getLength(); i++) {
 797             Node node = nl.item(i);
 798             if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
 799                 Element replacedMethodEle = (Element) node;
 800                 String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
 801                 String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
 802                 ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
 803                 // Look for arg-type match elements.
 804                 List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
 805                 for (Element argTypeEle : argTypeEles) {
 806                     String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
 807                     match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
 808                     if (StringUtils.hasText(match)) {
 809                         replaceOverride.addTypeIdentifier(match);
 810                     }
 811                 }
 812                 replaceOverride.setSource(extractSource(replacedMethodEle));
 813                 overrides.addOverride(replaceOverride);
 814             }
 815         }
 816     }
 817 
 818     /**
 819      * Parse a constructor-arg element.
 820      */
 821     public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
 822         String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
 823         String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
 824         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
 825         if (StringUtils.hasLength(indexAttr)) {
 826             try {
 827                 int index = Integer.parseInt(indexAttr);
 828                 if (index < 0) {
 829                     error("'index' cannot be lower than 0", ele);
 830                 }
 831                 else {
 832                     try {
 833                         this.parseState.push(new ConstructorArgumentEntry(index));
 834                         Object value = parsePropertyValue(ele, bd, null);
 835                         ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
 836                         if (StringUtils.hasLength(typeAttr)) {
 837                             valueHolder.setType(typeAttr);
 838                         }
 839                         if (StringUtils.hasLength(nameAttr)) {
 840                             valueHolder.setName(nameAttr);
 841                         }
 842                         valueHolder.setSource(extractSource(ele));
 843                         if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
 844                             error("Ambiguous constructor-arg entries for index " + index, ele);
 845                         }
 846                         else {
 847                             bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
 848                         }
 849                     }
 850                     finally {
 851                         this.parseState.pop();
 852                     }
 853                 }
 854             }
 855             catch (NumberFormatException ex) {
 856                 error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
 857             }
 858         }
 859         else {
 860             try {
 861                 this.parseState.push(new ConstructorArgumentEntry());
 862                 Object value = parsePropertyValue(ele, bd, null);
 863                 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
 864                 if (StringUtils.hasLength(typeAttr)) {
 865                     valueHolder.setType(typeAttr);
 866                 }
 867                 if (StringUtils.hasLength(nameAttr)) {
 868                     valueHolder.setName(nameAttr);
 869                 }
 870                 valueHolder.setSource(extractSource(ele));
 871                 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
 872             }
 873             finally {
 874                 this.parseState.pop();
 875             }
 876         }
 877     }
 878 
 879     /**
 880      * Parse a property element.
 881      */
 882     public void parsePropertyElement(Element ele, BeanDefinition bd) {
 883         String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
 884         if (!StringUtils.hasLength(propertyName)) {
 885             error("Tag 'property' must have a 'name' attribute", ele);
 886             return;
 887         }
 888         this.parseState.push(new PropertyEntry(propertyName));
 889         try {
 890             if (bd.getPropertyValues().contains(propertyName)) {
 891                 error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
 892                 return;
 893             }
 894             Object val = parsePropertyValue(ele, bd, propertyName);
 895             PropertyValue pv = new PropertyValue(propertyName, val);
 896             parseMetaElements(ele, pv);
 897             pv.setSource(extractSource(ele));
 898             bd.getPropertyValues().addPropertyValue(pv);
 899         }
 900         finally {
 901             this.parseState.pop();
 902         }
 903     }
 904 
 905     /**
 906      * Parse a qualifier element.
 907      */
 908     public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
 909         String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
 910         if (!StringUtils.hasLength(typeName)) {
 911             error("Tag 'qualifier' must have a 'type' attribute", ele);
 912             return;
 913         }
 914         this.parseState.push(new QualifierEntry(typeName));
 915         try {
 916             AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
 917             qualifier.setSource(extractSource(ele));
 918             String value = ele.getAttribute(VALUE_ATTRIBUTE);
 919             if (StringUtils.hasLength(value)) {
 920                 qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
 921             }
 922             NodeList nl = ele.getChildNodes();
 923             for (int i = 0; i < nl.getLength(); i++) {
 924                 Node node = nl.item(i);
 925                 if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
 926                     Element attributeEle = (Element) node;
 927                     String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
 928                     String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
 929                     if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
 930                         BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
 931                         attribute.setSource(extractSource(attributeEle));
 932                         qualifier.addMetadataAttribute(attribute);
 933                     }
 934                     else {
 935                         error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
 936                         return;
 937                     }
 938                 }
 939             }
 940             bd.addQualifier(qualifier);
 941         }
 942         finally {
 943             this.parseState.pop();
 944         }
 945     }
 946 
 947     /**
 948      * Get the value of a property element. May be a list etc.
 949      * Also used for constructor arguments, "propertyName" being null in this case.
 950      */
 951     public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
 952         String elementName = (propertyName != null) ?
 953                         "<property> element for property '" + propertyName + "'" :
 954                         "<constructor-arg> element";
 955 
 956         // Should only have one child element: ref, value, list, etc.
 957         NodeList nl = ele.getChildNodes();
 958         Element subElement = null;
 959         for (int i = 0; i < nl.getLength(); i++) {
 960             Node node = nl.item(i);
 961             if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
 962                     !nodeNameEquals(node, META_ELEMENT)) {
 963                 // Child element is what we're looking for.
 964                 if (subElement != null) {
 965                     error(elementName + " must not contain more than one sub-element", ele);
 966                 }
 967                 else {
 968                     subElement = (Element) node;
 969                 }
 970             }
 971         }
 972 
 973         boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
 974         boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
 975         if ((hasRefAttribute && hasValueAttribute) ||
 976                 ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
 977             error(elementName +
 978                     " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
 979         }
 980 
 981         if (hasRefAttribute) {
 982             String refName = ele.getAttribute(REF_ATTRIBUTE);
 983             if (!StringUtils.hasText(refName)) {
 984                 error(elementName + " contains empty 'ref' attribute", ele);
 985             }
 986             RuntimeBeanReference ref = new RuntimeBeanReference(refName);
 987             ref.setSource(extractSource(ele));
 988             return ref;
 989         }
 990         else if (hasValueAttribute) {
 991             TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
 992             valueHolder.setSource(extractSource(ele));
 993             return valueHolder;
 994         }
 995         else if (subElement != null) {
 996             return parsePropertySubElement(subElement, bd);
 997         }
 998         else {
 999             // Neither child element nor "ref" or "value" attribute found.
1000             error(elementName + " must specify a ref or value", ele);
1001             return null;
1002         }
1003     }
1004 
1005     public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
1006         return parsePropertySubElement(ele, bd, null);
1007     }
1008 
1009     /**
1010      * Parse a value, ref or collection sub-element of a property or
1011      * constructor-arg element.
1012      * @param ele subelement of property element; we don't know which yet
1013      * @param defaultValueType the default type (class name) for any
1014      * {@code <value>} tag that might be created
1015      */
1016     public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
1017         if (!isDefaultNamespace(ele)) {
1018             return parseNestedCustomElement(ele, bd);
1019         }
1020         else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
1021             BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
1022             if (nestedBd != null) {
1023                 nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
1024             }
1025             return nestedBd;
1026         }
1027         else if (nodeNameEquals(ele, REF_ELEMENT)) {
1028             // A generic reference to any name of any bean.
1029             String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
1030             boolean toParent = false;
1031             if (!StringUtils.hasLength(refName)) {
1032                 // A reference to the id of another bean in the same XML file.
1033                 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
1034                 if (!StringUtils.hasLength(refName)) {
1035                     // A reference to the id of another bean in a parent context.
1036                     refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
1037                     toParent = true;
1038                     if (!StringUtils.hasLength(refName)) {
1039                         error("'bean', 'local' or 'parent' is required for <ref> element", ele);
1040                         return null;
1041                     }
1042                 }
1043             }
1044             if (!StringUtils.hasText(refName)) {
1045                 error("<ref> element contains empty target attribute", ele);
1046                 return null;
1047             }
1048             RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
1049             ref.setSource(extractSource(ele));
1050             return ref;
1051         }
1052         else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
1053             return parseIdRefElement(ele);
1054         }
1055         else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
1056             return parseValueElement(ele, defaultValueType);
1057         }
1058         else if (nodeNameEquals(ele, NULL_ELEMENT)) {
1059             // It's a distinguished null value. Let's wrap it in a TypedStringValue
1060             // object in order to preserve the source location.
1061             TypedStringValue nullHolder = new TypedStringValue(null);
1062             nullHolder.setSource(extractSource(ele));
1063             return nullHolder;
1064         }
1065         else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
1066             return parseArrayElement(ele, bd);
1067         }
1068         else if (nodeNameEquals(ele, LIST_ELEMENT)) {
1069             return parseListElement(ele, bd);
1070         }
1071         else if (nodeNameEquals(ele, SET_ELEMENT)) {
1072             return parseSetElement(ele, bd);
1073         }
1074         else if (nodeNameEquals(ele, MAP_ELEMENT)) {
1075             return parseMapElement(ele, bd);
1076         }
1077         else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
1078             return parsePropsElement(ele);
1079         }
1080         else {
1081             error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
1082             return null;
1083         }
1084     }
1085 
1086     /**
1087      * Return a typed String value Object for the given 'idref' element.
1088      */
1089     public Object parseIdRefElement(Element ele) {
1090         // A generic reference to any name of any bean.
1091         String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
1092         if (!StringUtils.hasLength(refName)) {
1093             // A reference to the id of another bean in the same XML file.
1094             refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
1095             if (!StringUtils.hasLength(refName)) {
1096                 error("Either 'bean' or 'local' is required for <idref> element", ele);
1097                 return null;
1098             }
1099         }
1100         if (!StringUtils.hasText(refName)) {
1101             error("<idref> element contains empty target attribute", ele);
1102             return null;
1103         }
1104         RuntimeBeanNameReference ref = new RuntimeBeanNameReference(refName);
1105         ref.setSource(extractSource(ele));
1106         return ref;
1107     }
1108 
1109     /**
1110      * Return a typed String value Object for the given value element.
1111      */
1112     public Object parseValueElement(Element ele, String defaultTypeName) {
1113         // It's a literal value.
1114         String value = DomUtils.getTextValue(ele);
1115         String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE);
1116         String typeName = specifiedTypeName;
1117         if (!StringUtils.hasText(typeName)) {
1118             typeName = defaultTypeName;
1119         }
1120         try {
1121             TypedStringValue typedValue = buildTypedStringValue(value, typeName);
1122             typedValue.setSource(extractSource(ele));
1123             typedValue.setSpecifiedTypeName(specifiedTypeName);
1124             return typedValue;
1125         }
1126         catch (ClassNotFoundException ex) {
1127             error("Type class [" + typeName + "] not found for <value> element", ele, ex);
1128             return value;
1129         }
1130     }
1131 
1132     /**
1133      * Build a typed String value Object for the given raw value.
1134      * @see org.springframework.beans.factory.config.TypedStringValue
1135      */
1136     protected TypedStringValue buildTypedStringValue(String value, String targetTypeName)
1137             throws ClassNotFoundException {
1138 
1139         ClassLoader classLoader = this.readerContext.getBeanClassLoader();
1140         TypedStringValue typedValue;
1141         if (!StringUtils.hasText(targetTypeName)) {
1142             typedValue = new TypedStringValue(value);
1143         }
1144         else if (classLoader != null) {
1145             Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader);
1146             typedValue = new TypedStringValue(value, targetType);
1147         }
1148         else {
1149             typedValue = new TypedStringValue(value, targetTypeName);
1150         }
1151         return typedValue;
1152     }
1153 
1154     /**
1155      * Parse an array element.
1156      */
1157     public Object parseArrayElement(Element arrayEle, BeanDefinition bd) {
1158         String elementType = arrayEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
1159         NodeList nl = arrayEle.getChildNodes();
1160         ManagedArray target = new ManagedArray(elementType, nl.getLength());
1161         target.setSource(extractSource(arrayEle));
1162         target.setElementTypeName(elementType);
1163         target.setMergeEnabled(parseMergeAttribute(arrayEle));
1164         parseCollectionElements(nl, target, bd, elementType);
1165         return target;
1166     }
1167 
1168     /**
1169      * Parse a list element.
1170      */
1171     public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) {
1172         String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
1173         NodeList nl = collectionEle.getChildNodes();
1174         ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
1175         target.setSource(extractSource(collectionEle));
1176         target.setElementTypeName(defaultElementType);
1177         target.setMergeEnabled(parseMergeAttribute(collectionEle));
1178         parseCollectionElements(nl, target, bd, defaultElementType);
1179         return target;
1180     }
1181 
1182     /**
1183      * Parse a set element.
1184      */
1185     public Set<Object> parseSetElement(Element collectionEle, BeanDefinition bd) {
1186         String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
1187         NodeList nl = collectionEle.getChildNodes();
1188         ManagedSet<Object> target = new ManagedSet<Object>(nl.getLength());
1189         target.setSource(extractSource(collectionEle));
1190         target.setElementTypeName(defaultElementType);
1191         target.setMergeEnabled(parseMergeAttribute(collectionEle));
1192         parseCollectionElements(nl, target, bd, defaultElementType);
1193         return target;
1194     }
1195 
1196     protected void parseCollectionElements(
1197             NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
1198 
1199         for (int i = 0; i < elementNodes.getLength(); i++) {
1200             Node node = elementNodes.item(i);
1201             if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
1202                 target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
1203             }
1204         }
1205     }
1206 
1207     /**
1208      * Parse a map element.
1209      */
1210     public Map<Object, Object> parseMapElement(Element mapEle, BeanDefinition bd) {
1211         String defaultKeyType = mapEle.getAttribute(KEY_TYPE_ATTRIBUTE);
1212         String defaultValueType = mapEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
1213 
1214         List<Element> entryEles = DomUtils.getChildElementsByTagName(mapEle, ENTRY_ELEMENT);
1215         ManagedMap<Object, Object> map = new ManagedMap<Object, Object>(entryEles.size());
1216         map.setSource(extractSource(mapEle));
1217         map.setKeyTypeName(defaultKeyType);
1218         map.setValueTypeName(defaultValueType);
1219         map.setMergeEnabled(parseMergeAttribute(mapEle));
1220 
1221         for (Element entryEle : entryEles) {
1222             // Should only have one value child element: ref, value, list, etc.
1223             // Optionally, there might be a key child element.
1224             NodeList entrySubNodes = entryEle.getChildNodes();
1225             Element keyEle = null;
1226             Element valueEle = null;
1227             for (int j = 0; j < entrySubNodes.getLength(); j++) {
1228                 Node node = entrySubNodes.item(j);
1229                 if (node instanceof Element) {
1230                     Element candidateEle = (Element) node;
1231                     if (nodeNameEquals(candidateEle, KEY_ELEMENT)) {
1232                         if (keyEle != null) {
1233                             error("<entry> element is only allowed to contain one <key> sub-element", entryEle);
1234                         }
1235                         else {
1236                             keyEle = candidateEle;
1237                         }
1238                     }
1239                     else {
1240                         // Child element is what we're looking for.
1241                         if (nodeNameEquals(candidateEle, DESCRIPTION_ELEMENT)) {
1242                             // the element is a <description> -> ignore it
1243                         }
1244                         else if (valueEle != null) {
1245                             error("<entry> element must not contain more than one value sub-element", entryEle);
1246                         }
1247                         else {
1248                             valueEle = candidateEle;
1249                         }
1250                     }
1251                 }
1252             }
1253 
1254             // Extract key from attribute or sub-element.
1255             Object key = null;
1256             boolean hasKeyAttribute = entryEle.hasAttribute(KEY_ATTRIBUTE);
1257             boolean hasKeyRefAttribute = entryEle.hasAttribute(KEY_REF_ATTRIBUTE);
1258             if ((hasKeyAttribute && hasKeyRefAttribute) ||
1259                     ((hasKeyAttribute || hasKeyRefAttribute)) && keyEle != null) {
1260                 error("<entry> element is only allowed to contain either " +
1261                         "a 'key' attribute OR a 'key-ref' attribute OR a <key> sub-element", entryEle);
1262             }
1263             if (hasKeyAttribute) {
1264                 key = buildTypedStringValueForMap(entryEle.getAttribute(KEY_ATTRIBUTE), defaultKeyType, entryEle);
1265             }
1266             else if (hasKeyRefAttribute) {
1267                 String refName = entryEle.getAttribute(KEY_REF_ATTRIBUTE);
1268                 if (!StringUtils.hasText(refName)) {
1269                     error("<entry> element contains empty 'key-ref' attribute", entryEle);
1270                 }
1271                 RuntimeBeanReference ref = new RuntimeBeanReference(refName);
1272                 ref.setSource(extractSource(entryEle));
1273                 key = ref;
1274             }
1275             else if (keyEle != null) {
1276                 key = parseKeyElement(keyEle, bd, defaultKeyType);
1277             }
1278             else {
1279                 error("<entry> element must specify a key", entryEle);
1280             }
1281 
1282             // Extract value from attribute or sub-element.
1283             Object value = null;
1284             boolean hasValueAttribute = entryEle.hasAttribute(VALUE_ATTRIBUTE);
1285             boolean hasValueRefAttribute = entryEle.hasAttribute(VALUE_REF_ATTRIBUTE);
1286             boolean hasValueTypeAttribute = entryEle.hasAttribute(VALUE_TYPE_ATTRIBUTE);
1287             if ((hasValueAttribute && hasValueRefAttribute) ||
1288                     ((hasValueAttribute || hasValueRefAttribute)) && valueEle != null) {
1289                 error("<entry> element is only allowed to contain either " +
1290                         "'value' attribute OR 'value-ref' attribute OR <value> sub-element", entryEle);
1291             }
1292             if ((hasValueTypeAttribute && hasValueRefAttribute) ||
1293                 (hasValueTypeAttribute && !hasValueAttribute) ||
1294                     (hasValueTypeAttribute && valueEle != null)) {
1295                 error("<entry> element is only allowed to contain a 'value-type' " +
1296                         "attribute when it has a 'value' attribute", entryEle);
1297             }
1298             if (hasValueAttribute) {
1299                 String valueType = entryEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
1300                 if (!StringUtils.hasText(valueType)) {
1301                     valueType = defaultValueType;
1302                 }
1303                 value = buildTypedStringValueForMap(entryEle.getAttribute(VALUE_ATTRIBUTE), valueType, entryEle);
1304             }
1305             else if (hasValueRefAttribute) {
1306                 String refName = entryEle.getAttribute(VALUE_REF_ATTRIBUTE);
1307                 if (!StringUtils.hasText(refName)) {
1308                     error("<entry> element contains empty 'value-ref' attribute", entryEle);
1309                 }
1310                 RuntimeBeanReference ref = new RuntimeBeanReference(refName);
1311                 ref.setSource(extractSource(entryEle));
1312                 value = ref;
1313             }
1314             else if (valueEle != null) {
1315                 value = parsePropertySubElement(valueEle, bd, defaultValueType);
1316             }
1317             else {
1318                 error("<entry> element must specify a value", entryEle);
1319             }
1320 
1321             // Add final key and value to the Map.
1322             map.put(key, value);
1323         }
1324 
1325         return map;
1326     }
1327 
1328     /**
1329      * Build a typed String value Object for the given raw value.
1330      * @see org.springframework.beans.factory.config.TypedStringValue
1331      */
1332     protected final Object buildTypedStringValueForMap(String value, String defaultTypeName, Element entryEle) {
1333         try {
1334             TypedStringValue typedValue = buildTypedStringValue(value, defaultTypeName);
1335             typedValue.setSource(extractSource(entryEle));
1336             return typedValue;
1337         }
1338         catch (ClassNotFoundException ex) {
1339             error("Type class [" + defaultTypeName + "] not found for Map key/value type", entryEle, ex);
1340             return value;
1341         }
1342     }
1343 
1344     /**
1345      * Parse a key sub-element of a map element.
1346      */
1347     protected Object parseKeyElement(Element keyEle, BeanDefinition bd, String defaultKeyTypeName) {
1348         NodeList nl = keyEle.getChildNodes();
1349         Element subElement = null;
1350         for (int i = 0; i < nl.getLength(); i++) {
1351             Node node = nl.item(i);
1352             if (node instanceof Element) {
1353                 // Child element is what we're looking for.
1354                 if (subElement != null) {
1355                     error("<key> element must not contain more than one value sub-element", keyEle);
1356                 }
1357                 else {
1358                     subElement = (Element) node;
1359                 }
1360             }
1361         }
1362         return parsePropertySubElement(subElement, bd, defaultKeyTypeName);
1363     }
1364 
1365     /**
1366      * Parse a props element.
1367      */
1368     public Properties parsePropsElement(Element propsEle) {
1369         ManagedProperties props = new ManagedProperties();
1370         props.setSource(extractSource(propsEle));
1371         props.setMergeEnabled(parseMergeAttribute(propsEle));
1372 
1373         List<Element> propEles = DomUtils.getChildElementsByTagName(propsEle, PROP_ELEMENT);
1374         for (Element propEle : propEles) {
1375             String key = propEle.getAttribute(KEY_ATTRIBUTE);
1376             // Trim the text value to avoid unwanted whitespace
1377             // caused by typical XML formatting.
1378             String value = DomUtils.getTextValue(propEle).trim();
1379             TypedStringValue keyHolder = new TypedStringValue(key);
1380             keyHolder.setSource(extractSource(propEle));
1381             TypedStringValue valueHolder = new TypedStringValue(value);
1382             valueHolder.setSource(extractSource(propEle));
1383             props.put(keyHolder, valueHolder);
1384         }
1385 
1386         return props;
1387     }
1388 
1389     /**
1390      * Parse the merge attribute of a collection element, if any.
1391      */
1392     public boolean parseMergeAttribute(Element collectionElement) {
1393         String value = collectionElement.getAttribute(MERGE_ATTRIBUTE);
1394         if (DEFAULT_VALUE.equals(value)) {
1395             value = this.defaults.getMerge();
1396         }
1397         return TRUE_VALUE.equals(value);
1398     }
1399 
1400     public BeanDefinition parseCustomElement(Element ele) {
1401         return parseCustomElement(ele, null);
1402     }
1403 
1404     public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
1405         String namespaceUri = getNamespaceURI(ele);
1406         NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
1407         if (handler == null) {
1408             error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
1409             return null;
1410         }
1411         return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
1412     }
1413 
1414     public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
1415         return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
1416     }
1417 
1418     public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
1419             Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
1420 
1421         BeanDefinitionHolder finalDefinition = definitionHolder;
1422 
1423         // Decorate based on custom attributes first.
1424         NamedNodeMap attributes = ele.getAttributes();
1425         for (int i = 0; i < attributes.getLength(); i++) {
1426             Node node = attributes.item(i);
1427             finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
1428         }
1429 
1430         // Decorate based on custom nested elements.
1431         NodeList children = ele.getChildNodes();
1432         for (int i = 0; i < children.getLength(); i++) {
1433             Node node = children.item(i);
1434             if (node.getNodeType() == Node.ELEMENT_NODE) {
1435                 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
1436             }
1437         }
1438         return finalDefinition;
1439     }
1440 
1441     public BeanDefinitionHolder decorateIfRequired(
1442             Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
1443 
1444         String namespaceUri = getNamespaceURI(node);
1445         if (!isDefaultNamespace(namespaceUri)) {
1446             NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
1447             if (handler != null) {
1448                 return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
1449             }
1450             else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
1451                 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
1452             }
1453             else {
1454                 // A custom namespace, not to be handled by Spring - maybe "xml:...".
1455                 if (logger.isDebugEnabled()) {
1456                     logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
1457                 }
1458             }
1459         }
1460         return originalDef;
1461     }
1462 
1463     private BeanDefinitionHolder parseNestedCustomElement(Element ele, BeanDefinition containingBd) {
1464         BeanDefinition innerDefinition = parseCustomElement(ele, containingBd);
1465         if (innerDefinition == null) {
1466             error("Incorrect usage of element '" + ele.getNodeName() + "' in a nested manner. " +
1467                     "This tag cannot be used nested inside <property>.", ele);
1468             return null;
1469         }
1470         String id = ele.getNodeName() + BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR +
1471                 ObjectUtils.getIdentityHexString(innerDefinition);
1472         if (logger.isDebugEnabled()) {
1473             logger.debug("Using generated bean name [" + id +
1474                     "] for nested custom element '" + ele.getNodeName() + "'");
1475         }
1476         return new BeanDefinitionHolder(innerDefinition, id);
1477     }
1478 
1479 
1480     /**
1481      * Get the namespace URI for the supplied node.
1482      * <p>The default implementation uses {@link Node#getNamespaceURI}.
1483      * Subclasses may override the default implementation to provide a
1484      * different namespace identification mechanism.
1485      * @param node the node
1486      */
1487     public String getNamespaceURI(Node node) {
1488         return node.getNamespaceURI();
1489     }
1490 
1491     /**
1492      * Get the local name for the supplied {@link Node}.
1493      * <p>The default implementation calls {@link Node#getLocalName}.
1494      * Subclasses may override the default implementation to provide a
1495      * different mechanism for getting the local name.
1496      * @param node the {@code Node}
1497      */
1498     public String getLocalName(Node node) {
1499         return node.getLocalName();
1500     }
1501 
1502     /**
1503      * Determine whether the name of the supplied node is equal to the supplied name.
1504      * <p>The default implementation checks the supplied desired name against both
1505      * {@link Node#getNodeName()} and {@link Node#getLocalName()}.
1506      * <p>Subclasses may override the default implementation to provide a different
1507      * mechanism for comparing node names.
1508      * @param node the node to compare
1509      * @param desiredName the name to check for
1510      */
1511     public boolean nodeNameEquals(Node node, String desiredName) {
1512         return desiredName.equals(node.getNodeName()) || desiredName.equals(getLocalName(node));
1513     }
1514 
1515     public boolean isDefaultNamespace(String namespaceUri) {
1516         return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
1517     }
1518 
1519     public boolean isDefaultNamespace(Node node) {
1520         return isDefaultNamespace(getNamespaceURI(node));
1521     }
1522 
1523     private boolean isCandidateElement(Node node) {
1524         return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode())));
1525     }
1526 
1527 }

容器的基础XmlBeanFactory

1  BeanFactory bf  = new XmlBeanFactory(new ClassPathResource("beans.xml"));

逻辑处理顺序

调用ClassPathResource的构造函数来构造Resource资源文件的实例对象,这样后续的资源处理就可以用Resource提供的各种服务来操作,当有了Resource后就可以进行XmlBeanFactory的初始化了。

配置文件封装

Spring的配置文件读取是通过ClassPathResource进行封装的,如new ClassPathResource("beans.xml"),那么ClassPathResource究竟做了什么呢?

在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHanler)来处理不同来源的资源额读取逻辑,一般handler的类型使用不同前缀来识别,然而URL没有默认定义相对的Classpath或ServletContext等资源的handler,虽然可以注册自己的URLStreamHandler来解析特定的URL前缀,比如“classpath:”。Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源。

  1 /*
  2  * Copyright 2002-2017 the original author or authors.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package org.springframework.core.io;
 18 
 19 import java.io.File;
 20 import java.io.IOException;
 21 import java.net.URI;
 22 import java.net.URL;
 23 
 24 /**
 25  * Interface for a resource descriptor that abstracts from the actual
 26  * type of underlying resource, such as a file or class path resource.
 27  *
 28  * <p>An InputStream can be opened for every resource if it exists in
 29  * physical form, but a URL or File handle can just be returned for
 30  * certain resources. The actual behavior is implementation-specific.
 31  *
 32  * @author Juergen Hoeller
 33  * @since 28.12.2003
 34  * @see #getInputStream()
 35  * @see #getURL()
 36  * @see #getURI()
 37  * @see #getFile()
 38  * @see WritableResource
 39  * @see ContextResource
 40  * @see UrlResource
 41  * @see ClassPathResource
 42  * @see FileSystemResource
 43  * @see PathResource
 44  * @see ByteArrayResource
 45  * @see InputStreamResource
 46  */
 47 public interface Resource extends InputStreamSource {
 48 
 49     /**
 50      * Determine whether this resource actually exists in physical form.
 51      * <p>This method performs a definitive existence check, whereas the
 52      * existence of a {@code Resource} handle only guarantees a valid
 53      * descriptor handle.
 54      */
 55     boolean exists();
 56 
 57     /**
 58      * Indicate whether the contents of this resource can be read via
 59      * {@link #getInputStream()}.
 60      * <p>Will be {@code true} for typical resource descriptors;
 61      * note that actual content reading may still fail when attempted.
 62      * However, a value of {@code false} is a definitive indication
 63      * that the resource content cannot be read.
 64      * @see #getInputStream()
 65      */
 66     boolean isReadable();
 67 
 68     /**
 69      * Indicate whether this resource represents a handle with an open stream.
 70      * If {@code true}, the InputStream cannot be read multiple times,
 71      * and must be read and closed to avoid resource leaks.
 72      * <p>Will be {@code false} for typical resource descriptors.
 73      */
 74     boolean isOpen();
 75 
 76     /**
 77      * Return a URL handle for this resource.
 78      * @throws IOException if the resource cannot be resolved as URL,
 79      * i.e. if the resource is not available as descriptor
 80      */
 81     URL getURL() throws IOException;
 82 
 83     /**
 84      * Return a URI handle for this resource.
 85      * @throws IOException if the resource cannot be resolved as URI,
 86      * i.e. if the resource is not available as descriptor
 87      * @since 2.5
 88      */
 89     URI getURI() throws IOException;
 90 
 91     /**
 92      * Return a File handle for this resource.
 93      * @throws java.io.FileNotFoundException if the resource cannot be resolved as
 94      * absolute file path, i.e. if the resource is not available in a file system
 95      * @throws IOException in case of general resolution/reading failures
 96      * @see #getInputStream()
 97      */
 98     File getFile() throws IOException;
 99 
100     /**
101      * Determine the content length for this resource.
102      * @throws IOException if the resource cannot be resolved
103      * (in the file system or as some other known physical resource type)
104      */
105     long contentLength() throws IOException;
106 
107     /**
108      * Determine the last-modified timestamp for this resource.
109      * @throws IOException if the resource cannot be resolved
110      * (in the file system or as some other known physical resource type)
111      */
112     long lastModified() throws IOException;
113 
114     /**
115      * Create a resource relative to this resource.
116      * @param relativePath the relative path (relative to this resource)
117      * @return the resource handle for the relative resource
118      * @throws IOException if the relative resource cannot be determined
119      */
120     Resource createRelative(String relativePath) throws IOException;
121 
122     /**
123      * Determine a filename for this resource, i.e. typically the last
124      * part of the path: for example, "myfile.txt".
125      * <p>Returns {@code null} if this type of resource does not
126      * have a filename.
127      */
128     String getFilename();
129 
130     /**
131      * Return a description for this resource,
132      * to be used for error output when working with the resource.
133      * <p>Implementations are also encouraged to return this value
134      * from their {@code toString} method.
135      * @see Object#toString()
136      */
137     String getDescription();
138 
139 }

Resource接口抽象了所有Spring内部使用到的底层资源:File、URL、Classpath等,首先它定义了3个判断当前资源状态的方法:存在性(exists)、可读性(isReadable)、是否处于打开状态(isOpen).另外,Resource接口还提供了不同资源到URL、URI、File类型的转换,以及获取lastModified属性、文件名的方法。为了便于操作,Resource还提供了基于当前资源创建的一个相对资源的方法:createRelative().在错误处理中需要详细地打印出错的资源文件,因而Resource还提供了getDescription()方法用于在错误处理中的打印信息。

对不同来源的资源文件都有对应的Resource实现:文件(FileSystemResource)、Classpath资源(ClassPathResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等。

在平时加载资源文件,可以直接使用Spring提供的类,比如

1 Resource resource = new ClassPathResource("beans.xml");
2 InputStream inputStream = resource.getInputStream();

ClassPathResource通过class或者classLoader提供的底层方法进行调用:

 1 @Override
 2     public InputStream getInputStream() throws IOException {
 3         InputStream is;
 4         if (this.clazz != null) {
 5             is = this.clazz.getResourceAsStream(this.path);
 6         }
 7         else if (this.classLoader != null) {
 8             is = this.classLoader.getResourceAsStream(this.path);
 9         }
10         else {
11             is = ClassLoader.getSystemResourceAsStream(this.path);
12         }
13         if (is == null) {
14             throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
15         }
16         return is;
17     }

FileSystemResource直接使用FileInputStream对文件进行实例化:

1       @Override
2     public InputStream getInputStream() throws IOException {
3         return new FileInputStream(this.file);
4     }

当通过Resource相关类完成了对配置文件进行封装后配置文件的读取工作就全权交给XmlBeanDefinitionReader来处理了。

XmlBeanFactory的初始化过程:

 1 /**
 2      * Create a new XmlBeanFactory with the given resource,
 3      * which must be parsable using DOM.
 4      * @param resource XML resource to load bean definitions from
 5      * @throws BeansException in case of loading or parsing errors
 6      */
 7     public XmlBeanFactory(Resource resource) throws BeansException {
 8         this(resource, null);
 9     }
10 
11     /**
12      * Create a new XmlBeanFactory with the given input stream,
13      * which must be parsable using DOM.
14      * @param resource XML resource to load bean definitions from
15      * @param parentBeanFactory parent bean factory
16      * @throws BeansException in case of loading or parsing errors
17      */
18     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
19         super(parentBeanFactory);
20         this.reader.loadBeanDefinitions(resource);
21     }

自动装配时忽略给定的依赖接口,典型应用是通过其他方式解析Application上下文注册依赖,类似于BeanFactory通过BeanFactoryAware进行注入或者ApplicationContext通过ApplicationContextAware进行注入。

原文地址:https://www.cnblogs.com/dream-to-pku/p/7591059.html