Skip to content

Commit 2e1538a

Browse files
committed
Make sure inferred destroy method is set on the original bean definition
This commit updates InitDestroyAnnotationBeanPostProcessor to mutate the original bean definition rather than the merged one that can be recreated without it if the cache gets stale. See gh-28215
1 parent 3c76437 commit 2e1538a

File tree

5 files changed

+49
-22
lines changed

5 files changed

+49
-22
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
import org.springframework.beans.factory.BeanCreationException;
4242
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
4343
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
44+
import org.springframework.beans.factory.config.BeanDefinition;
4445
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
46+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
4547
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
4648
import org.springframework.beans.factory.support.RegisteredBean;
4749
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -158,20 +160,30 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C
158160

159161
@Override
160162
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
161-
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
162-
beanDefinition.resolveDestroyMethodIfNecessary();
163-
LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, registeredBean.getBeanClass());
164-
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
165-
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods);
166-
beanDefinition.setInitMethodNames(initMethodNames);
167-
}
168-
if (!CollectionUtils.isEmpty(metadata.destroyMethods)) {
169-
String[] destroyMethodNames = safeMerge(beanDefinition.getDestroyMethodNames(), metadata.destroyMethods);
170-
beanDefinition.setDestroyMethodNames(destroyMethodNames);
163+
AbstractBeanDefinition beanDefinition = getOriginalBeanDefinition(registeredBean);
164+
if (beanDefinition != null) {
165+
RootBeanDefinition mergedBeanDefinition = registeredBean.getMergedBeanDefinition();
166+
beanDefinition.resolveDestroyMethodIfNecessary();
167+
LifecycleMetadata metadata = findInjectionMetadata(mergedBeanDefinition, registeredBean.getBeanClass());
168+
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
169+
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods);
170+
beanDefinition.setInitMethodNames(initMethodNames);
171+
}
172+
if (!CollectionUtils.isEmpty(metadata.destroyMethods)) {
173+
String[] destroyMethodNames = safeMerge(beanDefinition.getDestroyMethodNames(), metadata.destroyMethods);
174+
beanDefinition.setDestroyMethodNames(destroyMethodNames);
175+
}
176+
registeredBean.getBeanFactory().clearMetadataCache();
171177
}
172178
return null;
173179
}
174180

181+
@Nullable
182+
private AbstractBeanDefinition getOriginalBeanDefinition(RegisteredBean registeredBean) {
183+
BeanDefinition beanDefinition = registeredBean.getBeanFactory().getBeanDefinition(registeredBean.getBeanName());
184+
return (beanDefinition instanceof AbstractBeanDefinition abd ? abd : null);
185+
}
186+
175187
private LifecycleMetadata findInjectionMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) {
176188
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
177189
metadata.checkConfigMembers(beanDefinition);

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,14 @@ else if (count == 1) {
11861186
}
11871187
}
11881188

1189+
/**
1190+
* Resolve the inferred destroy method if necessary.
1191+
* @since 6.0
1192+
*/
1193+
public void resolveDestroyMethodIfNecessary() {
1194+
setDestroyMethodNames(DisposableBeanAdapter
1195+
.inferDestroyMethodsIfNecessary(getResolvableType().toClass(), this));
1196+
}
11891197

11901198
/**
11911199
* Public declaration of Object's {@code clone()} method.

spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,14 @@ public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefin
344344
* interfaces, reflectively calling the "close" method on implementing beans as well.
345345
*/
346346
@Nullable
347-
static String[] inferDestroyMethodsIfNecessary(Class<?> target, RootBeanDefinition beanDefinition) {
347+
static String[] inferDestroyMethodsIfNecessary(Class<?> target, AbstractBeanDefinition beanDefinition) {
348348
String[] destroyMethodNames = beanDefinition.getDestroyMethodNames();
349349
if (destroyMethodNames != null && destroyMethodNames.length > 1) {
350350
return destroyMethodNames;
351351
}
352352

353-
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
353+
String destroyMethodName = (beanDefinition instanceof RootBeanDefinition rbd
354+
? rbd.resolvedDestroyMethodName : null);
354355
if (destroyMethodName == null) {
355356
destroyMethodName = beanDefinition.getDestroyMethodName();
356357
boolean autoCloseable = (AutoCloseable.class.isAssignableFrom(target));
@@ -378,7 +379,9 @@ static String[] inferDestroyMethodsIfNecessary(Class<?> target, RootBeanDefiniti
378379
}
379380
}
380381
}
381-
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
382+
if (beanDefinition instanceof RootBeanDefinition rbd) {
383+
rbd.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
384+
}
382385
}
383386
return (StringUtils.hasLength(destroyMethodName) ? new String[] {destroyMethodName} : null);
384387
}

spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -550,15 +550,6 @@ public Set<String> getExternallyManagedInitMethods() {
550550
}
551551
}
552552

553-
/**
554-
* Resolve the inferred destroy method if necessary.
555-
* @since 6.0
556-
*/
557-
public void resolveDestroyMethodIfNecessary() {
558-
setDestroyMethodNames(DisposableBeanAdapter
559-
.inferDestroyMethodsIfNecessary(getResolvableType().toClass(), this));
560-
}
561-
562553
/**
563554
* Register an externally managed configuration destruction method &mdash;
564555
* for example, a method annotated with JSR-250's

spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ void processAheadOfTimeWhenHasInferredDestroyMethodAddsDestroyMethodName() {
9090
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("close");
9191
}
9292

93+
@Test
94+
void processAheadOfTimeWhenHasInferredDestroyMethodIsRetainedIfMergedBeanDefinitionIsStale() {
95+
RootBeanDefinition beanDefinition = new RootBeanDefinition(InferredDestroyBean.class);
96+
beanDefinition.setDestroyMethodNames(AbstractBeanDefinition.INFER_METHOD);
97+
processAheadOfTime(beanDefinition);
98+
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
99+
assertThat(mergedBeanDefinition.getInitMethodNames()).isNull();
100+
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("close");
101+
RootBeanDefinition originalBeanDefinition = (RootBeanDefinition) this.beanFactory.getBeanDefinition("test");
102+
assertThat(originalBeanDefinition.getInitMethodNames()).isNull();
103+
assertThat(originalBeanDefinition.getDestroyMethodNames()).containsExactly("close");
104+
}
105+
93106
@Test
94107
void processAheadOfTimeWhenHasInferredDestroyMethodAndNoCandidateDoesNotMutateRootBeanDefinition() {
95108
RootBeanDefinition beanDefinition = new RootBeanDefinition(NoInitDestroyBean.class);

0 commit comments

Comments
 (0)