spring 表达式 (SpEL) 是否支持在投影集合对象的字段上设置值?

Is setting value on fields of objects of projected collection supported in spring expression (SpEL)?

spring 表达式 (SpEL) 是否支持投影集合对象的设置值?

我在尝试 运行 示例代码时遇到以下错误。

Exception in thread "main" org.springframework.expression.spel.SpelEvaluationException: EL1068E:(pos 1310742): the expression component '![comment]' is not assignable

我想做的是 - 为符合特定条件的所有对象设置一个值。 (下面的例子可能不符合最佳实践,因为我试图保持简单和快速)

package com.test;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.ArrayList;
import java.util.List;

public class SpELTest {

    public static class Person {
        public String name;
        public int age;
        public String comment;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", comment='" + comment + '\'' +
                '}';
        }
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Person 1", 10));
        people.add(new Person("Person 2", 20));
        people.add(new Person("Person 3", 30));
        people.add(new Person("Person 4", 40));
        people.add(new Person("Person 5", 50));
        people.add(new Person("Person 6", 60));
        people.add(new Person("Person 7", 70));
        people.add(new Person("Person 8", 80));

        ExpressionParser parser = new SpelExpressionParser();

        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable("people", people);

        // check the projected
        List<String> projected = (List<String>) parser.parseExpression("#people.?[age > 50].![name]").getValue(context);
        for (String p : projected) {
            System.out.println(p);
        }

        // use expression to set values
        parser.parseExpression("#people.?[age > 50].![comment]").setValue(context, "older than 50");

        for (Person p : people) {
            System.out.println(p);
        }
    }

}

输出:

Person 6
Person 7
Person 8
Exception in thread "main" org.springframework.expression.spel.SpelEvaluationException: EL1068E:(pos 1310742): the expression component '![comment]' is not assignable
    at org.springframework.expression.spel.ast.ValueRef$TypedValueHolderValueRef.setValue(ValueRef.java:78)
    at org.springframework.expression.spel.ast.CompoundExpression.setValue(CompoundExpression.java:87)
    at org.springframework.expression.spel.standard.SpelExpression.setValue(SpelExpression.java:169)
    at com.test.SpELTest.main(SpELTest.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:88)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
    at java.lang.reflect.Method.invoke(Method.java:613)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

您的代码中有两个问题。

首先 - 你不能对一些简单的投影进行赋值 属性。

让我们考虑以下 class:

class Person {
    String name
}

可以通过

更改Person的名字
person.name = "newValue"

但是你想做的是:

String projection = person.name
projection = "newValue"

这行不通。

第二件事是您不能将值 older than 50 分配给集合 (List)。 你可以这样做:

parser.parseExpression("(#people.?[age > 50 and comment == null])[0].comment")
      .setValue(context, "older than 50");

但这只会更改结果投影中的第一项。

SpEL 没有简单的方法可以做到这一点,但您可以通过 context.registerFunction 注册一些外部函数来使用它们。这样的函数可以接受 Person 对象的集合并对它们执行变异。