Add reflection hint on Publisher for bean destroy support

Prior to this commit, `DisposableBeanAdapter` supported reactive bean
destroy methods by detected if `Publisher` is available on the
classpath. The AOT engine did not contribute a reflection hint for this
call.

This commit ensures that this reflection hint is registered in all
cases, even if there are no destroy methods detected on beans.

Fixes gh-31278
This commit is contained in:
Brian Clozel 2023-09-20 14:43:12 +02:00
parent d46c26d903
commit a97ff39088
2 changed files with 12 additions and 0 deletions

View File

@ -35,6 +35,7 @@ import java.util.function.Predicate;
import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
@ -128,6 +129,8 @@ class BeanDefinitionPropertiesCodeGenerator {
private void addInitDestroyMethods(Builder code, AbstractBeanDefinition beanDefinition,
@Nullable String[] methodNames, String format) {
// For Publisher-based destroy methods
this.hints.reflection().registerType(TypeReference.of("org.reactivestreams.Publisher"));
if (!ObjectUtils.isEmpty(methodNames)) {
Class<?> beanType = ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
Arrays.stream(methodNames).forEach(methodName -> addInitDestroyHint(beanType, methodName));

View File

@ -31,6 +31,7 @@ import javax.lang.model.element.Modifier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
@ -413,6 +414,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
@Test
void noDestroyMethod() {
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).isNull());
assertReflectionOnPublisher();
}
@Test
@ -420,6 +422,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
beanDefinition.setDestroyMethodName("destroy");
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy"));
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy");
assertReflectionOnPublisher();
}
@Test
@ -427,6 +430,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
beanDefinition.setDestroyMethodName(privateDestroyMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly(privateDestroyMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "privateDestroy");
assertReflectionOnPublisher();
}
@Test
@ -434,6 +438,11 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
beanDefinition.setDestroyMethodNames("destroy", privateDestroyMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy", privateDestroyMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy", "privateDestroy");
assertReflectionOnPublisher();
}
private void assertReflectionOnPublisher() {
assertThat(RuntimeHintsPredicates.reflection().onType(Publisher.class)).accepts(generationContext.getRuntimeHints());
}
}