问题
I want to add a BigDecimal
to a list using Spring Expression Language.
public class SpelTest {
public List<BigDecimal> values;
StandardEvaluationContext context;
SpelExpressionParser parser;
@Before
public void setup() {
values = new ArrayList<>();
context = new StandardEvaluationContext(this);
parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
}
@Test
public void shouldChangeValue() {
values.add(BigDecimal.ONE);
parser.parseExpression("values[0]").setValue(context, "123.4");
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // passes
}
@Test
public void shouldAddValue() {
parser.parseExpression("values[0]").setValue(context, "123.4");
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // fails
}
}
Changing the first entry passes but adding an entry fails with
Caused by: java.lang.NoSuchMethodException: java.math.BigDecimal.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3349)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553)
at org.springframework.util.ReflectionUtils.accessibleConstructor(ReflectionUtils.java:185)
at org.springframework.expression.spel.ast.Indexer$CollectionIndexingValueRef.growCollectionIfNecessary(Indexer.java:715)
... 55 more
Not sure why SpEL isn't able to properly initialize a BigDecimal
when the list is empty. Surprisingly I found nothing about this problem.
Thanks for helping!
回答1:
You could avoid this problem by setting the whole list instead of single (not initialized) elements. Instead of
parser.parseExpression("values[0]").setValue(context, "123.4");
use:
parser.parseExpression("values").setValue(context, "123.4");
This also works for multiple elements, quite neat:
parser.parseExpression("values").setValue(context, "123.4, 456.7");
回答2:
The problem is that you activated autoGrowCollections
on the SpelParserConfiguraion
.
Therefore it tries to create an element with a default constructor if you try to access a non-existing element of the collection with the index operator []
. BigDecimal
has no default constructor and because of this, it fails.
What you could do is to create the object in the SpEL itself. E.G.:
@Test
public void shouldAddValue() {
parser.parseExpression("values.add(0, new java.math.BigDecimal(\"123.4\"))").getValue(context);
assertThat(values.size() > 0);
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4));
}
Or you could create a subclass of BigDecimal
witch has a default constructor and use this class.
回答3:
As @Nirud pointed out, the problem is, that BigDecimal
does not have a default constructor. I extended SpEL to add null
to the list, when there is no default constructor. See this pull request: https://github.com/spring-projects/spring-framework/pull/25367
来源:https://stackoverflow.com/questions/62771421/how-to-add-an-object-without-a-constructor-to-a-list-using-spring-expression-lan