Solon v3.6.0

SnEL 表达式上下文增强和定制

</> markdown

SnEL 的上下文接口为 java.util.function.Function,可以接收 Map::get 作为上下文。

1、基础上下文

Map<String, Object> context = new HashMap<>();
context.put("Math", Math.class);
System.out.println(SnEL.eval("Math.abs(-5) > 4 ? 'A' : 'B'", context));

2、增强上下文

Function 接口有很大的限局,不能接收 Bean 作为上下文。使用 EnhanceContext 可以增强上下文:

  • Map 作为上下文
  • Bean 作为上下文
  • 可以支持虚拟变量 rootthis

使用 map 作上下文

Map<String, Object> map = new HashMap<>();
map.put("Math", Math.class);

System.out.println(SnEL.eval("Math.abs(-5) > 4 ? 'A' : 'B'", new EnhanceContext(map)));

使用 bean 作上下文

User user = new User();

System.out.println(SnEL.eval("userId > 12 ? 'A' : 'B'", new EnhanceContext(user)));
System.out.println(SnEL.eval("root.userId > 12 ? 'A' : 'B'", new EnhanceContext(user)));

使用 root 虚拟变量

System.out.println(SnEL.eval("root ? 'A' : 'B'", new EnhanceContext(true)));

3、上下文定制参考

参考 EnhanceContext 的实现

public class EnhanceContext<T extends Object, Slf extends EnhanceContext> implements Function<String, Object>, TypeGuidance, PropertiesGuidance, ReturnGuidance {
    protected final T target;
    protected final boolean isMap;

    private TypeGuidance typeGuidance = TypeGuidanceUnsafety.INSTANCE;
    private Properties properties;

    private boolean allowPropertyDefault = true;
    private boolean allowPropertyNesting = false;
    private boolean allowTextAsProperty = false;
    private boolean allowReturnNull = false;

    public EnhanceContext(T target) {
        this.target = target;
        this.isMap = target instanceof Map;
    }

    public Slf forProperties(Properties properties) {
        this.properties = properties;
        return (Slf) this;
    }

    public Slf forAllowPropertyDefault(boolean allowPropertyDefault) {
        this.allowPropertyDefault = allowPropertyDefault;
        return (Slf) this;
    }

    public Slf forAllowPropertyNesting(boolean allowPropertyNesting) {
        this.allowPropertyNesting = allowPropertyNesting;
        return (Slf) this;
    }

    public Slf forAllowTextAsProperty(boolean allowTextAsProperty) {
        this.allowTextAsProperty = allowTextAsProperty;
        return (Slf) this;
    }

    public Slf forAllowReturnNull(boolean allowReturnNull) {
        this.allowReturnNull = allowReturnNull;
        return (Slf) this;
    }

    public Slf forTypeGuidance(TypeGuidance typeGuidance) {
        this.typeGuidance = typeGuidance;
        return (Slf) this;
    }

    private Object lastValue;

    @Override
    public Object apply(String name) {
        if (target == null) {
            return null;
        }

        if ("root".equals(name)) {
            return target;
        }

        if ("this".equals(name)) {
            if (lastValue == null) {
                return target;
            } else {
                return lastValue;
            }
        }

        if (isMap) {
            lastValue = ((Map) target).get(name);
        } else {
            PropertyHolder tmp = ReflectionUtil.getProperty(target.getClass(), name);

            try {
                lastValue = tmp.getValue(target);
            } catch (Throwable e) {
                throw new EvaluationException("Failed to access property: " + name, e);
            }
        }

        return lastValue;
    }

    //TypeGuidance
    @Override
    public Class<?> getType(String typeName) throws EvaluationException {
        if (typeGuidance == null) {
            throw new IllegalStateException("The current context is not supported: 'T(.)'");
        } else {
            return typeGuidance.getType(typeName);
        }
    }

    public T getTarget() {
        //方便单测用
        return target;
    }

    public TypeGuidance getTypeGuidance() {
        //方便单测用
        return typeGuidance;
    }

    //PropertiesGuidance
    @Override
    public Properties getProperties() {
        return properties;
    }

    @Override
    public boolean allowPropertyDefault() {
        return allowPropertyDefault;
    }

    @Override
    public boolean allowPropertyNesting() {
        return allowPropertyNesting;
    }

    @Override
    public boolean allowTextAsProperty() {
        return allowTextAsProperty;
    }

    //ReturnGuidance
    @Override
    public boolean allowReturnNull() {
        return allowReturnNull;
    }
}