From 1a0d40674f6e3e0dfbfa96c6263ca45f6c8b6ab2 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 13 Mar 2025 14:29:41 -0400 Subject: [PATCH] GH-9890: Fix `ControlBusCommandRegistry` for `requiresDestruction()` Fixes: #9890 Issue link: https://github.com/spring-projects/spring-integration/issues/9890 The `DestructionAwareBeanPostProcessor.requiresDestruction()` defaults to `true`. As a result, beans that are not meant to be processed during the destroy lifecycle are being registered for the `AbstractBeanFactory#registerDisposableBeanIfNecessary`. for example, the `SimpleThreadScope` does not support destruction callbacks. In other words, the `DestructionAwareBeanPostProcessor.requiresDestruction()` contract is not merely for `DestructionAwareBeanPostProcessor` logic, but also has an effect on the whole `BeanFactory` * Implement `requiresDestruction()` in the `ControlBusCommandRegistry` for same filter as `registerControlBusCommands()` does right now. * Optimize behaviour using `MergedAnnotations` for the class instead of `AnnotationUtils.findAnnotation()` twice, which creates the mentioned `MergedAnnotations` internally. (cherry picked from commit 74cf59bacb4d99abb91b45c64be01421c040c838) --- .../management/ControlBusCommandRegistry.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/ControlBusCommandRegistry.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/ControlBusCommandRegistry.java index 8a069b65fd..6ace09e3ae 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/ControlBusCommandRegistry.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/ControlBusCommandRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 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. @@ -37,6 +37,8 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.Lifecycle; import org.springframework.core.MethodIntrospector; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.RepeatableContainers; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -115,16 +117,17 @@ public class ControlBusCommandRegistry * @param bean the bean for registration */ public void registerControlBusCommands(String beanName, Object bean) { - Class beanClass = bean.getClass(); - if (bean instanceof Lifecycle || bean instanceof CustomizableThreadCreator - || AnnotationUtils.findAnnotation(beanClass, ManagedResource.class) != null - || AnnotationUtils.findAnnotation(beanClass, IntegrationManagedResource.class) != null) { - - ReflectionUtils.doWithMethods(beanClass, method -> populateExpressionForCommand(beanName, method), + if (isBeanEligible(bean)) { + ReflectionUtils.doWithMethods(bean.getClass(), method -> populateExpressionForCommand(beanName, method), CONTROL_BUS_METHOD_FILTER); } } + @Override + public boolean requiresDestruction(Object bean) { + return isBeanEligible(bean); + } + @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { this.controlBusCommands.remove(beanName); @@ -221,6 +224,17 @@ public class ControlBusCommandRegistry return buildExpressionForMethodToCall(commandMethod, methodForCommand.get()); } + private static boolean isBeanEligible(Object bean) { + MergedAnnotations mergedAnnotations = + MergedAnnotations.from(bean.getClass(), MergedAnnotations.SearchStrategy.TYPE_HIERARCHY, + RepeatableContainers.none()); + + return bean instanceof Lifecycle + || bean instanceof CustomizableThreadCreator + || mergedAnnotations.isPresent(ManagedResource.class) + || mergedAnnotations.isPresent(IntegrationManagedResource.class); + } + private static Expression buildExpressionForMethodToCall(CommandMethod commandMethod, Method methodForCommand) { Assert.isTrue(CONTROL_BUS_METHOD_FILTER.matches(methodForCommand), () -> "The method '%s' is not valid Control Bus command".formatted(methodForCommand));