📄 abstractbeanfactory.java
字号:
}
}
// 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 (mergedBeanDefinition.isSingleton()) {
throw new BeanDefinitionStoreException(
"Cannot specify arguments in the getBean() method when referring to a singleton bean definition");
}
else if (mergedBeanDefinition.getFactoryMethodName() == null) {
throw new BeanDefinitionStoreException(
"Can only specify arguments in the getBean() method in conjunction with a factory method");
}
}
}
/**
* Get the object for the given shared bean, either the bean
* instance itself or its created object in case of a FactoryBean.
* @param name name that may include factory dereference prefix
* @param beanInstance the shared bean instance
* @return the singleton instance of the bean
*/
protected Object getObjectForSharedInstance(String name, Object beanInstance) throws BeansException {
String beanName = transformedBeanName(name);
// Don't let calling code try to dereference the
// bean factory if the bean isn't a factory.
if (isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, 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) {
if (!isFactoryDereference(name)) {
// Return bean instance from factory.
FactoryBean factory = (FactoryBean) beanInstance;
if (logger.isDebugEnabled()) {
logger.debug("Bean with name '" + beanName + "' is a factory bean");
}
try {
beanInstance = factory.getObject();
}
catch (Exception ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
if (beanInstance == null) {
throw new FactoryBeanNotInitializedException(
beanName, "FactoryBean returned null object: " +
"probably not fully initialized (maybe due to circular bean reference)");
}
}
else {
// The user wants the factory itself.
if (logger.isDebugEnabled()) {
logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'");
}
}
}
return beanInstance;
}
/**
* Determine whether the bean with the given name is a FactoryBean.
* @param name the name of the bean to check
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
*/
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name);
Object beanInstance = null;
synchronized (this.singletonCache) {
beanInstance = this.singletonCache.get(beanName);
}
if (beanInstance != null) {
return (beanInstance instanceof FactoryBean);
}
else {
// 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 (bd.hasBeanClass() && FactoryBean.class.equals(bd.getBeanClass()));
}
}
/**
* 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 mergedBeanDefinition the bean definition for the bean
* @see RootBeanDefinition#isSingleton
* @see RootBeanDefinition#getDependsOn
* @see #registerDisposableBean
* @see #registerDependentBean
*/
protected void registerDisposableBeanIfNecessary(
final String beanName, final Object bean, final RootBeanDefinition mergedBeanDefinition) {
if (mergedBeanDefinition.isSingleton()) {
final boolean isDisposableBean = (bean instanceof DisposableBean);
final boolean hasDestroyMethod = (mergedBeanDefinition.getDestroyMethodName() != null);
if (isDisposableBean || hasDestroyMethod || hasDestructionAwareBeanPostProcessors()) {
// Determine unique key for registration of disposable bean
int counter = 1;
String id = beanName;
synchronized (this.disposableBeans) {
while (this.disposableBeans.containsKey(id)) {
counter++;
id = beanName + "#" + counter;
}
}
// Register a DisposableBean implementation that performs all destruction
// work for the given bean: DestructionAwareBeanPostProcessors,
// DisposableBean interface, custom destroy method.
registerDisposableBean(id, new DisposableBean() {
public void destroy() throws Exception {
if (hasDestructionAwareBeanPostProcessors()) {
if (logger.isDebugEnabled()) {
logger.debug("Applying DestructionAwareBeanPostProcessors to bean with name '" + beanName + "'");
}
for (int i = getBeanPostProcessors().size() - 1; i >= 0; i--) {
Object beanProcessor = getBeanPostProcessors().get(i);
if (beanProcessor instanceof DestructionAwareBeanPostProcessor) {
((DestructionAwareBeanPostProcessor) beanProcessor).postProcessBeforeDestruction(bean, beanName);
}
}
}
if (isDisposableBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking destroy() on bean with name '" + beanName + "'");
}
((DisposableBean) bean).destroy();
}
if (hasDestroyMethod) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking custom destroy method on bean with name '" + beanName + "'");
}
invokeCustomDestroyMethod(beanName, bean, mergedBeanDefinition.getDestroyMethodName(),
mergedBeanDefinition.isEnforceDestroyMethod());
}
}
});
}
// Register bean as dependent on other beans, if necessary,
// for correct shutdown order.
String[] dependsOn = mergedBeanDefinition.getDependsOn();
if (dependsOn != null) {
for (int i = 0; i < dependsOn.length; i++) {
registerDependentBean(dependsOn[i], beanName);
}
}
}
}
/**
* Add the given bean to the list of further disposable beans in this factory.
* @param beanName the name of the bean
* @param bean the bean instance
*/
protected void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
/**
* Register a dependent bean for the given bean,
* to be destroyed before the given bean is destroyed.
* @param beanName the name of the bean
* @param dependentBeanName the name of the dependent bean
*/
protected void registerDependentBean(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
Set dependencies = (Set) this.dependentBeanMap.get(beanName);
if (dependencies == null) {
dependencies = CollectionFactory.createLinkedSetIfPossible(8);
this.dependentBeanMap.put(beanName, dependencies);
}
dependencies.add(dependentBeanName);
}
}
/**
* Destroy the given bean. Delegates to destroyBean if a
* corresponding disposable bean instance is found.
* @param beanName name of the bean
* @see #destroyBean
*/
private void destroyDisposableBean(String beanName) {
// Remove a registered singleton of the given name, if any.
removeSingleton(beanName);
// Destroy the corresponding DisposableBean instance.
Object disposableBean = null;
synchronized (this.disposableBeans) {
disposableBean = this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
/**
* Destroy the given bean. Must destroy beans that depend on the given
* bean before the bean itself. Should not throw any exceptions.
* @param beanName name of the bean
* @param bean the bean instance to destroy
*/
protected void destroyBean(String beanName, Object bean) {
if (logger.isDebugEnabled()) {
logger.debug("Retrieving dependent beans for bean '" + beanName + "'");
}
Set dependencies = null;
synchronized (this.dependentBeanMap) {
dependencies = (Set) this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
for (Iterator it = dependencies.iterator(); it.hasNext();) {
String dependentBeanName = (String) it.next();
destroyDisposableBean(dependentBeanName);
}
}
if (bean instanceof DisposableBean) {
try {
((DisposableBean) bean).destroy();
}
catch (Throwable ex) {
logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
}
}
}
/**
* Invoke the specified custom destroy method on the given bean.
* <p>This implementation invokes a no-arg method if found, else checking
* for a method with a single boolean argument (passing in "true",
* assuming a "force" parameter), else logging an error.
* <p>Can be overridden in subclasses for custom resolution of destroy
* methods with arguments.
* @param beanName the bean has in the factory. Used for debug output.
* @param bean new bean instance we may need to notify of destruction
* @param destroyMethodName the name of the custom destroy method
* @param enforceDestroyMethod indicates whether the defined destroy method needs to exist
*/
protected void invokeCustomDestroyMethod(
String beanName, Object bean, String destroyMethodName, boolean enforceDestroyMethod) {
Method destroyMethod =
BeanUtils.findDeclaredMethodWithMinimalParameters(bean.getClass(), destroyMethodName);
if (destroyMethod == null) {
if (enforceDestroyMethod) {
logger.error("Couldn't find a destroy method named '" + destroyMethodName +
"' on bean with name '" + beanName + "'");
}
}
else {
Class[] paramTypes = destroyMethod.getParameterTypes();
if (paramTypes.length > 1) {
logger.error("Method '" + destroyMethodName + "' of bean '" + beanName +
"' has more than one parameter - not supported as destroy method");
}
else if (paramTypes.length == 1 && !paramTypes[0].equals(boolean.class)) {
logger.error("Method '" + destroyMethodName + "' of bean '" + beanName +
"' has a non-boolean parameter - not supported as destroy method");
}
else {
Object[] args = new Object[paramTypes.length];
if (paramTypes.length == 1) {
args[0] = Boolean.TRUE;
}
if (!Modifier.isPublic(destroyMethod.getModifiers())) {
destroyMethod.setAccessible(true);
}
try {
destroyMethod.invoke(bean, args);
}
catch (InvocationTargetException ex) {
logger.error("Couldn't invoke destroy method '" + destroyMethodName +
"' of bean with name '" + beanName + "'", ex.getTargetException());
}
catch (Throwable ex) {
logger.error("Couldn't invoke destroy method '" + destroyMethodName +
"' of bean with name '" + beanName + "'", ex);
}
}
}
}
//---------------------------------------------------------------------
// 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 name of the bean to find a definition for
* @return the BeanDefinition for this prototype name. Must never return 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.
* <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 name of the bean
* @param mergedBeanDefinition the 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 mergedBeanDefinition, Object[] args) throws BeanCreationException;
/**
* Determine the bean type for the given bean definition,
* as far as possible.
* <p>Default implementation returns <code>null</code> to indicate that the
* type cannot be determined. Subclasses are encouraged to try to determine
* the actual return type here, matching their strategy of resolving
* factory methods in the <code>createBean</code> implementation.
* @param beanName name of the bean
* @param mergedBeanDefinition the bean definition for the bean
* @return the type for the bean if determinable, or <code>null</code> else
* @see #createBean
*/
protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition mergedBeanDefinition) {
return null;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -