|
|
|
|
@ -61,6 +61,8 @@ import org.springframework.util.StringUtils;
@@ -61,6 +61,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"; |
|
|
|
|
@ -72,12 +74,11 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -72,12 +74,11 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
|
|
|
|
|
private final String beanName; |
|
|
|
|
|
|
|
|
|
private final boolean invokeDisposableBean; |
|
|
|
|
|
|
|
|
|
private final boolean nonPublicAccessAllowed; |
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
private final AccessControlContext acc; |
|
|
|
|
private final boolean invokeDisposableBean; |
|
|
|
|
|
|
|
|
|
private boolean invokeAutoCloseable; |
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
private String destroyMethodName; |
|
|
|
|
@ -88,6 +89,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -88,6 +89,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
@Nullable |
|
|
|
|
private final List<DestructionAwareBeanPostProcessor> beanPostProcessors; |
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
private final AccessControlContext acc; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Create a new DisposableBeanAdapter for the given bean. |
|
|
|
|
@ -103,38 +107,45 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -103,38 +107,45 @@ 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.acc = acc; |
|
|
|
|
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); |
|
|
|
|
this.acc = acc; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -149,26 +160,27 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -149,26 +160,27 @@ 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.acc = acc; |
|
|
|
|
this.invokeDisposableBean = (this.bean instanceof DisposableBean); |
|
|
|
|
this.beanPostProcessors = filterPostProcessors(postProcessors, bean); |
|
|
|
|
this.acc = acc; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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.acc = null; |
|
|
|
|
this.invokeDisposableBean = invokeDisposableBean; |
|
|
|
|
this.invokeAutoCloseable = invokeAutoCloseable; |
|
|
|
|
this.destroyMethodName = destroyMethodName; |
|
|
|
|
this.beanPostProcessors = postProcessors; |
|
|
|
|
this.acc = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -211,7 +223,32 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -211,7 +223,32 @@ 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 { |
|
|
|
|
if (System.getSecurityManager() != null) { |
|
|
|
|
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { |
|
|
|
|
((AutoCloseable) this.bean).close(); |
|
|
|
|
return null; |
|
|
|
|
}, this.acc); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
((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) { |
|
|
|
|
@ -259,7 +296,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -259,7 +296,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 { |
|
|
|
|
@ -282,7 +319,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -282,7 +319,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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()); |
|
|
|
|
@ -292,7 +329,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -292,7 +329,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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -312,8 +349,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -312,8 +349,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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -323,10 +361,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -323,10 +361,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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -348,21 +383,27 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
@@ -348,21 +383,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
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|