Add support of init and destroy methods

This commit updates InitDestroyBeanPostProcessor so that it contributes
init or destroy method names to the `RootBeanDefinition`. This is then
used by the generator to provide these methods to the optimized AOT
context.

Invocation of those init methods still happen using reflection so
dedicated hints are contributed for them.

Closes gh-28151
This commit is contained in:
Stephane Nicoll
2022-03-10 11:20:16 +01:00
parent 1b7892c559
commit 672555a568
10 changed files with 458 additions and 18 deletions

View File

@@ -43,9 +43,11 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.testfixture.context.generator.SimpleComponent;
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.JavaFile;
@@ -94,6 +96,41 @@ class ApplicationContextAotGeneratorTests {
}));
}
@Test
void generateApplicationContextWithInitDestroyMethods() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
context.registerBeanDefinition("initDestroyComponent", new RootBeanDefinition(InitDestroyComponent.class));
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> {
assertThat(aotContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent");
InitDestroyComponent bean = aotContext.getBean(InitDestroyComponent.class);
assertThat(bean.events).containsExactly("init");
aotContext.close();
assertThat(bean.events).containsExactly("init", "destroy");
}));
}
@Test
void generateApplicationContextWithMultipleInitDestroyMethods() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class);
beanDefinition.setInitMethodName("customInit");
beanDefinition.setDestroyMethodName("customDestroy");
context.registerBeanDefinition("initDestroyComponent", beanDefinition);
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> {
assertThat(aotContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent");
InitDestroyComponent bean = aotContext.getBean(InitDestroyComponent.class);
assertThat(bean.events).containsExactly("customInit", "init");
aotContext.close();
assertThat(bean.events).containsExactly("customInit", "init", "customDestroy", "destroy");
}));
}
@Test
void generateApplicationContextWitNoContributors() {
GeneratedTypeContext generationContext = createGenerationContext();