For advanced needs, consider using a {@link DefaultListableBeanFactory} with an {@link XmlBeanDefinitionReader}. The latter allows for reading from multiple XML resources and is highly configurable in its actual XML parsing behavior.
private Class<?> clazz; // ... ... public InputStream getInputStream()throws IOException { InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); }elseif (this.classLoader != null) { is = this.classLoader.getResourceAsStream(this.path); }else { is = ClassLoader.getSystemResourceAsStream(this.path); }if (is == null) { thrownewFileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; } }
publicintloadBeanDefinitions(EncodedResource encodedResource)throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); }
StringbeanName= id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } }
if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); }
AbstractBeanDefinitionbeanDefinition= parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); }else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. StringbeanClassName= beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } }catch (Exception ex) { error(ex.getMessage(), ele); returnnull; } } String[] aliasesArray = StringUtils.toStringArray(aliases); returnnewBeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } returnnull; }
In general, use this {@code GenericBeanDefinition} class for the purpose of registering user-visible bean definitions (which a post-processor might operate on, potentially even reconfiguring the parent name). Use {@code RootBeanDefinition} / {@code ChildBeanDefinition} where parent/child relationships happen to be pre-determined.
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // 不再兼容singleton,提示改用scope error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); }elseif (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // 解析scope属性 bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); }elseif (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); }
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { // 解析abstract属性 bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); }
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { StringelementName= (propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element");
// 除了meta和description之外,只允许有一个直属子节点 // Should only have one child element: ref, value, list, etc. NodeListnl= ele.getChildNodes(); ElementsubElement=null; for (inti=0; i < nl.getLength(); i++) { Nodenode= nl.item(i); if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { // Child element is what we're looking for. if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); }else { subElement = (Element) node; } } }
// ref属性、value属性、或者子节点 只能三者选其一 booleanhasRefAttribute= ele.hasAttribute(REF_ATTRIBUTE); booleanhasValueAttribute= ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); }
if (hasRefAttribute) { // ref StringrefName= ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReferenceref=newRuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; }elseif (hasValueAttribute) { // value TypedStringValuevalueHolder=newTypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; }elseif (subElement != null) { // 子节点 return parsePropertySubElement(subElement, bd); }else { // 非法 // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); returnnull; } }
// Decorate based on custom attributes first. NamedNodeMapattributes= ele.getAttributes(); for (inti=0; i < attributes.getLength(); i++) { Nodenode= attributes.item(i); finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); // 遍历检查属性 }
// Decorate based on custom nested elements. NodeListchildren= ele.getChildNodes(); for (inti=0; i < children.getLength(); i++) { Nodenode= children.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); // 遍历检查子节点 } } return finalDefinition; }
public BeanDefinitionHolder decorateIfRequired( Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { StringnamespaceUri= getNamespaceURI(node); if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { NamespaceHandlerhandler=this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler != null) { BeanDefinitionHolderdecorated= handler.decorate(node, originalDef, newParserContext(this.readerContext, this, containingBd)); if (decorated != null) { return decorated; } }elseif (namespaceUri.startsWith("http://www.springframework.org/")) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); }else { // A custom namespace, not to be handled by Spring - maybe "xml:...". if (logger.isDebugEnabled()) { logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); } } } return originalDef; }
// Map of bean definition objects, keyed by bean name privatefinal Map<String, BeanDefinition> beanDefinitionMap = newConcurrentHashMap<>(256);
// List of bean definition names, in registration order privatevolatile List<String> beanDefinitionNames = newArrayList<>(256);
publicvoidregisterBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// Map from alias to canonical name privatefinal Map<String, String> aliasMap = newConcurrentHashMap<>(16); publicvoidregisterAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } }else { StringregisteredName=this.aliasMap.get(alias); if (registeredName != null) { if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } if (!allowAliasOverriding()) { thrownewIllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } checkForAliasCircle(name, alias); // 当存在A->B时,若再存在A->C->B则抛出异常 this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } }
protectedvoidimportBeanDefinitionResource(Element ele) { Stringlocation= ele.getAttribute(RESOURCE_ATTRIBUTE); // 获取资源路径 if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; }
// Resolve system properties: e.g. "${user.dir}" // 替换环境变量 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
// Discover whether the location is an absolute or relative URI booleanabsoluteLocation=false; // 先尝试绝对路径获取,如果不行再按相对当前文件的地址获取 try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); }catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix "classpath*:" }
// Absolute or relative? if (absoluteLocation) { try { intimportCount= getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isTraceEnabled()) { logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } }catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } }else { // No URL -> considering resource location as relative to the current file. try { int importCount; ResourcerelativeResource= getReaderContext().getResource().createRelative(location); if (relativeResource.exists()) { importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); }else { StringbaseLocation= getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isTraceEnabled()) { logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } }catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); }catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } Resource[] actResArray = actualResources.toArray(newResource[0]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }