Skip to content

Commit ba56953

Browse files
committed
Skip ValueObjectBinder if parameter names cannot be discovered
Update `ValueObjectBinder` so that it is skipped if parameter names cannot be discovered. This is much more likely as of Since Spring Framework 6.1 as it no longer performs ASM parsing to discover names. Fixes gh-38201
1 parent 1a487d5 commit ba56953

File tree

2 files changed

+71
-15
lines changed

2 files changed

+71
-15
lines changed

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

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import kotlin.reflect.KFunction;
3232
import kotlin.reflect.KParameter;
3333
import kotlin.reflect.jvm.ReflectJvmMapping;
34+
import org.apache.commons.logging.Log;
35+
import org.apache.commons.logging.LogFactory;
3436

3537
import org.springframework.beans.BeanUtils;
3638
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
@@ -43,6 +45,7 @@
4345
import org.springframework.core.annotation.MergedAnnotation;
4446
import org.springframework.core.annotation.MergedAnnotations;
4547
import org.springframework.core.convert.ConversionException;
48+
import org.springframework.core.log.LogMessage;
4649
import org.springframework.util.Assert;
4750

4851
/**
@@ -55,6 +58,8 @@
5558
*/
5659
class ValueObjectBinder implements DataObjectBinder {
5760

61+
private static final Log logger = LogFactory.getLog(ValueObjectBinder.class);
62+
5863
private final BindConstructorProvider constructorProvider;
5964

6065
ValueObjectBinder(BindConstructorProvider constructorProvider) {
@@ -261,15 +266,31 @@ private static final class DefaultValueObject<T> extends ValueObject<T> {
261266

262267
private final List<ConstructorParameter> constructorParameters;
263268

264-
private DefaultValueObject(Constructor<T> constructor, ResolvableType type) {
269+
private DefaultValueObject(Constructor<T> constructor, List<ConstructorParameter> constructorParameters) {
265270
super(constructor);
266-
this.constructorParameters = parseConstructorParameters(constructor, type);
271+
this.constructorParameters = constructorParameters;
272+
}
273+
274+
@Override
275+
List<ConstructorParameter> getConstructorParameters() {
276+
return this.constructorParameters;
277+
}
278+
279+
@SuppressWarnings("unchecked")
280+
static <T> ValueObject<T> get(Constructor<?> bindConstructor, ResolvableType type) {
281+
String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(bindConstructor);
282+
if (names == null) {
283+
logger.debug(LogMessage.format(
284+
"Unable to use value object binding with %s as parameter names cannot be discovered",
285+
bindConstructor));
286+
return null;
287+
}
288+
List<ConstructorParameter> constructorParameters = parseConstructorParameters(bindConstructor, type, names);
289+
return new DefaultValueObject<>((Constructor<T>) bindConstructor, constructorParameters);
267290
}
268291

269292
private static List<ConstructorParameter> parseConstructorParameters(Constructor<?> constructor,
270-
ResolvableType type) {
271-
String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor);
272-
Assert.state(names != null, () -> "Failed to extract parameter names for " + constructor);
293+
ResolvableType type, String[] names) {
273294
Parameter[] parameters = constructor.getParameters();
274295
List<ConstructorParameter> result = new ArrayList<>(parameters.length);
275296
for (int i = 0; i < parameters.length; i++) {
@@ -285,16 +306,6 @@ private static List<ConstructorParameter> parseConstructorParameters(Constructor
285306
return Collections.unmodifiableList(result);
286307
}
287308

288-
@Override
289-
List<ConstructorParameter> getConstructorParameters() {
290-
return this.constructorParameters;
291-
}
292-
293-
@SuppressWarnings("unchecked")
294-
static <T> ValueObject<T> get(Constructor<?> bindConstructor, ResolvableType type) {
295-
return new DefaultValueObject<>((Constructor<T>) bindConstructor, type);
296-
}
297-
298309
}
299310

300311
/**

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
import java.util.Objects;
2727
import java.util.Optional;
2828

29+
import com.jayway.jsonpath.JsonPath;
2930
import org.junit.jupiter.api.Test;
3031

3132
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
3233
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
3334
import org.springframework.boot.context.properties.source.MockConfigurationPropertySource;
35+
import org.springframework.core.DefaultParameterNameDiscoverer;
3436
import org.springframework.core.ResolvableType;
3537
import org.springframework.core.convert.ConversionService;
3638
import org.springframework.core.test.tools.SourceFile;
@@ -391,6 +393,25 @@ public record RecordProperties(
391393
});
392394
}
393395

396+
@Test // gh-38201
397+
void bindWithNonExtractableParameterNamesAndNonIterablePropertySource() throws Exception {
398+
verifyJsonPathParametersCannotBeResolved();
399+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
400+
source.put("test.value", "test");
401+
this.sources.add(source.nonIterable());
402+
Bindable<NonExtractableParameterName> target = Bindable.of(NonExtractableParameterName.class);
403+
NonExtractableParameterName bound = this.binder.bindOrCreate("test", target);
404+
assertThat(bound.getValue()).isEqualTo("test");
405+
}
406+
407+
private void verifyJsonPathParametersCannotBeResolved() throws NoSuchFieldException {
408+
Class<?> jsonPathClass = NonExtractableParameterName.class.getDeclaredField("jsonPath").getType();
409+
Constructor<?>[] constructors = jsonPathClass.getDeclaredConstructors();
410+
assertThat(constructors).hasSize(1);
411+
constructors[0].setAccessible(true);
412+
assertThat(new DefaultParameterNameDiscoverer().getParameterNames(constructors[0])).isNull();
413+
}
414+
394415
private void noConfigurationProperty(BindException ex) {
395416
assertThat(ex.getProperty()).isNull();
396417
}
@@ -845,4 +866,28 @@ String getImportName() {
845866

846867
}
847868

869+
static class NonExtractableParameterName {
870+
871+
private String value;
872+
873+
private JsonPath jsonPath;
874+
875+
String getValue() {
876+
return this.value;
877+
}
878+
879+
void setValue(String value) {
880+
this.value = value;
881+
}
882+
883+
JsonPath getJsonPath() {
884+
return this.jsonPath;
885+
}
886+
887+
void setJsonPath(JsonPath jsonPath) {
888+
this.jsonPath = jsonPath;
889+
}
890+
891+
}
892+
848893
}

0 commit comments

Comments
 (0)