Как добавить объект без конструктора в список с помощью Spring Expression Language

Я хочу добавить BigDecimal в список с помощью 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
    }
}

Изменение первой записи проходит, но добавить запись не удается с

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

Не уверен, почему SpEL не может правильно инициализировать BigDecimalкогда список пуст. Удивительно, но я ничего не нашел об этой проблеме.

Спасибо за помощь!

3 ответа

Решение

Вы можете избежать этой проблемы, установив весь список вместо отдельных (не инициализированных) элементов. Вместо того

parser.parseExpression("values[0]").setValue(context, "123.4");

использование:

parser.parseExpression("values").setValue(context, "123.4");

Это также работает для нескольких элементов, довольно аккуратно:

parser.parseExpression("values").setValue(context, "123.4, 456.7");

Проблема в том, что вы активировали autoGrowCollections на SpelParserConfiguraion. Поэтому он пытается создать элемент с конструктором по умолчанию, если вы пытаетесь получить доступ к несуществующему элементу коллекции с помощью оператора индекса[]. BigDecimal не имеет конструктора по умолчанию и из-за этого не работает.

Что вы можете сделать, так это создать объект в самом SpEL. НАПРИМЕР:

    @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)); 
    }

Или вы можете создать подкласс BigDecimal ведьма имеет конструктор по умолчанию и использует этот класс.

Как отметил @Nirud, проблема в том, что BigDecimalне имеет конструктора по умолчанию. Я расширил SpEL, чтобы добавитьnullв список, когда нет конструктора по умолчанию. См. Этот запрос на перенос: https://github.com/spring-projects/spring-framework/pull/25367

Другие вопросы по тегам