Register runtime hints for @ConfigurationProperties

Prior to this commit, the `@ConfigurationProperties` annotation would
not be registered for reflection hints: this means it could be missing
at runtime in a native image and would not be registered for JDK
proxying - this can fail the synthesized annotation resolution.

This commit ensures that hints are registered for this annotation if
configuration properties are declared in the bean factory.

Fixes gh-31227
pull/31277/head
Brian Clozel 2 years ago
parent e2c42e4bbf
commit 3f0c14187a

@ -34,6 +34,7 @@ import java.util.Set;
import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.support.RuntimeHintsUtils;
import org.springframework.beans.BeanInfoFactory; import org.springframework.beans.BeanInfoFactory;
import org.springframework.beans.ExtendedBeanInfoFactory; import org.springframework.beans.ExtendedBeanInfoFactory;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
@ -86,6 +87,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessor implements Be
@Override @Override
public void applyTo(GenerationContext generationContext, public void applyTo(GenerationContext generationContext,
BeanFactoryInitializationCode beanFactoryInitializationCode) { BeanFactoryInitializationCode beanFactoryInitializationCode) {
RuntimeHintsUtils.registerAnnotation(generationContext.getRuntimeHints(), ConfigurationProperties.class);
for (Class<?> type : this.types) { for (Class<?> type : this.types) {
TypeProcessor.processConfigurationProperties(type, generationContext.getRuntimeHints()); TypeProcessor.processConfigurationProperties(type, generationContext.getRuntimeHints());
} }

@ -76,21 +76,28 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("test", new SampleProperties()); beanFactory.registerSingleton("test", new SampleProperties());
RuntimeHints runtimeHints = process(beanFactory); RuntimeHints runtimeHints = process(beanFactory);
assertThat(runtimeHints.reflection().typeHints()).singleElement() assertThat(runtimeHints.reflection().getTypeHint(SampleProperties.class))
.satisfies(javaBeanBinding(SampleProperties.class)); .satisfies(javaBeanBinding(SampleProperties.class));
} }
@Test
void registerConfigurationPropertiesAnnotation() {
RuntimeHints runtimeHints = process(SampleProperties.class);
assertThat(runtimeHints.reflection().getTypeHint(ConfigurationProperties.class)).satisfies(
(hint) -> assertThat(hint.getMemberCategories()).contains(MemberCategory.INVOKE_DECLARED_METHODS));
}
@Test @Test
void processJavaBeanConfigurationProperties() { void processJavaBeanConfigurationProperties() {
RuntimeHints runtimeHints = process(SampleProperties.class); RuntimeHints runtimeHints = process(SampleProperties.class);
assertThat(runtimeHints.reflection().typeHints()).singleElement() assertThat(runtimeHints.reflection().getTypeHint(SampleProperties.class))
.satisfies(javaBeanBinding(SampleProperties.class)); .satisfies(javaBeanBinding(SampleProperties.class));
} }
@Test @Test
void processJavaBeanConfigurationPropertiesWithSeveralConstructors() throws NoSuchMethodException { void processJavaBeanConfigurationPropertiesWithSeveralConstructors() throws NoSuchMethodException {
RuntimeHints runtimeHints = process(SamplePropertiesWithSeveralConstructors.class); RuntimeHints runtimeHints = process(SamplePropertiesWithSeveralConstructors.class);
assertThat(runtimeHints.reflection().typeHints()).singleElement() assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithSeveralConstructors.class))
.satisfies(javaBeanBinding(SamplePropertiesWithSeveralConstructors.class, .satisfies(javaBeanBinding(SamplePropertiesWithSeveralConstructors.class,
SamplePropertiesWithSeveralConstructors.class.getDeclaredConstructor())); SamplePropertiesWithSeveralConstructors.class.getDeclaredConstructor()));
} }
@ -101,7 +108,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList(); List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithMap.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithMap.class));
assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class));
assertThat(typeHints).hasSize(2); assertThat(typeHints).hasSize(3);
} }
@Test @Test
@ -110,7 +117,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList(); List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithList.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithList.class));
assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class));
assertThat(typeHints).hasSize(2); assertThat(typeHints).hasSize(3);
} }
@Test @Test
@ -119,7 +126,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList(); List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithArray.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithArray.class));
assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class));
assertThat(typeHints).hasSize(2); assertThat(typeHints).hasSize(3);
} }
@Test @Test
@ -127,7 +134,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
RuntimeHints runtimeHints = process(SamplePropertiesWithSimpleList.class); RuntimeHints runtimeHints = process(SamplePropertiesWithSimpleList.class);
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList(); List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithSimpleList.class)); assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithSimpleList.class));
assertThat(typeHints).hasSize(1); assertThat(typeHints).hasSize(2);
} }
@Test @Test
@ -136,7 +143,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList(); List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutableProperties.class, assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutableProperties.class,
SampleImmutableProperties.class.getDeclaredConstructors()[0])); SampleImmutableProperties.class.getDeclaredConstructors()[0]));
assertThat(typeHints).hasSize(1); assertThat(typeHints).hasSize(2);
} }
@Test @Test
@ -145,7 +152,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList(); List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithSeveralConstructors.class, assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithSeveralConstructors.class,
SampleImmutablePropertiesWithSeveralConstructors.class.getDeclaredConstructor(String.class))); SampleImmutablePropertiesWithSeveralConstructors.class.getDeclaredConstructor(String.class)));
assertThat(typeHints).hasSize(1); assertThat(typeHints).hasSize(2);
} }
@Test @Test
@ -156,13 +163,13 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
SampleImmutablePropertiesWithList.class.getDeclaredConstructors()[0])); SampleImmutablePropertiesWithList.class.getDeclaredConstructors()[0]));
assertThat(typeHints).anySatisfy(valueObjectBinding(Person.class, Person.class.getDeclaredConstructors()[0])); assertThat(typeHints).anySatisfy(valueObjectBinding(Person.class, Person.class.getDeclaredConstructors()[0]));
assertThat(typeHints).anySatisfy(valueObjectBinding(Address.class, Address.class.getDeclaredConstructors()[0])); assertThat(typeHints).anySatisfy(valueObjectBinding(Address.class, Address.class.getDeclaredConstructors()[0]));
assertThat(typeHints).hasSize(3); assertThat(typeHints).hasSize(4);
} }
@Test @Test
void processConfigurationPropertiesWithNestedTypeNotUsedIsIgnored() { void processConfigurationPropertiesWithNestedTypeNotUsedIsIgnored() {
RuntimeHints runtimeHints = process(SamplePropertiesWithNested.class); RuntimeHints runtimeHints = process(SamplePropertiesWithNested.class);
assertThat(runtimeHints.reflection().typeHints()).singleElement() assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithNested.class))
.satisfies(javaBeanBinding(SamplePropertiesWithNested.class)); .satisfies(javaBeanBinding(SamplePropertiesWithNested.class));
} }
@ -172,7 +179,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
assertThat(runtimeHints.reflection().typeHints()) assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithExternalNested.class)) .anySatisfy(javaBeanBinding(SamplePropertiesWithExternalNested.class))
.anySatisfy(javaBeanBinding(SampleType.class)).anySatisfy(javaBeanBinding(SampleType.Nested.class)) .anySatisfy(javaBeanBinding(SampleType.class)).anySatisfy(javaBeanBinding(SampleType.Nested.class))
.hasSize(3); .hasSize(4);
} }
@Test @Test
@ -180,7 +187,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
RuntimeHints runtimeHints = process(SamplePropertiesWithRecursive.class); RuntimeHints runtimeHints = process(SamplePropertiesWithRecursive.class);
assertThat(runtimeHints.reflection().typeHints()) assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithRecursive.class)) .anySatisfy(javaBeanBinding(SamplePropertiesWithRecursive.class))
.anySatisfy(javaBeanBinding(Recursive.class)).hasSize(2); .anySatisfy(javaBeanBinding(Recursive.class)).hasSize(3);
} }
@Test @Test
@ -191,7 +198,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
SampleImmutablePropertiesWithRecursive.class.getDeclaredConstructors()[0])) SampleImmutablePropertiesWithRecursive.class.getDeclaredConstructors()[0]))
.anySatisfy(valueObjectBinding(ImmutableRecursive.class, .anySatisfy(valueObjectBinding(ImmutableRecursive.class,
ImmutableRecursive.class.getDeclaredConstructors()[0])) ImmutableRecursive.class.getDeclaredConstructors()[0]))
.hasSize(2); .hasSize(3);
} }
@Test @Test
@ -200,7 +207,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
assertThat(runtimeHints.reflection().typeHints()) assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithWellKnownTypes.class)) .anySatisfy(javaBeanBinding(SamplePropertiesWithWellKnownTypes.class))
// TODO // TODO
.hasSize(1); .hasSize(2);
} }
@Test @Test
@ -209,7 +216,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
assertThat(runtimeHints.reflection().typeHints()) assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithCrossReference.class)) .anySatisfy(javaBeanBinding(SamplePropertiesWithCrossReference.class))
.anySatisfy(javaBeanBinding(CrossReferenceA.class)).anySatisfy(javaBeanBinding(CrossReferenceB.class)) .anySatisfy(javaBeanBinding(CrossReferenceA.class)).anySatisfy(javaBeanBinding(CrossReferenceB.class))
.hasSize(3); .hasSize(4);
} }
@Test @Test

Loading…
Cancel
Save