|
|
|
|
@ -56,6 +56,8 @@ import org.springframework.util.StringUtils;
@@ -56,6 +56,8 @@ import org.springframework.util.StringUtils;
|
|
|
|
|
@SuppressWarnings("serial") |
|
|
|
|
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { |
|
|
|
|
|
|
|
|
|
private static final String DESTROY_METHOD_NAME = "destroy"; |
|
|
|
|
|
|
|
|
|
private static final String CLOSE_METHOD_NAME = "close"; |
|
|
|
|
|
|
|
|
|
private static final String SHUTDOWN_METHOD_NAME = "shutdown"; |
|
|
|
|
@ -67,9 +69,11 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -67,9 +69,11 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
|
|
|
|
|
private final String beanName; |
|
|
|
|
|
|
|
|
|
private final boolean nonPublicAccessAllowed; |
|
|
|
|
|
|
|
|
|
private final boolean invokeDisposableBean; |
|
|
|
|
|
|
|
|
|
private final boolean nonPublicAccessAllowed; |
|
|
|
|
private boolean invokeAutoCloseable; |
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
private String destroyMethodName; |
|
|
|
|
@ -95,36 +99,43 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -95,36 +99,43 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
Assert.notNull(bean, "Disposable bean must not be null"); |
|
|
|
|
this.bean = bean; |
|
|
|
|
this.beanName = beanName; |
|
|
|
|
this.invokeDisposableBean = |
|
|
|
|
(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy")); |
|
|
|
|
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed(); |
|
|
|
|
this.invokeDisposableBean = (bean instanceof DisposableBean && |
|
|
|
|
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME)); |
|
|
|
|
|
|
|
|
|
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition); |
|
|
|
|
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) && |
|
|
|
|
if (destroyMethodName != null && |
|
|
|
|
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) && |
|
|
|
|
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) { |
|
|
|
|
this.destroyMethodName = destroyMethodName; |
|
|
|
|
Method destroyMethod = determineDestroyMethod(destroyMethodName); |
|
|
|
|
if (destroyMethod == null) { |
|
|
|
|
if (beanDefinition.isEnforceDestroyMethod()) { |
|
|
|
|
throw new BeanDefinitionValidationException("Could not find a destroy method named '" + |
|
|
|
|
destroyMethodName + "' on bean with name '" + beanName + "'"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
if (destroyMethod.getParameterCount() > 0) { |
|
|
|
|
Class<?>[] paramTypes = destroyMethod.getParameterTypes(); |
|
|
|
|
if (paramTypes.length > 1) { |
|
|
|
|
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + |
|
|
|
|
beanName + "' has more than one parameter - not supported as destroy method"); |
|
|
|
|
|
|
|
|
|
this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName)); |
|
|
|
|
if (!this.invokeAutoCloseable) { |
|
|
|
|
this.destroyMethodName = destroyMethodName; |
|
|
|
|
Method destroyMethod = determineDestroyMethod(destroyMethodName); |
|
|
|
|
if (destroyMethod == null) { |
|
|
|
|
if (beanDefinition.isEnforceDestroyMethod()) { |
|
|
|
|
throw new BeanDefinitionValidationException("Could not find a destroy method named '" + |
|
|
|
|
destroyMethodName + "' on bean with name '" + beanName + "'"); |
|
|
|
|
} |
|
|
|
|
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { |
|
|
|
|
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + |
|
|
|
|
beanName + "' has a non-boolean parameter - not supported as destroy method"); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
if (destroyMethod.getParameterCount() > 0) { |
|
|
|
|
Class<?>[] paramTypes = destroyMethod.getParameterTypes(); |
|
|
|
|
if (paramTypes.length > 1) { |
|
|
|
|
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + |
|
|
|
|
beanName + "' has more than one parameter - not supported as destroy method"); |
|
|
|
|
} |
|
|
|
|
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { |
|
|
|
|
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + |
|
|
|
|
beanName + "' has a non-boolean parameter - not supported as destroy method"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod); |
|
|
|
|
} |
|
|
|
|
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod); |
|
|
|
|
this.destroyMethod = destroyMethod; |
|
|
|
|
} |
|
|
|
|
this.destroyMethod = destroyMethod; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.beanPostProcessors = filterPostProcessors(postProcessors, bean); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -138,22 +149,23 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -138,22 +149,23 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
Assert.notNull(bean, "Disposable bean must not be null"); |
|
|
|
|
this.bean = bean; |
|
|
|
|
this.beanName = bean.getClass().getName(); |
|
|
|
|
this.invokeDisposableBean = (this.bean instanceof DisposableBean); |
|
|
|
|
this.nonPublicAccessAllowed = true; |
|
|
|
|
this.invokeDisposableBean = (this.bean instanceof DisposableBean); |
|
|
|
|
this.beanPostProcessors = filterPostProcessors(postProcessors, bean); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Create a new DisposableBeanAdapter for the given bean. |
|
|
|
|
*/ |
|
|
|
|
private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDisposableBean, |
|
|
|
|
boolean nonPublicAccessAllowed, @Nullable String destroyMethodName, |
|
|
|
|
private DisposableBeanAdapter(Object bean, String beanName, boolean nonPublicAccessAllowed, |
|
|
|
|
boolean invokeDisposableBean, boolean invokeAutoCloseable, @Nullable String destroyMethodName, |
|
|
|
|
@Nullable List<DestructionAwareBeanPostProcessor> postProcessors) { |
|
|
|
|
|
|
|
|
|
this.bean = bean; |
|
|
|
|
this.beanName = beanName; |
|
|
|
|
this.invokeDisposableBean = invokeDisposableBean; |
|
|
|
|
this.nonPublicAccessAllowed = nonPublicAccessAllowed; |
|
|
|
|
this.invokeDisposableBean = invokeDisposableBean; |
|
|
|
|
this.invokeAutoCloseable = invokeAutoCloseable; |
|
|
|
|
this.destroyMethodName = destroyMethodName; |
|
|
|
|
this.beanPostProcessors = postProcessors; |
|
|
|
|
} |
|
|
|
|
@ -190,7 +202,24 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -190,7 +202,24 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (this.destroyMethod != null) { |
|
|
|
|
if (this.invokeAutoCloseable) { |
|
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
|
logger.trace("Invoking close() on bean with name '" + this.beanName + "'"); |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
((AutoCloseable) this.bean).close(); |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'"; |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.warn(msg, ex); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
logger.warn(msg + ": " + ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (this.destroyMethod != null) { |
|
|
|
|
invokeCustomDestroyMethod(this.destroyMethod); |
|
|
|
|
} |
|
|
|
|
else if (this.destroyMethodName != null) { |
|
|
|
|
@ -233,7 +262,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -233,7 +262,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
args[0] = Boolean.TRUE; |
|
|
|
|
} |
|
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
|
logger.trace("Invoking destroy method '" + this.destroyMethodName + |
|
|
|
|
logger.trace("Invoking custom destroy method '" + this.destroyMethodName + |
|
|
|
|
"' on bean with name '" + this.beanName + "'"); |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
@ -241,7 +270,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -241,7 +270,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
destroyMethod.invoke(this.bean, args); |
|
|
|
|
} |
|
|
|
|
catch (InvocationTargetException ex) { |
|
|
|
|
String msg = "Destroy method '" + this.destroyMethodName + "' on bean with name '" + |
|
|
|
|
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" + |
|
|
|
|
this.beanName + "' threw an exception"; |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.warn(msg, ex.getTargetException()); |
|
|
|
|
@ -251,7 +280,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -251,7 +280,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
logger.warn("Failed to invoke destroy method '" + this.destroyMethodName + |
|
|
|
|
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName + |
|
|
|
|
"' on bean with name '" + this.beanName + "'", ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -271,8 +300,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -271,8 +300,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return new DisposableBeanAdapter(this.bean, this.beanName, this.invokeDisposableBean, |
|
|
|
|
this.nonPublicAccessAllowed, this.destroyMethodName, serializablePostProcessors); |
|
|
|
|
return new DisposableBeanAdapter( |
|
|
|
|
this.bean, this.beanName, this.nonPublicAccessAllowed, this.invokeDisposableBean, |
|
|
|
|
this.invokeAutoCloseable, this.destroyMethodName, serializablePostProcessors); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -282,10 +312,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -282,10 +312,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
* @param beanDefinition the corresponding bean definition |
|
|
|
|
*/ |
|
|
|
|
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) { |
|
|
|
|
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return inferDestroyMethodIfNecessary(bean, beanDefinition) != null; |
|
|
|
|
return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -307,21 +334,27 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -307,21 +334,27 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
String destroyMethodName = beanDefinition.resolvedDestroyMethodName; |
|
|
|
|
if (destroyMethodName == null) { |
|
|
|
|
destroyMethodName = beanDefinition.getDestroyMethodName(); |
|
|
|
|
boolean autoCloseable = (bean instanceof AutoCloseable); |
|
|
|
|
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) || |
|
|
|
|
(destroyMethodName == null && bean instanceof AutoCloseable)) { |
|
|
|
|
// Only perform destroy method inference or Closeable detection
|
|
|
|
|
// in case of the bean not explicitly implementing DisposableBean
|
|
|
|
|
(destroyMethodName == null && autoCloseable)) { |
|
|
|
|
// Only perform destroy method inference in case of the bean
|
|
|
|
|
// not explicitly implementing the DisposableBean interface
|
|
|
|
|
destroyMethodName = null; |
|
|
|
|
if (!(bean instanceof DisposableBean)) { |
|
|
|
|
try { |
|
|
|
|
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName(); |
|
|
|
|
if (autoCloseable) { |
|
|
|
|
destroyMethodName = CLOSE_METHOD_NAME; |
|
|
|
|
} |
|
|
|
|
catch (NoSuchMethodException ex) { |
|
|
|
|
else { |
|
|
|
|
try { |
|
|
|
|
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName(); |
|
|
|
|
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName(); |
|
|
|
|
} |
|
|
|
|
catch (NoSuchMethodException ex2) { |
|
|
|
|
// no candidate destroy method found
|
|
|
|
|
catch (NoSuchMethodException ex) { |
|
|
|
|
try { |
|
|
|
|
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName(); |
|
|
|
|
} |
|
|
|
|
catch (NoSuchMethodException ex2) { |
|
|
|
|
// no candidate destroy method found
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|