/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.xml;

import org.w3c.dom.Element;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

Abstract BeanDefinitionParser implementation providing a number of convenience methods and a template method that subclasses must override to provide the actual parsing logic.

Use this BeanDefinitionParser implementation when you want to parse some arbitrarily complex XML into one or more BeanDefinitions. If you just want to parse some XML into a single BeanDefinition, you may wish to consider the simpler convenience extensions of this class, namely AbstractSingleBeanDefinitionParser and AbstractSimpleBeanDefinitionParser.

Author:Rob Harrop, Juergen Hoeller, Rick Evans, Dave Syer
Since:2.0
/** * Abstract {@link BeanDefinitionParser} implementation providing * a number of convenience methods and a * {@link AbstractBeanDefinitionParser#parseInternal template method} * that subclasses must override to provide the actual parsing logic. * * <p>Use this {@link BeanDefinitionParser} implementation when you want * to parse some arbitrarily complex XML into one or more * {@link BeanDefinition BeanDefinitions}. If you just want to parse some * XML into a single {@code BeanDefinition}, you may wish to consider * the simpler convenience extensions of this class, namely * {@link AbstractSingleBeanDefinitionParser} and * {@link AbstractSimpleBeanDefinitionParser}. * * @author Rob Harrop * @author Juergen Hoeller * @author Rick Evans * @author Dave Syer * @since 2.0 */
public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
Constant for the "id" attribute.
/** Constant for the "id" attribute. */
public static final String ID_ATTRIBUTE = "id";
Constant for the "name" attribute.
/** Constant for the "name" attribute. */
public static final String NAME_ATTRIBUTE = "name"; @Override @Nullable public final BeanDefinition parse(Element element, ParserContext parserContext) { AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { String id = resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error( "Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element); } String[] aliases = null; if (shouldParseNameAsAliases()) { String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { String msg = ex.getMessage(); parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element); return null; } } return definition; }
Resolve the ID for the supplied BeanDefinition.

When using generation, a name is generated automatically. Otherwise, the ID is extracted from the "id" attribute, potentially with a fallback to a generated id.

Params:
  • element – the element that the bean definition has been built from
  • definition – the bean definition to be registered
  • parserContext – the object encapsulating the current state of the parsing process; provides access to a BeanDefinitionRegistry
Throws:
Returns:the resolved id
/** * Resolve the ID for the supplied {@link BeanDefinition}. * <p>When using {@link #shouldGenerateId generation}, a name is generated automatically. * Otherwise, the ID is extracted from the "id" attribute, potentially with a * {@link #shouldGenerateIdAsFallback() fallback} to a generated id. * @param element the element that the bean definition has been built from * @param definition the bean definition to be registered * @param parserContext the object encapsulating the current state of the parsing process; * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} * @return the resolved id * @throws BeanDefinitionStoreException if no unique name could be generated * for the given bean definition */
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { if (shouldGenerateId()) { return parserContext.getReaderContext().generateBeanName(definition); } else { String id = element.getAttribute(ID_ATTRIBUTE); if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) { id = parserContext.getReaderContext().generateBeanName(definition); } return id; } }
Register the supplied bean with the supplied registry.

Subclasses can override this method to control whether or not the supplied bean is actually even registered, or to register even more beans.

The default implementation registers the supplied bean with the supplied registry only if the isNested parameter is false, because one typically does not want inner beans to be registered as top level beans.

Params:
  • definition – the bean definition to be registered
  • registry – the registry that the bean is to be registered with
See Also:
/** * Register the supplied {@link BeanDefinitionHolder bean} with the supplied * {@link BeanDefinitionRegistry registry}. * <p>Subclasses can override this method to control whether or not the supplied * {@link BeanDefinitionHolder bean} is actually even registered, or to * register even more beans. * <p>The default implementation registers the supplied {@link BeanDefinitionHolder bean} * with the supplied {@link BeanDefinitionRegistry registry} only if the {@code isNested} * parameter is {@code false}, because one typically does not want inner beans * to be registered as top level beans. * @param definition the bean definition to be registered * @param registry the registry that the bean is to be registered with * @see BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry) */
protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry); }
Central template method to actually parse the supplied Element into one or more BeanDefinitions.
Params:
  • element – the element that is to be parsed into one or more BeanDefinitions
  • parserContext – the object encapsulating the current state of the parsing process; provides access to a BeanDefinitionRegistry
See Also:
Returns:the primary BeanDefinition resulting from the parsing of the supplied Element
/** * Central template method to actually parse the supplied {@link Element} * into one or more {@link BeanDefinition BeanDefinitions}. * @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions} * @param parserContext the object encapsulating the current state of the parsing process; * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} * @return the primary {@link BeanDefinition} resulting from the parsing of the supplied {@link Element} * @see #parse(org.w3c.dom.Element, ParserContext) * @see #postProcessComponentDefinition(org.springframework.beans.factory.parsing.BeanComponentDefinition) */
@Nullable protected abstract AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext);
Should an ID be generated instead of read from the passed in Element?

Disabled by default; subclasses can override this to enable ID generation. Note that this flag is about always generating an ID; the parser won't even check for an "id" attribute in this case.

Returns:whether the parser should always generate an id
/** * Should an ID be generated instead of read from the passed in {@link Element}? * <p>Disabled by default; subclasses can override this to enable ID generation. * Note that this flag is about <i>always</i> generating an ID; the parser * won't even check for an "id" attribute in this case. * @return whether the parser should always generate an id */
protected boolean shouldGenerateId() { return false; }
Should an ID be generated instead if the passed in Element does not specify an "id" attribute explicitly?

Disabled by default; subclasses can override this to enable ID generation as fallback: The parser will first check for an "id" attribute in this case, only falling back to a generated ID if no value was specified.

Returns:whether the parser should generate an id if no id was specified
/** * Should an ID be generated instead if the passed in {@link Element} does not * specify an "id" attribute explicitly? * <p>Disabled by default; subclasses can override this to enable ID generation * as fallback: The parser will first check for an "id" attribute in this case, * only falling back to a generated ID if no value was specified. * @return whether the parser should generate an id if no id was specified */
protected boolean shouldGenerateIdAsFallback() { return false; }
Determine whether the element's "name" attribute should get parsed as bean definition aliases, i.e. alternative bean definition names.

The default implementation returns true.

Returns:whether the parser should evaluate the "name" attribute as aliases
Since:4.1.5
/** * Determine whether the element's "name" attribute should get parsed as * bean definition aliases, i.e. alternative bean definition names. * <p>The default implementation returns {@code true}. * @return whether the parser should evaluate the "name" attribute as aliases * @since 4.1.5 */
protected boolean shouldParseNameAsAliases() { return true; }
Determine whether this parser is supposed to fire a BeanComponentDefinition event after parsing the bean definition.

This implementation returns true by default; that is, an event will be fired when a bean definition has been completely parsed. Override this to return false in order to suppress the event.

See Also:
Returns:true in order to fire a component registration event after parsing the bean definition; false to suppress the event
/** * Determine whether this parser is supposed to fire a * {@link org.springframework.beans.factory.parsing.BeanComponentDefinition} * event after parsing the bean definition. * <p>This implementation returns {@code true} by default; that is, * an event will be fired when a bean definition has been completely parsed. * Override this to return {@code false} in order to suppress the event. * @return {@code true} in order to fire a component registration event * after parsing the bean definition; {@code false} to suppress the event * @see #postProcessComponentDefinition * @see org.springframework.beans.factory.parsing.ReaderContext#fireComponentRegistered */
protected boolean shouldFireEvents() { return true; }
Hook method called after the primary parsing of a BeanComponentDefinition but before the BeanComponentDefinition has been registered with a BeanDefinitionRegistry.

Derived classes can override this method to supply any custom logic that is to be executed after all the parsing is finished.

The default implementation is a no-op.

Params:
/** * Hook method called after the primary parsing of a * {@link BeanComponentDefinition} but before the * {@link BeanComponentDefinition} has been registered with a * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}. * <p>Derived classes can override this method to supply any custom logic that * is to be executed after all the parsing is finished. * <p>The default implementation is a no-op. * @param componentDefinition the {@link BeanComponentDefinition} that is to be processed */
protected void postProcessComponentDefinition(BeanComponentDefinition componentDefinition) { } }