Open Source Repository

Home /spring/spring-beans-3.0.5 | Repository Home



org/springframework/beans/factory/support/AbstractBeanFactory.java
/*
 * Copyright 2002-2010 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.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.BeanUtils;
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.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.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
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.DecoratingClassLoader;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
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
 @author Costin Leau
 @since 15 April 2001
 @see #getBeanDefinition
 @see #createBean
 @see AbstractAutowireCapableBeanFactory#createBean
 @see DefaultListableBeanFactory#getBeanDefinition
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport 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;

  /** Resolution strategy for expressions in bean definition values */
  private BeanExpressionResolver beanExpressionResolver;

  /** Spring 3.0 ConversionService to use instead of PropertyEditors */
  private ConversionService conversionService;

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

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

  /** Custom PropertyEditors to apply to the beans of this factory */
  private final Map<Class, Class<? extends PropertyEditor>> customEditors =
      new HashMap<Class, Class<? extends PropertyEditor>>(4);

  /** String resolvers to apply e.g. to annotation attribute values */
  private final List<StringValueResolver> embeddedValueResolvers = new LinkedList<StringValueResolver>();

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

  /** 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<String, Scope> scopes = new HashMap<String, Scope>();

  /** Security context used when running with a SecurityManager */
  private SecurityContextProvider securityContextProvider;

  /** Map from bean name to merged RootBeanDefinition */
  private final Map<String, RootBeanDefinition> mergedBeanDefinitions =
      new ConcurrentHashMap<String, RootBeanDefinition>();

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

  /** Names of beans that are currently in creation */
  private final ThreadLocal<Object> prototypesCurrentlyInCreation =
      new NamedThreadLocal<Object>("Prototype beans currently in creation");

  /**
   * 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 namethrows BeansException {
    return doGetBean(name, null, null, false);
  }
    
  public <T> T getBean(String name, Class<T> requiredTypethrows BeansException {
    return doGetBean(name, requiredType, null, false);
  }

  public Object getBean(String name, Object... argsthrows BeansException {
    return doGetBean(name, null, args, false);
  }

  /**
   * 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 <T> T getBean(String name, Class<T> requiredType, Object... argsthrows BeansException {
    return doGetBean(name, requiredType, args, false);
  }

  /**
   * 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.
   @param typeCheckOnly whether the instance is obtained for a type check,
   * not for actual use
   @return an instance of the bean
   @throws BeansException if the bean could not be created
   */
  @SuppressWarnings("unchecked")
  protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == 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, null);
    }

    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 (TparentBeanFactory.getBean(nameToLookup, args);
        }
        else {
          // No args -> delegate to standard getBean method.
          return parentBeanFactory.getBean(nameToLookup, requiredType);
        }
      }

      if (!typeCheckOnly) {
        markBeanAsCreated(beanName);
      }

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

      // Guarantee initialization of beans that the current bean depends on.
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dependsOnBean : dependsOn) {
          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, mbd);
      }

      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, mbd);
      }

      else {
        String scopeName = mbd.getScope();
        final 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, mbd);
        }
        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 (Tbean;
  }

  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 namethrows NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

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

    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()) {
        if (isFactoryBean(beanName, mbd)) {
          if (BeanFactoryUtils.isFactoryDereference(name)) {
            return true;
          }
          FactoryBean factoryBean = (FactoryBeangetBean(FACTORY_BEAN_PREFIX + beanName);
          return factoryBean.isSingleton();
        }
        else {
          return !BeanFactoryUtils.isFactoryDereference(name);
        }
      }
      else {
        return false;
      }
    }
  }

  public boolean isPrototype(String namethrows 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.
      return (!BeanFactoryUtils.isFactoryDereference(name|| isFactoryBean(beanName, mbd));
    }
    else {
      // Singleton or scoped - not a prototype.
      // However, FactoryBean may still produce a prototype object...
      if (BeanFactoryUtils.isFactoryDereference(name)) {
        return false;
      }
      if (isFactoryBean(beanName, mbd)) {
        final FactoryBean factoryBean = (FactoryBeangetBean(FACTORY_BEAN_PREFIX + beanName);
        if (System.getSecurityManager() != null) {
          return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
            public Boolean run() {
              return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBeanfactoryBean).isPrototype()) ||
                  !factoryBean.isSingleton());
            }
          }, getAccessControlContext());
        }
        else {
          return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBeanfactoryBean).isPrototype()) ||
              !factoryBean.isSingleton());
        }
      }
      else {
        return false;
      }
    }
  }

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

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

    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);
      }

      // Retrieve corresponding bean definition.
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

      // Check decorated bean definition, if any: We assume it'll be easier
      // to determine the decorated bean's type than the proxy's type.
      BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
      if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
        RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
        Class targetClass = predictBeanType(dbd.getBeanName(), tbd, FactoryBean.class, typeToMatch);
        if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
          return typeToMatch.isAssignableFrom(targetClass);
        }
      }

      Class beanClass = predictBeanType(beanName, mbd, FactoryBean.class, typeToMatch);
      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 namethrows NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

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

    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);

      // Check decorated bean definition, if any: We assume it'll be easier
      // to determine the decorated bean's type than the proxy's type.
      BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
      if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
        RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
        Class targetClass = predictBeanType(dbd.getBeanName(), tbd);
        if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
          return targetClass;
        }
      }

      Class beanClass = predictBeanType(beanName, mbd);

      // 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 at the factory class.
          return getTypeForFactoryBean(beanName, mbd);
        }
        else {
          return beanClass;
        }
      }
      else {
        return (!BeanFactoryUtils.isFactoryDereference(name? beanClass : null);
      }
    }
  }

  @Override
  public String[] getAliases(String name) {
    String beanName = transformedBeanName(name);
    List<String> aliases = new ArrayList<String>();
    boolean factoryPrefix = name.startsWith(FACTORY_BEAN_PREFIX);
    String fullBeanName = beanName;
    if (factoryPrefix) {
      fullBeanName = FACTORY_BEAN_PREFIX + beanName;
    }
    if (!fullBeanName.equals(name)) {
      aliases.add(fullBeanName);
    }
    String[] retrievedAliases = super.getAliases(beanName);
    for (String retrievedAlias : retrievedAliases) {
      String alias = (factoryPrefix ? FACTORY_BEAN_PREFIX : ""+ retrievedAlias;
      if (!alias.equals(name)) {
        aliases.add(alias);
      }
    }
    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 setBeanExpressionResolver(BeanExpressionResolver resolver) {
    this.beanExpressionResolver = resolver;
  }

  public BeanExpressionResolver getBeanExpressionResolver() {
    return this.beanExpressionResolver;
  }

  public void setConversionService(ConversionService conversionService) {
    this.conversionService = conversionService;
  }

  public ConversionService getConversionService() {
    return this.conversionService;
  }

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

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

  public void registerCustomEditor(Class requiredType, Class<? extends PropertyEditor> propertyEditorClass) {
    Assert.notNull(requiredType, "Required type must not be null");
    Assert.isAssignable(PropertyEditor.class, propertyEditorClass);
    this.customEditors.put(requiredType, propertyEditorClass);
  }

  public void copyRegisteredEditorsTo(PropertyEditorRegistry registry) {
    registerCustomEditors(registry);
  }

  /**
   * Return the map of custom editors, with Classes as keys and PropertyEditor classes as values.
   */
  public Map<Class, Class<? extends PropertyEditor>> 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();
      typeConverter.setConversionService(getConversionService());
      registerCustomEditors(typeConverter);
      return typeConverter;
    }
  }

  public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
    Assert.notNull(valueResolver, "StringValueResolver must not be null");
    this.embeddedValueResolvers.add(valueResolver);
  }

  public String resolveEmbeddedValue(String value) {
    String result = value;
    for (StringValueResolver resolver : this.embeddedValueResolvers) {
      result = resolver.resolveStringValue(result);
    }
    return result;
  }

  public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
    this.beanPostProcessors.remove(beanPostProcessor);
    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<BeanPostProcessor> 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 this.scopes.get(scopeName);
  }

  /**
   * Set the security context provider for this bean factory. If a security manager
   * is set, interaction with the user code will be executed using the privileged
   * of the provided security context.
   */
  public void setSecurityContextProvider(SecurityContextProvider securityProvider) {
    this.securityContextProvider = securityProvider;
  }

  /**
   * Delegate the creation of the access control context to the
   {@link #setSecurityContextProvider SecurityContextProvider}.
   */
  @Override
  public AccessControlContext getAccessControlContext() {
    return (this.securityContextProvider != null ?
        this.securityContextProvider.getAccessControlContext() :
        AccessController.getContext());
  }

  public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
    Assert.notNull(otherFactory, "BeanFactory must not be null");
    setBeanClassLoader(otherFactory.getBeanClassLoader());
    setCacheBeanMetadata(otherFactory.isCacheBeanMetadata());
    setBeanExpressionResolver(otherFactory.getBeanExpressionResolver());
    if (otherFactory instanceof AbstractBeanFactory) {
      AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactoryotherFactory;
      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);
      this.securityContextProvider = otherAbstractFactory.securityContextProvider;
    }
    else {
      setTypeConverter(otherFactory.getTypeConverter());
    }
  }

  /**
   * 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 namethrows BeansException {
    String beanName = transformedBeanName(name);

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

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

    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null) {
      return (beanInstance instanceof FactoryBean);
    }
    else if (containsSingleton(beanName)) {
      // null instance registered
      return false;
    }

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

    return isFactoryBean(beanName, 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
   */
  @SuppressWarnings("unchecked")
  protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
      this.prototypesCurrentlyInCreation.set(beanName);
    }
    else if (curVal instanceof String) {
      Set<String> beanNameSet = new HashSet<String>(2);
      beanNameSet.add((StringcurVal);
      beanNameSet.add(beanName);
      this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    else {
      Set<String> beanNameSet = (Set<String>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
   */
  @SuppressWarnings("unchecked")
  protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
      this.prototypesCurrentlyInCreation.remove();
    }
    else if (curVal instanceof Set) {
      Set<String> beanNameSet = (Set<String>curVal;
      beanNameSet.remove(beanName);
      if (beanNameSet.isEmpty()) {
        this.prototypesCurrentlyInCreation.remove();
      }
    }
  }

  /**
   * 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 && ((SetcurVal).contains(beanName))));
  }

  public boolean isCurrentlyInCreation(String beanName) {
    Assert.notNull(beanName, "Bean name must not be null");
    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(), getAccessControlContext()).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 mutable scope");
    }
    String scopeName = mbd.getScope();
    Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
      throw new IllegalStateException("No Scope SPI 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) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
  }

  /**
   * 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;
  }

  /**
   * 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 {@link #registerCustomEditors}.
   * Can be overridden in subclasses.
   @param bw the BeanWrapper to initialize
   */
  protected void initBeanWrapper(BeanWrapper bw) {
    bw.setConversionService(getConversionService());
    registerCustomEditors(bw);
  }

  /**
   * Initialize the given PropertyEditorRegistry with the custom editors
   * that have been 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 ? (PropertyEditorRegistrySupportregistry : null);
    if (registrySupport != null) {
      registrySupport.useConfigValueEditors();
    }
    if (!this.propertyEditorRegistrars.isEmpty()) {
      for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
        try {
          registrar.registerCustomEditors(registry);
        }
        catch (BeanCreationException ex) {
          Throwable rootCause = ex.getMostSpecificCause();
          if (rootCause instanceof BeanCurrentlyInCreationException) {
            BeanCreationException bce = (BeanCreationExceptionrootCause;
            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 (Map.Entry<Class, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
        Class requiredType = entry.getKey();
        Class<? extends PropertyEditor> editorClass = entry.getValue();
        registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass));
      }
    }
  }


  /**
   * 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 beanNamethrows BeansException {
    // Quick check on the concurrent map first, with minimal locking.
    RootBeanDefinition mbd = 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 = this.mergedBeanDefinitions.get(beanName);
      }

      if (mbd == null) {
        if (bd.getParentName() == null) {
          // Use copy of given root bean definition.
          if (bd instanceof RootBeanDefinition) {
            mbd = ((RootBeanDefinitionbd).cloneBeanDefinition();
          }
          else {
            mbd = new RootBeanDefinition(bd);
          }
        }
        else {
          // Child bean definition: needs to be merged with parent.
          BeanDefinition pbd;
          try {
            String parentBeanName = transformedBeanName(bd.getParentName());
            if (!beanName.equals(parentBeanName)) {
              pbd = getMergedBeanDefinition(parentBeanName);
            }
            else {
              if (getParentBeanFactory() instanceof ConfigurableBeanFactory) {
                pbd = ((ConfigurableBeanFactorygetParentBeanFactory()).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);
        }

        // Set default singleton scope, if not configured before.
        if (!StringUtils.hasLength(mbd.getScope())) {
          mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
        }

        // 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() && isBeanEligibleForMetadataCaching(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)
   @param typesToMatch the types to match in case of internal type matching purposes
   * (also signals that the returned <code>Class</code> will never be 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(final RootBeanDefinition mbd, String beanName, final Class... typesToMatch)
      throws CannotLoadBeanClassException {
    try {
      if (mbd.hasBeanClass()) {
        return mbd.getBeanClass();
      }
      if (System.getSecurityManager() != null) {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<Class>() {
          public Class run() throws Exception {
            return doResolveBeanClass(mbd, typesToMatch);
          }
        }, getAccessControlContext());
      }
      else {
        return doResolveBeanClass(mbd, typesToMatch);
      }
    }
    catch (PrivilegedActionException pae) {
      ClassNotFoundException ex = (ClassNotFoundExceptionpae.getException();
      throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
    }
    catch (ClassNotFoundException ex) {
      throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
    }
    catch (LinkageError err) {
      throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
    }
  }
  
  private Class doResolveBeanClass(RootBeanDefinition mbd, Class... typesToMatchthrows ClassNotFoundException {
    if (!ObjectUtils.isEmpty(typesToMatch)) {
      ClassLoader tempClassLoader = getTempClassLoader();
      if (tempClassLoader != null) {
        if (tempClassLoader instanceof DecoratingClassLoader) {
          DecoratingClassLoader dcl = (DecoratingClassLoadertempClassLoader;
          for (Class<?> typeToMatch : typesToMatch) {
            dcl.excludeClass(typeToMatch.getName());
          }
        }
        String className = mbd.getBeanClassName();
        return (className != null ? ClassUtils.forName(className, tempClassLoadernull);
      }
    }
    return mbd.resolveBeanClass(getBeanClassLoader());
  }

  /**
   * Evaluate the given String as contained in a bean definition,
   * potentially resolving it as an expression.
   @param value the value to check
   @param beanDefinition the bean definition that the value comes from
   @return the resolved value
   @see #setBeanExpressionResolver
   */
  protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanDefinition) {
    if (this.beanExpressionResolver == null) {
      return value;
    }
    Scope scope = (beanDefinition != null ? getRegisteredScope(beanDefinition.getScope()) null);
    return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
  }


  /**
   * 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 typesToMatch the types to match in case of internal type matching purposes
   * (also signals that the returned <code>Class</code> will never be exposed to application code)
   @return the type of the bean, or <code>null</code> if not predictable
   */
  protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) {
    if (mbd.getFactoryMethodName() != null) {
      return null;
    }
    return resolveBeanClass(mbd, beanName, typesToMatch);
  }

  /**
   * Check whether the given bean is defined as a {@link FactoryBean}.
   @param beanName the name of the bean
   @param mbd the corresponding bean definition
   */
  protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
    Class beanClass = predictBeanType(beanName, mbd, FactoryBean.class);
    return (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass));
  }

  /**
   * 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 = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
      return getTypeForFactoryBean(factoryBean);
    }
    catch (BeanCreationException ex) {
      // Can only happen when getting a FactoryBean.
      if (logger.isDebugEnabled()) {
        logger.debug("Ignoring bean creation exception on FactoryBean type check: " + ex);
      }
      onSuppressedException(ex);
      return null;
    }
  }

  /**
   * Mark the specified bean as already created (or about to be created).
   <p>This allows the bean factory to optimize its caching for repeated
   * creation of the specified bean.
   @param beanName the name of the bean
   */
  protected void markBeanAsCreated(String beanName) {
    this.alreadyCreated.add(beanName);
  }

  /**
   * Determine whether the specified bean is eligible for having
   * its bean definition metadata cached.
   @param beanName the name of the bean
   @return <code>true</code> if the bean's metadata may be cached
   * at this point already
   */
  protected boolean isBeanEligibleForMetadataCaching(String beanName) {
    return this.alreadyCreated.contains(beanName);
  }

  /**
   * Remove the singleton instance (if any) for the given bean name,
   * but only if it hasn't been used for other purposes than type checking.
   @param beanName the name of the bean
   @return <code>true</code> if actually removed, <code>false</code> otherwise
   */
  protected boolean removeSingletonIfCreatedForTypeCheckOnly(String beanName) {
    if (!this.alreadyCreated.contains(beanName)) {
      removeSingleton(beanName);
      return true;
    }
    else {
      return false;
    }
  }

  /**
   * 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
   @param mbd the merged bean definition
   @return the object to expose for the bean
   */
  protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    if (BeanFactoryUtils.isFactoryDereference(name&& !(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    // 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|| BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
    }

    Object object = null;
    if (mbd == null) {
      object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
      // Return bean instance from factory.
      FactoryBean factory = (FactoryBeanbeanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
        mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
  }

  /**
   * Determine whether the given bean name is already in use within this factory,
   * i.e. whether there is a local bean or alias registered under this name or
   * an inner bean created with this name.
   @param beanName the name to check
   */
  public boolean isBeanNameInUse(String beanName) {
    return isAlias(beanName|| 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 != null &&
        (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) {
    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() null);
    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(), acc));
      }
      else {
        // A bean with a custom 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(), acc));
      }
    }
  }


  //---------------------------------------------------------------------
  // 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 beanNamethrows 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;

}