Attempt properties binding after constructor
Prior to this commit, the `GraphQlArgumentBinder` would choose between two strategies: * if there is a constructor with arguments, instantiate the target and bind it using the constructor * otherwise, use the default constructor and use bean properties binding This commit extends the first case and attempts to further bind bean properties after constructor instantiation. Closes gh-1163
This commit is contained in:
@@ -292,7 +292,12 @@ public class GraphQlArgumentBinder {
|
||||
}
|
||||
|
||||
try {
|
||||
return BeanUtils.instantiateClass(constructor, constructorArguments);
|
||||
Object target = BeanUtils.instantiateClass(constructor, constructorArguments);
|
||||
// only attempt further properties binding if there were no errors
|
||||
if (!bindingResult.hasErrors()) {
|
||||
bindProperties(rawMap, ownerType, bindingResult, target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
catch (BeanInstantiationException ex) {
|
||||
// Ignore, if we had binding errors to begin with
|
||||
@@ -308,6 +313,11 @@ public class GraphQlArgumentBinder {
|
||||
ArgumentsBindingResult bindingResult) {
|
||||
|
||||
Object target = BeanUtils.instantiateClass(constructor);
|
||||
bindProperties(rawMap, ownerType, bindingResult, target);
|
||||
return target;
|
||||
}
|
||||
|
||||
private void bindProperties(Map<String, Object> rawMap, ResolvableType ownerType, ArgumentsBindingResult bindingResult, Object target) {
|
||||
BeanWrapper beanWrapper = (this.fallBackOnDirectFieldAccess ?
|
||||
new DirectFieldAccessFallbackBeanWrapper(target) : PropertyAccessorFactory.forBeanPropertyAccess(target));
|
||||
|
||||
@@ -343,10 +353,9 @@ public class GraphQlArgumentBinder {
|
||||
bindingResult.rejectArgumentValue(key, value, "invalidPropertyValue", "Failed to set property value");
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private <T> T convertValue(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2023 the original author or authors.
|
||||
* Copyright 2020-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -195,6 +195,16 @@ class GraphQlArgumentBinderTests {
|
||||
assertThat(result).hasFieldOrPropertyWithValue("name", "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mixedConstructorProperties() throws Exception {
|
||||
|
||||
Object result = bind("{\"name\":\"test\", \"age\":30}", ResolvableType.forClass(MixedConstructorPropertiesBean.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(MixedConstructorPropertiesBean.class);
|
||||
assertThat(result).hasFieldOrPropertyWithValue("name", "test")
|
||||
.hasFieldOrPropertyWithValue("age", 30);
|
||||
}
|
||||
|
||||
@Test
|
||||
void primaryConstructorWithBeanArgument() throws Exception {
|
||||
|
||||
@@ -457,6 +467,29 @@ class GraphQlArgumentBinderTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class MixedConstructorPropertiesBean {
|
||||
|
||||
private final String name;
|
||||
|
||||
private int age;
|
||||
|
||||
public MixedConstructorPropertiesBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return this.age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class PrimaryConstructorItemBean {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user