001/* 002 * Copyright 2002-2017 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 org.w3c.dom.Element; 020 021import org.springframework.beans.factory.BeanDefinitionStoreException; 022import org.springframework.beans.factory.config.BeanDefinition; 023import org.springframework.beans.factory.config.BeanDefinitionHolder; 024import org.springframework.beans.factory.parsing.BeanComponentDefinition; 025import org.springframework.beans.factory.support.AbstractBeanDefinition; 026import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; 027import org.springframework.beans.factory.support.BeanDefinitionRegistry; 028import org.springframework.util.StringUtils; 029 030/** 031 * Abstract {@link BeanDefinitionParser} implementation providing 032 * a number of convenience methods and a 033 * {@link AbstractBeanDefinitionParser#parseInternal template method} 034 * that subclasses must override to provide the actual parsing logic. 035 * 036 * <p>Use this {@link BeanDefinitionParser} implementation when you want 037 * to parse some arbitrarily complex XML into one or more 038 * {@link BeanDefinition BeanDefinitions}. If you just want to parse some 039 * XML into a single {@code BeanDefinition}, you may wish to consider 040 * the simpler convenience extensions of this class, namely 041 * {@link AbstractSingleBeanDefinitionParser} and 042 * {@link AbstractSimpleBeanDefinitionParser}. 043 * 044 * @author Rob Harrop 045 * @author Juergen Hoeller 046 * @author Rick Evans 047 * @author Dave Syer 048 * @since 2.0 049 */ 050public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser { 051 052 /** Constant for the "id" attribute */ 053 public static final String ID_ATTRIBUTE = "id"; 054 055 /** Constant for the "name" attribute */ 056 public static final String NAME_ATTRIBUTE = "name"; 057 058 059 @Override 060 public final BeanDefinition parse(Element element, ParserContext parserContext) { 061 AbstractBeanDefinition definition = parseInternal(element, parserContext); 062 if (definition != null && !parserContext.isNested()) { 063 try { 064 String id = resolveId(element, definition, parserContext); 065 if (!StringUtils.hasText(id)) { 066 parserContext.getReaderContext().error( 067 "Id is required for element '" + parserContext.getDelegate().getLocalName(element) 068 + "' when used as a top-level tag", element); 069 } 070 String[] aliases = null; 071 if (shouldParseNameAsAliases()) { 072 String name = element.getAttribute(NAME_ATTRIBUTE); 073 if (StringUtils.hasLength(name)) { 074 aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); 075 } 076 } 077 BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); 078 registerBeanDefinition(holder, parserContext.getRegistry()); 079 if (shouldFireEvents()) { 080 BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); 081 postProcessComponentDefinition(componentDefinition); 082 parserContext.registerComponent(componentDefinition); 083 } 084 } 085 catch (BeanDefinitionStoreException ex) { 086 parserContext.getReaderContext().error(ex.getMessage(), element); 087 return null; 088 } 089 } 090 return definition; 091 } 092 093 /** 094 * Resolve the ID for the supplied {@link BeanDefinition}. 095 * <p>When using {@link #shouldGenerateId generation}, a name is generated automatically. 096 * Otherwise, the ID is extracted from the "id" attribute, potentially with a 097 * {@link #shouldGenerateIdAsFallback() fallback} to a generated id. 098 * @param element the element that the bean definition has been built from 099 * @param definition the bean definition to be registered 100 * @param parserContext the object encapsulating the current state of the parsing process; 101 * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} 102 * @return the resolved id 103 * @throws BeanDefinitionStoreException if no unique name could be generated 104 * for the given bean definition 105 */ 106 protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) 107 throws BeanDefinitionStoreException { 108 109 if (shouldGenerateId()) { 110 return parserContext.getReaderContext().generateBeanName(definition); 111 } 112 else { 113 String id = element.getAttribute(ID_ATTRIBUTE); 114 if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) { 115 id = parserContext.getReaderContext().generateBeanName(definition); 116 } 117 return id; 118 } 119 } 120 121 /** 122 * Register the supplied {@link BeanDefinitionHolder bean} with the supplied 123 * {@link BeanDefinitionRegistry registry}. 124 * <p>Subclasses can override this method to control whether or not the supplied 125 * {@link BeanDefinitionHolder bean} is actually even registered, or to 126 * register even more beans. 127 * <p>The default implementation registers the supplied {@link BeanDefinitionHolder bean} 128 * with the supplied {@link BeanDefinitionRegistry registry} only if the {@code isNested} 129 * parameter is {@code false}, because one typically does not want inner beans 130 * to be registered as top level beans. 131 * @param definition the bean definition to be registered 132 * @param registry the registry that the bean is to be registered with 133 * @see BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry) 134 */ 135 protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { 136 BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry); 137 } 138 139 140 /** 141 * Central template method to actually parse the supplied {@link Element} 142 * into one or more {@link BeanDefinition BeanDefinitions}. 143 * @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions} 144 * @param parserContext the object encapsulating the current state of the parsing process; 145 * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} 146 * @return the primary {@link BeanDefinition} resulting from the parsing of the supplied {@link Element} 147 * @see #parse(org.w3c.dom.Element, ParserContext) 148 * @see #postProcessComponentDefinition(org.springframework.beans.factory.parsing.BeanComponentDefinition) 149 */ 150 protected abstract AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext); 151 152 /** 153 * Should an ID be generated instead of read from the passed in {@link Element}? 154 * <p>Disabled by default; subclasses can override this to enable ID generation. 155 * Note that this flag is about <i>always</i> generating an ID; the parser 156 * won't even check for an "id" attribute in this case. 157 * @return whether the parser should always generate an id 158 */ 159 protected boolean shouldGenerateId() { 160 return false; 161 } 162 163 /** 164 * Should an ID be generated instead if the passed in {@link Element} does not 165 * specify an "id" attribute explicitly? 166 * <p>Disabled by default; subclasses can override this to enable ID generation 167 * as fallback: The parser will first check for an "id" attribute in this case, 168 * only falling back to a generated ID if no value was specified. 169 * @return whether the parser should generate an id if no id was specified 170 */ 171 protected boolean shouldGenerateIdAsFallback() { 172 return false; 173 } 174 175 /** 176 * Determine whether the element's "name" attribute should get parsed as 177 * bean definition aliases, i.e. alternative bean definition names. 178 * <p>The default implementation returns {@code true}. 179 * @return whether the parser should evaluate the "name" attribute as aliases 180 * @since 4.1.5 181 */ 182 protected boolean shouldParseNameAsAliases() { 183 return true; 184 } 185 186 /** 187 * Determine whether this parser is supposed to fire a 188 * {@link org.springframework.beans.factory.parsing.BeanComponentDefinition} 189 * event after parsing the bean definition. 190 * <p>This implementation returns {@code true} by default; that is, 191 * an event will be fired when a bean definition has been completely parsed. 192 * Override this to return {@code false} in order to suppress the event. 193 * @return {@code true} in order to fire a component registration event 194 * after parsing the bean definition; {@code false} to suppress the event 195 * @see #postProcessComponentDefinition 196 * @see org.springframework.beans.factory.parsing.ReaderContext#fireComponentRegistered 197 */ 198 protected boolean shouldFireEvents() { 199 return true; 200 } 201 202 /** 203 * Hook method called after the primary parsing of a 204 * {@link BeanComponentDefinition} but before the 205 * {@link BeanComponentDefinition} has been registered with a 206 * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}. 207 * <p>Derived classes can override this method to supply any custom logic that 208 * is to be executed after all the parsing is finished. 209 * <p>The default implementation is a no-op. 210 * @param componentDefinition the {@link BeanComponentDefinition} that is to be processed 211 */ 212 protected void postProcessComponentDefinition(BeanComponentDefinition componentDefinition) { 213 } 214 215}