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:
@@ -18,10 +18,12 @@ package org.springframework.beans.factory.annotation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.Destroy;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.InferredDestroyBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.Init;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.InitDestroyBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.MultiInitDestroyBean;
|
||||
@@ -40,7 +42,7 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenNoCallbackDoesNotMutateRootBeanDefinition() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(NoInitDestroyBean.class);
|
||||
processAheadOfTime(beanDefinition);
|
||||
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
|
||||
assertThat(mergedBeanDefinition.getInitMethodNames()).isNull();
|
||||
@@ -78,6 +80,26 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasInferredDestroyMethodAddsDestroyMethodName() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(InferredDestroyBean.class);
|
||||
beanDefinition.setDestroyMethodNames(AbstractBeanDefinition.INFER_METHOD);
|
||||
processAheadOfTime(beanDefinition);
|
||||
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
|
||||
assertThat(mergedBeanDefinition.getInitMethodNames()).isNull();
|
||||
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("close");
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasInferredDestroyMethodAndNoCandidateDoesNotMutateRootBeanDefinition() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(NoInitDestroyBean.class);
|
||||
beanDefinition.setDestroyMethodNames(AbstractBeanDefinition.INFER_METHOD);
|
||||
processAheadOfTime(beanDefinition);
|
||||
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
|
||||
assertThat(mergedBeanDefinition.getInitMethodNames()).isNull();
|
||||
assertThat(mergedBeanDefinition.getDestroyMethodNames()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasMultipleInitDestroyAnnotationsAddsAllMethodNames() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(MultiInitDestroyBean.class);
|
||||
@@ -110,4 +132,6 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
return beanPostProcessor;
|
||||
}
|
||||
|
||||
static class NoInitDestroyBean {}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
@@ -225,9 +224,8 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWhenSingleInferredInitMethod() {
|
||||
void setInitMethodWhenNoInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setInitMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
compile((actual, compiled) -> assertThat(actual.getInitMethodNames()).isNull());
|
||||
}
|
||||
|
||||
@@ -241,13 +239,6 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, methodNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWithInferredMethodFirst() {
|
||||
this.beanDefinition.setInitMethodNames(AbstractBeanDefinition.INFER_METHOD, "init");
|
||||
compile((actual, compiled) -> assertThat(compiled.getSourceFile().getContent())
|
||||
.contains("beanDefinition.setInitMethodNames(\"init\");"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenDestroyInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
@@ -260,9 +251,8 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenSingleInferredInitMethod() {
|
||||
void setDestroyMethodWhenNoDestroyMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
compile((actual, compiled) -> assertThat(actual.getDestroyMethodNames()).isNull());
|
||||
}
|
||||
|
||||
@@ -277,13 +267,6 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, methodNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWithInferredMethodFirst() {
|
||||
this.beanDefinition.setDestroyMethodNames(AbstractBeanDefinition.INFER_METHOD, "destroy");
|
||||
compile((actual, compiled) -> assertThat(compiled.getSourceFile().getContent())
|
||||
.contains("beanDefinition.setDestroyMethodNames(\"destroy\");"));
|
||||
}
|
||||
|
||||
private void assertHasMethodInvokeHints(Class<?> beanType, String... methodNames) {
|
||||
assertThat(methodNames).allMatch(methodName -> RuntimeHintsPredicates.reflection()
|
||||
.onMethod(beanType, methodName).invoke()
|
||||
|
||||
@@ -57,4 +57,39 @@ class RootBeanDefinitionTests {
|
||||
verify(instanceSupplier).getFactoryMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithMatchingCandidateReplacedInferredVaue() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanWithCloseMethod.class);
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("close");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithNoCandidateSetDestroyMethodNameToNull() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanWithNoDestroyMethod.class);
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithNoResolvableType() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).isNull();
|
||||
}
|
||||
|
||||
static class BeanWithCloseMethod {
|
||||
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class BeanWithNoDestroyMethod {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user