Spring SpEL表达式的使用

文章目录

  • 一、SpEL介绍
  • 二、SpEL用法
    • 1. 在@Value注解中使用
    • 2. 在XML配置中使用
    • 3. 在代码中创建Expression对象
  • 三、SpEL原理
    • 1. 解析器:ExpressionParser
    • 2. 表达式:Expression
    • 3. 上下文:EvaluationContext
    • 使用流程
  • 四、表达式语法
    • 1. 基本表达式
      • ① 字面量表达式
      • ② 算数运算表达式
      • ③ 关系运算表达式
      • ④ 逻辑运算表达式
      • ⑤ 字符串连接及截取表达式
      • ⑥ 三目运算
      • ⑦ Elivis表达式
      • ⑧ 正则表达式
    • 2. 类相关表达式
      • ① 类类型
      • ② 类实例
      • ③ instanceof
      • ④ 变量定义及引用
      • ⑤ 赋值
      • ⑥ 自定义函数
      • ⑦ 对象属性获取及安全导航
      • ⑧ 对象方法调用
      • ⑨ Bean引用
    • 3. 集合相关表达式
      • ① 内联数组定义
      • ② 内联集合定义
      • ③ 数组、集合、字典元素访问
      • ④ 数组、集合、字典元素修改
      • ⑤ 数组、集合、字典投影
      • ⑥ 数组、集合、字典选择
    • 4. 其它表达式
      • ① 模板表达式
  • 五、扩展:Java中#{}和${}的使用
    • 1. #{}的使用
    • 2. ${}的使用


一、SpEL介绍

SpEL(Spring Expression Language):Spring表达式语言。

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

依赖配置

    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-expressionartifactId>
      <version>5.1.9.RELEASEversion>
    dependency>

SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用。

二、SpEL用法

常见的三种用法:

  • 在@Value注解中使用
  • 在XML配置中使用
  • 在代码中创建Expression对象,利用Expression对象来执行SpEL

1. 在@Value注解中使用

@Configuration("testConfig11")
public class TestConfig {

    @Bean(name = "user11")
    public User user11() {
        User user = new User();
        user.setId("123");
        return user;
    }
}
@RestController
public class TestController {
    @Value("#{user11.id}")
    private String userId;
}

将Spring容器中name为user11的bean的id属性值赋值给userId。

2. 在XML配置中使用

<bean id="testController " class="com.joker.controller.TestController ">
    <property name="userId" value="#{user11.id}">
</bean>

和@Value类似,将Spring容器中name为user11的bean的id属性值赋值给userId。

3. 在代码中创建Expression对象

    @GetMapping("test4")
    public String test4() {
        // 创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        
        // 得到SpelExpression
        // 使用ctx.setVariable时,需要前缀
        SpelExpression exp = (SpelExpression)parser.parseExpression("#user.id");
        // 使用ctx.setRootObject时,不需要#符号和前缀
        // SpelExpression exp = (SpelExpression)parser.parseExpression("id");
        
        // 创建一个虚拟的容器EvaluationContext
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        // 向容器内添加bean
        User user = new User();
        user.setId("123");
        ctx.setVariable("user", user);
        // ctx.setRootObject(user);
        
        // 得到user对象的id属性值
        String value = (String)exp.getValue(ctx);
        return value;
    }

吃段代码主要为了得到user对象的id属性值。这样的写法看着有点废哈,确实是的,但这只是个用法举例哈。

实际上这种在方式在Spring用的挺广泛的,比如Spring Security的权限校验注解(PreAuthorize、PostAuthorize、PreFilter、PostFilter)都用到了SpEL。

用法举例:

当你想对接口做防止重复提交的拦截,你一般会考虑加锁,锁的key会根据请求中的一些特殊参数来生成。如果直接在每个接口里面写这个生成key和加锁的逻辑,显然很臃肿。

你可以这么做:

  • 定义一个注解,该注解用在接口的方法上
  • 注解里定义一个String[]类型的属性来指定需要用于加锁的字段(SpEL表达式的方式,如:#user.id、#order.id)
  • 使用AOP切面来处理这个注解,得到使用了该注解的接口方法的请求参数
  • 根据注解中String[]类型的属性的值(SpEL表达式)解析出用于生成key的属性值
  • 生成锁key,获取锁
    • 如果拿到锁,调用接口方法执行具体逻辑,然后释放锁;
    • 如果没拿到锁,说明是重复提交,直接返回。

三、SpEL原理

1. 解析器:ExpressionParser

表达式解析器:负责解析表达式字符串,将字符串表达式解析为表达式对象。

接口的方法:

	Expression parseExpression(String expressionString) throws ParseException;

	Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
  • parseExpression:将字符串表达式转化为表达式对象
  • parseExpression:将字符串表达式转化为表达式对象(根据表达式解析模板上下文)

接口的实现:

  • SpelExpressionParser:SpEL解析器。实例是可重用的,且是线程安全的(最常用)。
  • TemplateAwareExpressionParser:模板感知表达式解析器,可以被不提供一流模板支持的表达式解析器子类化
  • InternalSpelExpressionParser:手写的SpEL解析器。 实例是可重用的,但不是线程安全的,是TemplateAwareExpressionParser的子类。

2. 表达式:Expression

表达式:提供getValue方法用于获取表达式值,提供setValue方法用于设置对象值。

接口的方法:


	String getExpressionString();

	Object getValue() throws EvaluationException;

	<T> T getValue(@Nullable Class<T> desiredResultType) throws EvaluationException;

	Object getValue(@Nullable Object rootObject) throws EvaluationException;

	<T> T getValue(@Nullable Object rootObject, @Nullable Class<T> desiredResultType) throws EvaluationException;

	Object getValue(EvaluationContext context) throws EvaluationException;

	Object getValue(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException;

	<T> T getValue(EvaluationContext context, @Nullable Class<T> desiredResultType) throws EvaluationException;

	<T> T getValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Class<T> desiredResultType)
			throws EvaluationException;

	Class<?> getValueType() throws EvaluationException;

	Class<?> getValueType(@Nullable Object rootObject) throws EvaluationException;

	Class<?> getValueType(EvaluationContext context) throws EvaluationException;

	Class<?> getValueType(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException;

	TypeDescriptor getValueTypeDescriptor() throws EvaluationException;

	TypeDescriptor getValueTypeDescriptor(@Nullable Object rootObject) throws EvaluationException;

	TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;

	TypeDescriptor getValueTypeDescriptor(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException;

	boolean isWritable(@Nullable Object rootObject) throws EvaluationException;

	boolean isWritable(EvaluationContext context) throws EvaluationException;

	boolean isWritable(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException;

	void setValue(@Nullable Object rootObject, @Nullable Object value) throws EvaluationException;

	void setValue(EvaluationContext context, @Nullable Object value) throws EvaluationException;

	void setValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Object value) throws EvaluationException;

主要方法

  • getValue:获取表达式值
  • setValue:设置对象值

接口的实现:

  • CompositeStringExpression:复合字符串表达式(里面封装一个表达式对象集合)
  • SpelExpression:SpEL表达式(最常用)
  • LiteralExpression:文字表达式

3. 上下文:EvaluationContext

上下文环境:使用setRootObject方法来设置根对象,使用setVariable方法来注册自定义变量,使用registerFunction来注册自定义函数等。

接口的方法:

	TypedValue getRootObject();

	List<PropertyAccessor> getPropertyAccessors();

	List<ConstructorResolver> getConstructorResolvers();

	List<MethodResolver> getMethodResolvers();

	BeanResolver getBeanResolver();

	TypeLocator getTypeLocator();

	TypeConverter getTypeConverter();

	TypeComparator getTypeComparator();

	OperatorOverloader getOperatorOverloader();

	void setVariable(String name, @Nullable Object value);

	Object lookupVariable(String name);

主要方法:

  • setVariable:注册自定义变量

接口的实现:

  • StandardEvaluationContext:标准上下文(最常用)
  • SimpleEvaluationContext:
  • ThymeleafEvaluationContext
  • MethodBasedEvaluationContext
  • CacheEvaluationContext

使用流程

  1. 创建解析器:创建解析器ExpressionParser(如:SpelExpressionParser)
  2. 解析表达式:使用ExpressionParser的parseExpression来解析表达式得到表达式对象Expression(如:SpelExpression)。
  3. 构造上下文:创建上下文EvaluationContext(如:StandardEvaluationContext),设置需要的数据。
  4. 得到值:通过Expression的getValue方法根据上下文获得表达式值。

四、表达式语法

  • 基本表达式

    • 字面量表达式
    • 算数运算表达式
    • 关系运算表达式
    • 逻辑运算表达式
    • 字符串连接及截取表达式
    • 三目运算
    • Elivis表达式
    • 正则表达式
  • 类相关表达式

    • 类类型
    • 类实例
    • instanceof
    • 变量定义及引用
    • 赋值
    • 自定义函数
    • 对象属获取及安全导航
    • 对象方法调用
    • Bean引用
  • 集合相关表达式

    • 内联数组定义
    • 内联集合定义
    • 数组(集合、字典)元素访问
    • 数组(集合、字典)元素修改
    • 数组(集合、字典)投影
    • 数组(集合、字典)选择
  • 其他表达式

    • 模板表达式

需要注意:SpEL表达式中的关键字是不区分大小写的。

1. 基本表达式

① 字面量表达式

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 字符串
        String str1 = parser.parseExpression("'test'").getValue(String.class);
        String str2 = parser.parseExpression("\"test\"").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+1").getValue(double.class);
        int hex1 = parser.parseExpression("0xf").getValue(Integer.class);
        // 布尔类型
        boolean true1 = parser.parseExpression("true").getValue(boolean.class);
        // null类型
        Object null1 = parser.parseExpression("null").getValue(Object.class);
    }

② 算数运算表达式

SpEL支持:加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)等算数运算。

并且支持用英文替代符号,如:MOD等价%、DIV等价/,且不区分大小写。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        int int1 = parser.parseExpression("3+2").getValue(Integer.class);// 5
        int int2 = parser.parseExpression("3*2").getValue(Integer.class);// 6
        int int3 = parser.parseExpression("3%2").getValue(Integer.class);// 1
        int int4 = parser.parseExpression("3^2").getValue(Integer.class);// 9
    }

③ 关系运算表达式

SpEL支持:等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)、小于等于(<=),区间(between)等关系运算。

“EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”来表示等于、不等于、大于、大于等于、小于、小于等于

并且支持用英文替代符号,如:EQ等价==、NE等价!=、GT等价>、GE等价>=、LT等价<、LE等价<=,且不区分大小写。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        boolean b1 = parser.parseExpression("3==2").getValue(Boolean.class);// false
        boolean b2 = parser.parseExpression("3>=2").getValue(Boolean.class);// true
        boolean b3 = parser.parseExpression("2 between {2, 3}").getValue(Boolean.class);// true
    }

④ 逻辑运算表达式

SpEL支持:或(or)、且(and)、非(!或NOT)。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        boolean b1 = parser.parseExpression("2>1 and false").getValue(Boolean.class);// false
        boolean b2 = parser.parseExpression("2>1 or false").getValue(Boolean.class);// true
        boolean b3 = parser.parseExpression("NOT false and (2>1 and 3>1)").getValue(Boolean.class);// true
    }

⑤ 字符串连接及截取表达式

SpEL支持字符串拼接和字符串截取(目前只支持截取一个字符)。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        String str1 = parser.parseExpression("'hello' + ' java'").getValue(String.class);// hello java
        String str2 = parser.parseExpression("'hello java'[0]").getValue(String.class);// h
        String str3 = parser.parseExpression("'hello java'[1]").getValue(String.class);// e
    }

⑥ 三目运算

SpEL支持三目运算。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        Boolean b1 = parser.parseExpression("3 > 2 ? true : false").getValue(Boolean.class);// true
        System.out.println(b1);
    }

⑦ Elivis表达式

SpEL支持Elivis表达式。

Elivis运算符

表达式格式:表达式1?:表达式2
Elivis运算符是从Groovy语言引入用于简化三目运算符(表达式1? 表达式1:表达式2)的。

  • 当表达式1为非null时则返回表达式1,当表达式1为null时则返回表达式2
    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        String str1 = parser.parseExpression("'a' ?: 'b'").getValue(String.class);// a
        String str2 = parser.parseExpression("null ?: 'b'").getValue(String.class);// b
        Boolean b1 = parser.parseExpression("3 > 2 ?: false").getValue(Boolean.class);// true
        Boolean b2 = parser.parseExpression("null ?: false").getValue(Boolean.class);// false
        Boolean b3 = parser.parseExpression("false ?: true").getValue(Boolean.class);// false
    }

⑧ 正则表达式

SpEL支持正则表达式

具体使用格式:str matches regex,其中str表示需要校验的字符串,regex表示正则表达式。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        Boolean b1 = parser.parseExpression("'123' matches '\\d{3}'").getValue(Boolean.class);// true
        Boolean b2 = parser.parseExpression("'123' matches '\\d{2}'").getValue(Boolean.class);// false
    }

2. 类相关表达式

① 类类型

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

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // java.lang包
        Class class1 = parser.parseExpression("T(String)").getValue(Class.class);
        // 其他包
        Class class2 = parser.parseExpression("T(com.joker.pojo.User)").getValue(Class.class);
        // 类静态字段访问
        int result3 = parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class);
        // 类静态方法调用
        int result4 = parser.parseExpression("T(Integer).parseInt('2')").getValue(int.class);
    }

② 类实例

SpEL支持类实例化,使用java关键字new,类名必须是全限定名,但java.lang包内的类型除外,如String、Integer。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // java.lang包
        String str = parser.parseExpression("new String('str')").getValue(String.class);
        // 其他包
        Date date = parser.parseExpression("new java.util.Date()").getValue(Date.class);
    }

③ instanceof

SpEL支持instanceof运算符,跟Java内使用方法相同。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        Boolean b1 = parser.parseExpression("'haha' instanceof T(String)").getValue(Boolean.class);// true
        Boolean b2 = parser.parseExpression("123 instanceof T(String)").getValue(Boolean.class);// false
        Boolean b3 = parser.parseExpression("123 instanceof T(java.util.Date)").getValue(Boolean.class);// false
    }

④ 变量定义及引用

SpEL支持:

  • 使用#variableName引用通过EvaluationContext接口的setVariable(variableName, value)方法定义的变量;
  • 使用#root引用根对象
  • 使用#this引用当前上下文对象
    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // #variableName
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("name1", "value1");
        String str1 = parser.parseExpression("#name1").getValue(context, String.class);// value1
        User user = new User();
        user.setId("1");
        context = new StandardEvaluationContext(user);
        // #root(#root可以省略)
        String str2 = parser.parseExpression("#root.id").getValue(context, String.class);// 1
        String str3 = parser.parseExpression("id").getValue(context, String.class);// 1
        // #this
        String str4 = parser.parseExpression("#this.id").getValue(user, String.class);// 1
	}

⑤ 赋值

SpEL支持给自定义变量赋值,也允许给根对象赋值,直接使用#variableName=value即可赋值。

  • 使用#variable=value给自定义变量赋值
  • 使用#root=value给根对象赋值
  • 使用#this=value给当前上下文对象赋值
    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // #variableName
        EvaluationContext context = new StandardEvaluationContext();
        User user = new User();
        user.setId("1");
        context.setVariable("name1", user);
        String str1 = parser.parseExpression("#name1.id='2'").getValue(context, String.class);// 2
        context = new StandardEvaluationContext(user);
        // #root(#root可以省略)
        String str2 = parser.parseExpression("#root.id='3'").getValue(context, String.class);// 3
        String str3 = parser.parseExpression("id='4'").getValue(context, String.class);// 4
        // #this
        String str4 = parser.parseExpression("#this.id='5'").getValue(user, String.class);// 5
    }

⑥ 自定义函数

SpEL支持类静态方法注册为自定义函数。SpEL使用StandardEvaluationContext的registerFunction方法进行注册自定义函数(等同于使用setVariable)。

    public static void main(String[] args) throws NoSuchMethodException {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        Method parseInt = Integer.class.getDeclaredMethod("valueOf", String.class);
        context.registerFunction("value", parseInt);
        // 等同于Integer.valueOf("2").byteValue()
        Byte b = parser.parseExpression("#value('2').byteValue()").getValue(context, Byte.class);// 2
    }

⑦ 对象属性获取及安全导航

SpEL支持:

  • 对象属性获取:使用如object.property.property这种点缀式获取
  • 安全导航:SpEL引入了Groovy语言中的安全导航运算符"(对象|属性)?.属性",用来避免"?."前边的表达式为null时抛出空指针异常,而是返回null;修改对象属性值则可以通过赋值表达式或Expression接口的setValue方法修改。
    public static void main(String[] args) throws NoSuchMethodException {
        ExpressionParser parser = new SpelExpressionParser();
        User user = new User();
        user.setId("1");
        StandardEvaluationContext context = new StandardEvaluationContext(user);
        // 对象属性获取
        String str1 = parser.parseExpression("id").getValue(context, String.class);// 1
        String str2 = parser.parseExpression("Id").getValue(context, String.class);// 1
        // 安全导航
        User user1 = parser.parseExpression("#root?.#root").getValue(context, User.class);// {"id":"1"}
        String str3 = parser.parseExpression("id?.#root.id").getValue(context, String.class);// 1
        String str4 = parser.parseExpression("userName?.#root.userName").getValue(context, String.class);// null
    }

注意:

  • SpEL对于属性名首字母是不区分大小写的。
  • 安全导航运算符前面的#root可以省略,但后面的#root不可省略。

⑧ 对象方法调用

SpEL支持对象方法调用,使用方法跟Java语法一样。对于根对象可以直接调用方法。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext("user");
        // 对象方法调用
        String str1 = parser.parseExpression("#root.substring(1, 2)").getValue(context, String.class);// s
        String str2 = parser.parseExpression("substring(1, 2)").getValue(context, String.class);// 1
    }

⑨ Bean引用

SpEL支持使用@符号来引用Bean,在引用Bean时需要使用BeanResolver接口实现来查找Bean,Spring提供BeanFactoryResolver实现。

    @Autowired
    private ApplicationContext applicationContext;

    @GetMapping("test1")
    public ApiResult test1() {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(new BeanFactoryResolver(applicationContext));
        // 获取Spring容器中beanName为systemProperties的bean
        Properties systemProperties = parser.parseExpression("@systemProperties").getValue(context, Properties.class);
        System.out.println(JSON.toJSONString(systemProperties));
        // RedisProperties redisProperties = parser.parseExpression("@spring.redis-org.springframework.boot.autoconfigure.data.redis.RedisProperties").getValue(context, RedisProperties.class);
        // System.out.println(JSON.toJSONString(redisProperties));
        return ApiUtil.success();
    }

注意:特殊的bean名称直接使用@beanName会报错。

比如RedisProperties,它的beanName为spring.redis-org.springframework.boot.autoconfigure.data.redis.RedisProperties,会误解析为:

  • Spring容器的beanName为spring
  • redis-org、springframework、boot、autoconfigure、data、redis、RedisProperties为它的一层层的属性。

这种具体怎么处理暂未研究。

3. 集合相关表达式

注意:SpEL不支持内联字典(Map)定义。

① 内联数组定义

SpEL支持内联数组(Array)定义。但不支持多维内联数组初始化。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 定义一维数组并初始化
        int[] array1 = parser.parseExpression("new int[2]{1,2}").getValue(int[].class);
        System.out.println(JSON.toJSONString(array1));
        // 定义二维数组但不初始化
        String[][] array2 = parser.parseExpression("new String[2][2]").getValue(String[][].class);
        System.out.println(JSON.toJSONString(array2));
        // 定义二维数组并初始化(会报错)
        String[][] array3 = parser.parseExpression("new String[2][2]{{'1','2'},{'3','4'}}").getValue(String[][].class);
        System.out.println(JSON.toJSONString(array3));
    }

② 内联集合定义

SpEL支持内联集合(List)定义。使用{表达式,……}定义内联List,如{1,2,3}将返回一个整型的ArrayList,而{}将返回空的List。

对于字面量表达式列表,SpEL会使用java.util.Collections.unmodifiableList方法将列表设置为不可修改。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 将返回不可修改的空List
        List<Integer> list1= parser.parseExpression("{}").getValue(List.class);
        // 对于字面量列表也将返回不可修改的List
        List<Integer> list2 = parser.parseExpression("{1,2,3}").getValue(List.class);
        //对于列表中只要有一个不是字面量表达式,将只返回原始List(可修改)
        String expression3 = "{{1+2,2+4},{3,4+4}}";
        List<List<Integer>> list3 = parser.parseExpression(expression3).getValue(List.class);
        result3.get(0).set(0, 1);
    }

③ 数组、集合、字典元素访问

SpEL支持集合、字典元素访问。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 数组元素访问
        int[] array1 = {1,2,3};
        Integer int1 = parser.parseExpression("[0]").getValue(array1, int.class);
        // 集合元素访问
        Integer int2 = parser.parseExpression("{1,2,3}[0]").getValue(int.class);
        List<Integer> list1 = Stream.of(1, 2, 3).collect(Collectors.toList());
        Integer int3 = parser.parseExpression("[0]").getValue(list1, Integer.class);
        // 字典元素访问
        Map<String, Integer> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        Integer int4 = parser.parseExpression("['b']").getValue(map, Integer.class);
    }

④ 数组、集合、字典元素修改

SpEL支持数组、集合、字典元素修改。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 数组元素修改
        int[] array = new int[] {1, 2, 3};
        parser.parseExpression("[0] = 3").getValue(array, int.class);
        // 集合元素修改
        List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());
        parser.parseExpression("[0] = 3").getValue(list, Integer.class);
        // 字典元素修改
        Map<String, Integer> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        parser.parseExpression("['b'] = 3").getValue(map, Integer.class);
    }

⑤ 数组、集合、字典投影

SpEL支持数组、集合、字典投影。SpEL根据原集合中的元素中通过选择来构造另一个集合,该集合和原集合具有相同数量的元素。数组和集合类似,字典构造后是集合(不是字典)。

SpEL使用list|map.![投影表达式]来进行投影运算。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 数组投影(#this可省略)
        int[] array = new int[] {1, 2, 3};
        int[] array1 = parser.parseExpression("#root.![#this+1]").getValue(array, int[].class);
        // 集合投影(#this可省略)
        List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());
        List list1 = parser.parseExpression("#root.![#this+1]").getValue(list, List.class);
        // 字典投影(#this可省略)
        Map<String, Integer> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        List list2 = parser.parseExpression("#root.![#this.key+1]").getValue(map, List.class);
        List list3 = parser.parseExpression("#root.![#this.value+1]").getValue(map, List.class);
    }
  • 数组、集合中的#this表示每个元素
  • 字典中的#this表示每个Map.Entry,所有可以用#this.key、#this.value来获取键和值。
  • 代码中.!后面的#this都可以省略,但.!前面的#root不可省略

⑥ 数组、集合、字典选择

SpEL支持数组、集合、字典选择。SpEL根据原集合通过条件表达式选择出满足条件的元素并构造为新的集合。数组和字典类似。

SpEL使用“(list|map).?[选择表达式]”,其中选择表达式结果必须是boolean类型,如果true则选择的元素将添加到新集合中,false将不添加到新集合中。

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        // 数组选择
        int[] array = new int[] {1, 2, 3};
        int[] array1 = parser.parseExpression("#root.?[#this>1]").getValue(array, int[].class);
        // 集合选择
        List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());
        List list1 = parser.parseExpression("#root.?[#this>1]").getValue(list, List.class);
        // 字典选择
        Map<String, Integer> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        Map map1 = parser.parseExpression("#root.?[#this.key!='a']").getValue(map, Map.class);
        Map map2 = parser.parseExpression("#root.?[#this.value>1]").getValue(map, Map.class);
    }

4. 其它表达式

① 模板表达式

SpEL支持模板表达式。模板表达式由字面量与一个或多个表达式块组成。

表达式块由:"前缀+表达式+后缀"形式组成。

SpEL使用ParserContext接口实现来定义表达式是否是模板及前缀和后缀定义。

常见的前缀后缀:#{}${}

模板表达式举例:

  • "${1+2}":前缀${、后缀}、表达式1+2。
  • "Error ${#v0} ${#v1}":字面量Error 、前缀${、后缀}、表达式#v0、表达式#v1,其中v0和v1表示自定义变量,需要在上下文定义。

五、扩展:Java中#{}和${}的使用

1. #{}的使用

  1. SpEL模板表达式

  2. MyBatis中的占位符,以预编译的方式传入参数,可以有效的防止SQL注入

    向占位符输入参数,MyBatis会自动进行Java类型jdbc类型的转换,且不需要考虑参数的类型,例如:传入字符串,MyBatis最终拼接好的SQL就是参数两边加单引号。

2. ${}的使用

  1. SpEL模板表达式

  2. 作用于@Value等注解的属性,用于获取配置文件的配置值

  3. MyBatis的SQL拼接,将参数的内容字符串替换方式拼接在SQL中,可能存在SQL注入的安全隐患

    在用于传入数据库对象,例如传入表名和列名,还有排序时使用order by动态参数时可以使用 ${ } 。

你可能感兴趣的:(Spring,spring,java)