Add AOT support for TypedStringValue

This commit adds support for TypeStringValue when generating AOT code.
If the value does not specify an explicit type, it's specified as is.
Otherwise, the TypeStringValue instance is restored via the appropriate
code generation.

Closes gh-29074
This commit is contained in:
Stephane Nicoll
2023-09-12 15:43:30 +02:00
committed by Stéphane Nicoll
parent 15c19411f6
commit 37c2619fc4
8 changed files with 197 additions and 4 deletions

View File

@@ -52,6 +52,8 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
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.Employee;
import org.springframework.beans.testfixture.beans.Pet;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
@@ -62,6 +64,7 @@ import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.context.testfixture.context.annotation.AutowiredComponent;
import org.springframework.context.testfixture.context.annotation.AutowiredGenericTemplate;
import org.springframework.context.testfixture.context.annotation.CglibConfiguration;
@@ -79,6 +82,7 @@ import org.springframework.context.testfixture.context.generator.SimpleComponent
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
import org.springframework.core.test.tools.Compiled;
@@ -441,6 +445,59 @@ class ApplicationContextAotGeneratorTests {
}
@Nested
class XmlSupport {
@Test
void processAheadOfTimeWhenHasTypedStringValue() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-values.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
Employee employee = freshApplicationContext.getBean(Employee.class);
assertThat(employee.getName()).isEqualTo("John Smith");
assertThat(employee.getAge()).isEqualTo(42);
assertThat(employee.getCompany()).isEqualTo("Acme Widgets, Inc.");
assertThat(freshApplicationContext.getBean("pet", Pet.class)
.getName()).isEqualTo("Fido");
});
}
@Test
void processAheadOfTimeWhenHasTypedStringValueWithType() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-values-types.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
Employee employee = freshApplicationContext.getBean(Employee.class);
assertThat(employee.getName()).isEqualTo("John Smith");
assertThat(employee.getAge()).isEqualTo(42);
assertThat(employee.getCompany()).isEqualTo("Acme Widgets, Inc.");
assertThat(compiled.getSourceFile(".*Employee__BeanDefinitions"))
.contains("new TypedStringValue(\"42\", Integer.class");
});
}
@Test
void processAheadOfTimeWhenHasTypedStringValueWithExpression() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-values-expressions.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
Employee employee = freshApplicationContext.getBean(Employee.class);
assertThat(employee.getName()).isEqualTo("John Smith");
assertThat(employee.getAge()).isEqualTo(42);
assertThat(employee.getCompany()).isEqualTo("Acme Widgets, Inc.");
assertThat(freshApplicationContext.getBean("pet", Pet.class)
.getName()).isEqualTo("Fido");
});
}
}
private Consumer<List<? extends JdkProxyHint>> doesNotHaveProxyFor(Class<?> target) {
return hints -> assertThat(hints).noneMatch(hint ->
hint.getProxiedInterfaces().get(0).equals(TypeReference.of(target)));

View File

@@ -34,6 +34,7 @@ import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.GenericBeanDefinition;
@@ -362,6 +363,34 @@ class GenericApplicationContextTests {
context.close();
}
@Test
void refreshForAotLoadsTypedStringValueClassNameInProperty() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition("java.lang.Integer");
beanDefinition.getPropertyValues().add("value", new TypedStringValue("42", "java.lang.Integer"));
context.registerBeanDefinition("number", beanDefinition);
context.refreshForAotProcessing(new RuntimeHints());
assertThat(getBeanDefinition(context, "number").getPropertyValues().get("value"))
.isInstanceOfSatisfying(TypedStringValue.class, typeStringValue ->
assertThat(typeStringValue.getTargetType()).isEqualTo(Integer.class));
context.close();
}
@Test
void refreshForAotLoadsTypedStringValueClassNameInConstructorArgument() {
GenericApplicationContext context = new GenericApplicationContext();
RootBeanDefinition beanDefinition = new RootBeanDefinition("java.lang.Integer");
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
new TypedStringValue("42", "java.lang.Integer"));
context.registerBeanDefinition("number", beanDefinition);
context.refreshForAotProcessing(new RuntimeHints());
assertThat(getBeanDefinition(context, "number").getConstructorArgumentValues()
.getIndexedArgumentValue(0, TypedStringValue.class).getValue())
.isInstanceOfSatisfying(TypedStringValue.class, typeStringValue ->
assertThat(typeStringValue.getTargetType()).isEqualTo(Integer.class));
context.close();
}
@Test
void refreshForAotInvokesBeanFactoryPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();