Add AOT support for generic constructor argument values

This commit improves compatibility with the core container when running
in AOT mode by adding support for generic constructor argument values.

Previously, these were ignored altogether. We now have code generation
support for them as well as resolution that is similar to what
AbstractAutowiredCapableBeanFactory does in a regular runtime.

This commit also improves AOT support for XML bean configurations by
adding more support for TypedStringValue and inner bean definitions.

Closes gh-31420
This commit is contained in:
Stéphane Nicoll
2023-10-12 15:01:28 +02:00
parent ca4d0d784b
commit 85388aa642
11 changed files with 445 additions and 57 deletions

View File

@@ -459,8 +459,10 @@ class ApplicationContextAotGeneratorTests {
assertThat(employee.getName()).isEqualTo("John Smith");
assertThat(employee.getAge()).isEqualTo(42);
assertThat(employee.getCompany()).isEqualTo("Acme Widgets, Inc.");
assertThat(freshApplicationContext.getBean("pet", Pet.class)
assertThat(freshApplicationContext.getBean("petIndexed", Pet.class)
.getName()).isEqualTo("Fido");
assertThat(freshApplicationContext.getBean("petGeneric", Pet.class)
.getName()).isEqualTo("Dofi");
});
}
@@ -496,6 +498,20 @@ class ApplicationContextAotGeneratorTests {
});
}
@Test
void processAheadOfTimeWhenXmlHasBeanReferences() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-references.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBean("petInnerBean", Pet.class)
.getName()).isEqualTo("Fido");
assertThat(freshApplicationContext.getBean("petRefBean", Pet.class)
.getName()).isEqualTo("Dofi");
});
}
}
private Consumer<List<? extends JdkProxyHint>> doesNotHaveProxyFor(Class<?> target) {

View File

@@ -331,7 +331,7 @@ class GenericApplicationContextTests {
}
@Test
void refreshForAotLoadsBeanClassNameOfConstructorArgumentInnerBeanDefinition() {
void refreshForAotLoadsBeanClassNameOfIndexedConstructorArgumentInnerBeanDefinition() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
GenericBeanDefinition innerBeanDefinition = new GenericBeanDefinition();
@@ -347,6 +347,23 @@ class GenericApplicationContextTests {
context.close();
}
@Test
void refreshForAotLoadsBeanClassNameOfGenericConstructorArgumentInnerBeanDefinition() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
GenericBeanDefinition innerBeanDefinition = new GenericBeanDefinition();
innerBeanDefinition.setBeanClassName("java.lang.Integer");
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(innerBeanDefinition);
context.registerBeanDefinition("test",beanDefinition);
context.refreshForAotProcessing(new RuntimeHints());
RootBeanDefinition bd = getBeanDefinition(context, "test");
GenericBeanDefinition value = (GenericBeanDefinition) bd.getConstructorArgumentValues()
.getGenericArgumentValues().get(0).getValue();
assertThat(value.hasBeanClass()).isTrue();
assertThat(value.getBeanClass()).isEqualTo(Integer.class);
context.close();
}
@Test
void refreshForAotLoadsBeanClassNameOfPropertyValueInnerBeanDefinition() {
GenericApplicationContext context = new GenericApplicationContext();
@@ -377,7 +394,7 @@ class GenericApplicationContextTests {
}
@Test
void refreshForAotLoadsTypedStringValueClassNameInConstructorArgument() {
void refreshForAotLoadsTypedStringValueClassNameInIndexedConstructorArgument() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition("java.lang.Integer");
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
@@ -391,6 +408,21 @@ class GenericApplicationContextTests {
context.close();
}
@Test
void refreshForAotLoadsTypedStringValueClassNameInGenericConstructorArgument() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition("java.lang.Integer");
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(
new TypedStringValue("42", "java.lang.Integer"));
context.registerBeanDefinition("number", beanDefinition);
context.refreshForAotProcessing(new RuntimeHints());
assertThat(getBeanDefinition(context, "number").getConstructorArgumentValues()
.getGenericArgumentValue(TypedStringValue.class).getValue())
.isInstanceOfSatisfying(TypedStringValue.class, typeStringValue ->
assertThat(typeStringValue.getTargetType()).isEqualTo(Integer.class));
context.close();
}
@Test
void refreshForAotInvokesBeanFactoryPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
@@ -414,7 +446,7 @@ class GenericApplicationContextTests {
}
@Test
void refreshForAotInvokesMergedBeanDefinitionPostProcessorsOnConstructorArgument() {
void refreshForAotInvokesMergedBeanDefinitionPostProcessorsOnIndexedConstructorArgument() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanD.class);
GenericBeanDefinition innerBeanDefinition = new GenericBeanDefinition();
@@ -430,6 +462,23 @@ class GenericApplicationContextTests {
context.close();
}
@Test
void refreshForAotInvokesMergedBeanDefinitionPostProcessorsOnGenericConstructorArgument() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanD.class);
GenericBeanDefinition innerBeanDefinition = new GenericBeanDefinition();
innerBeanDefinition.setBeanClassName("java.lang.Integer");
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(innerBeanDefinition);
context.registerBeanDefinition("test", beanDefinition);
MergedBeanDefinitionPostProcessor bpp = registerMockMergedBeanDefinitionPostProcessor(context);
context.refreshForAotProcessing(new RuntimeHints());
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(bpp).postProcessMergedBeanDefinition(getBeanDefinition(context, "test"), BeanD.class, "test");
verify(bpp).postProcessMergedBeanDefinition(any(RootBeanDefinition.class), eq(Integer.class), captor.capture());
assertThat(captor.getValue()).startsWith("(inner bean)");
context.close();
}
@Test
void refreshForAotInvokesMergedBeanDefinitionPostProcessorsOnPropertyValue() {
GenericApplicationContext context = new GenericApplicationContext();