Skip to content

Commit a17c2cc

Browse files
committed
Preserve resolved destroy method name in RootBeanDefinition
Closes gh-26498 (cherry picked from commit 809813d)
1 parent 64df931 commit a17c2cc

File tree

2 files changed

+83
-78
lines changed

2 files changed

+83
-78
lines changed

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

Lines changed: 77 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -87,7 +87,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
8787
private transient Method destroyMethod;
8888

8989
@Nullable
90-
private List<DestructionAwareBeanPostProcessor> beanPostProcessors;
90+
private final List<DestructionAwareBeanPostProcessor> beanPostProcessors;
9191

9292

9393
/**
@@ -120,14 +120,16 @@ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition be
120120
}
121121
}
122122
else {
123-
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
124-
if (paramTypes.length > 1) {
125-
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
126-
beanName + "' has more than one parameter - not supported as destroy method");
127-
}
128-
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
129-
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
130-
beanName + "' has a non-boolean parameter - not supported as destroy method");
123+
if (destroyMethod.getParameterCount() > 0) {
124+
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
125+
if (paramTypes.length > 1) {
126+
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
127+
beanName + "' has more than one parameter - not supported as destroy method");
128+
}
129+
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
130+
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
131+
beanName + "' has a non-boolean parameter - not supported as destroy method");
132+
}
131133
}
132134
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
133135
}
@@ -169,67 +171,6 @@ private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDispos
169171
}
170172

171173

172-
/**
173-
* If the current value of the given beanDefinition's "destroyMethodName" property is
174-
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
175-
* Candidate methods are currently limited to public, no-arg methods named "close" or
176-
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
177-
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
178-
* to the name of the inferred method. This constant serves as the default for the
179-
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
180-
* used in XML within the {@code <bean destroy-method="">} or {@code
181-
* <beans default-destroy-method="">} attributes.
182-
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
183-
* interfaces, reflectively calling the "close" method on implementing beans as well.
184-
*/
185-
@Nullable
186-
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
187-
String destroyMethodName = beanDefinition.getDestroyMethodName();
188-
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
189-
(destroyMethodName == null && bean instanceof AutoCloseable)) {
190-
// Only perform destroy method inference or Closeable detection
191-
// in case of the bean not explicitly implementing DisposableBean
192-
if (!(bean instanceof DisposableBean)) {
193-
try {
194-
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
195-
}
196-
catch (NoSuchMethodException ex) {
197-
try {
198-
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
199-
}
200-
catch (NoSuchMethodException ex2) {
201-
// no candidate destroy method found
202-
}
203-
}
204-
}
205-
return null;
206-
}
207-
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
208-
}
209-
210-
/**
211-
* Search for all DestructionAwareBeanPostProcessors in the List.
212-
* @param processors the List to search
213-
* @return the filtered List of DestructionAwareBeanPostProcessors
214-
*/
215-
@Nullable
216-
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) {
217-
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
218-
if (!CollectionUtils.isEmpty(processors)) {
219-
filteredPostProcessors = new ArrayList<>(processors.size());
220-
for (BeanPostProcessor processor : processors) {
221-
if (processor instanceof DestructionAwareBeanPostProcessor) {
222-
DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
223-
if (dabpp.requiresDestruction(bean)) {
224-
filteredPostProcessors.add(dabpp);
225-
}
226-
}
227-
}
228-
}
229-
return filteredPostProcessors;
230-
}
231-
232-
233174
@Override
234175
public void run() {
235176
destroy();
@@ -384,12 +325,50 @@ public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefin
384325
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
385326
return true;
386327
}
387-
String destroyMethodName = beanDefinition.getDestroyMethodName();
388-
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
389-
return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
390-
ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
328+
return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;
329+
}
330+
331+
332+
/**
333+
* If the current value of the given beanDefinition's "destroyMethodName" property is
334+
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
335+
* Candidate methods are currently limited to public, no-arg methods named "close" or
336+
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
337+
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
338+
* to the name of the inferred method. This constant serves as the default for the
339+
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
340+
* used in XML within the {@code <bean destroy-method="">} or {@code
341+
* <beans default-destroy-method="">} attributes.
342+
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
343+
* interfaces, reflectively calling the "close" method on implementing beans as well.
344+
*/
345+
@Nullable
346+
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
347+
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
348+
if (destroyMethodName == null) {
349+
destroyMethodName = beanDefinition.getDestroyMethodName();
350+
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
351+
(destroyMethodName == null && bean instanceof AutoCloseable)) {
352+
// Only perform destroy method inference or Closeable detection
353+
// in case of the bean not explicitly implementing DisposableBean
354+
destroyMethodName = null;
355+
if (!(bean instanceof DisposableBean)) {
356+
try {
357+
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
358+
}
359+
catch (NoSuchMethodException ex) {
360+
try {
361+
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
362+
}
363+
catch (NoSuchMethodException ex2) {
364+
// no candidate destroy method found
365+
}
366+
}
367+
}
368+
}
369+
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
391370
}
392-
return StringUtils.hasLength(destroyMethodName);
371+
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
393372
}
394373

395374
/**
@@ -411,4 +390,26 @@ public static boolean hasApplicableProcessors(Object bean, List<BeanPostProcesso
411390
return false;
412391
}
413392

393+
/**
394+
* Search for all DestructionAwareBeanPostProcessors in the List.
395+
* @param processors the List to search
396+
* @return the filtered List of DestructionAwareBeanPostProcessors
397+
*/
398+
@Nullable
399+
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) {
400+
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
401+
if (!CollectionUtils.isEmpty(processors)) {
402+
filteredPostProcessors = new ArrayList<>(processors.size());
403+
for (BeanPostProcessor processor : processors) {
404+
if (processor instanceof DestructionAwareBeanPostProcessor) {
405+
DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
406+
if (dabpp.requiresDestruction(bean)) {
407+
filteredPostProcessors.add(dabpp);
408+
}
409+
}
410+
}
411+
}
412+
return filteredPostProcessors;
413+
}
414+
414415
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -65,7 +65,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
6565

6666
boolean allowCaching = true;
6767

68-
boolean isFactoryMethodUnique = false;
68+
boolean isFactoryMethodUnique;
6969

7070
@Nullable
7171
volatile ResolvableType targetType;
@@ -86,6 +86,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
8686
@Nullable
8787
volatile Method factoryMethodToIntrospect;
8888

89+
/** Package-visible field for caching a resolved destroy method name (also for inferred). */
90+
@Nullable
91+
volatile String resolvedDestroyMethodName;
92+
8993
/** Common lock for the four constructor fields below. */
9094
final Object constructorArgumentLock = new Object();
9195

0 commit comments

Comments
 (0)