/* * Copyright 2002-2008 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.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.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.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.core.DecoratingClassLoader; import org.springframework.core.NamedThreadLocal; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** * 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 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; /** Custom PropertyEditorRegistrars to apply to the beans of this factory */ private final Set propertyEditorRegistrars = new LinkedHashSet(4); /** Custom PropertyEditors to apply to the beans of this factory */ private final Map customEditors = new HashMap(4); /** 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 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 NamedThreadLocal("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 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(String name, Class requiredType, Object[] args) throws 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 */ protected Object doGetBean( final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) 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 && 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 parentBeanFactory.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 (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, 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 = (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 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, false); 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()) { if (isFactoryBean(beanName, mbd)) { 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. 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)) { 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, false); 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, new Class[] {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 name) throws 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((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, null); // 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); } String[] retrievedAliases = super.getAliases(beanName); for (int i = 0; i < retrievedAliases.length; i++) { String alias = (factoryPrefix ? FACTORY_BEAN_PREFIX : "") + retrievedAliases[i]; 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 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, Class propertyEditorClass) { Assert.notNull(requiredType, "Required type must not be null"); Assert.isAssignable(PropertyEditor.class, propertyEditorClass); this.customEditors.put(requiredType, propertyEditorClass); } 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); } public void copyRegisteredEditorsTo(PropertyEditorRegistry registry) { registerCustomEditors(registry); } /** * Return the map of custom editors, with Classes as keys * and PropertyEditor instances or PropertyEditor classes 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); } } /** * 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); } public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException { String beanName = transformedBeanName(name); Object beanInstance = getSingleton(beanName, false); 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); } 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 */ 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) { 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) { 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 ? (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 requiredType = (Class) entry.getKey(); Object value = entry.getValue(); if (value instanceof PropertyEditor) { PropertyEditor editor = (PropertyEditor) value; // Register the editor as shared instance, if possible, // to make it clear that it might be used concurrently. if (registrySupport != null) { registrySupport.registerSharedEditor(requiredType, editor); } else { registry.registerCustomEditor(requiredType, editor); } } else if (value instanceof Class) { Class editorClass = (Class) value; registry.registerCustomEditor(requiredType, (PropertyEditor) BeanUtils.instantiateClass(editorClass)); } else { throw new IllegalStateException("Illegal custom editor value type: " + value.getClass().getName()); } } } } /** * 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() && 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) * @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, null); } /** * 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(RootBeanDefinition mbd, String beanName, Class[] typesToMatch) throws CannotLoadBeanClassException { try { if (mbd.hasBeanClass()) { return mbd.getBeanClass(); } if (typesToMatch != null) { ClassLoader tempClassLoader = getTempClassLoader(); if (tempClassLoader != null) { if (tempClassLoader instanceof DecoratingClassLoader) { DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader; for (int i = 0; i < typesToMatch.length; i++) { dcl.excludeClass(typesToMatch[i].getName()); } } String className = mbd.getBeanClassName(); return (className != null ? ClassUtils.forName(className, tempClassLoader) : 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 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, new Class[] {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 = (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 = (FactoryBean) beanInstance; // 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 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())); } } } //--------------------------------------------------------------------- // 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; }