Ensure private init/destroy method is invoked only once

Closes gh-28083
This commit is contained in:
Sam Brannen 2022-03-01 15:02:57 +01:00
parent a7d5fbfbea
commit f96872404d
3 changed files with 67 additions and 5 deletions

View File

@ -1844,7 +1844,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
throws Throwable { throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean); boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
} }
@ -1868,7 +1868,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
String initMethodName = mbd.getInitMethodName(); String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) && if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) { !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd); invokeCustomInitMethod(beanName, bean, mbd);
} }
} }

View File

@ -52,6 +52,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Costin Leau * @author Costin Leau
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Sam Brannen
* @since 2.0 * @since 2.0
* @see AbstractBeanFactory * @see AbstractBeanFactory
* @see org.springframework.beans.factory.DisposableBean * @see org.springframework.beans.factory.DisposableBean
@ -109,12 +110,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
this.beanName = beanName; this.beanName = beanName;
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed(); this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
this.invokeDisposableBean = (bean instanceof DisposableBean && this.invokeDisposableBean = (bean instanceof DisposableBean &&
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME)); !beanDefinition.hasAnyExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition); String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
if (destroyMethodName != null && if (destroyMethodName != null &&
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) && !(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) { !beanDefinition.hasAnyExternallyManagedDestroyMethod(destroyMethodName)) {
this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName)); this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName));
if (!this.invokeAutoCloseable) { if (!this.invokeAutoCloseable) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -49,6 +49,7 @@ import org.springframework.util.Assert;
* *
* @author Rod Johnson * @author Rod Johnson
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @see GenericBeanDefinition * @see GenericBeanDefinition
* @see ChildBeanDefinition * @see ChildBeanDefinition
*/ */
@ -479,6 +480,36 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
} }
} }
/**
* Determine if the given method name indicates an externally managed
* initialization method, regardless of method visibility.
* <p>In contrast to {@link #isExternallyManagedInitMethod(String)}, this
* method also returns {@code true} if there is a {@code private} external
* init method that has been
* {@linkplain #registerExternallyManagedInitMethod(String) registered}
* using a fully qualified method name instead of a simple method name.
* @since 5.3.17
*/
boolean hasAnyExternallyManagedInitMethod(String initMethod) {
synchronized (this.postProcessingLock) {
if (isExternallyManagedInitMethod(initMethod)) {
return true;
}
if (this.externallyManagedInitMethods != null) {
for (String candidate : this.externallyManagedInitMethods) {
int indexOfDot = candidate.lastIndexOf(".");
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(initMethod)) {
return true;
}
}
}
}
return false;
}
}
/** /**
* Return all externally managed initialization methods (as an immutable Set). * Return all externally managed initialization methods (as an immutable Set).
* @since 5.3.11 * @since 5.3.11
@ -513,6 +544,36 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
} }
} }
/**
* Determine if the given method name indicates an externally managed
* destruction method, regardless of method visibility.
* <p>In contrast to {@link #isExternallyManagedDestroyMethod(String)}, this
* method also returns {@code true} if there is a {@code private} external
* destroy method that has been
* {@linkplain #registerExternallyManagedDestroyMethod(String) registered}
* using a fully qualified method name instead of a simple method name.
* @since 5.3.17
*/
boolean hasAnyExternallyManagedDestroyMethod(String destroyMethod) {
synchronized (this.postProcessingLock) {
if (isExternallyManagedDestroyMethod(destroyMethod)) {
return true;
}
if (this.externallyManagedDestroyMethods != null) {
for (String candidate : this.externallyManagedDestroyMethods) {
int indexOfDot = candidate.lastIndexOf(".");
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(destroyMethod)) {
return true;
}
}
}
}
return false;
}
}
/** /**
* Return all externally managed destruction methods (as an immutable Set). * Return all externally managed destruction methods (as an immutable Set).
* @since 5.3.11 * @since 5.3.11