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