Skip to content

Commit

Permalink
Register runtime hints for @ConfigurationProperties
Browse files Browse the repository at this point in the history
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 spring-projectsgh-31227
  • Loading branch information
bclozel committed Jun 7, 2022
1 parent e2c42e4 commit 3f0c141
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.support.RuntimeHintsUtils;
import org.springframework.beans.BeanInfoFactory;
import org.springframework.beans.ExtendedBeanInfoFactory;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
Expand Down Expand Up @@ -86,6 +87,7 @@ private ConfigurationPropertiesReflectionHintsContribution(Iterable<Class<?>> ty
@Override
public void applyTo(GenerationContext generationContext,
BeanFactoryInitializationCode beanFactoryInitializationCode) {
RuntimeHintsUtils.registerAnnotation(generationContext.getRuntimeHints(), ConfigurationProperties.class);
for (Class<?> type : this.types) {
TypeProcessor.processConfigurationProperties(type, generationContext.getRuntimeHints());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,28 @@ void processManuallyRegisteredSingleton() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("test", new SampleProperties());
RuntimeHints runtimeHints = process(beanFactory);
assertThat(runtimeHints.reflection().typeHints()).singleElement()
assertThat(runtimeHints.reflection().getTypeHint(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
void processJavaBeanConfigurationProperties() {
RuntimeHints runtimeHints = process(SampleProperties.class);
assertThat(runtimeHints.reflection().typeHints()).singleElement()
assertThat(runtimeHints.reflection().getTypeHint(SampleProperties.class))
.satisfies(javaBeanBinding(SampleProperties.class));
}

@Test
void processJavaBeanConfigurationPropertiesWithSeveralConstructors() throws NoSuchMethodException {
RuntimeHints runtimeHints = process(SamplePropertiesWithSeveralConstructors.class);
assertThat(runtimeHints.reflection().typeHints()).singleElement()
assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithSeveralConstructors.class))
.satisfies(javaBeanBinding(SamplePropertiesWithSeveralConstructors.class,
SamplePropertiesWithSeveralConstructors.class.getDeclaredConstructor()));
}
Expand All @@ -101,7 +108,7 @@ void processJavaBeanConfigurationPropertiesWithMapOfPojo() {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithMap.class));
assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class));
assertThat(typeHints).hasSize(2);
assertThat(typeHints).hasSize(3);
}

@Test
Expand All @@ -110,7 +117,7 @@ void processJavaBeanConfigurationPropertiesWithListOfPojo() {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithList.class));
assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class));
assertThat(typeHints).hasSize(2);
assertThat(typeHints).hasSize(3);
}

@Test
Expand All @@ -119,15 +126,15 @@ void processJavaBeanConfigurationPropertiesWitArrayOfPojo() {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithArray.class));
assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class));
assertThat(typeHints).hasSize(2);
assertThat(typeHints).hasSize(3);
}

@Test
void processJavaBeanConfigurationPropertiesWithListOfJavaType() {
RuntimeHints runtimeHints = process(SamplePropertiesWithSimpleList.class);
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithSimpleList.class));
assertThat(typeHints).hasSize(1);
assertThat(typeHints).hasSize(2);
}

@Test
Expand All @@ -136,7 +143,7 @@ void processValueObjectConfigurationProperties() {
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutableProperties.class,
SampleImmutableProperties.class.getDeclaredConstructors()[0]));
assertThat(typeHints).hasSize(1);
assertThat(typeHints).hasSize(2);
}

@Test
Expand All @@ -145,7 +152,7 @@ void processValueObjectConfigurationPropertiesWithSpecificConstructor() throws N
List<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithSeveralConstructors.class,
SampleImmutablePropertiesWithSeveralConstructors.class.getDeclaredConstructor(String.class)));
assertThat(typeHints).hasSize(1);
assertThat(typeHints).hasSize(2);
}

@Test
Expand All @@ -156,13 +163,13 @@ void processValueObjectConfigurationPropertiesWithSeveralLayersOfPojo() {
SampleImmutablePropertiesWithList.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).hasSize(3);
assertThat(typeHints).hasSize(4);
}

@Test
void processConfigurationPropertiesWithNestedTypeNotUsedIsIgnored() {
RuntimeHints runtimeHints = process(SamplePropertiesWithNested.class);
assertThat(runtimeHints.reflection().typeHints()).singleElement()
assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithNested.class))
.satisfies(javaBeanBinding(SamplePropertiesWithNested.class));
}

Expand All @@ -172,15 +179,15 @@ void processConfigurationPropertiesWithNestedExternalType() {
assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithExternalNested.class))
.anySatisfy(javaBeanBinding(SampleType.class)).anySatisfy(javaBeanBinding(SampleType.Nested.class))
.hasSize(3);
.hasSize(4);
}

@Test
void processConfigurationPropertiesWithRecursiveType() {
RuntimeHints runtimeHints = process(SamplePropertiesWithRecursive.class);
assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithRecursive.class))
.anySatisfy(javaBeanBinding(Recursive.class)).hasSize(2);
.anySatisfy(javaBeanBinding(Recursive.class)).hasSize(3);
}

@Test
Expand All @@ -191,7 +198,7 @@ void processValueObjectConfigurationPropertiesWithRecursiveType() {
SampleImmutablePropertiesWithRecursive.class.getDeclaredConstructors()[0]))
.anySatisfy(valueObjectBinding(ImmutableRecursive.class,
ImmutableRecursive.class.getDeclaredConstructors()[0]))
.hasSize(2);
.hasSize(3);
}

@Test
Expand All @@ -200,7 +207,7 @@ void processConfigurationPropertiesWithWellKnownTypes() {
assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithWellKnownTypes.class))
// TODO
.hasSize(1);
.hasSize(2);
}

@Test
Expand All @@ -209,7 +216,7 @@ void processConfigurationPropertiesWithCrossReference() {
assertThat(runtimeHints.reflection().typeHints())
.anySatisfy(javaBeanBinding(SamplePropertiesWithCrossReference.class))
.anySatisfy(javaBeanBinding(CrossReferenceA.class)).anySatisfy(javaBeanBinding(CrossReferenceB.class))
.hasSize(3);
.hasSize(4);
}

@Test
Expand Down

0 comments on commit 3f0c141

Please sign in to comment.