Java 5 Closeable and Java 7 AutoCloseable automatically detected as destroy methods
Also, @Bean destroy method inference not applying for DisposableBean implementers anymore (avoiding double destruction). Issue: SPR-10034
This commit is contained in:
parent
5cb1930550
commit
9c6aa3e43b
|
|
@ -52,7 +52,6 @@ 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;
|
||||
|
|
@ -1466,8 +1465,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
*/
|
||||
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
|
||||
return (bean != null &&
|
||||
(bean instanceof DisposableBean || mbd.getDestroyMethodName() != null ||
|
||||
hasDestructionAwareBeanPostProcessors()));
|
||||
(DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || hasDestructionAwareBeanPostProcessors()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.io.Closeable;
|
||||
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;
|
||||
|
|
@ -36,6 +36,7 @@ import org.springframework.beans.factory.DisposableBean;
|
|||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -58,8 +59,22 @@ import org.springframework.util.ReflectionUtils;
|
|||
@SuppressWarnings("serial")
|
||||
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
||||
private static final String CLOSE_METHOD_NAME = "close";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
|
||||
|
||||
private static Class closeableInterface;
|
||||
|
||||
static {
|
||||
try {
|
||||
closeableInterface = DisposableBeanAdapter.class.getClassLoader().loadClass("java.lang.AutoCloseable");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
closeableInterface = Closeable.class;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Object bean;
|
||||
|
||||
private final String beanName;
|
||||
|
|
@ -95,8 +110,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();
|
||||
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
|
||||
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
|
||||
this.destroyMethodName = destroyMethodName;
|
||||
|
|
@ -122,31 +136,6 @@ 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.
|
||||
*/
|
||||
|
|
@ -164,6 +153,37 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
|
||||
* interfaces, reflectively calling the "close" method on implementing beans as well.
|
||||
*/
|
||||
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(beanDefinition.getDestroyMethodName()) ||
|
||||
(beanDefinition.getDestroyMethodName() == null && closeableInterface.isInstance(bean))) {
|
||||
// Only perform destroy method inference or Closeable detection
|
||||
// in case of the bean not explicitly implementing DisposableBean
|
||||
if (!(bean instanceof DisposableBean)) {
|
||||
try {
|
||||
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// no candidate destroy method found
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return beanDefinition.getDestroyMethodName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all DestructionAwareBeanPostProcessors in the List.
|
||||
* @param postProcessors the List to search
|
||||
|
|
@ -335,4 +355,21 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
this.nonPublicAccessAllowed, this.destroyMethodName, serializablePostProcessors);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given bean has any kind of destroy method to call.
|
||||
* @param bean the bean instance
|
||||
* @param beanDefinition the corresponding bean definition
|
||||
*/
|
||||
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
|
||||
if (bean instanceof DisposableBean || closeableInterface.isInstance(bean)) {
|
||||
return true;
|
||||
}
|
||||
String destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
|
||||
return ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME);
|
||||
}
|
||||
return (destroyMethodName != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -435,15 +435,21 @@
|
|||
The name of the custom initialization method to invoke after setting
|
||||
bean properties. The method must have no arguments, but may throw any
|
||||
exception.
|
||||
|
||||
This is an alternative to implementing Spring's InitializingBean
|
||||
interface or marking a method with the PostConstruct annotation.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="destroy-method" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the custom destroy method to invoke on bean factory
|
||||
shutdown. The method must have no arguments, but may throw any
|
||||
exception.
|
||||
The name of the custom destroy method to invoke on bean factory shutdown.
|
||||
The method must have no arguments, but may throw any exception.
|
||||
|
||||
This is an alternative to implementing Spring's DisposableBean
|
||||
interface or the standard Java Closeable/AutoCloseable interface,
|
||||
or marking a method with the PreDestroy annotation.
|
||||
|
||||
Note: Only invoked on beans whose lifecycle is under the full
|
||||
control of the factory - which is always the case for singletons,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.security.AccessControlContext;
|
||||
|
|
@ -1909,7 +1910,6 @@ public class DefaultListableBeanFactoryTests {
|
|||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
|
|
@ -1920,6 +1920,25 @@ public class DefaultListableBeanFactoryTests {
|
|||
assertTrue("Destroy method invoked", BeanWithDisposableBean.closed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanPostProcessorWithWrappedObjectAndCloseable() {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition bd = new RootBeanDefinition(BeanWithCloseable.class);
|
||||
lbf.registerBeanDefinition("test", bd);
|
||||
lbf.addBeanPostProcessor(new BeanPostProcessor() {
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return new TestBean();
|
||||
}
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
});
|
||||
BeanWithDisposableBean.closed = false;
|
||||
lbf.preInstantiateSingletons();
|
||||
lbf.destroySingletons();
|
||||
assertTrue("Destroy method invoked", BeanWithCloseable.closed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanPostProcessorWithWrappedObjectAndDestroyMethod() {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
|
|
@ -1930,7 +1949,6 @@ public class DefaultListableBeanFactoryTests {
|
|||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
|
|
@ -2250,6 +2268,16 @@ public class DefaultListableBeanFactoryTests {
|
|||
}
|
||||
|
||||
|
||||
public static class BeanWithCloseable implements Closeable {
|
||||
|
||||
private static boolean closed;
|
||||
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class BeanWithDestroyMethod {
|
||||
|
||||
private static boolean closed;
|
||||
|
|
|
|||
|
|
@ -207,7 +207,6 @@ public @interface Bean {
|
|||
* application context, for example a {@code close()} method on a JDBC {@code
|
||||
* DataSource} implementation, or a Hibernate {@code SessionFactory} object.
|
||||
* The method must have no arguments but may throw any exception.
|
||||
*
|
||||
* <p>As a convenience to the user, the container will attempt to infer a destroy
|
||||
* method against an object returned from the {@code @Bean} method. For example, given a
|
||||
* {@code @Bean} method returning an Apache Commons DBCP {@code BasicDataSource}, the
|
||||
|
|
@ -217,16 +216,16 @@ public @interface Bean {
|
|||
* 'close'. The method may be declared at any level of the inheritance hierarchy and
|
||||
* will be detected regardless of the return type of the {@code @Bean} method (i.e.,
|
||||
* detection occurs reflectively against the bean instance itself at creation time).
|
||||
*
|
||||
* <p>To disable destroy method inference for a particular {@code @Bean}, specify an
|
||||
* empty string as the value, e.g. {@code @Bean(destroyMethod="")}.
|
||||
*
|
||||
* empty string as the value, e.g. {@code @Bean(destroyMethod="")}. Note that the
|
||||
* {@link org.springframework.beans.factory.DisposableBean} and the
|
||||
* {@link java.io.Closeable}/{@link java.lang.AutoCloseable} interfaces will
|
||||
* nevertheless get detected and the corresponding destroy/close method invoked.
|
||||
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
|
||||
* factory, which is always the case for singletons but not guaranteed for any
|
||||
* other scope.
|
||||
*
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#close()
|
||||
*/
|
||||
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue