Package org.springframework.beans.factory.support

Source Code of org.springframework.beans.factory.support.AbstractBeanFactory

/*
* Copyright 2002-2007 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
*
*      http://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.support;

import java.beans.PropertyEditor;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.PropertyEditorRegistrySupport;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanIsAbstractException;
import org.springframework.beans.factory.BeanIsNotAFactoryException;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.CollectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;

/**
* Abstract base class for {@link org.springframework.beans.factory.BeanFactory}
* implementations, providing the full capabilities of the
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory} SPI.
* Does <i>not</i> assume a listable bean factory: can therefore also be used
* as base class for bean factory implementations which obtain bean definitions
* from some backend resource (where bean definition access is an expensive operation).
*
* <p>This class provides a singleton cache (through its base class
* {@link org.springframework.beans.factory.support.DefaultSingletonBeanRegistry},
* singleton/prototype determination, {@link org.springframework.beans.factory.FactoryBean}
* handling, aliases, bean definition merging for child bean definitions,
* and bean destruction ({@link org.springframework.beans.factory.DisposableBean}
* interface, custom destroy methods). Furthermore, it can manage a bean factory
* hierarchy (delegating to the parent in case of an unknown bean), through implementing
* the {@link org.springframework.beans.factory.HierarchicalBeanFactory} interface.
*
* <p>The main template methods to be implemented by subclasses are
* {@link #getBeanDefinition} and {@link #createBean}, retrieving a bean definition
* for a given bean name and creating a bean instance for a given bean definition,
* respectively. Default implementations of those operations can be found in
* {@link DefaultListableBeanFactory} and {@link AbstractAutowireCapableBeanFactory}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 15 April 2001
* @see #getBeanDefinition
* @see #createBean
* @see AbstractAutowireCapableBeanFactory#createBean
* @see DefaultListableBeanFactory#getBeanDefinition
*/
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {

  /** Parent bean factory, for bean inheritance support */
  private BeanFactory parentBeanFactory;

  /** ClassLoader to resolve bean class names with, if necessary */
  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

  /** ClassLoader to temporarily resolve bean class names with, if necessary */
  private ClassLoader tempClassLoader;

  /** Whether to cache bean metadata or rather reobtain it for every access */
  private boolean cacheBeanMetadata = true;

  /** Custom PropertyEditors to apply to the beans of this factory */
  private final Map customEditors = new HashMap();

  /** Custom PropertyEditorRegistrars to apply to the beans of this factory */
  private final Set propertyEditorRegistrars = new LinkedHashSet(16);

  /** A custom TypeConverter to use, overriding the default PropertyEditor mechanism */
  private TypeConverter typeConverter;

  /** BeanPostProcessors to apply in createBean */
  private final List beanPostProcessors = new ArrayList();

  /** Indicates whether any InstantiationAwareBeanPostProcessors have been registered */
  private boolean hasInstantiationAwareBeanPostProcessors;

  /** Indicates whether any DestructionAwareBeanPostProcessors have been registered */
  private boolean hasDestructionAwareBeanPostProcessors;

  /** Map from scope identifier String to corresponding Scope */
  private final Map scopes = new HashMap();

  /** Map from alias to canonical bean name */
  private final Map aliasMap = CollectionFactory.createConcurrentMapIfPossible(16);

  /** Map from bean name to merged RootBeanDefinition */
  private final Map mergedBeanDefinitions = CollectionFactory.createConcurrentMapIfPossible(16);

  /** Names of beans that have already been created at least once */
  private final Set alreadyCreated = Collections.synchronizedSet(new HashSet());

  /** Names of beans that are currently in creation */
  private final ThreadLocal prototypesCurrentlyInCreation = new ThreadLocal();

  /** Cache of singleton objects created by FactoryBeans: FactoryBean name --> object */
  private final Map factoryBeanObjectCache = new HashMap();


  /**
   * Create a new AbstractBeanFactory.
   */
  public AbstractBeanFactory() {
  }

  /**
   * Create a new AbstractBeanFactory with the given parent.
   * @param parentBeanFactory parent bean factory, or <code>null</code> if none
   * @see #getBean
   */
  public AbstractBeanFactory(BeanFactory parentBeanFactory) {
    this.parentBeanFactory = parentBeanFactory;
  }


  //---------------------------------------------------------------------
  // Implementation of BeanFactory interface
  //---------------------------------------------------------------------

  public Object getBean(String name) throws BeansException {
    return getBean(name, null, null);
  }
   
  public Object getBean(String name, Class requiredType) throws BeansException {
    return getBean(name, requiredType, null);
  }

  public Object getBean(String name, Object[] args) throws BeansException {
    return getBean(name, null, args);
  }

  /**
   * Return an instance, which may be shared or independent, of the specified bean.
   * @param name the name of the bean to retrieve
   * @param requiredType the required type of the bean to retrieve
   * @param args arguments to use if creating a prototype using explicit arguments to a
   * static factory method. It is invalid to use a non-null args value in any other case.
   * @return an instance of the bean
   * @throws BeansException if the bean could not be created
   */
  public Object getBean(final String name, final Class requiredType, final Object[] args) throws BeansException {
    final String beanName = transformedBeanName(name);
    Object bean = null;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null) {
      if (logger.isDebugEnabled()) {
        if (isSingletonCurrentlyInCreation(beanName)) {
          logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
              "' that is not fully initialized yet - a consequence of a circular reference");
        }
        else {
          logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
        }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName);
    }

    else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check if bean definition exists in this factory.
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        // Not found -> check parent.
        String nameToLookup = originalBeanName(name);
        if (args != null) {
          // Delegation to parent with explicit args.
          return parentBeanFactory.getBean(nameToLookup, args);
        }
        else {
          // No args -> delegate to standard getBean method.
          return parentBeanFactory.getBean(nameToLookup, requiredType);
        }
      }

      this.alreadyCreated.add(beanName);

      final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // Guarantee initialization of beans that the current one depends on.
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (int i = 0; i < dependsOn.length; i++) {
          String dependsOnBean = dependsOn[i];
          getBean(dependsOnBean);
          registerDependentBean(dependsOnBean, beanName);
        }
      }

      // Create bean instance.
      if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, new ObjectFactory() {
          public Object getObject() throws BeansException {
            try {
              return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
              // Explicitly remove instance from singleton cache: It might have been put there
              // eagerly by the creation process, to allow for circular reference resolution.
              // Also remove any beans that received a temporary reference to the bean.
              destroySingleton(beanName);
              throw ex;
            }
          }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName);
      }

      else if (mbd.isPrototype()) {
        // It's a prototype -> create a new instance.
        Object prototypeInstance = null;
        try {
          beforePrototypeCreation(beanName);
          prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
          afterPrototypeCreation(beanName);
        }
        bean = getObjectForBeanInstance(prototypeInstance, name, beanName);
      }

      else {
        String scopeName = mbd.getScope();
        final Scope scope = (Scope) this.scopes.get(scopeName);
        if (scope == null) {
          throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
        }
        try {
          Object scopedInstance = scope.get(beanName, new ObjectFactory() {
            public Object getObject() throws BeansException {
              beforePrototypeCreation(beanName);
              try {
                return createBean(beanName, mbd, args);
              }
              finally {
                afterPrototypeCreation(beanName);
              }
            }
          });
          bean = getObjectForBeanInstance(scopedInstance, name, beanName);
        }
        catch (IllegalStateException ex) {
          throw new BeanCreationException(beanName,
              "Scope '" + scopeName + "' is not active for the current thread; " +
              "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
              ex);
        }
      }
    }

    // Check if required type matches the type of the actual bean instance.
    if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    }
    return bean;
  }

  public boolean containsBean(String name) {
    String beanName = transformedBeanName(name);
    if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
      return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
    }
    // Not found -> check parent.
    BeanFactory parentBeanFactory = getParentBeanFactory();
    return (parentBeanFactory != null && parentBeanFactory.containsBean(originalBeanName(name)));
  }

  public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

    Object beanInstance = getSingleton(beanName);
    if (beanInstance != null) {
      if (beanInstance instanceof FactoryBean) {
        return (BeanFactoryUtils.isFactoryDereference(name) || ((FactoryBean) beanInstance).isSingleton());
      }
      else {
        return !BeanFactoryUtils.isFactoryDereference(name);
      }
    }

    else {
      // No singleton instance found -> check bean definition.
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        // No bean definition found in this factory -> delegate to parent.
        return parentBeanFactory.isSingleton(originalBeanName(name));
      }

      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

      // In case of FactoryBean, return singleton status of created object if not a dereference.
      if (mbd.isSingleton()) {
        Class beanClass = predictBeanType(beanName, mbd, true);
        if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
          if (BeanFactoryUtils.isFactoryDereference(name)) {
            return true;
          }
          FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
          return factoryBean.isSingleton();
        }
        else {
          return !BeanFactoryUtils.isFactoryDereference(name);
        }
      }
      else {
        return false;
      }
    }
  }

  public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // No bean definition found in this factory -> delegate to parent.
      return parentBeanFactory.isPrototype(originalBeanName(name));
    }

    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    if (mbd.isPrototype()) {
      // In case of FactoryBean, return singleton status of created object if not a dereference.
      if (!BeanFactoryUtils.isFactoryDereference(name)) {
        return true;
      }
      Class beanClass = predictBeanType(beanName, mbd, true);
      return (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass));
    }
    else {
      // Singleton or scoped - not a prototype.
      // However, FactoryBean may still produce a prototype object...
      if (BeanFactoryUtils.isFactoryDereference(name)) {
        return false;
      }
      Class beanClass = predictBeanType(beanName, mbd, true);
      if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
        FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
        return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean).isPrototype()) ||
            !factoryBean.isSingleton());
      }
      else {
        return false;
      }
    }
  }

  public boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);
    Class typeToMatch = (targetType != null ? targetType : Object.class);

    // Check manually registered singletons.
    Object beanInstance = getSingleton(beanName);
    if (beanInstance != null) {
      if (beanInstance instanceof FactoryBean) {
        if (!BeanFactoryUtils.isFactoryDereference(name)) {
          Class type = getTypeForFactoryBean((FactoryBean) beanInstance);
          return (type != null && typeToMatch.isAssignableFrom(type));
        }
        else {
          return typeToMatch.isAssignableFrom(beanInstance.getClass())          ;
        }
      }
      else {
        return !BeanFactoryUtils.isFactoryDereference(name) &&
            typeToMatch.isAssignableFrom(beanInstance.getClass());
      }
    }

    else {
      // No singleton instance found -> check bean definition.
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        // No bean definition found in this factory -> delegate to parent.
        return parentBeanFactory.isTypeMatch(originalBeanName(name), targetType);
      }

      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      Class beanClass = predictBeanType(beanName, mbd, true);

      if (beanClass == null) {
        return false;
      }

      // Check bean class whether we're dealing with a FactoryBean.
      if (FactoryBean.class.isAssignableFrom(beanClass)) {
        if (!BeanFactoryUtils.isFactoryDereference(name)) {
          // If it's a FactoryBean, we want to look at what it creates, not the factory class.
          Class type = getTypeForFactoryBean(beanName, mbd);
          return (type != null && typeToMatch.isAssignableFrom(type));
        }
        else {
          return typeToMatch.isAssignableFrom(beanClass);
        }
      }
      else {
        return !BeanFactoryUtils.isFactoryDereference(name) &&
            typeToMatch.isAssignableFrom(beanClass);
      }
    }
  }

  public Class getType(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

    // Check manually registered singletons.
    Object beanInstance = getSingleton(beanName);
    if (beanInstance != null) {
      if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
        return getTypeForFactoryBean((FactoryBean) beanInstance);
      }
      else {
        return beanInstance.getClass();
      }
    }

    else {
      // No singleton instance found -> check bean definition.
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        // No bean definition found in this factory -> delegate to parent.
        return parentBeanFactory.getType(originalBeanName(name));
      }

      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      Class beanClass = predictBeanType(beanName, mbd, false);

      // Check bean class whether we're dealing with a FactoryBean.
      if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
        if (!BeanFactoryUtils.isFactoryDereference(name)) {
          // If it's a FactoryBean, we want to look at what it creates, not the factory class.
          return getTypeForFactoryBean(beanName, mbd);
        }
        else {
          return beanClass;
        }
      }
      else {
        return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
      }
    }
  }

  public String[] getAliases(String name) {
    String beanName = transformedBeanName(name);
    List aliases = new ArrayList();
    boolean factoryPrefix = name.startsWith(FACTORY_BEAN_PREFIX);
    String fullBeanName = beanName;
    if (factoryPrefix) {
      fullBeanName = FACTORY_BEAN_PREFIX + beanName;
    }
    if (!fullBeanName.equals(name)) {
      aliases.add(fullBeanName);
    }
    synchronized (this.aliasMap) {
      for (Iterator it = this.aliasMap.entrySet().iterator(); it.hasNext();) {
        Map.Entry entry = (Map.Entry) it.next();
        String registeredName = (String) entry.getValue();
        if (registeredName.equals(beanName)) {
          String key = (factoryPrefix ? FACTORY_BEAN_PREFIX : "") + entry.getKey();
          if (!key.equals(name)) {
            aliases.add(key);
          }
        }
      }
    }
    if (!containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null) {
        aliases.addAll(Arrays.asList(parentBeanFactory.getAliases(fullBeanName)));
      }
    }
    return StringUtils.toStringArray(aliases);
  }


  //---------------------------------------------------------------------
  // Implementation of HierarchicalBeanFactory interface
  //---------------------------------------------------------------------

  public BeanFactory getParentBeanFactory() {
    return this.parentBeanFactory;
  }

  public boolean containsLocalBean(String name) {
    String beanName = transformedBeanName(name);
    return ((containsSingleton(beanName) || containsBeanDefinition(beanName)) &&
        (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName)));
  }


  //---------------------------------------------------------------------
  // Implementation of ConfigurableBeanFactory interface
  //---------------------------------------------------------------------

  public void setParentBeanFactory(BeanFactory parentBeanFactory) {
    if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
      throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
    }
    this.parentBeanFactory = parentBeanFactory;
  }

  public void setBeanClassLoader(ClassLoader beanClassLoader) {
    this.beanClassLoader = (beanClassLoader != null ? beanClassLoader : ClassUtils.getDefaultClassLoader());
  }

  public ClassLoader getBeanClassLoader() {
    return this.beanClassLoader;
  }

  public void setTempClassLoader(ClassLoader tempClassLoader) {
    this.tempClassLoader = tempClassLoader;
  }

  public ClassLoader getTempClassLoader() {
    return this.tempClassLoader;
  }

  public void setCacheBeanMetadata(boolean cacheBeanMetadata) {
    this.cacheBeanMetadata = cacheBeanMetadata;
  }

  public boolean isCacheBeanMetadata() {
    return this.cacheBeanMetadata;
  }

  public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
    Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
    this.propertyEditorRegistrars.add(registrar);
  }

  /**
   * Return the set of PropertyEditorRegistrars.
   */
  public Set getPropertyEditorRegistrars() {
    return this.propertyEditorRegistrars;
  }

  public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor) {
    Assert.notNull(requiredType, "Required type must not be null");
    Assert.notNull(propertyEditor, "PropertyEditor must not be null");
    this.customEditors.put(requiredType, propertyEditor);
  }

  /**
   * Return the map of custom editors, with Classes as keys
   * and PropertyEditors as values.
   */
  public Map getCustomEditors() {
    return this.customEditors;
  }

  public void setTypeConverter(TypeConverter typeConverter) {
    this.typeConverter = typeConverter;
  }

  /**
   * Return the custom TypeConverter to use, if any.
   * @return the custom TypeConverter, or <code>null</code> if none specified
   */
  protected TypeConverter getCustomTypeConverter() {
    return this.typeConverter;
  }

  public TypeConverter getTypeConverter() {
    TypeConverter customConverter = getCustomTypeConverter();
    if (customConverter != null) {
      return customConverter;
    }
    else {
      // Build default TypeConverter, registering custom editors.
      SimpleTypeConverter typeConverter = new SimpleTypeConverter();
      registerCustomEditors(typeConverter);
      return typeConverter;
    }
  }

  public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
    this.beanPostProcessors.add(beanPostProcessor);
    if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
      this.hasInstantiationAwareBeanPostProcessors = true;
    }
    if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
      this.hasDestructionAwareBeanPostProcessors = true;
    }
  }

  public int getBeanPostProcessorCount() {
    return this.beanPostProcessors.size();
  }

  /**
   * Return the list of BeanPostProcessors that will get applied
   * to beans created with this factory.
   */
  public List getBeanPostProcessors() {
    return this.beanPostProcessors;
  }

  /**
   * Return whether this factory holds a InstantiationAwareBeanPostProcessor
   * that will get applied to singleton beans on shutdown.
   * @see #addBeanPostProcessor
   * @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
   */
  protected boolean hasInstantiationAwareBeanPostProcessors() {
    return this.hasInstantiationAwareBeanPostProcessors;
  }

  /**
   * Return whether this factory holds a DestructionAwareBeanPostProcessor
   * that will get applied to singleton beans on shutdown.
   * @see #addBeanPostProcessor
   * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
   */
  protected boolean hasDestructionAwareBeanPostProcessors() {
    return this.hasDestructionAwareBeanPostProcessors;
  }

  public void registerScope(String scopeName, Scope scope) {
    Assert.notNull(scopeName, "Scope identifier must not be null");
    Assert.notNull(scope, "Scope must not be null");
    if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
      throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
    }
    this.scopes.put(scopeName, scope);
  }

  public String[] getRegisteredScopeNames() {
    return StringUtils.toStringArray(this.scopes.keySet());
  }

  public Scope getRegisteredScope(String scopeName) {
    Assert.notNull(scopeName, "Scope identifier must not be null");
    return (Scope) this.scopes.get(scopeName);
  }

  public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
    Assert.notNull(otherFactory, "BeanFactory must not be null");
    setBeanClassLoader(otherFactory.getBeanClassLoader());
    setCacheBeanMetadata(otherFactory.isCacheBeanMetadata());
    if (otherFactory instanceof AbstractBeanFactory) {
      AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactory) otherFactory;
      this.customEditors.putAll(otherAbstractFactory.customEditors);
      this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
      this.beanPostProcessors.addAll(otherAbstractFactory.beanPostProcessors);
      this.hasInstantiationAwareBeanPostProcessors = this.hasInstantiationAwareBeanPostProcessors ||
          otherAbstractFactory.hasInstantiationAwareBeanPostProcessors;
      this.hasDestructionAwareBeanPostProcessors = this.hasDestructionAwareBeanPostProcessors ||
          otherAbstractFactory.hasDestructionAwareBeanPostProcessors;
      this.scopes.putAll(otherAbstractFactory.scopes);
    }
  }

  public void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException {
    Assert.hasText(beanName, "'beanName' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    if (!alias.equals(beanName)) {
      // Only actually register the alias if it is not equal to the bean name itself.
      if (logger.isDebugEnabled()) {
        logger.debug("Registering alias '" + alias + "' for bean with name '" + beanName + "'");
      }
      synchronized (this.aliasMap) {
        String registeredName = (String) this.aliasMap.get(alias);
        if (registeredName != null && !registeredName.equals(beanName)) {
          throw new BeanDefinitionStoreException("Cannot register alias '" + alias + "' for bean name '" +
              beanName + "': It is already registered for bean name '" + registeredName + "'.");
        }
        this.aliasMap.put(alias, beanName);
      }
    }
  }

  public void resolveAliases(StringValueResolver valueResolver) {
    Assert.notNull(valueResolver, "StringValueResolver must not be null");
    synchronized (this.aliasMap) {
      Map aliasCopy = new HashMap(this.aliasMap);
      for (Iterator it = aliasCopy.keySet().iterator(); it.hasNext();) {
        String alias = (String) it.next();
        String registeredName = (String) aliasCopy.get(alias);
        String resolvedAlias = valueResolver.resolveStringValue(alias);
        String resolvedName = valueResolver.resolveStringValue(registeredName);
        if (!resolvedAlias.equals(alias)) {
          String existingName = (String) this.aliasMap.get(resolvedAlias);
          if (existingName != null && !existingName.equals(resolvedName)) {
            throw new BeanDefinitionStoreException("Cannot register resolved alias '" +
                resolvedAlias + "' (original: '" + alias + "') for bean name '" + resolvedName +
                "': It is already registered for bean name '" + registeredName + "'.");
          }
          this.aliasMap.put(resolvedAlias, resolvedName);
          this.aliasMap.remove(alias);
        }
        else if (!registeredName.equals(resolvedName)) {
          this.aliasMap.put(alias, resolvedName);
        }
      }
    }
  }

  /**
   * Return a 'merged' BeanDefinition for the given bean name,
   * merging a child bean definition with its parent if necessary.
   * <p>This <code>getMergedBeanDefinition</code> considers bean definition
   * in ancestors as well.
   * @param name the name of the bean to retrieve the merged definition for
   * (may be an alias)
   * @return a (potentially merged) RootBeanDefinition for the given bean
   * @throws NoSuchBeanDefinitionException if there is no bean with the given name
   * @throws BeanDefinitionStoreException in case of an invalid bean definition
   */
  public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
    String beanName = transformedBeanName(name);

    // Efficiently check whether bean definition exists in this factory.
    if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
      return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
    }
    // Resolve merged bean definition locally.
    return getMergedLocalBeanDefinition(beanName);
  }

  /**
   * Callback before prototype creation.
   * <p>The default implementation register the prototype as currently in creation.
   * @param beanName the name of the prototype about to be created
   * @see #isPrototypeCurrentlyInCreation
   */
  protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
      this.prototypesCurrentlyInCreation.set(beanName);
    }
    else if (curVal instanceof String) {
      Set beanNameSet = new HashSet(2);
      beanNameSet.add(curVal);
      beanNameSet.add(beanName);
      this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    else {
      Set beanNameSet = (Set) curVal;
      beanNameSet.add(beanName);
    }
  }

  /**
   * Callback after prototype creation.
   * <p>The default implementation marks the prototype as not in creation anymore.
   * @param beanName the name of the prototype that has been created
   * @see #isPrototypeCurrentlyInCreation
   */
  protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
      this.prototypesCurrentlyInCreation.set(null);
    }
    else if (curVal instanceof Set) {
      Set beanNameSet = (Set) curVal;
      beanNameSet.remove(beanName);
      if (beanNameSet.isEmpty()) {
        this.prototypesCurrentlyInCreation.set(null);
      }
    }
  }

  /**
   * Return whether the specified prototype bean is currently in creation
   * (within the current thread).
   * @param beanName the name of the bean
   */
  protected final boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null &&
        (curVal.equals(beanName) || (curVal instanceof Set && ((Set) curVal).contains(beanName))));
  }

  public boolean isCurrentlyInCreation(String beanName) {
    return isSingletonCurrentlyInCreation(beanName) || isPrototypeCurrentlyInCreation(beanName);
  }

  public void destroyBean(String beanName, Object beanInstance) {
    destroyBean(beanName, beanInstance, getMergedLocalBeanDefinition(beanName));
  }

  /**
   * Destroy the given bean instance (usually a prototype instance
   * obtained from this factory) according to the given bean definition.
   * @param beanName the name of the bean definition
   * @param beanInstance the bean instance to destroy
   * @param mbd the merged bean definition
   */
  protected void destroyBean(String beanName, Object beanInstance, RootBeanDefinition mbd) {
    new DisposableBeanAdapter(beanInstance, beanName, mbd, getBeanPostProcessors()).destroy();
  }

  public void destroyScopedBean(String beanName) {
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    if (mbd.isSingleton() || mbd.isPrototype()) {
      throw new IllegalArgumentException(
          "Bean name '" + beanName + "' does not correspond to an object in a Scope");
    }
    String scopeName = mbd.getScope();
    Scope scope = (Scope) this.scopes.get(scopeName);
    if (scope == null) {
      throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
    }
    Object bean = scope.remove(beanName);
    if (bean != null) {
      destroyBean(beanName, bean, mbd);
    }
  }


  //---------------------------------------------------------------------
  // Implementation methods
  //---------------------------------------------------------------------

  /**
   * Return the bean name, stripping out the factory dereference prefix if necessary,
   * and resolving aliases to canonical names.
   * @param name the user-specified name
   * @return the transformed bean name
   */
  protected String transformedBeanName(String name) {
    String canonicalName = BeanFactoryUtils.transformedBeanName(name);
    // Handle aliasing.
    String resolvedName = null;
    do {
      resolvedName = (String) this.aliasMap.get(canonicalName);
      if (resolvedName != null) {
        canonicalName = resolvedName;
      }
    }
    while (resolvedName != null);
    return canonicalName;
  }

  /**
   * Determine the original bean name, resolving locally defined aliases to canonical names.
   * @param name the user-specified name
   * @return the original bean name
   */
  protected String originalBeanName(String name) {
    String beanName = transformedBeanName(name);
    if (name.startsWith(FACTORY_BEAN_PREFIX)) {
      beanName = FACTORY_BEAN_PREFIX + beanName;
    }
    return beanName;
  }

  /**
   * Determine whether this given bean name is defines as an alias
   * (as opposed to the name of an actual bean definition).
   * @param beanName the bean name to check
   * @return whether the given name is an alias
   */
  protected boolean isAlias(String beanName) {
    return this.aliasMap.containsKey(beanName);
  }

  /**
   * Initialize the given BeanWrapper with the custom editors registered
   * with this factory. To be called for BeanWrappers that will create
   * and populate bean instances.
   * <p>The default implementation delegates to <code>registerCustomEditors</code>.
   * Can be overridden in subclasses.
   * @param bw the BeanWrapper to initialize
   * @see #registerCustomEditors
   */
  protected void initBeanWrapper(BeanWrapper bw) {
    registerCustomEditors(bw);
  }

  /**
   * Initialize the given PropertyEditorRegistry with the custom editors
   * registered with this BeanFactory.
   * <p>To be called for BeanWrappers that will create and populate bean
   * instances, and for SimpleTypeConverter used for constructor argument
   * and factory method type conversion.
   * @param registry the PropertyEditorRegistry to initialize
   */
  protected void registerCustomEditors(PropertyEditorRegistry registry) {
    PropertyEditorRegistrySupport registrySupport =
        (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
    if (registrySupport != null) {
      registrySupport.useConfigValueEditors();
    }
    if (!this.propertyEditorRegistrars.isEmpty()) {
      for (Iterator it = this.propertyEditorRegistrars.iterator(); it.hasNext();) {
        PropertyEditorRegistrar registrar = (PropertyEditorRegistrar) it.next();
        try {
          registrar.registerCustomEditors(registry);
        }
        catch (BeanCreationException ex) {
          Throwable rootCause = ex.getMostSpecificCause();
          if (rootCause instanceof BeanCurrentlyInCreationException) {
            BeanCreationException bce = (BeanCreationException) rootCause;
            if (isCurrentlyInCreation(bce.getBeanName())) {
              if (logger.isDebugEnabled()) {
                logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
                    "] failed because it tried to obtain currently created bean '" + ex.getBeanName() +
                    "': " + ex.getMessage());
              }
              onSuppressedException(ex);
              continue;
            }
          }
          throw ex;
        }
      }
    }
    if (!this.customEditors.isEmpty()) {
      for (Iterator it = this.customEditors.entrySet().iterator(); it.hasNext();) {
        Map.Entry entry = (Map.Entry) it.next();
        Class clazz = (Class) entry.getKey();
        PropertyEditor editor = (PropertyEditor) entry.getValue();
        // Register the editor as shared instance, if possible,
        // to make it clear that it might be used concurrently.
        if (registrySupport != null) {
          registrySupport.registerSharedEditor(clazz, editor);
        }
        else {
          registry.registerCustomEditor(clazz, editor);
        }
      }
    }
  }


  /**
   * Return a merged RootBeanDefinition, traversing the parent bean definition
   * if the specified bean corresponds to a child bean definition.
   * @param beanName the name of the bean to retrieve the merged definition for
   * @return a (potentially merged) RootBeanDefinition for the given bean
   * @throws NoSuchBeanDefinitionException if there is no bean with the given name
   * @throws BeanDefinitionStoreException in case of an invalid bean definition
   */
  protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // Quick check on the concurrent map first, with minimal locking.
    RootBeanDefinition mbd = (RootBeanDefinition) this.mergedBeanDefinitions.get(beanName);
    if (mbd != null) {
      return mbd;
    }
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
  }

  /**
   * Return a RootBeanDefinition for the given top-level bean, by merging with
   * the parent if the given bean's definition is a child bean definition.
   * @param beanName the name of the bean definition
   * @param bd the original bean definition (Root/ChildBeanDefinition)
   * @return a (potentially merged) RootBeanDefinition for the given bean
   * @throws BeanDefinitionStoreException in case of an invalid bean definition
   */
  protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
      throws BeanDefinitionStoreException {

    return getMergedBeanDefinition(beanName, bd, null);
  }

  /**
   * Return a RootBeanDefinition for the given bean, by merging with the
   * parent if the given bean's definition is a child bean definition.
   * @param beanName the name of the bean definition
   * @param bd the original bean definition (Root/ChildBeanDefinition)
   * @param containingBd the containing bean definition in case of inner bean,
   * or <code>null</code> in case of a top-level bean
   * @return a (potentially merged) RootBeanDefinition for the given bean
   * @throws BeanDefinitionStoreException in case of an invalid bean definition
   */
  protected RootBeanDefinition getMergedBeanDefinition(
      String beanName, BeanDefinition bd, BeanDefinition containingBd)
      throws BeanDefinitionStoreException {

    synchronized (this.mergedBeanDefinitions) {
      RootBeanDefinition mbd = null;

      // Check with full lock now in order to enforce the same merged instance.
      if (containingBd == null) {
        mbd = (RootBeanDefinition) this.mergedBeanDefinitions.get(beanName);
      }

      if (mbd == null) {
        if (bd.getParentName() == null) {
          // Use copy of given root bean definition.
          mbd = new RootBeanDefinition(bd);
        }
        else {
          // Child bean definition: needs to be merged with parent.
          BeanDefinition pbd = null;
          try {
            String parentBeanName = transformedBeanName(bd.getParentName());
            if (!beanName.equals(parentBeanName)) {
              pbd = getMergedBeanDefinition(parentBeanName);
            }
            else {
              if (getParentBeanFactory() instanceof ConfigurableBeanFactory) {
                pbd = ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(parentBeanName);
              }
              else {
                throw new NoSuchBeanDefinitionException(bd.getParentName(),
                    "Parent name '" + bd.getParentName() + "' is equal to bean name '" + beanName +
                    "': cannot be resolved without an AbstractBeanFactory parent");
              }
            }
          }
          catch (NoSuchBeanDefinitionException ex) {
            throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
          }
          // Deep copy with overridden values.
          mbd = new RootBeanDefinition(pbd);
          mbd.overrideFrom(bd);
        }

        // A bean contained in a non-singleton bean cannot be a singleton itself.
        // Let's correct this on the fly here, since this might be the result of
        // parent-child merging for the outer bean, in which case the original inner bean
        // definition will not have inherited the merged outer bean's singleton status.
        if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
          mbd.setScope(containingBd.getScope());
        }

        // Only cache the merged bean definition if we're already about to create an
        // instance of the bean, or at least have already created an instance before.
        if (containingBd == null && isCacheBeanMetadata() && this.alreadyCreated.contains(beanName)) {
          this.mergedBeanDefinitions.put(beanName, mbd);
        }
      }

      return mbd;
    }
  }

  /**
   * Check the given merged bean definition,
   * potentially throwing validation exceptions.
   * @param mbd the merged bean definition to check
   * @param beanName the name of the bean
   * @param args the arguments for bean creation, if any
   * @throws BeanDefinitionStoreException in case of validation failure
   */
  protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, Object[] args)
      throws BeanDefinitionStoreException {

    // check if bean definition is not abstract
    if (mbd.isAbstract()) {
      throw new BeanIsAbstractException(beanName);
    }

    // Check validity of the usage of the args parameter. This can
    // only be used for prototypes constructed via a factory method.
    if (args != null && !mbd.isPrototype()) {
      throw new BeanDefinitionStoreException(
          "Can only specify arguments for the getBean method when referring to a prototype bean definition");
    }
  }

  /**
   * Remove the merged bean definition for the specified bean,
   * recreating it on next access.
   * @param beanName the bean name to clear the merged definition for
   */
  protected void clearMergedBeanDefinition(String beanName) {
    this.mergedBeanDefinitions.remove(beanName);
  }

  /**
   * Resolve the bean class for the specified bean definition,
   * resolving a bean class name into a Class reference (if necessary)
   * and storing the resolved Class in the bean definition for further use.
   * @param mbd the merged bean definition to determine the class for
   * @param beanName the name of the bean (for error handling purposes)
   * @return the resolved bean class (or <code>null</code> if none)
   * @throws CannotLoadBeanClassException if we failed to load the class
   */
  protected Class resolveBeanClass(RootBeanDefinition mbd, String beanName) {
    return resolveBeanClass(mbd, beanName, false);
  }

  /**
   * Resolve the bean class for the specified bean definition,
   * resolving a bean class name into a Class reference (if necessary)
   * and storing the resolved Class in the bean definition for further use.
   * @param mbd the merged bean definition to determine the class for
   * @param beanName the name of the bean (for error handling purposes)
   * @param typeMatchOnly whether the returned {@link Class} is only used
   * for internal type matching purposes (that is, never exposed to application code)
   * @return the resolved bean class (or <code>null</code> if none)
   * @throws CannotLoadBeanClassException if we failed to load the class
   */
  protected Class resolveBeanClass(RootBeanDefinition mbd, String beanName, boolean typeMatchOnly)
      throws CannotLoadBeanClassException {
    try {
      if (mbd.hasBeanClass()) {
        return mbd.getBeanClass();
      }
      if (typeMatchOnly && getTempClassLoader() != null) {
        String className = mbd.getBeanClassName();
        return (className != null ? ClassUtils.forName(className, getTempClassLoader()) : null);
      }
      return mbd.resolveBeanClass(getBeanClassLoader());
    }
    catch (ClassNotFoundException ex) {
      throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
    }
    catch (LinkageError err) {
      throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
    }
  }


  /**
   * Predict the eventual bean type (of the processed bean instance) for the
   * specified bean. Called by {@link #getType} and {@link #isTypeMatch}.
   * Does not need to handle FactoryBeans specifically, since it is only
   * supposed to operate on the raw bean type.
   * <p>This implementation is simplistic in that it is not able to
   * handle factory methods and InstantiationAwareBeanPostProcessors.
   * It only predicts the bean type correctly for a standard bean.
   * To be overridden in subclasses, applying more sophisticated type detection.
   * @param beanName the name of the bean
   * @param mbd the merged bean definition to determine the type for
   * @param typeMatchOnly whether the predicated is only used for internal
   * type matching purposes (i.e. never exposed to application code)
   * @return the type of the bean, or <code>null</code> if not predictable
   */
  protected Class predictBeanType(String beanName, RootBeanDefinition mbd, boolean typeMatchOnly) {
    if (mbd.getFactoryMethodName() != null) {
      return null;
    }
    return resolveBeanClass(mbd, beanName, typeMatchOnly);
  }

  /**
   * Determine the bean type for the given FactoryBean definition, as far as possible.
   * Only called if there is no singleton instance registered for the target bean already.
   * <p>The default implementation creates the FactoryBean via <code>getBean</code>
   * to call its <code>getObjectType</code> method. Subclasses are encouraged to optimize
   * this, typically by just instantiating the FactoryBean but not populating it yet,
   * trying whether its <code>getObjectType</code> method already returns a type.
   * If no type found, a full FactoryBean creation as performed by this implementation
   * should be used as fallback.
   * @param beanName the name of the bean
   * @param mbd the merged bean definition for the bean
   * @return the type for the bean if determinable, or <code>null</code> else
   * @see org.springframework.beans.factory.FactoryBean#getObjectType()
   * @see #getBean(String)
   */
  protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
    if (!mbd.isSingleton()) {
      return null;
    }
    try {
      FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
      return getTypeForFactoryBean(factoryBean);
    }
    catch (BeanCreationException ex) {
      // Can only happen when getting a FactoryBean.
      logger.debug("Ignoring bean creation exception on FactoryBean type check", ex);
      onSuppressedException(ex);
      return null;
    }
  }

  /**
   * Determine the type for the given FactoryBean.
   * @param factoryBean the FactoryBean instance to check
   * @return the FactoryBean's object type,
   * or <code>null</code> if the type cannot be determined yet
   */
  protected Class getTypeForFactoryBean(FactoryBean factoryBean) {
    try {
      return factoryBean.getObjectType();
    }
    catch (Throwable ex) {
      // Thrown from the FactoryBean's getObjectType implementation.
      logger.warn("FactoryBean threw exception from getObjectType, despite the contract saying " +
          "that it should return null if the type of its object cannot be determined yet", ex);
      return null;
    }
  }


  /**
   * Get the object for the given bean instance, either the bean
   * instance itself or its created object in case of a FactoryBean.
   * @param beanInstance the shared bean instance
   * @param name name that may include factory dereference prefix
   * @param beanName the canonical bean name
   * @return the object to expose for the bean
   */
  protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName) {
    // Don't let calling code try to dereference the
    // bean factory if the bean isn't a factory.
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    Object object = beanInstance;

    // Now we have the bean instance, which may be a normal bean or a FactoryBean.
    // If it's a FactoryBean, we use it to create a bean instance, unless the
    // caller actually wants a reference to the factory.
    if (beanInstance instanceof FactoryBean) {
      if (!BeanFactoryUtils.isFactoryDereference(name)) {
        // Return bean instance from factory.
        FactoryBean factory = (FactoryBean) beanInstance;
        // Cache object obtained from FactoryBean if it is a singleton.
        RootBeanDefinition mbd =
            (containsBeanDefinition(beanName) ? getMergedLocalBeanDefinition(beanName) null);
        boolean shared = (mbd == null || mbd.isSingleton());
        if (shared && factory.isSingleton()) {
          synchronized (getSingletonMutex()) {
            object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
              object = getObjectFromFactoryBean(factory, beanName, mbd);
              this.factoryBeanObjectCache.put(beanName, object);
            }
          }
        }
        else {
          object = getObjectFromFactoryBean(factory, beanName, mbd);
        }
      }
    }

    return object;
  }

  /**
   * Obtain an object to expose from the given FactoryBean.
   * @param factory the FactoryBean instance
   * @param beanName the name of the bean
   * @param mbd the merged bean definition
   * @return the object obtained from the FactoryBean
   * @throws BeanCreationException if FactoryBean object creation failed
   * @see org.springframework.beans.factory.FactoryBean#getObject()
   */
  protected Object getObjectFromFactoryBean(
      final FactoryBean factory, final String beanName, final RootBeanDefinition mbd)
      throws BeanCreationException {

    return AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        Object object;

        try {
          object = factory.getObject();
        }
        catch (FactoryBeanNotInitializedException ex) {
          throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
          throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }

        // Do not accept a null value for a FactoryBean that's not fully
        // initialized yet: Many FactoryBeans just return null then.
        if (object == null && isSingletonCurrentlyInCreation(beanName)) {
          throw new BeanCurrentlyInCreationException(
              beanName, "FactoryBean which is currently in creation returned null from getObject");
        }

        if (object != null && (mbd == null || !mbd.isSynthetic())) {
          try {
            object = postProcessObjectFromFactoryBean(object, beanName);
          }
          catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Post-processing of the FactoryBean's object failed", ex);
          }
        }

        return object;
      }
    });
  }

  /**
   * Post-process the given object that has been obtained from the FactoryBean.
   * The resulting object will get exposed for bean references.
   * <p>The default implementation simply returns the given object as-is.
   * Subclasses may override this, for example, to apply post-processors.
   * @param object the object obtained from the FactoryBean.
   * @param beanName the name of the bean
   * @return the object to expose
   * @throws BeansException if any post-processing failed
   */
  protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
    return object;
  }

  public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

    Object beanInstance = getSingleton(beanName);
    if (beanInstance != null) {
      return (beanInstance instanceof FactoryBean);
    }

    // No singleton instance found -> check bean definition.
    if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
      // No bean definition found in this factory -> delegate to parent.
      return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
    }

    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    Class beanClass = predictBeanType(beanName, mbd, true);
    return (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass));
  }

  /**
   * Return whether the bean definition for the given bean name has been
   * marked as a primary bean.
   */
  protected boolean isPrimary(String beanName, Object beanInstance) {
    return (containsBeanDefinition(beanName) && getMergedLocalBeanDefinition(beanName).isPrimary());
  }

  /**
   * Determine whether the given bean name is already in use within this factory,
   * i.e. whether there is a local bean registered under this name or an inner
   * bean created with this name.
   * @param beanName the name to check
   */
  public boolean isBeanNameInUse(String beanName) {
    return containsLocalBean(beanName) || hasDependentBean(beanName);
  }

  /**
   * Determine whether the given bean requires destruction on shutdown.
   * <p>The default implementation checks the DisposableBean interface as well as
   * a specified destroy method and registered DestructionAwareBeanPostProcessors.
   * @param bean the bean instance to check
   * @param mbd the corresponding bean definition
   * @see org.springframework.beans.factory.DisposableBean
   * @see AbstractBeanDefinition#getDestroyMethodName()
   * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
   */
  protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
    return (bean instanceof DisposableBean || mbd.getDestroyMethodName() != null ||
        hasDestructionAwareBeanPostProcessors());
  }

  /**
   * Add the given bean to the list of disposable beans in this factory,
   * registering its DisposableBean interface and/or the given destroy method
   * to be called on factory shutdown (if applicable). Only applies to singletons.
   * @param beanName the name of the bean
   * @param bean the bean instance
   * @param mbd the bean definition for the bean
   * @see RootBeanDefinition#isSingleton
   * @see RootBeanDefinition#getDependsOn
   * @see #registerDisposableBean
   * @see #registerDependentBean
   */
  protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
      if (mbd.isSingleton()) {
        // Register a DisposableBean implementation that performs all destruction
        // work for the given bean: DestructionAwareBeanPostProcessors,
        // DisposableBean interface, custom destroy method.
        registerDisposableBean(beanName,
            new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors()));
      }
      else {
        // A bean with a custom scope...
        Scope scope = (Scope) this.scopes.get(mbd.getScope());
        if (scope == null) {
          throw new IllegalStateException("No Scope registered for scope '" + mbd.getScope() + "'");
        }
        scope.registerDestructionCallback(beanName,
            new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors()));
      }
    }
  }

  /**
   * Overridden to clear the FactoryBean object cache as well.
   */
  protected void removeSingleton(String beanName) {
    super.removeSingleton(beanName);
    this.factoryBeanObjectCache.remove(beanName);
  }


  //---------------------------------------------------------------------
  // Abstract methods to be implemented by subclasses
  //---------------------------------------------------------------------

  /**
   * Check if this bean factory contains a bean definition with the given name.
   * Does not consider any hierarchy this factory may participate in.
   * Invoked by <code>containsBean</code> when no cached singleton instance is found.
   * <p>Depending on the nature of the concrete bean factory implementation,
   * this operation might be expensive (for example, because of directory lookups
   * in external registries). However, for listable bean factories, this usually
   * just amounts to a local hash lookup: The operation is therefore part of the
   * public interface there. The same implementation can serve for both this
   * template method and the public interface method in that case.
   * @param beanName the name of the bean to look for
   * @return if this bean factory contains a bean definition with the given name
   * @see #containsBean
   * @see org.springframework.beans.factory.ListableBeanFactory#containsBeanDefinition
   */
  protected abstract boolean containsBeanDefinition(String beanName);

  /**
   * Return the bean definition for the given bean name.
   * Subclasses should normally implement caching, as this method is invoked
   * by this class every time bean definition metadata is needed.
   * <p>Depending on the nature of the concrete bean factory implementation,
   * this operation might be expensive (for example, because of directory lookups
   * in external registries). However, for listable bean factories, this usually
   * just amounts to a local hash lookup: The operation is therefore part of the
   * public interface there. The same implementation can serve for both this
   * template method and the public interface method in that case.
   * @param beanName the name of the bean to find a definition for
   * @return the BeanDefinition for this prototype name (never <code>null</code>)
   * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
   * if the bean definition cannot be resolved
   * @throws BeansException in case of errors
   * @see RootBeanDefinition
   * @see ChildBeanDefinition
   * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#getBeanDefinition
   */
  protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

  /**
   * Create a bean instance for the given bean definition.
   * The bean definition will already have been merged with the parent
   * definition in case of a child definition.
   * <p>All the other methods in this class invoke this method, although
   * beans may be cached after being instantiated by this method. All bean
   * instantiation within this class is performed by this method.
   * @param beanName the name of the bean
   * @param mbd the merged bean definition for the bean
   * @param args arguments to use if creating a prototype using explicit arguments to a
   * static factory method. This parameter must be <code>null</code> except in this case.
   * @return a new instance of the bean
   * @throws BeanCreationException if the bean could not be created
   */
  protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
      throws BeanCreationException;

}
TOP

Related Classes of org.springframework.beans.factory.support.AbstractBeanFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.