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