Java: SpEL表达式

发布时间 2023-04-12 22:02:48作者: AaronTanooo

名词解释

SpEL(Spring Expression Language),即Spring表达式语言,能在运行时构建复杂表达式、存取对象属性、对象方法调用等等,并且能与Spring功能完美整合,如能用来配置Bean定义。

实现原理

  1. 创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现
  2. 解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象
  3. 构造上下文:准备比如变量定义等等表达式需要的上下文数据。
  4. 求值:通过Expression接口的getValue方法根据上下文(EvaluationContext,RootObject)获得表达式值。

语法

基本表达式

字面量表达式

    ExpressionParser parser = new SpelExpressionParser();

    String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);
    int int1 = parser.parseExpression("1").getValue(Integer.class);
    long long1 = parser.parseExpression("-1L").getValue(Long.class);
    float float1 = parser.parseExpression("1.1").getValue(Float.class);
    double double1 = parser.parseExpression("1.1E+2").getValue(Double.class);
    int hex1 = parser.parseExpression("0xa").getValue(Integer.class);
    long hex2 = parser.parseExpression("0xaL").getValue(long.class);
    boolean true1 = parser.parseExpression("true").getValue(boolean.class);
    Object null1 = parser.parseExpression("null").getValue(Object.class);

算数运算表达式

  • 加减乘除 + - * /
  • 取余 %
  • 幂 ^

关系表达式

  • 常规 == ; != ; > ; >= ; < ; <=
  • 特殊 between
    运算符右边操作数必须是列表类型,且只能包含2个元素。第一个元素为开始,第二个元素为结束,区间运算是包含边界值的,即 xxx>=list.get(0) && xxx<=list.get(1)
	parser.parseExpression("1 between {1, 2}").getValue(boolean.class);  // true

逻辑表达式

  • && ; || ; !

三目运算表达式

  • 1 > 2 ? true : false -> false

类相关表达式

使用“T(Type)”来表示java.lang.Class实例,“Type”必须是类全限定名,“java.lang”包除外,即该包下的类可以不指定包名;使用类类型表达式还可以进行访问类静态方法及类静态字段。

        ExpressionParser parser = new SpelExpressionParser();

        String expression = "T(Math).random()";
        Double value = parser.parseExpression(expression).getValue(Double.class);

        String dateExpression = "new java.util.Date().toInstant()";
        Instant instant = parser.parseExpression(dateExpression).getValue(Instant.class);

        // 这边#this就是指带传进去的instant实例,把它作为rootObject
        String dateClassExpression = "T(java.util.Date).from(#this)";
        Date date = parser.parseExpression(dateClassExpression).getValue(instant, Date.class);
0.4828331204176707
2023-04-12T12:50:06.429Z
Wed Apr 12 20:50:06 CST 2023

StandardEvaluationContext相关表达式

  • Variable
        Order order = new Order();
        order.setOrderPrice(new OrderPrice(88));

        OrderItem orderItem = new OrderItem();
        orderItem.setId(2);

        StandardEvaluationContext context = new StandardEvaluationContext();
        ExpressionParser parser = new SpelExpressionParser();

        context.setVariable("order", order);
        context.setVariable("orderItem", orderItem);

	// 使用“#variable”来引用在EvaluationContext定义的变量
        String expression = "#order.orderPrice.price * #orderItem.id";
        Integer value = parser.parseExpression(expression).getValue(context, Integer.class);
176
  • RootObject
        StandardEvaluationContext context = new StandardEvaluationContext();
        ExpressionParser parser = new SpelExpressionParser();
		
 	context.setRootObject(this);

	// “#this”引用当前上下文对象,使用“#root”引用根对象
        Object thisObj = parser.parseExpression("#this").getValue(context);
        Object thisRoot = parser.parseExpression("#root").getValue(context);
com.th.test.SpELTest@67d18ed7
com.th.test.SpELTest@67d18ed7
  • BeanResolver
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        Order order = new Order();
        order.setId(55);

        factory.registerSingleton("test", order);

        StandardEvaluationContext context = new StandardEvaluationContext();
        ExpressionParser parser = new SpelExpressionParser();

        context.setBeanResolver(new BeanFactoryResolver(factory));

	// 使用“@”符号来引用Bean
        Integer id = parser.parseExpression("@test.id").getValue(context, Integer.class);
55

集合相关表达式

        ExpressionParser parser = new SpelExpressionParser();

        Collection list = parser.parseExpression("{4,5,6,7}").getValue(Collection.class);
        Integer first = parser.parseExpression("#this[0]").getValue(list, Integer.class);
[4, 5, 6, 7]
4

表达式模版

每个表达式块由“前缀+表达式+后缀”形式组成

        SpelExpressionParser parser = new SpelExpressionParser();

        ParserContext context = new TemplateParserContext("%{", "}");
        Expression expression = parser.parseExpression("Hello, My name is %{#name}, age is %{#age}", context);

        EvaluationContext evaluationContext = new StandardEvaluationContext();
        evaluationContext.setVariable("name", "Aaron");
        evaluationContext.setVariable("age", 23);

        String value = expression.getValue(evaluationContext, String.class);
Hello, My name is Aaron, age is 23

整合Spring替换模版

使用BeanFactoryPostProcessor接口提供postProcessBeanFactory回调方法,它是在IoC容器创建好实例化Bean对象之前被ApplicationContext实现调用,因此在这个阶段把SpEL前缀及后缀修改掉是安全的

    @Component
    public class SpelBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanExpressionResolver beanExpressionResolver = beanFactory.getBeanExpressionResolver();
            if (beanExpressionResolver instanceof StandardBeanExpressionResolver) {
                StandardBeanExpressionResolver resolver = (StandardBeanExpressionResolver) beanExpressionResolver;
                resolver.setExpressionPrefix("%{");
                resolver.setExpressionSuffix("}");
            }
        }
    }