org.springframework.beans.factory.support.AbstractBeanFactory Maven / Gradle / Ivy
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.support;
import java.beans.PropertyEditor;
import java.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.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.PropertyEditorRegistrySupport;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanIsAbstractException;
import org.springframework.beans.factory.BeanIsNotAFactoryException;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.CollectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* 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 not 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).
*
* 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.
*
*
The main template methods to be implemented by subclasses are
* {@link #getBeanDefinition} and {@link #createBean}, retrieving a bean definition
* for a given bean name and creating a bean instance for a given bean definition,
* respectively. Default implementations of those operations can be found in
* {@link DefaultListableBeanFactory} and {@link AbstractAutowireCapableBeanFactory}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 15 April 2001
* @see #getBeanDefinition
* @see #createBean
* @see AbstractAutowireCapableBeanFactory#createBean
* @see DefaultListableBeanFactory#getBeanDefinition
*/
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
/** Parent bean factory, for bean inheritance support */
private BeanFactory parentBeanFactory;
/** ClassLoader to resolve bean class names with, if necessary */
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/** Whether to cache bean metadata or rather reobtain it for every access */
private boolean cacheBeanMetadata = true;
/** Custom PropertyEditors to apply to the beans of this factory */
private final Map customEditors = new HashMap();
/** Custom PropertyEditorRegistrars to apply to the beans of this factory */
private final Set propertyEditorRegistrars = CollectionFactory.createLinkedSetIfPossible(16);
/** BeanPostProcessors to apply in createBean */
private final List beanPostProcessors = new ArrayList();
/** Indicates whether any InstantiationAwareBeanPostProcessors have been registered */
private boolean hasInstantiationAwareBeanPostProcessors;
/** Indicates whether any DestructionAwareBeanPostProcessors have been registered */
private boolean hasDestructionAwareBeanPostProcessors;
/** Map from scope identifier String to corresponding Scope */
private final Map scopes = new HashMap();
/** Map from alias to canonical bean name */
private final Map aliasMap = new HashMap();
/** Map from ChildBeanDefinition to merged RootBeanDefinition */
private final Map mergedBeanDefinitions = CollectionFactory.createIdentityMapIfPossible(16);
/** Names of beans that have already been created at least once */
private final Set alreadyCreated = Collections.synchronizedSet(new HashSet());
/** Names of beans that are currently in creation */
private final ThreadLocal prototypesCurrentlyInCreation = new ThreadLocal();
/** Cache of singleton objects created by FactoryBeans: FactoryBean name --> object */
private final Map factoryBeanObjectCache = new HashMap();
/**
* Create a new AbstractBeanFactory.
*/
public AbstractBeanFactory() {
}
/**
* Create a new AbstractBeanFactory with the given parent.
* @param parentBeanFactory parent bean factory, or null
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);
}
/**
* Return an instance, which may be shared or independent, of the specified bean.
* @param name the name 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, 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, final Object[] args) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean = null;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null) {
if (isSingletonCurrentlyInCreation(beanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
if (containsBeanDefinition(beanName)) {
RootBeanDefinition mbd = getMergedBeanDefinition(beanName, false);
bean = getObjectForBeanInstance(sharedInstance, name, mbd);
}
else {
bean = getObjectForBeanInstance(sharedInstance, name, 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 (parentBeanFactory instanceof AbstractBeanFactory) {
// Delegation to parent with args only possible for AbstractBeanFactory.
return ((AbstractBeanFactory) parentBeanFactory).getBean(nameToLookup, requiredType, args);
}
else if (args == null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
throw new NoSuchBeanDefinitionException(beanName,
"Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
}
}
this.alreadyCreated.add(beanName);
final RootBeanDefinition mbd = getMergedBeanDefinition(beanName, false);
checkMergedBeanDefinition(mbd, beanName, args);
// 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, 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, 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 {
Object bean = createBean(beanName, mbd, args);
if (requiresDestruction(bean, mbd)) {
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors()));
}
return bean;
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, 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 && !requiredType.isAssignableFrom(bean.getClass())) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return bean;
}
public boolean containsBean(String name) {
if (containsLocalBean(name)) {
return true;
}
// Not found -> check parent.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null) {
return parentBeanFactory.containsBean(originalBeanName(name));
}
return false;
}
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name);
Object beanInstance = getSingleton(beanName);
if (beanInstance != null) {
if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
return ((FactoryBean) beanInstance).isSingleton();
}
else {
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 bd = getMergedBeanDefinition(beanName, false);
// In case of FactoryBean, return singleton status of created object if not a dereference.
if (bd.isSingleton() && !BeanFactoryUtils.isFactoryDereference(name)) {
if (isBeanClassMatch(beanName, bd, FactoryBean.class)) {
FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
return factoryBean.isSingleton();
}
}
return bd.isSingleton();
}
}
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 bd = getMergedBeanDefinition(beanName, false);
// In case of FactoryBean, return singleton status of created object if not a dereference.
if (bd.isSingleton() && !BeanFactoryUtils.isFactoryDereference(name)) {
if (isBeanClassMatch(beanName, bd, FactoryBean.class)) {
FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean).isPrototype()) ||
!factoryBean.isSingleton());
}
}
return bd.isPrototype();
}
public boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException {
if ((targetType == null || Object.class.equals(targetType)) && containsLocalBean(name)) {
return true;
}
String beanName = transformedBeanName(name);
// Check manually registered singletons.
Object beanInstance = getSingleton(beanName);
if (beanInstance != null) {
if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
Class type = getTypeForFactoryBean((FactoryBean) beanInstance);
return (type != null && targetType.isAssignableFrom(type));
}
else {
return targetType.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 = getMergedBeanDefinition(beanName, false);
Class beanClass = predictBeanType(beanName, mbd);
if (beanClass == null) {
return false;
}
// Check bean class whether we're dealing with a FactoryBean.
if (FactoryBean.class.isAssignableFrom(beanClass) && !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 && targetType.isAssignableFrom(type));
}
else {
return targetType.isAssignableFrom(beanClass);
}
}
}
public Class getType(String name) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name);
// Check manually registered singletons.
Object beanInstance = getSingleton(beanName);
if (beanInstance != null) {
if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
return getTypeForFactoryBean((FactoryBean) beanInstance);
}
else {
return beanInstance.getClass();
}
}
else {
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.getType(originalBeanName(name));
}
RootBeanDefinition mbd = getMergedBeanDefinition(beanName, false);
Class beanClass = predictBeanType(beanName, mbd);
// Check bean class whether we're dealing with a FactoryBean.
if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass) &&
!BeanFactoryUtils.isFactoryDereference(name)) {
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
return getTypeForFactoryBean(beanName, mbd);
}
return beanClass;
}
}
public String[] getAliases(String name) {
String beanName = transformedBeanName(name);
List aliases = new ArrayList();
boolean factoryPrefix = name.startsWith(FACTORY_BEAN_PREFIX);
String fullBeanName = beanName;
if (factoryPrefix) {
fullBeanName = FACTORY_BEAN_PREFIX + beanName;
}
if (!fullBeanName.equals(name)) {
aliases.add(fullBeanName);
}
synchronized (this.aliasMap) {
for (Iterator it = this.aliasMap.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if (entry.getValue().equals(beanName)) {
String key = (factoryPrefix ? FACTORY_BEAN_PREFIX : "") + entry.getKey();
if (!key.equals(name)) {
aliases.add(key);
}
}
}
}
if (!containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null) {
aliases.addAll(Arrays.asList(parentBeanFactory.getAliases(fullBeanName)));
}
}
return StringUtils.toStringArray(aliases);
}
//---------------------------------------------------------------------
// Implementation of HierarchicalBeanFactory interface
//---------------------------------------------------------------------
public BeanFactory getParentBeanFactory() {
return this.parentBeanFactory;
}
public boolean containsLocalBean(String name) {
String beanName = transformedBeanName(name);
return (containsSingleton(beanName) || containsBeanDefinition(beanName));
}
//---------------------------------------------------------------------
// 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 setCacheBeanMetadata(boolean cacheBeanMetadata) {
this.cacheBeanMetadata = cacheBeanMetadata;
}
public boolean isCacheBeanMetadata() {
return this.cacheBeanMetadata;
}
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
this.propertyEditorRegistrars.add(registrar);
}
/**
* Return the set of PropertyEditorRegistrars.
*/
public Set getPropertyEditorRegistrars() {
return this.propertyEditorRegistrars;
}
public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor) {
Assert.notNull(requiredType, "Required type must not be null");
Assert.notNull(propertyEditor, "PropertyEditor must not be null");
this.customEditors.put(requiredType, propertyEditor);
}
/**
* Return the map of custom editors, with Classes as keys
* and PropertyEditors as values.
*/
public Map getCustomEditors() {
return this.customEditors;
}
public TypeConverter getTypeConverter() {
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
registerCustomEditors(typeConverter);
return typeConverter;
}
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
this.beanPostProcessors.add(beanPostProcessor);
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true;
}
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true;
}
}
public int getBeanPostProcessorCount() {
return this.beanPostProcessors.size();
}
/**
* Return the list of BeanPostProcessors that will get applied
* to beans created with this factory.
*/
public List getBeanPostProcessors() {
return this.beanPostProcessors;
}
/**
* Return whether this factory holds a InstantiationAwareBeanPostProcessor
* that will get applied to singleton beans on shutdown.
* @see #addBeanPostProcessor
* @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
*/
protected boolean hasInstantiationAwareBeanPostProcessors() {
return this.hasInstantiationAwareBeanPostProcessors;
}
/**
* Return whether this factory holds a DestructionAwareBeanPostProcessor
* that will get applied to singleton beans on shutdown.
* @see #addBeanPostProcessor
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
*/
protected boolean hasDestructionAwareBeanPostProcessors() {
return this.hasDestructionAwareBeanPostProcessors;
}
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
this.scopes.put(scopeName, scope);
}
public String[] getRegisteredScopeNames() {
return StringUtils.toStringArray(this.scopes.keySet());
}
public Scope getRegisteredScope(String scopeName) {
Assert.notNull(scopeName, "Scope identifier must not be null");
return (Scope) this.scopes.get(scopeName);
}
public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
Assert.notNull(otherFactory, "BeanFactory must not be null");
setBeanClassLoader(otherFactory.getBeanClassLoader());
setCacheBeanMetadata(otherFactory.isCacheBeanMetadata());
if (otherFactory instanceof AbstractBeanFactory) {
AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactory) otherFactory;
this.customEditors.putAll(otherAbstractFactory.customEditors);
this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
this.beanPostProcessors.addAll(otherAbstractFactory.beanPostProcessors);
this.hasInstantiationAwareBeanPostProcessors = this.hasInstantiationAwareBeanPostProcessors ||
otherAbstractFactory.hasInstantiationAwareBeanPostProcessors;
this.hasDestructionAwareBeanPostProcessors = this.hasDestructionAwareBeanPostProcessors ||
otherAbstractFactory.hasDestructionAwareBeanPostProcessors;
this.scopes.putAll(otherAbstractFactory.scopes);
}
}
public void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
if (!alias.equals(beanName)) {
// Only actually register the alias if it is not equal to the bean name itself.
if (logger.isDebugEnabled()) {
logger.debug("Registering alias '" + alias + "' for bean with name '" + beanName + "'");
}
synchronized (this.aliasMap) {
Object registeredName = this.aliasMap.get(alias);
if (registeredName != null && !registeredName.equals(beanName)) {
throw new BeanDefinitionStoreException("Cannot register alias '" + alias + "' for bean name '" +
beanName + "': It's already registered for bean name '" + registeredName + "'.");
}
this.aliasMap.put(alias, beanName);
}
}
}
/**
* Callback before prototype creation.
*
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.
*
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, getMergedBeanDefinition(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 = getMergedBeanDefinition(beanName);
if (mbd.isSingleton() || mbd.isPrototype()) {
throw new IllegalArgumentException(
"Bean name '" + beanName + "' does not correspond to an object in a Scope");
}
String scopeName = mbd.getScope();
Scope scope = (Scope) this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
Object bean = scope.remove(beanName);
if (bean != null) {
destroyBean(beanName, bean, mbd);
}
}
//---------------------------------------------------------------------
// Implementation methods
//---------------------------------------------------------------------
/**
* Return the bean name, stripping out the factory dereference prefix if necessary,
* and resolving aliases to canonical names.
* @param name the user-specified name
* @return the transformed bean name
*/
protected String transformedBeanName(String name) {
String canonicalName = BeanFactoryUtils.transformedBeanName(name);
// Handle aliasing.
synchronized (this.aliasMap) {
String resolvedName = null;
do {
resolvedName = (String) this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
}
return canonicalName;
}
/**
* Determine the original bean name, resolving locally defined aliases to canonical names.
* @param name the user-specified name
* @return the original bean name
*/
protected String originalBeanName(String name) {
String beanName = transformedBeanName(name);
if (name.startsWith(FACTORY_BEAN_PREFIX)) {
beanName = FACTORY_BEAN_PREFIX + beanName;
}
return beanName;
}
/**
* Determine whether this given bean name is defines as an alias
* (as opposed to the name of an actual bean definition).
* @param beanName the bean name to check
* @return whether the given name is an alias
*/
protected boolean isAlias(String beanName) {
synchronized (this.aliasMap) {
return this.aliasMap.containsKey(beanName);
}
}
/**
* Initialize the given BeanWrapper with the custom editors registered
* with this factory. To be called for BeanWrappers that will create
* and populate bean instances.
*
The default implementation delegates to registerCustomEditors
.
* Can be overridden in subclasses.
* @param bw the BeanWrapper to initialize
* @see #registerCustomEditors
*/
protected void initBeanWrapper(BeanWrapper bw) {
registerCustomEditors(bw);
}
/**
* Initialize the given PropertyEditorRegistry with the custom editors
* registered with this BeanFactory.
*
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();
}
for (Iterator it = this.propertyEditorRegistrars.iterator(); it.hasNext();) {
PropertyEditorRegistrar registrar = (PropertyEditorRegistrar) it.next();
registrar.registerCustomEditors(registry);
}
for (Iterator it = this.customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Class clazz = (Class) entry.getKey();
PropertyEditor editor = (PropertyEditor) entry.getValue();
// Register the editor as shared instance, if possible,
// to make it clear that it might be used concurrently.
if (registrySupport != null) {
registrySupport.registerSharedEditor(clazz, editor);
}
else {
registry.registerCustomEditor(clazz, editor);
}
}
}
/**
* Return a RootBeanDefinition for the given bean name,
* merging a child bean definition with its parent if necessary.
* @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
*/
public RootBeanDefinition getMergedBeanDefinition(String beanName) throws BeansException {
return getMergedBeanDefinition(beanName, false);
}
/**
* Return a RootBeanDefinition, even by traversing parent if the parameter is a
* child definition. Can ask the parent bean factory if not found in this instance.
* @param beanName the name of the bean to retrieve the merged definition for
* @param includingAncestors whether to ask the parent bean factory if not found
* in this instance
* @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 getMergedBeanDefinition(String beanName, boolean includingAncestors)
throws BeansException {
// Efficiently check whether bean definition exists in this factory.
if (includingAncestors && !containsBeanDefinition(beanName) &&
getParentBeanFactory() instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName, true);
}
// Resolve merged bean definition locally.
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 null
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 = (RootBeanDefinition) this.mergedBeanDefinitions.get(bd);
if (mbd == null) {
if (bd instanceof RootBeanDefinition) {
// Use copy of given root bean definition.
mbd = new RootBeanDefinition((RootBeanDefinition) bd);
}
else if (bd instanceof ChildBeanDefinition) {
// Child bean definition: needs to be merged with parent.
ChildBeanDefinition cbd = (ChildBeanDefinition) bd;
RootBeanDefinition pbd = null;
try {
String parentBeanName = transformedBeanName(cbd.getParentName());
if (!beanName.equals(parentBeanName)) {
pbd = getMergedBeanDefinition(parentBeanName, true);
}
else {
if (getParentBeanFactory() instanceof AbstractBeanFactory) {
AbstractBeanFactory parentFactory = (AbstractBeanFactory) getParentBeanFactory();
pbd = parentFactory.getMergedBeanDefinition(parentBeanName, true);
}
else {
throw new NoSuchBeanDefinitionException(cbd.getParentName(),
"Parent name '" + cbd.getParentName() + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(cbd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + cbd.getParentName() + "'", ex);
}
// Deep copy with overridden values.
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(cbd);
}
else {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Definition is neither a RootBeanDefinition nor a ChildBeanDefinition: " + 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.setSingleton(false);
}
// Only cache the merged bean definition if we're already about to create an
// instance of the bean, or at least have already created an instance before.
if (containingBd == null && isCacheBeanMetadata() && this.alreadyCreated.contains(beanName)) {
this.mergedBeanDefinitions.put(bd, 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) {
if (mbd.isSingleton()) {
throw new BeanDefinitionStoreException(
"Cannot specify arguments in the getBean() method when referring to a singleton bean definition");
}
else if (mbd.getFactoryMethodName() == null) {
throw new BeanDefinitionStoreException(
"Can only specify arguments in the getBean() method in conjunction with a factory method");
}
}
}
/**
* 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 null
if none)
* @throws CannotLoadBeanClassException if we failed to load the class
*/
protected Class resolveBeanClass(RootBeanDefinition mbd, String beanName) throws CannotLoadBeanClassException {
if (mbd.hasBeanClass()) {
return mbd.getBeanClass();
}
try {
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);
}
}
/**
* Check whether the bean class of the given bean definition matches
* the specified target type. Allows for lazy loading of the actual
* bean class, provided that the type match can be determined otherwise.
*
The default implementation simply delegates to the standard
* resolveBeanClass
method. Subclasses may override this
* to use a different strategy, such as a throwaway class loaer.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition to determine the class for
* @param targetType the type to match against (never null
)
* @return the resolved bean class (or null
if none)
* @throws CannotLoadBeanClassException if we failed to load the class
* @see #resolveBeanClass
*/
protected boolean isBeanClassMatch(String beanName, RootBeanDefinition mbd, Class targetType)
throws CannotLoadBeanClassException {
Class beanClass = resolveBeanClass(mbd, beanName);
return (beanClass != null && targetType.isAssignableFrom(beanClass));
}
/**
* 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.
*
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
* @return the type of the bean, or null
if not predictable
*/
protected Class predictBeanType(String beanName, RootBeanDefinition mbd) {
if (mbd.getFactoryMethodName() == null) {
return resolveBeanClass(mbd, beanName);
}
return null;
}
/**
* 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.
*
Default implementation creates the FactoryBean via getBean
* to call its getObjectType
method. Subclasses are encouraged to optimize
* this, typically by just instantiating the FactoryBean but not populating it yet,
* trying whether its getObjectType
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 null
else
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
* @see #getBean(String)
*/
protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
try {
FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
return getTypeForFactoryBean(factoryBean);
}
catch (BeanCreationException ex) {
// Can only happen when getting a FactoryBean.
logger.debug("Ignoring bean creation exception on FactoryBean type check", ex);
return null;
}
}
/**
* Determine the type for the given FactoryBean.
* @param factoryBean the FactoryBean instance to check
* @return the FactoryBean's object type,
* or null
if the type cannot be determined yet
*/
protected Class getTypeForFactoryBean(FactoryBean factoryBean) {
try {
return factoryBean.getObjectType();
}
catch (Throwable ex) {
// Thrown from the FactoryBean's getObjectType implementation.
logger.warn("FactoryBean threw exception from getObjectType, despite the contract saying " +
"that it should return null if the type of its object cannot be determined yet", ex);
return null;
}
}
/**
* Get the object for the given bean instance, either the bean
* instance itself or its created object in case of a FactoryBean.
* @param beanInstance the shared bean instance
* @param name name that may include factory dereference prefix
* @param mbd the merged bean definition
* @return the object to expose for the bean
*/
protected Object getObjectForBeanInstance(Object beanInstance, String name, RootBeanDefinition mbd) {
// Don't let calling code try to dereference the
// bean factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
boolean shared = (mbd == null || mbd.isSingleton());
Object object = beanInstance;
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (beanInstance instanceof FactoryBean) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
// Return bean instance from factory.
FactoryBean factory = (FactoryBean) beanInstance;
String beanName = transformedBeanName(name);
// Cache object obtained from FactoryBean if it is a singleton.
if (shared && factory.isSingleton()) {
synchronized (getSingletonMutex()) {
object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = getObjectFromFactoryBean(factory, beanName, mbd);
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
else {
object = getObjectFromFactoryBean(factory, beanName, mbd);
}
}
}
return object;
}
/**
* Obtain an object to expose from the given FactoryBean.
* @param factory the FactoryBean instance
* @param beanName the name of the bean
* @param mbd the merged bean definition
* @return the object obtained from the FactoryBean
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
private Object getObjectFromFactoryBean(FactoryBean factory, String beanName, RootBeanDefinition mbd)
throws BeanCreationException {
Object object;
try {
object = factory.getObject();
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
if (object != null && (mbd == null || !mbd.isSynthetic())) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of the FactoryBean's object failed", ex);
}
}
return object;
}
/**
* Post-process the given object that has been obtained from the FactoryBean.
* The resulting object will get exposed for bean references.
*
The default implementation simply returns the given object as-is.
* Subclasses may override this, for example, to apply post-processors.
* @param object the object obtained from the FactoryBean.
* @param beanName the name of the bean
* @return the object to expose
* @throws BeansException if any post-processing failed
*/
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
return object;
}
/**
* Determine whether the bean with the given name is a FactoryBean.
* @param name the name of the bean to check
* @return whether the bean is a FactoryBean
* (false
means the bean exists but is not a FactoryBean)
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
*/
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name);
Object beanInstance = getSingleton(beanName);
if (beanInstance != null) {
return (beanInstance instanceof FactoryBean);
}
// No singleton instance found -> check bean definition.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof AbstractBeanFactory) {
// No bean definition found in this factory -> delegate to parent.
return ((AbstractBeanFactory) getParentBeanFactory()).isFactoryBean(name);
}
RootBeanDefinition bd = getMergedBeanDefinition(beanName, false);
return isBeanClassMatch(beanName, bd, FactoryBean.class);
}
/**
* Determine whether the given bean name is already in use within this factory,
* that is, whether there is a local bean registered under this name or
* an inner bean created with this name.
* @param beanName the name to check
*/
protected boolean isBeanNameInUse(String beanName) {
return containsLocalBean(beanName) || hasDependentBean(beanName);
}
/**
* Determine whether the given bean requires destruction on shutdown.
*
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.
*
Also registers bean as dependent on other beans, according to the
* "depends-on" configuration in the bean definition.
* @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.isSingleton() && requiresDestruction(bean, mbd)) {
// 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()));
// Register bean as dependent on other beans, if necessary,
// for correct shutdown order.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (int i = 0; i < dependsOn.length; i++) {
registerDependentBean(dependsOn[i], beanName);
}
}
}
}
/**
* Overridden to clear the FactoryBean object cache as well.
*/
protected void removeSingleton(String beanName) {
super.removeSingleton(beanName);
this.factoryBeanObjectCache.remove(beanName);
}
//---------------------------------------------------------------------
// Abstract methods to be implemented by subclasses
//---------------------------------------------------------------------
/**
* Check if this bean factory contains a bean definition with the given name.
* Does not consider any hierarchy this factory may participate in.
* Invoked by containsBean
when no cached singleton instance is found.
*
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.
*
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 null
)
* @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.
*
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 null
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;
}