Resolve infer destroy method at build-time
This commit allows a RootBeanDefinition to resolve its infer destroy method if necessary. Contrary to BeanInstanceAdapter that uses the actual bean instance, the new method works against the type exposed in the bean definition. The AOT contribution of InitDestroyAnnotationBeanPostProcessor uses the new method to make sure the special '(inferred)' placeholder is handled prior to code generation. Closes gh-28215
This commit is contained in:
@@ -159,6 +159,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, registeredBean.getBeanClass());
|
||||
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
|
||||
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods);
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -140,23 +139,16 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
|
||||
private void addInitDestroyMethods(Builder builder,
|
||||
AbstractBeanDefinition beanDefinition, @Nullable String[] methodNames, String format) {
|
||||
List<String> filteredMethodNames = (!ObjectUtils.isEmpty(methodNames))
|
||||
? Arrays.stream(methodNames).filter(this::isNotInferredMethod).toList()
|
||||
: Collections.emptyList();
|
||||
if (!filteredMethodNames.isEmpty()) {
|
||||
if (!ObjectUtils.isEmpty(methodNames)) {
|
||||
Class<?> beanType = ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
|
||||
filteredMethodNames.forEach(methodName -> addInitDestroyHint(beanType, methodName));
|
||||
CodeBlock arguments = filteredMethodNames.stream()
|
||||
Arrays.stream(methodNames).forEach(methodName -> addInitDestroyHint(beanType, methodName));
|
||||
CodeBlock arguments = Arrays.stream(methodNames)
|
||||
.map(name -> CodeBlock.of("$S", name))
|
||||
.collect(CodeBlock.joining(", "));
|
||||
builder.addStatement(format, BEAN_DEFINITION_VARIABLE, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotInferredMethod(String candidate) {
|
||||
return !AbstractBeanDefinition.INFER_METHOD.equals(candidate);
|
||||
}
|
||||
|
||||
private void addInitDestroyHint(Class<?> beanUserClass, String methodName) {
|
||||
Method method = ReflectionUtils.findMethod(beanUserClass, methodName);
|
||||
if (method != null) {
|
||||
|
||||
@@ -105,7 +105,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
this.invokeDisposableBean = (bean instanceof DisposableBean &&
|
||||
!beanDefinition.hasAnyExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
|
||||
|
||||
String[] destroyMethodNames = inferDestroyMethodsIfNecessary(bean, beanDefinition);
|
||||
String[] destroyMethodNames = inferDestroyMethodsIfNecessary(bean.getClass(), beanDefinition);
|
||||
if (!ObjectUtils.isEmpty(destroyMethodNames) &&
|
||||
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodNames[0])) &&
|
||||
!beanDefinition.hasAnyExternallyManagedDestroyMethod(destroyMethodNames[0])) {
|
||||
@@ -325,7 +325,8 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
* @param beanDefinition the corresponding bean definition
|
||||
*/
|
||||
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
|
||||
return (bean instanceof DisposableBean || inferDestroyMethodsIfNecessary(bean, beanDefinition) != null);
|
||||
return (bean instanceof DisposableBean
|
||||
|| inferDestroyMethodsIfNecessary(bean.getClass(), beanDefinition) != null);
|
||||
}
|
||||
|
||||
|
||||
@@ -343,7 +344,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
* interfaces, reflectively calling the "close" method on implementing beans as well.
|
||||
*/
|
||||
@Nullable
|
||||
private static String[] inferDestroyMethodsIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
|
||||
static String[] inferDestroyMethodsIfNecessary(Class<?> target, RootBeanDefinition beanDefinition) {
|
||||
String[] destroyMethodNames = beanDefinition.getDestroyMethodNames();
|
||||
if (destroyMethodNames != null && destroyMethodNames.length > 1) {
|
||||
return destroyMethodNames;
|
||||
@@ -352,23 +353,23 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
|
||||
if (destroyMethodName == null) {
|
||||
destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
boolean autoCloseable = (bean instanceof AutoCloseable);
|
||||
boolean autoCloseable = (AutoCloseable.class.isAssignableFrom(target));
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
|
||||
(destroyMethodName == null && autoCloseable)) {
|
||||
// Only perform destroy method inference in case of the bean
|
||||
// not explicitly implementing the DisposableBean interface
|
||||
destroyMethodName = null;
|
||||
if (!(bean instanceof DisposableBean)) {
|
||||
if (!(DisposableBean.class.isAssignableFrom(target))) {
|
||||
if (autoCloseable) {
|
||||
destroyMethodName = CLOSE_METHOD_NAME;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
|
||||
destroyMethodName = target.getMethod(CLOSE_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
try {
|
||||
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
|
||||
destroyMethodName = target.getMethod(SHUTDOWN_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex2) {
|
||||
// no candidate destroy method found
|
||||
|
||||
@@ -550,6 +550,15 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the inferred destroy method if necessary.
|
||||
* @since 6.0
|
||||
*/
|
||||
public void resolveDestroyMethodIfNecessary() {
|
||||
setDestroyMethodNames(DisposableBeanAdapter
|
||||
.inferDestroyMethodsIfNecessary(getResolvableType().toClass(), this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an externally managed configuration destruction method —
|
||||
* for example, a method annotated with JSR-250's
|
||||
|
||||
Reference in New Issue
Block a user