📄 abstractbeanfactory.java
字号:
/**
* 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.
* <p>The default implementation simply delegates to the standard
* <code>resolveBeanClass</code> 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 <code>null</code>)
* @return the resolved bean class (or <code>null</code> 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.
* <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
* @return the type of the bean, or <code>null</code> if not predictable
*/
protected Class predictBeanType(String beanName, RootBeanDefinition mbd) {
if (mbd.getFactoryMethodName() != null) {
return null;
}
return resolveBeanClass(mbd, beanName);
}
/**
* Determine the bean type for the given FactoryBean definition, as far as possible.
* Only called if there is no singleton instance registered for the target bean already.
* <p>The default implementation creates the FactoryBean via <code>getBean</code>
* to call its <code>getObjectType</code> method. Subclasses are encouraged to optimize
* this, typically by just instantiating the FactoryBean but not populating it yet,
* trying whether its <code>getObjectType</code> method already returns a type.
* If no type found, a full FactoryBean creation as performed by this implementation
* should be used as fallback.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @return the type for the bean if determinable, or <code>null</code> else
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
* @see #getBean(String)
*/
protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
if (!mbd.isSingleton()) {
return null;
}
try {
FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
return getTypeForFactoryBean(factoryBean);
}
catch (BeanCreationException ex) {
// Can only happen when getting a FactoryBean.
if (ex.contains(BeanCurrentlyInCreationException.class)) {
logger.debug("Ignoring bean creation exception on FactoryBean type check", ex);
}
else {
String msg =
"Encountered FactoryBean creation exception for bean '" + beanName + "' - couldn't check type!";
if (logger.isDebugEnabled()) {
logger.debug(msg, ex);
}
else if (logger.isWarnEnabled()) {
logger.warn(msg + " " + ex.toString());
}
}
return null;
}
}
/**
* Determine the type for the given FactoryBean.
* @param factoryBean the FactoryBean instance to check
* @return the FactoryBean's object type,
* or <code>null</code> if the type cannot be determined yet
*/
protected Class getTypeForFactoryBean(FactoryBean factoryBean) {
try {
return factoryBean.getObjectType();
}
catch (Throwable ex) {
// Thrown from the FactoryBean's getObjectType implementation.
logger.warn("FactoryBean threw exception from getObjectType, despite the contract saying " +
"that it should return null if the type of its object cannot be determined yet", ex);
return null;
}
}
/**
* Get the object for the given bean instance, either the bean
* instance itself or its created object in case of a FactoryBean.
* @param beanInstance the shared bean instance
* @param name name that may include factory dereference prefix
* @param 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()
*/
protected 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.
* <p>The default implementation simply returns the given object as-is.
* Subclasses may override this, for example, to apply post-processors.
* @param object the object obtained from the FactoryBean.
* @param beanName the name of the bean
* @return the object to expose
* @throws BeansException if any post-processing failed
*/
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
return object;
}
/**
* 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
* (<code>false</code> 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.
* <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.
* <p>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 <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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -