Support destroy method inference

Anywhere the value of a destroy method may be expressed, specifying
the value "(inferred)" now indicates that the container should attempt
to automatically discover a destroy method. This functionality is
currently limited to detecting public, no-arg methods named 'close';
this is particularly useful for commonly used types such as Hibernate
SessionFactory most JDBC DataSource implementations, JMS connection
factories, and so forth.

This special value is captured as the constant
AbstractBeanDefinition#INFER_METHOD, which in turn serves as the default
value of the @Bean#destroyMethod attribute.

For example in the following case

    @Bean
    public BasicDataSource dataSource() { ... }

the container will automatically detect BasicDataSource#close and invoke
it when the enclosing ApplicationContext is closed. This is exactly
equivalent to

    @Bean(destroyMethod="(inferred)")
    public BasicDataSource dataSource() { ... }

A user may override this inference-by-default convention simply by
specifying a different method

    @Bean(destroyMethod="myClose")
    public MyBasicDataSource dataSource() { ... }

or, in the case of a bean that has an otherwise inferrable 'close'
method, but the user wishes to disable handling it entirely, an empty
string may be specified

    @Bean(destroyMethod="")
    public MyBasicDataSource dataSource() { ... }

The special destroy method name "(inferred)" may also be specified in
an XML context, e.g.

    <bean destroy-method="(inferred)">
        or
    <beans default-destroy-method="(inferred)">

Note that "(inferred)" is the default value for @Bean#destroyMethod,
but NOT for the destroy-method and default-destroy-method attributes
in the spring-beans XML schema.

The principal reason for introducing this feature is to avoid forcing
@Configuration class users to type destroyMethod="close" every time a
closeable bean is configured. This kind of boilerplate is easily
forgotten, and this simple convention means the right thing is done
by default, while allowing the user full control over customization or
disablement in special cases.

Issue: SPR-8751
This commit is contained in:
Chris Beams
2011-10-12 02:09:04 +00:00
parent 8cafb7ee13
commit 38e90105a0
6 changed files with 223 additions and 11 deletions

View File

@@ -123,6 +123,15 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
*/
public static final int DEPENDENCY_CHECK_ALL = 3;
/**
* Constant that indicates the container should attempt to infer the {@link
* #setDestroyMethodName destroy method name} for a bean as opposed to explicit
* specification of a method name. The value {@value} is specifically designed to
* include characters otherwise illegal in a method name, ensuring no possibility of
* collisions with a legitimately named methods having the same name.
*/
public static final String INFER_METHOD = "(inferred)";
private volatile Object beanClass;

View File

@@ -19,6 +19,7 @@ package org.springframework.beans.factory.support;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -94,7 +95,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
this.acc = acc;
inferDestroyMethodIfNecessary(beanDefinition);
final String destroyMethodName = beanDefinition.getDestroyMethodName();
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
@@ -121,6 +122,31 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
this.beanPostProcessors = filterPostProcessors(postProcessors);
}
/**
* If the current value of the given beanDefinition's destroyMethodName property is
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
* Candidate methods are currently limited to public, no-arg methods named 'close'
* (whether declared locally or inherited). The given beanDefinition's
* destroyMethodName is updated to be null if no such method is found, otherwise set
* to the name of the inferred method. This constant serves as the default for the
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
* used in XML within the {@code <bean destroy-method="">} or {@code
* <beans default-destroy-method="">} attributes.
*/
private void inferDestroyMethodIfNecessary(RootBeanDefinition beanDefinition) {
if ("(inferred)".equals(beanDefinition.getDestroyMethodName())) {
try {
Method candidate = bean.getClass().getMethod("close");
if (Modifier.isPublic(candidate.getModifiers())) {
beanDefinition.setDestroyMethodName(candidate.getName());
}
} catch (NoSuchMethodException ex) {
// no candidate destroy method found
beanDefinition.setDestroyMethodName(null);
}
}
}
/**
* Create a new DisposableBeanAdapter for the given bean.
*/