Skip to content

Commit 7855888

Browse files
committed
Fix nested type discovery in ConfigurationPropertiesReflectionHintsProcessor
The old implementation picked up nested types only if they have been annotated with NestedConfigurationProperty, which not all nested properties are. Now the processor takes into account all referenced types. It ignores only types in the java package and interfaces. This commit also moves some of the tests from ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests to ConfigurationPropertiesReflectionHintsProcessorTests for easier discoverability Closes gh-31708
1 parent 81fdb76 commit 7855888

File tree

3 files changed

+458
-347
lines changed

3 files changed

+458
-347
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.beans.Introspector;
2222
import java.beans.PropertyDescriptor;
2323
import java.lang.reflect.Constructor;
24-
import java.lang.reflect.Field;
2524
import java.lang.reflect.Method;
2625
import java.util.Arrays;
2726
import java.util.Collection;
@@ -35,12 +34,10 @@
3534
import org.springframework.beans.ExtendedBeanInfoFactory;
3635
import org.springframework.boot.context.properties.bind.Bindable;
3736
import org.springframework.core.ResolvableType;
38-
import org.springframework.core.annotation.MergedAnnotations;
39-
import org.springframework.util.ReflectionUtils;
4037

4138
/**
4239
* Registers a given type on {@link ReflectionHints} for binding purposes, discovering any
43-
* nested type it may expose via a property.
40+
* referenced types it may expose via a property.
4441
*
4542
* @author Andy Wilkinson
4643
* @author Moritz Halbritter
@@ -77,12 +74,22 @@ public static void processConfigurationProperties(Class<?> type, ReflectionHints
7774
.process(reflectionHints);
7875
}
7976

80-
private void processNestedType(Class<?> type, ReflectionHints reflectionHints) {
81-
processNestedType(type, getBindConstructor(type, true), reflectionHints);
77+
private void processType(Class<?> type, ReflectionHints reflectionHints) {
78+
if (isTypeIgnored(type)) {
79+
return;
80+
}
81+
new ConfigurationPropertiesReflectionHintsProcessor(type, getBindConstructor(type, true), this.seen)
82+
.process(reflectionHints);
8283
}
8384

84-
private void processNestedType(Class<?> type, Constructor<?> bindConstructor, ReflectionHints reflectionHints) {
85-
new ConfigurationPropertiesReflectionHintsProcessor(type, bindConstructor, this.seen).process(reflectionHints);
85+
private boolean isTypeIgnored(Class<?> type) {
86+
if (type.getPackageName().startsWith("java.")) {
87+
return true;
88+
}
89+
if (type.isInterface()) {
90+
return true;
91+
}
92+
return false;
8693
}
8794

8895
private static Constructor<?> getBindConstructor(Class<?> type, boolean nestedType) {
@@ -118,9 +125,8 @@ private void handleConstructor(ReflectionHints reflectionHints) {
118125

119126
private void handleValueObjectProperties(ReflectionHints reflectionHints) {
120127
for (int i = 0; i < this.bindConstructor.getParameterCount(); i++) {
121-
String propertyName = this.bindConstructor.getParameters()[i].getName();
122128
ResolvableType propertyType = ResolvableType.forConstructorParameter(this.bindConstructor, i);
123-
handleProperty(reflectionHints, propertyName, propertyType);
129+
registerType(reflectionHints, propertyType);
124130
}
125131
}
126132

@@ -129,16 +135,12 @@ private void handleJavaBeanProperties(ReflectionHints reflectionHints) {
129135
Method readMethod = propertyDescriptor.getReadMethod();
130136
if (readMethod != null) {
131137
ResolvableType propertyType = ResolvableType.forMethodReturnType(readMethod, this.type);
132-
String propertyName = propertyDescriptor.getName();
133-
if (isSetterMandatory(propertyName, propertyType) && propertyDescriptor.getWriteMethod() == null) {
134-
continue;
135-
}
136-
handleProperty(reflectionHints, propertyName, propertyType);
138+
registerType(reflectionHints, propertyType);
137139
}
138140
}
139141
}
140142

141-
private void handleProperty(ReflectionHints reflectionHints, String propertyName, ResolvableType propertyType) {
143+
private void registerType(ReflectionHints reflectionHints, ResolvableType propertyType) {
142144
Class<?> propertyClass = propertyType.resolve();
143145
if (propertyClass == null) {
144146
return;
@@ -148,25 +150,11 @@ private void handleProperty(ReflectionHints reflectionHints, String propertyName
148150
}
149151
Class<?> componentType = getComponentType(propertyType);
150152
if (componentType != null) {
151-
// Can be a list of simple types
152-
if (!isJavaType(componentType)) {
153-
processNestedType(componentType, reflectionHints);
154-
}
155-
}
156-
else if (isNestedType(propertyName, propertyClass)) {
157-
processNestedType(propertyClass, reflectionHints);
158-
}
159-
}
160-
161-
private boolean isSetterMandatory(String propertyName, ResolvableType propertyType) {
162-
Class<?> propertyClass = propertyType.resolve();
163-
if (propertyClass == null) {
164-
return true;
153+
processType(componentType, reflectionHints);
165154
}
166-
if (getComponentType(propertyType) != null) {
167-
return false;
155+
else {
156+
processType(propertyClass, reflectionHints);
168157
}
169-
return !isNestedType(propertyName, propertyClass);
170158
}
171159

172160
private Class<?> getComponentType(ResolvableType propertyType) {
@@ -183,27 +171,6 @@ else if (Map.class.isAssignableFrom(propertyClass)) {
183171
return null;
184172
}
185173

186-
/**
187-
* Specify whether the specified property refer to a nested type. A nested type
188-
* represents a sub-namespace that need to be fully resolved.
189-
* @param propertyName the name of the property
190-
* @param propertyType the type of the property
191-
* @return whether the specified {@code propertyType} is a nested type
192-
*/
193-
private boolean isNestedType(String propertyName, Class<?> propertyType) {
194-
if (this.type.equals(propertyType.getDeclaringClass())) {
195-
return true;
196-
}
197-
else {
198-
Field field = ReflectionUtils.findField(this.type, propertyName);
199-
return field != null && MergedAnnotations.from(field).isPresent(NestedConfigurationProperty.class);
200-
}
201-
}
202-
203-
private boolean isJavaType(Class<?> candidate) {
204-
return candidate.getPackageName().startsWith("java.");
205-
}
206-
207174
private static BeanInfo getBeanInfo(Class<?> beanType) {
208175
try {
209176
BeanInfo beanInfo = beanInfoFactory.getBeanInfo(beanType);

0 commit comments

Comments
 (0)