Skip to content

Commit

Permalink
Detect nested types that are not directly nested to the current type
Browse files Browse the repository at this point in the history
This commit makes sure that sub-namespace that are defined in a flat
manner in a configuration properties are considered for runtime hints

Closes gh-36909
  • Loading branch information
snicoll committed Aug 17, 2023
1 parent 87569a0 commit f3450fd
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,20 @@ private boolean isMap(ResolvableType type) {
*/
private boolean isNestedType(String propertyName, Class<?> propertyType) {
Class<?> declaringClass = propertyType.getDeclaringClass();
if (declaringClass != null && declaringClass.isAssignableFrom(this.type)) {
if (declaringClass != null && isNested(declaringClass, this.type)) {
return true;
}
Field field = ReflectionUtils.findField(this.type, propertyName);
return (field != null) && MergedAnnotations.from(field).isPresent(Nested.class);
}

private static boolean isNested(Class<?> type, Class<?> candidate) {
if (type.isAssignableFrom(candidate)) {
return true;
}
return (candidate.getDeclaringClass() != null && isNested(type, candidate.getDeclaringClass()));
}

private boolean isJavaType(Class<?> candidate) {
return candidate.getPackageName().startsWith("java.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
import org.springframework.boot.context.properties.ConfigurationPropertiesBean;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrarTests.BaseProperties.InheritedNested;
import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrarTests.ComplexNestedProperties.ListenerRetry;
import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrarTests.ComplexNestedProperties.Retry;
import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrarTests.ComplexNestedProperties.Simple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
Expand Down Expand Up @@ -259,6 +262,23 @@ void registerHintsWhenHasInheritedNestedProperties() {
.satisfies(javaBeanBinding(InheritedNested.class, "getAlpha", "setAlpha"));
}

@Test
void registerHintsWhenHasComplexNestedProperties() {
RuntimeHints runtimeHints = registerHints(ComplexNestedProperties.class);
assertThat(runtimeHints.reflection().typeHints()).hasSize(4);
assertThat(runtimeHints.reflection().getTypeHint(Retry.class)).satisfies((entry) -> {
assertThat(entry.getMemberCategories()).isEmpty();
assertThat(entry.methods()).extracting(ExecutableHint::getName)
.containsExactlyInAnyOrder("getCount", "setCount");
});
assertThat(runtimeHints.reflection().getTypeHint(ListenerRetry.class))
.satisfies(javaBeanBinding(ListenerRetry.class, "isStateless", "setStateless"));
assertThat(runtimeHints.reflection().getTypeHint(Simple.class))
.satisfies(javaBeanBinding(Simple.class, "getRetry"));
assertThat(runtimeHints.reflection().getTypeHint(ComplexNestedProperties.class))
.satisfies(javaBeanBinding(ComplexNestedProperties.class, "getSimple"));
}

private Consumer<TypeHint> javaBeanBinding(Class<?> type, String... expectedMethods) {
return javaBeanBinding(type, type.getDeclaredConstructors()[0], expectedMethods);
}
Expand Down Expand Up @@ -723,4 +743,52 @@ public void setBravo(String bravo) {

}

public static class ComplexNestedProperties {

private final Simple simple = new Simple();

public Simple getSimple() {
return this.simple;
}

public static class Simple {

private final ListenerRetry retry = new ListenerRetry();

public ListenerRetry getRetry() {
return this.retry;
}

}

public abstract static class Retry {

private int count = 5;

public int getCount() {
return this.count;
}

public void setCount(int count) {
this.count = count;
}

}

public static class ListenerRetry extends Retry {

private boolean stateless;

public boolean isStateless() {
return this.stateless;
}

public void setStateless(boolean stateless) {
this.stateless = stateless;
}

}

}

}

0 comments on commit f3450fd

Please sign in to comment.