SpEL(2)

基本表达式

  1. 字面量表达式

    字符串、数字类型(int、long、float、double)、boolean、null类型

类型示例
字符串String str=parser.parseExpression(“‘Hello’”).getValue(String.class);
数字类型int i=parser.parseExpression(“1”).getValue(Integer.class);
int i=parser.parseExpression(“0xf”).getValue(Integer.class);//十六进制
布尔类型boolean b1=parser.parseExpression(“false”).getValue(boolean.class);
boolean b1=parser.parseExpression(“true”).getValue(boolean.class);
null类型Object object=parser.parseExpression(“null”).getValue(Object.class);

2. 算术运算表达式

类型示例
加减乘除int i=parser.parseExpression(“4+2*3/2”).getValue(Integer.class);
求余int i=parser.parseExpression(“4%3”).getValue(Integer.class);
幂运算int i=parser.parseExpression(“2^4”).getValue(Integer.class);

3. 关系表达式

等于(==)、不等于(!=)、大于(>)、小于(<)、区间(between)

between运算符右边操作数必须是列表类型,且只能有2个元素,区间包含边界值,如 boolean b1=parser.parseExpression(“1 between {1,2}”).getValue(boolean.class);
4. 逻辑表达式
与(and)或(or)非(!或者NOT),不支持java的&& 和 ||
5. 字符串连接及截取表达式
使用”+”连接,如“‘hello’+’world’”
使用“‘String’[Index]”截取,但只支持截取一个字符,如“‘hello’[0]”
6. 三目运算及Elivis运算表达式
三目运算:表达式1?表达式2:表达式3
Elivis运算:表达式1?表达式2(表达式1不为null则返回表达式1,否则返回表达式2)
7. 正则表达式
8. 括号优先级表达式

类相关表达式

1)类类型表达式
使用“T(type)”来表示java.lang.Class实例,“Type”必须是类全限定名,java.lang包除外,即除了该包,其他类都需要指定包名;还可以访问静态方法以及静态字段

ClassTypeTest .java

package com.test;

import org.junit.Test;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class ClassTypeTest {

    @Test
    public void test(){
        ExpressionParser parser=new SpelExpressionParser();
        //java.lang包访问
        Class<String> result=parser.parseExpression("T(String)").getValue(Class.class);
        //静态变量
        int result2=parser.parseExpression("T(Integer).MIN_VALUE").getValue(int.class);
        //静态方法
        int result3=parser.parseExpression("T(Integer).parseInt('1')").getValue(int.class);
        System.out.println(result.equals(String.class));
        System.out.println(result2);
        System.out.println(result3);
    }

}

2)类实例化
使用java关键字“new”,类名必须是全限定名

        String result4 =parser.parseExpression("new String('hello')").getValue(String.class);

3)instanceof
4)变量定义以及引用
通过EvaluationContext接口的setVariable(Stirng name,String value)方法定义;在表达式中使用“#name”引用;“#root”使用根对象;“#this”使用上下文对象

ClassTypeTest .java

package com.test;

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

public class ClassTypeTest {

    @Test
    public void test(){
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context=new StandardEvaluationContext();
        context.setVariable("variable", "hello");
        String result =parser.parseExpression("#variable").getValue(context,String.class);
        context=new StandardEvaluationContext("hello");
        String result2 =parser.parseExpression("#root").getValue(context,String.class);
        String result3 =parser.parseExpression("#root").getValue(context,String.class);
        System.out.println(result);
        System.out.println(result2);
        System.out.println(result3);
    }

}

5) 自定义函数
只支持静态方法注册为自定义函数;使用StandardEvaluationContext的setVariable方法进行注册自定义函数;

ClassTypeTest .java

package com.test;

import java.lang.reflect.Method;

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

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context=new StandardEvaluationContext();
        Method parseInt=Integer.class.getDeclaredMethod("parseInt", String.class);
        context.setVariable("parseInt1", parseInt);
        String result =parser.parseExpression("#parseInt1('1')").getValue(context,String.class);
        System.out.println(result);

    }

}

6)赋值表达式
SpEL允许给自定义变量赋值也允许给对象赋值,使用“#name=value”可以赋值

package com.test;

import java.lang.reflect.Method;

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

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context=new StandardEvaluationContext("root or this(context)");
        context.setVariable("variable", "variable");
        String result=parser.parseExpression("#variable='value'").getValue(context,String.class);
        String result2=parser.parseExpression("#variable=#root").getValue(context,String.class);
        String result3=parser.parseExpression("#variable=#this").getValue(context,String.class);
        System.out.println(result);
        System.out.println(result2);
        System.out.println(result3);

    }

}

7)对象属性存取以及安全导航表达式
- 取值方式:”对象.属性.属性“
- SpEL属性首字母不区分大小写
- SpEL引入了Grooby语言中的安全导航运算符“(对象|属性)?.属性”,“?”前的表达式若为NULL则抛出异常并且返回null
- 修改属性值可以通过赋值表达式或者Expression的SetValue方法

package com.test;

import java.lang.reflect.Method;
import java.util.Date;

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

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        Date date =new Date();
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context=new StandardEvaluationContext(date);
        //1访问上下文对象的属性或者方法可以直接使用属性名或者方法名
        int result=parser.parseExpression("year").getValue(context,int.class);
        //2给属性赋值
        parser.parseExpression("year").setValue(context, 123);
        int result2=parser.parseExpression("year").getValue(context,int.class);
        //3安全访问
        context=new StandardEvaluationContext();
        Object result3=parser.parseExpression("#root?.year").getValue(context,Object.class);

        System.out.println(date.getYear());
        System.out.println(result);
        System.out.println(result2);
        System.out.println(result3);


    }

}

打印结果

123
115
123
null

8)对象方法调用
和java语法一样

 package com.test;

import java.lang.reflect.Method;
import java.util.Date;

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

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        Date date =new Date();
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context=new StandardEvaluationContext(date);

        String result=parser.parseExpression("'hello'.indexOf('l')").getValue(context,String.class);
        //根对象可直接调用方法
        int result2=parser.parseExpression("getYear()").getValue(context,int.class);

        System.out.println(date.getYear());
        System.out.println(result);
        System.out.println(result2);


    }

}

9)bean引用
用“@beanName”来引用bean,引用时需要使用BeanResolver接口来查找bean,Spring提供BeanFactoryResolver来实现

ResourceUtil .java

package com.resource;

import java.io.IOException;
import java.io.InputStream;

import org.springframework.core.io.Resource;

public class ResourceUtil {
private static InputStream is=null;
public static void stream(Resource resource ){
    try {
        //获取文件资源输入流
        is=resource.getInputStream();
        //读取资源
        byte[] byteArray=new byte[is.available()];
        is.read(byteArray);
        System.out.println(new String(byteArray));
    } catch (IOException e) {
        e.printStackTrace();
    }
    finally{
        try {
        //关闭流
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
}

ResourceBean .java

package com.resource;


import org.springframework.core.io.Resource;

public class ResourceBean {
    private Resource resource;
    public Resource getResource() {
        return resource;
    }

    public void setResource(Resource resource) {
        this.resource = resource;
    }

}

applicationContextResource.xml

       <bean id="resourceBean2" class="com.resource.ResourceBean">
       <property name="resource" value="classpath:applicationContextResource.xml"/>        
       </bean>

测试类

package com.test;

import org.junit.Test;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import com.resource.ResourceBean;
import com.resource.ResourceUtil;

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        StandardEvaluationContext context=new StandardEvaluationContext();
        ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContextResource.xml");
        ctx.refresh();
        context.setBeanResolver(new BeanFactoryResolver(ctx));
        ResourceBean result=parser.parseExpression("@resourceBean2").getValue(context,ResourceBean.class);
        ResourceUtil.stream(result.getResource());
    }

}

集合相关表达式

1)内联List
“{1,2,3}”将返回一个整形的ArrayList,而“{}”将返回一个空的List,对于字面量表达式列表,SpEL会使用java.util.Collections.unmodifiableList方法将列表设置为不可以修改


public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        List<Integer> list1=parser.parseExpression("{1,2,3}").getValue(List.class);
        List<Integer> list2=parser.parseExpression("{}").getValue(List.class);
        for(Integer i:list1){
            System.out.println(i);
        }
        System.out.println(list2.size());

        try {
            //不可修改
            list2.set(0, 4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

只要列表中有一个不是字面量表达式,将返回原始List,并不会作不可修改处理

package com.test;


import java.util.List;

import org.junit.Test;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        List<Integer> list1=parser.parseExpression("{1,2+1,3}").getValue(List.class);


        try {
            //可修改
            list1.set(0, 4);
            for(Integer i:list1){
                System.out.println(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

2)内联数组
只在定义时进行多维数组初始化

        int[][][] arr=parser.parseExpression("new int[1][2][3]").getValue(int[][][].class);

3)集合和map的访问
使用“集合[索引]”访问集合元素,使用“map[key]”访问字典元素

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context =new StandardEvaluationContext();
        List<Integer> list=new ArrayList<Integer>();
        Map<String, String> map=new HashMap<String, String>();
        list.add(0, 1);
        map.put("hello", "girl");
        context.setVariable("list", list);
        context.setVariable("map", map);
        int i=parser.parseExpression("#list[0]").getValue(context,int.class);
        String str=parser.parseExpression("#map['hello']").getValue(context,String.class);
        System.out.println(i);
        System.out.println(str);


    }

4)列表、map、数组元素修改
可以使用赋值表达式或者Expression接口的setValue方法修改

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context =new StandardEvaluationContext();
        List<Integer> list=new ArrayList<Integer>();
        Map<String, String> map=new HashMap<String, String>();
        list.add(0, 1);
        map.put("hello", "girl");
        context.setVariable("list", list);
        context.setVariable("map", map);
        int i=parser.parseExpression("#list[0]=2").getValue(context,int.class);
        String str=parser.parseExpression("#map['hello']='boy'").getValue(context,String.class);
        System.out.println(i);
        System.out.println(str);
        parser.parseExpression("#list[0]").setValue(context,3);
        parser.parseExpression("#map['hello']").setValue(context,"boys and girls");
        i=parser.parseExpression("#list[0]").getValue(context,int.class);
        str=parser.parseExpression("#map['hello']").getValue(context,String.class);
        System.out.println(i);
        System.out.println(str);

    }

5)集合投影
SpEL的集合投影指的是根据集合中的元素通过选择来构造另外一个集合,该集合和原集合具有相同数量的元素;SpEL使用”(List|Map).![投影表达式]”来进行投影

public class ClassTypeTest {

    @Test
    public void test()throws Exception{
        ExpressionParser parser=new SpelExpressionParser();
        EvaluationContext context =new StandardEvaluationContext();
        List<Integer> list=new ArrayList<Integer>();
        list.add(0,1);
        list.add(1,2);
        context.setVariable("list", list);
        List<Integer> list2=parser.parseExpression("#list.![#this+1]").getValue(context,List.class);
        for(Integer i:list2){
            System.out.println(i);
        }


    }

}

在投影表达式中”#this”表示每个集合或者数组的元素

public class ClassTypeTest {

    @Test
    public void test() throws Exception {
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();
        Map<String, String> map = new HashMap<String, String>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        context.setVariable("map", map);
        List<String> list2 = parser.parseExpression("#map.![key+'k']").getValue(context, List.class);
        for (String str : list2) {
            System.out.println(str);
        }
        List<String> list3 = parser.parseExpression("#map.![value+'v']").getValue(context, List.class);
        for (String str : list3) {
            System.out.println(str);
        }

    }

}

map的投影是List集合,”#this”是map.Entry,所以可以用“value”来获取值,“key”来获取键

6)集合选择
SpEL根据原集合通过条件表达式选择出满足条件的元素并构造为新的集合;使用“(List|Map).?[选择表达式]”,表达式的结果必须是boolean类型,true则添加进新的集合中

public class ClassTypeTest {

    @Test
    public void test() throws Exception {
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();
        Map<String, String> map = new HashMap<String, String>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        context.setVariable("map", map);
        Map<String, String> map2 = parser.parseExpression("#map.?[key.contains('1')]").getValue(context, Map.class);        
        System.out.println(map2.size());
        System.out.println(map2.get("k1"));
        Map<String, String> map3 = parser.parseExpression("#map.?[!value.contains('2')]").getValue(context, Map.class);        
        System.out.println(map3.size());
        System.out.println(map3.get("k1"));

    }

}

注意和集合投影不一样,集合选择map返回的map

表达式模板

SpEL(1)中的ParserContext接口实现来定义表达式是否为模板以及前缀后缀的定义

### SpEL 使用指南 Spring Expression Language (SpEL) 是 Spring 框架中用于操作和访问对象图的一种强大表达式语言。它支持运行时查询和操作对象图,具有高度的灵活性和可扩展性。SpEL 可以在多种场景中使用,例如配置、条件判断、缓存控制等。 #### 1. 基本语法 SpEL 的表达式通常以 `#{...}` 的形式出现,可以在 Spring 的 XML 配置文件或注解中使用。例如: ```java @Value("#{systemProperties['user.name']}") private String userName; ``` 该表达式从系统属性中获取 `user.name` 的值,并将其注入到 `userName` 字段中[^1]。 #### 2. 字面量 SpEL 支持多种字面量类型,包括字符串、数字、布尔值等。例如: ```java @Value("#{'Hello, World!'}") private String greeting; ``` 该表达式将字符串 `"Hello, World!"` 注入到 `greeting` 字段中[^1]。 #### 3. 运算符 SpEL 支持常见的算术运算符、比较运算符和逻辑运算符。例如: ```java @Value("#{2 + 3 * 4}") private int result; ``` 该表达式计算 `2 + 3 * 4` 的结果,并将其注入到 `result` 字段中。 #### 4. 方法调用 SpEL 允许调用对象的方法。例如: ```java @Value("#{T(java.util.Arrays).asList('a', 'b', 'c')}") private List<String> list; ``` 该表达式调用 `Arrays.asList` 方法,创建一个包含 `'a'`、`'b'` 和 `'c'` 的列表,并将其注入到 `list` 字段中。 #### 5. 属性访问 SpEL 支持通过点号运算符访问对象的属性。例如: ```java @Value("#{someBean.someProperty}") private String someProperty; ``` 该表达式从 `someBean` 对象中获取 `someProperty` 属性的值,并将其注入到 `someProperty` 字段中。 #### 6. 条件表达式 SpEL 支持三元运算符,用于条件判断。例如: ```java @Value("#{someBean.someValue > 10 ? 'Greater than 10' : 'Less than or equal to 10'}") private String conditionResult; ``` 该表达式根据 `someBean.someValue` 的值进行条件判断,并将结果注入到 `conditionResult` 字段中[^1]。 #### 7. 集合操作 SpEL 支持对集合进行操作,例如过滤和投影。例如: ```java @Value("#{someBean.listOfNumbers.?[#this > 5]}") private List<Integer> filteredList; ``` 该表达式从 `someBean.listOfNumbers` 中筛选出大于 5 的元素,并将其注入到 `filteredList` 字段中。 #### 8. 异常处理 SpEL 支持通过 `?:` 运算符处理异常。例如: ```java @Value("#{someBean.someMethod() ?: 'Default Value'}") private String valueWithDefault; ``` 如果 `someBean.someMethod()` 抛出异常,则注入 `'Default Value'` 到 `valueWithDefault` 字段中。 --- ### 常见问题及解决方案 #### 1. 表达式无法解析 **问题描述:** 在使用 SpEL 表达式时,可能会遇到 `ExpressionParseException` 异常,表示表达式无法解析。 **解决方案:** 检查表达式的语法是否正确,确保所有操作符和方法调用都符合 SpEL 的规范。 #### 2. 方法调用失败 **问题描述:** 在调用对象的方法时,可能会遇到 `MethodInvocationException` 异常。 **解决方案:** 确保调用的方法存在且具有正确的参数类型。如果方法需要参数,确保在表达式中提供正确的参数值。 #### 3. 集合操作性能问题 **问题描述:** 在对大型集合进行操作时,可能会遇到性能问题。 **解决方案:** 考虑在数据源端进行过滤或分页,避免在 SpEL 表达式中处理大量数据。 #### 4. 条件表达式逻辑错误 **问题描述:** 在使用条件表达式时,可能会出现逻辑错误,导致预期外的结果。 **解决方案:** 仔细检查条件表达式的逻辑,确保条件判断的顺序和逻辑正确。 --- ### 示例代码 以下是一个完整的示例,展示如何在 Spring 中使用 SpEL 表达式: ```java import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class SpELExample { @Value("#{systemProperties['user.name']}") private String userName; @Value("#{2 + 3 * 4}") private int result; @Value("#{T(java.util.Arrays).asList('a', 'b', 'c')}") private List<String> list; @Value("#{someBean.someProperty}") private String someProperty; @Value("#{someBean.someValue > 10 ? 'Greater than 10' : 'Less than or equal to 10'}") private String conditionResult; @Value("#{someBean.listOfNumbers.?[#this > 5]}") private List<Integer> filteredList; @Value("#{someBean.someMethod() ?: 'Default Value'}") private String valueWithDefault; // Getters and setters } ``` --- ### 调试技巧 1. **日志输出:** 在调试 SpEL 表达式时,可以通过日志输出表达式的结果,帮助定位问题。 2. **单元测试:** 编写单元测试来验证 SpEL 表达式的行为,确保其按预期工作。 3. **表达式评估:** 使用 Spring 的 `ExpressionParser` 手动评估表达式,检查其输出是否符合预期。 --- ### 总结 SpEL 是 Spring 框架中一个非常强大的工具,能够简化配置和逻辑处理。通过合理使用 SpEL,可以提高代码的灵活性和可维护性。然而,需要注意表达式的复杂性和性能影响,确保在实际应用中平衡功能和效率。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值