Skip to content

Commit

Permalink
Ensure private init/destroy method is invoked only once
Browse files Browse the repository at this point in the history
Closes gh-28083
  • Loading branch information
sbrannen committed Mar 1, 2022
1 parent a7d5fbf commit f968724
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 5 deletions.
Expand Up @@ -1844,7 +1844,7 @@ protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBea
throws Throwable {

boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
Expand All @@ -1868,7 +1868,7 @@ protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBea
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
Expand Down
Expand Up @@ -52,6 +52,7 @@
* @author Juergen Hoeller
* @author Costin Leau
* @author Stephane Nicoll
* @author Sam Brannen
* @since 2.0
* @see AbstractBeanFactory
* @see org.springframework.beans.factory.DisposableBean
Expand Down Expand Up @@ -109,12 +110,12 @@ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition be
this.beanName = beanName;
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
this.invokeDisposableBean = (bean instanceof DisposableBean &&
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
!beanDefinition.hasAnyExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));

String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
if (destroyMethodName != null &&
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
!beanDefinition.hasAnyExternallyManagedDestroyMethod(destroyMethodName)) {

this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName));
if (!this.invokeAutoCloseable) {
Expand Down
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -49,6 +49,7 @@
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @see GenericBeanDefinition
* @see ChildBeanDefinition
*/
Expand Down Expand Up @@ -479,6 +480,36 @@ public boolean isExternallyManagedInitMethod(String initMethod) {
}
}

/**
* 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(".");

This comment has been minimized.

Copy link
@andrei-ivanov

andrei-ivanov Mar 1, 2022

would lastIndexOf('.') be better?

This comment has been minimized.

Copy link
@sbrannen

sbrannen Mar 15, 2022

Author Member

Good catch. See 9fbf5dc.

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).
* @since 5.3.11
Expand Down Expand Up @@ -513,6 +544,36 @@ public boolean isExternallyManagedDestroyMethod(String destroyMethod) {
}
}

/**
* 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).
* @since 5.3.11
Expand Down

0 comments on commit f968724

Please sign in to comment.