Skip to content

Commit 809813d

Browse files
committed
Preserve resolved destroy method name in RootBeanDefinition
Closes gh-26498
1 parent d5e5dcb commit 809813d

File tree

2 files changed

+80
-75
lines changed

2 files changed

+80
-75
lines changed

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

+75-74
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 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.
@@ -119,14 +119,16 @@ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition be
119119
}
120120
}
121121
else {
122-
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
123-
if (paramTypes.length > 1) {
124-
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
125-
beanName + "' has more than one parameter - not supported as destroy method");
126-
}
127-
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
128-
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
129-
beanName + "' has a non-boolean parameter - not supported as destroy method");
122+
if (destroyMethod.getParameterCount() > 0) {
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");
131+
}
130132
}
131133
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
132134
}
@@ -170,66 +172,6 @@ private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDispos
170172
}
171173

172174

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

395375
/**
@@ -408,4 +388,25 @@ public static boolean hasApplicableProcessors(Object bean, List<DestructionAware
408388
return false;
409389
}
410390

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

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 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.
@@ -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)