Support [package-]private init/destroy methods in AOT mode

Prior to this commit, private (and non-visible package-private)
init/destroy methods were not supported in AOT mode. The reason is that
such methods are tracked using their fully-qualified method names, and
the AOT support for init/destroy methods previously did not take
fully-qualified method names into account. In addition, the invocation
order of init/destroy methods differed vastly between standard JVM mode
and AOT mode.

This commit addresses these issues in the following ways.

- AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(),
  DisposableBeanAdapter.determineDestroyMethod(), and
  BeanDefinitionPropertiesCodeGenerator.addInitDestroyHint() now parse
  fully-qualified method names to locate the correct init/destroy
  methods.

- AbstractAutowireCapableBeanFactory and DisposableBeanAdapter delegate
  to a new MethodDescriptor record which encapsulates the parsing of
  fully-qualified method names; however,
  BeanDefinitionPropertiesCodeGenerator duplicates this logic since it
  resides in a different package, and we do not currently want to make
  MethodDescriptor public.

- Init/destroy methods detected via annotations (such as @PostConstruct
  and @PreDestroy) are now invoked prior to init/destroy methods that
  are explicitly configured by name or convention. This aligns with the
  invocation order in standard JVM mode; however,
  InitializingBean#afterPropertiesSet() and DisposableBean#destroy()
  are still invoked before annotated init/destroy methods in AOT mode
  which differs from standard JVM mode.

- Unit and integration tests have been updated to test the revised
  behavior.

Closes gh-30692
This commit is contained in:
Sam Brannen
2023-06-20 17:47:01 +02:00
parent f86a69ebfb
commit 3181dca5ef
9 changed files with 252 additions and 28 deletions

View File

@@ -70,8 +70,8 @@ class InitDestroyAnnotationBeanPostProcessorTests {
beanDefinition.setDestroyMethodNames("customDestroyMethod");
processAheadOfTime(beanDefinition);
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
assertThat(mergedBeanDefinition.getInitMethodNames()).containsExactly("customInitMethod", "initMethod");
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("customDestroyMethod", "destroyMethod");
assertThat(mergedBeanDefinition.getInitMethodNames()).containsExactly("initMethod", "customInitMethod");
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod", "customDestroyMethod");
}
@Test
@@ -129,16 +129,16 @@ class InitDestroyAnnotationBeanPostProcessorTests {
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
assertSoftly(softly -> {
softly.assertThat(mergedBeanDefinition.getInitMethodNames()).containsExactly(
"afterPropertiesSet",
"customInit",
CustomAnnotatedPrivateInitDestroyBean.class.getName() + ".privateInit", // fully-qualified private method
CustomAnnotatedPrivateSameNameInitDestroyBean.class.getName() + ".privateInit" // fully-qualified private method
CustomAnnotatedPrivateSameNameInitDestroyBean.class.getName() + ".privateInit", // fully-qualified private method
"afterPropertiesSet",
"customInit"
);
softly.assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly(
"destroy",
"customDestroy",
CustomAnnotatedPrivateSameNameInitDestroyBean.class.getName() + ".privateDestroy", // fully-qualified private method
CustomAnnotatedPrivateInitDestroyBean.class.getName() + ".privateDestroy" // fully-qualified private method
CustomAnnotatedPrivateInitDestroyBean.class.getName() + ".privateDestroy", // fully-qualified private method
"destroy",
"customDestroy"
);
});
}

View File

@@ -376,6 +376,9 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
@Nested
class InitDestroyMethodTests {
private final String privateInitMethod = InitDestroyBean.class.getName() + ".privateInit";
private final String privateDestroyMethod = InitDestroyBean.class.getName() + ".privateDestroy";
@BeforeEach
void setTargetType() {
beanDefinition.setTargetType(InitDestroyBean.class);
@@ -393,11 +396,18 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
assertHasMethodInvokeHints(InitDestroyBean.class, "init");
}
@Test
void privateInitMethod() {
beanDefinition.setInitMethodName(privateInitMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly(privateInitMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "privateInit");
}
@Test
void multipleInitMethods() {
beanDefinition.setInitMethodNames("init", "init2");
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("init", "init2"));
assertHasMethodInvokeHints(InitDestroyBean.class, "init", "init2");
beanDefinition.setInitMethodNames("init", privateInitMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("init", privateInitMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "init", "privateInit");
}
@Test
@@ -412,11 +422,18 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy");
}
@Test
void privateDestroyMethod() {
beanDefinition.setDestroyMethodName(privateDestroyMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly(privateDestroyMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "privateDestroy");
}
@Test
void multipleDestroyMethods() {
beanDefinition.setDestroyMethodNames("destroy", "destroy2");
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy", "destroy2"));
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy", "destroy2");
beanDefinition.setDestroyMethodNames("destroy", privateDestroyMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy", privateDestroyMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy", "privateDestroy");
}
}
@@ -461,13 +478,15 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
void init() {
}
void init2() {
@SuppressWarnings("unused")
private void privateInit() {
}
void destroy() {
}
void destroy2() {
@SuppressWarnings("unused")
private void privateDestroy() {
}
}