Restore constructor binding support with AOT
This commit restores the generation of the BindMethod attribute that is required at runtime to figure out how to bind a particular configuration properties target. It also improves the test to use TestCompiler and assert that the generated contribution restores the proper behavior for both java bean and value object binding. Closes gh-31956
This commit is contained in:
parent
a8c558a671
commit
c05d0c51b7
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.context.properties;
|
||||
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
|
@ -30,6 +31,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationCodeFragments;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBean.BindMethod;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
|
||||
|
@ -59,6 +61,9 @@ class ConfigurationPropertiesBeanRegistrationAotProcessor implements BeanRegistr
|
|||
|
||||
private static class ConfigurationPropertiesBeanRegistrationCodeFragments extends BeanRegistrationCodeFragments {
|
||||
|
||||
private static final Predicate<String> INCLUDE_BIND_METHOD_ATTRIBUTE_FILTER = (name) -> name
|
||||
.equals(BindMethod.class.getName());
|
||||
|
||||
private static final String REGISTERED_BEAN_PARAMETER_NAME = "registeredBean";
|
||||
|
||||
private final RegisteredBean registeredBean;
|
||||
|
@ -69,6 +74,14 @@ class ConfigurationPropertiesBeanRegistrationAotProcessor implements BeanRegistr
|
|||
this.registeredBean = registeredBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition,
|
||||
Predicate<String> attributeFilter) {
|
||||
return super.generateSetBeanDefinitionPropertiesCode(generationContext, beanRegistrationCode,
|
||||
beanDefinition, INCLUDE_BIND_METHOD_ATTRIBUTE_FILTER.or(attributeFilter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
|
||||
|
|
|
@ -16,14 +16,30 @@
|
|||
|
||||
package org.springframework.boot.context.properties;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.test.generator.compile.CompileWithTargetClassAccess;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.aot.AotServices;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.context.properties.scan.valid.b.BScanConfiguration;
|
||||
import org.springframework.boot.context.properties.scan.valid.b.BScanConfiguration.BFirstProperties;
|
||||
import org.springframework.boot.context.properties.scan.valid.b.BScanConfiguration.BSecondProperties;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.aot.ApplicationContextAotGenerator;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.test.aot.generate.TestGenerationContext;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -73,6 +89,78 @@ class ConfigurationPropertiesBeanRegistrationAotProcessorTests {
|
|||
return this.processor.processAheadOfTime(registeredBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithTargetClassAccess
|
||||
void aotContributedInitializerBindsValueObject() {
|
||||
compile(createContext(ValueObjectSampleBeanConfiguration.class), (freshContext) -> {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(freshContext, "test.name=Hello");
|
||||
freshContext.refresh();
|
||||
ValueObjectSampleBean bean = freshContext.getBean(ValueObjectSampleBean.class);
|
||||
assertThat(bean.name).isEqualTo("Hello");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithTargetClassAccess
|
||||
void aotContributedInitializerBindsJavaBean() {
|
||||
compile(createContext(JavaBeanSampleBeanConfiguration.class), (freshContext) -> {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(freshContext, "test.name=Hello");
|
||||
freshContext.refresh();
|
||||
JavaBeanSampleBean bean = freshContext.getBean(JavaBeanSampleBean.class);
|
||||
assertThat(bean.getName()).isEqualTo("Hello");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithTargetClassAccess
|
||||
void aotContributedInitializerBindsScannedValueObject() {
|
||||
compile(createContext(ScanTestConfiguration.class), (freshContext) -> {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(freshContext, "b.first.name=Hello");
|
||||
freshContext.refresh();
|
||||
BFirstProperties bean = freshContext.getBean(BFirstProperties.class);
|
||||
assertThat(bean.getName()).isEqualTo("Hello");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithTargetClassAccess
|
||||
void aotContributedInitializerBindsScannedJavaBean() {
|
||||
compile(createContext(ScanTestConfiguration.class), (freshContext) -> {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(freshContext, "b.second.number=42");
|
||||
freshContext.refresh();
|
||||
BSecondProperties bean = freshContext.getBean(BSecondProperties.class);
|
||||
assertThat(bean.getNumber()).isEqualTo(42);
|
||||
});
|
||||
}
|
||||
|
||||
private GenericApplicationContext createContext(Class<?>... types) {
|
||||
GenericApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.registerBean(JavaBeanSampleBeanConfiguration.class);
|
||||
Arrays.stream(types).forEach((type) -> context.registerBean(type));
|
||||
return context;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void compile(GenericApplicationContext context, Consumer<GenericApplicationContext> freshContext) {
|
||||
TestGenerationContext generationContext = new TestGenerationContext(TestTarget.class);
|
||||
ClassName className = new ApplicationContextAotGenerator().generateApplicationContext(context,
|
||||
generationContext);
|
||||
generationContext.writeGeneratedContent();
|
||||
TestCompiler.forSystem().withFiles(generationContext.getGeneratedFiles()).compile((compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = new GenericApplicationContext();
|
||||
ApplicationContextInitializer<GenericApplicationContext> initializer = compiled
|
||||
.getInstance(ApplicationContextInitializer.class, className.toString());
|
||||
initializer.initialize(freshApplicationContext);
|
||||
freshContext.accept(freshApplicationContext);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(JavaBeanSampleBean.class)
|
||||
static class JavaBeanSampleBeanConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties("test")
|
||||
public static class JavaBeanSampleBean {
|
||||
|
||||
|
@ -88,6 +176,12 @@ class ConfigurationPropertiesBeanRegistrationAotProcessorTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(ValueObjectSampleBean.class)
|
||||
static class ValueObjectSampleBeanConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties("test")
|
||||
public static class ValueObjectSampleBean {
|
||||
|
||||
|
@ -100,4 +194,14 @@ class ConfigurationPropertiesBeanRegistrationAotProcessorTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConfigurationPropertiesScan(basePackageClasses = BScanConfiguration.class)
|
||||
static class ScanTestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class TestTarget {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue