柏竹 柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档

柏竹

奋斗柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档
  • Java基础

  • JavaWeb

  • 拓展技术

    • Java设计模式
    • Java序列化
    • Stream流
    • Lombok简化开发应用
    • JavaZip解压缩
    • Lua应用
    • OpenResty
    • C++快速上手
    • PHP快速上手
    • SpEL表达式
      • 前言
      • SpEL基础
        • ExpressionParser接口
        • 自定义模板
        • EvaluationContext接口
        • Expression接口
      • SpEL语法
        • 基本表达式
        • 运算表达式
        • 关系表达式
        • 逻辑表达式
        • 其他表达式
      • Java代码表达式解析
        • 类类型表达式
        • 类实例化
        • instanceof表达式
        • 变量定义及引用
        • 自定义函数
        • 变量赋值
        • 安全导航表达式
        • Bean引用
        • 集合应用
        • List
        • Map
        • 集合选择
    • velocity模板引擎
    • Flyway数据库版本管理工具
    • 企业项目实战问题
  • 框架技术

  • 数据库

  • 数据结构

  • Spring

  • SpringMVC

  • SpringBoot

  • SpringClound

  • Ruoyi-Vue-Plus

  • 后端
  • 拓展技术
柏竹
2023-10-10
目录

SpEL表达式转载

# 前言

自从学了 SpEL表达式语言 , 我发现 字符串 简直是万能类型 , 不仅能存 , 能定义 , 能访问 , 能调用 等 , 一应俱全 , 非常强大 . 特别是搭配SpringBoot常用的注解上 , 能够实现很多高级逻辑功能 , 仅通过简短的表达式解决了大多核心功能! 非常值得学习的一个知识点!!

转载文章 : https://zhuanlan.zhihu.com/p/174786047 (opens new window)

# SpEL基础

SpEL在求表达式值时一般分为四步 :

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

应用示例 :

public static void main(String[] args) {
    SpelExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression("('Hello'+'World').concat(#end)");
    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setVariable("end","!");
    String value = expression.getValue(context, String.class);
    System.out.println("value = " + value);
    System.out.println("original = " + "HelloWorld".concat("!"));
}

/* 结果
value = HelloWorld!
original = HelloWorld!
*/

# ExpressionParser接口

解析器 , 意图解析表达式

public interface ExpressionParser {
	Expression parseExpression(String expressionString) throws ParseException;

	/**
	 * 分析表达式字符串并返回可用于重复计算的表达式对象。
	 * 形参:
	 * expressionString – 要解析的原始表达式字符串 
	 * context – 影响此表达式解析例程的上下文(可选)
	 * 返回值: 已分析的表达式
	 */
	Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}

# 自定义模板

ParserContext是标识表达式解析的前后模板 , 如 : 前缀 #{ , 后缀 }

// 定义1
ParserContext parserContext = new ParserContext() {
    @Override
    public boolean isTemplate() {
        return true;
    }

    @Override
    public String getExpressionPrefix() {
        return "#{";
    }

    @Override
    public String getExpressionSuffix() {
        return "}";
    }
};
// 定义2
ParserContext parserContext = new TemplateParserContext("%{","}")

# EvaluationContext接口

构造上下文 , 意图 为表达式自定义变量赋值

核心方法 :

  • setRootObject() : 设置根对象
  • setVariable() : 设置自定义变量
点击展开代码
public interface EvaluationContext {

	/**
	 * 返回默认的根上下文对象,应根据该对象解析非限定属性/方法/等。计算表达式时可以覆盖此字段。
	 */
	TypedValue getRootObject();

	/**
	 * 返回将依次要求读取/写入属性的访问器列表。
	 */
	List<PropertyAccessor> getPropertyAccessors();

	/**
	 * 返回一个解析器列表,这些解析器将依次被要求查找构造函数。
	 */
	List<ConstructorResolver> getConstructorResolvers();

	/**
	 * 返回解析程序的列表,这些解析程序将依次被要求查找方法。
	 */
	List<MethodResolver> getMethodResolvers();

	/**
	 * 返回一个可以按名称查找 bean 的 Bean 解析器。
	 */
	@Nullable
	BeanResolver getBeanResolver();

	/**
	 * 返回可用于按短名称或完全限定名查找类型的类型定位器。
	 */
	TypeLocator getTypeLocator();

	/**
	 * 返回一个类型转换器,该转换器可以将值从一种类型转换(或强制)到另一种类型。
	 */
	TypeConverter getTypeConverter();

	/**
	 * 返回一个类型比较器,用于比较对象对的相等性。
	 */
	TypeComparator getTypeComparator();

	/**
	 * 返回一个运算符重载器,该运算符重载器可能支持超过标准类型集之间的数学运算。
	 */
	OperatorOverloader getOperatorOverloader();

	/**
	 * 将指定 Supplier 创建的值分配给此评估上下文中的命名变量。
	 * 与 setVariable(String, Object) 相反,此方法只应用于支持表达式中的赋值运算符 (=)。
	 * 形参:
	 * 	name – 要分配的变量的名称
     *  valueSupplier – 要分配给变量的值的供应商
	 * 返回值: TypedValue 包装分配的值
	 */
	default TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
		TypedValue typedValue = valueSupplier.get();
		setVariable(name, typedValue.getValue());
		return typedValue;
	}

	/**
	 * 将此评估上下文中的命名变量设置为指定值。
	 * 与 assignVariable(String, Supplier) 相反,此方法只应在直接与 EvaluationContext 交互时以编程方式调用 
	 * 形参:
	 *  name – 要设置的变量的名称 value – 要放置在变量中的值
	 */
	void setVariable(String name, @Nullable Object value);

	/**
	 * 在此评估上下文中查找命名变量。
	 * 形参:
	 *   name – 要查找的变量的名称
	 * 返回值: 变量的值,或者 null 如果未找到
	 */
	@Nullable
	Object lookupVariable(String name);

}

# Expression接口

表达式对象 , 意图 执行表达式得到的结果(求值)

点击展开代码
public interface Expression {

	String getExpressionString();

	@Nullable
	Object getValue() throws EvaluationException;

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

	@Nullable
	Object getValue(@Nullable Object rootObject) throws EvaluationException;

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

	@Nullable
	Object getValue(EvaluationContext context) throws EvaluationException;

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

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

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

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

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

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

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

	@Nullable
	TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
    
	@Nullable
	TypeDescriptor getValueTypeDescriptor(@Nullable Object rootObject) throws EvaluationException;

	@Nullable
	TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;

	@Nullable
	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;

}

应用示例 :

@Test
void test() {
    SpelExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression("HelloWorld%{#end}",new TemplateParserContext("%{","}"));
    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setVariable("end","!!");
    String value = expression.getValue(context, String.class);
    System.out.println("value = " + value);
    System.out.println("original = " + "HelloWorld".concat("!!"));
}
/* 结果
value = HelloWorld!!
original = HelloWorld!
*/

# SpEL语法

# 基本表达式

SpEL支持的字面量包括:字符串、数字类型(int、long、float、double)、布尔类型、null类型

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();

    System.out.println("'Hello World!' = " + 
                       parser.parseExpression("'Hello World!'").getValue(String.class));
    System.out.println("1 = " + parser.parseExpression("1").getValue(Integer.class));
    System.out.println("-1L = " + parser.parseExpression("-1L").getValue(long.class));
    System.out.println("1.1 = " + parser.parseExpression("1.1").getValue(Float.class));
    System.out.println("1.1E+2 = " + parser.parseExpression("1.1E+2").getValue(double.class));
    System.out.println("0xa = " + parser.parseExpression("0xa").getValue(Integer.class));
    System.out.println("0xaL = " + parser.parseExpression("0xaL").getValue(long.class));
    System.out.println("true = " + parser.parseExpression("true").getValue(boolean.class));
    System.out.println("false = " + parser.parseExpression("false").getValue(boolean.class));
    System.out.println("null = " + parser.parseExpression("null").getValue(Object.class));
}
/* 结果
'Hello World!' = Hello World!
1 = 1
-1L = -1
1.1 = 1.1
1.1E+2 = 110.0
0xa = 10
0xaL = 10
true = true
false = false
null = null
*/

# 运算表达式

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

点击展开代码
@Test
void test() {
    SpelExpressionParser parser = new SpelExpressionParser();
    System.out.println("1+1 = " + parser.parseExpression("1+1").getValue(Integer.class));;
    System.out.println("2-1 = " + parser.parseExpression("2-1").getValue(Integer.class));;
    System.out.println("2*2 = " + parser.parseExpression("2*2").getValue(Integer.class));;
    System.out.println("4/2 = " + parser.parseExpression("4/2").getValue(Integer.class));;
    System.out.println("4%3 = " + parser.parseExpression("4%3").getValue(Integer.class));;
    System.out.println("2^2 = " + parser.parseExpression("2^2").getValue(Integer.class));;
}
/* 结果
1+1 = 2
2-1 = 1
2*2 = 4
4/2 = 2
4%3 = 1
2^2 = 4
*/

# 关系表达式

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

SpEL也支持单词缩写应用 : (支持小写)

  • 等于 : EQ : ==
  • 不等于 : NE : !=
  • 大于 : GT : >
  • 大于等于 : GE : >=
  • 小于 : LT : <
  • 小于等于 : LE : <=
点击展开代码
@Test
void test() {
    SpelExpressionParser parser = new SpelExpressionParser();
    System.out.println("1<2 = " + 
        parser.parseExpression("1<2").getValue(Boolean.class));
    System.out.println("1 GT 2 (大于)= " + 
        parser.parseExpression("1 GT 2").getValue(Boolean.class));
    System.out.println("1!=2 = " + 
        parser.parseExpression("1!=2").getValue(Boolean.class));
    System.out.println("1 eq 2 (等于)= " + 
        parser.parseExpression("2 eq 2").getValue(Boolean.class));
    System.out.println("1 between {1,3} = " + 
        parser.parseExpression("2 between {1,3}").getValue(Boolean.class));
    System.out.println("6 between {1,3} = " + 
        parser.parseExpression("4 between {1,3}").getValue(Boolean.class));
}
/* 结果
1<2 = true
1 GT 2 (大于)= false
1!=2 = true
1 eq 2 (等于)= true
1 between {1,3} = true
6 between {1,3} = false
*/

# 逻辑表达式

且(and / &&)、或(or / ||)、非(! / NOT)

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();

    System.out.println("2>1 and (!true or !false) = " + 
        parser.parseExpression("2>1 and (!true or !false)").getValue(boolean.class));
    System.out.println("2>1 && (!true || !false) = " + 
        parser.parseExpression("2>1 && (!true || !false)").getValue(boolean.class));
    System.out.println("2>1 and (NOT true or NOT false) = " + 
        parser.parseExpression("2>1 and (NOT true or NOT false)").getValue(boolean.class));
    System.out.println("2>1 && (NOT true || NOT false) = " + 
        parser.parseExpression("2>1 && (NOT true || NOT false)").getValue(boolean.class));
}
/* 结果
2>1 and (!true or !false) = true
2>1 && (!true || !false) = true
2>1 and (NOT true or NOT false) = true
2>1 && (NOT true || NOT false) = true
*/

# 其他表达式

  • 三目运算
  • 正则表达
  • 括号优先表达式
点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();

    // 三目运算
    System.out.println("2>1?true:false = " +
        parser.parseExpression("2>1?true:false").getValue(boolean.class));
    // 简化三目运算符
    System.out.println("1>2?:true = " +
        parser.parseExpression("1>2?:true").getValue(boolean.class));
    // 正则表达式
    System.out.println("'abc'.matches('[a-z]+') = " +
        parser.parseExpression("'abc'.matches('[a-z]+')").getValue(boolean.class));
    System.out.println("'123' matches '\\d{3}' = " +
        parser.parseExpression("'123' matches '\\d{3}' ").getValue(boolean.class));
    // 括号优先
    System.out.println("(2+3)*(2+4) = " +
        parser.parseExpression("(2+3)*(2+4)").getValue());
    System.out.println("((2*2)+(2*4))+((3*2)+(3*4)) = " +
        parser.parseExpression("((2*2)+(2*4))+((3*2)+(3*4))").getValue());
}
/* 结果
2>1?true:false = true
1>2?:true = false
'abc'.matches('[a-z]+') = true
'123' matches '\d{3}' = true
(2+3)*(2+4) = 30
((2*2)+(2*4))+((3*2)+(3*4)) = 30
*/

# Java代码表达式解析

# 类类型表达式

使用 T(Type) 来表示 Class对象实例 , Type 必须是全限定类名 , 出了基础类型的包路径外 , 使用类表达式同时还可以访问静态资源(成员 变量/方法)

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();

    // 访问基础类型
    System.out.println("T(String) = " +
        parser.parseExpression("T(String)"));

    // 访问其他类 (其他类必须全限定名)
    System.out.println("T(com.ruoyi.test.SpelTest) = " +
        parser.parseExpression("T(com.ruoyi.test.SpelTest)"));

    // 静态成员访问 , 实际 Integer.MAX_VALUE
    System.out.println("T(Integer).MAX_VALUE = " +
        parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class));

    // 静态方法访问 , 实际 Integer.paraseInt('1')
    System.out.println("T(Integer).paraseInt('1') = " +
        parser.parseExpression("T(Integer).parseInt('1')").getValue(int.class));
}
/* 结果
T(String) = org.springframework.expression.spel.standard.SpelExpression@2a32de6c
T(com.ruoyi.test.SpelTest) = org.springframework.expression.spel.standard.SpelExpression@7692d9cc
T(Integer).MAX_VALUE = 2147483647
T(Integer).paraseInt('1') = 1
*/

# 类实例化

类实例化 , 通过Java new 关键字实现 , 类名要全限定名 , 除java基础类型外

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();
    System.out.println("new String('路人甲java') = " +
        parser.parseExpression("new String('路人甲java')").getValue(String.class));

    System.out.println("new java.util.Date() = " +
        parser.parseExpression("new java.util.Date()").getValue(Date.class));
}
/* 结果
new String('路人甲java') = 路人甲java
new java.util.Date() = Fri Oct 13 10:55:55 CST 2023
*/

# instanceof表达式

和Java原生用法同理 , 但需要借助 T(Type) 识别类型 , 不能使用 .class 识别

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();
    System.out.println("'路人甲' instanceof T(String) = " +
        parser.parseExpression("'路人甲' instanceof T(String)").getValue(Boolean.class));

    System.out.println("new com.ruoyi.test.SpelTest() instanceof T(com.ruoyi.test.SpelTest) = " +
        parser.parseExpression("new com.ruoyi.test.SpelTest() instanceof T(com.ruoyi.test.SpelTest)").getValue(Boolean.class));
}
/* 结果
'路人甲' instanceof T(String) = true
new com.ruoyi.test.SpelTest() instanceof T(com.ruoyi.test.SpelTest) = true
*/

# 变量定义及引用

在模板中可以实现变量定义 , 通过 EvaluationContext.setVariable()方法 实现

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("num1", 1);
    context.setVariable("num2", 2);

    System.out.println("#num1 + #num2 = " +
        parser.parseExpression("#num1 + #num2").getValue(context, Integer.class));

    //StandardEvaluationContext构造器传入root对象,可以通过#root来访问root对象
    String root = new String("我是root对象");
    context = new StandardEvaluationContext(root);
    System.out.println("#root = " +
        parser.parseExpression("#root").getValue(context, String.class));

    //#this用来访问当前上线文中的对象
    System.out.println("#this = " +
        parser.parseExpression("#this").getValue(context, String.class));
}
/* 结果
#num1 + #num2 = 3
#root = 我是root对象
#this = 我是root对象
*/

# 自定义函数

SpEL也可以实现像引入变量一样使用函数 , 但目前仅支持 静态方法 . 通过 EvaluationContext.registerFunction()方法 进行注册自定义函数 (使用方式和引入变量一样)

引入前提需要自行通过Java反射拿到 静态方法对象

点击展开代码
@Test
void test() throws SecurityException, NoSuchMethodException {
    //定义2个函数,registerFunction和setVariable都可以,不过从语义上面来看用registerFunction更恰当
    StandardEvaluationContext context = new StandardEvaluationContext();
    Method parseInt = Integer.class.getDeclaredMethod("parseInt", String.class);
    context.registerFunction("parseInt1", parseInt);
    context.setVariable("parseInt2", parseInt);

    ExpressionParser parser = new SpelExpressionParser();
    System.out.println("#parseInt1('3') = " +
        parser.parseExpression("#parseInt1('3')").getValue(context, int.class));
    System.out.println("#parseInt2('3') = " +
        parser.parseExpression("#parseInt2('3')").getValue(context, int.class));

    System.out.println("#parseInt1('3') == #parseInt2('3') = " +
        parser.parseExpression("#parseInt1('3') == #parseInt2('3')").getValue(context, boolean.class));
}
/* 结果
#parseInt1('3') = 3
#parseInt2('3') = 3
#parseInt1('3') == #parseInt2('3') = true
*/

# 变量赋值

变量赋值通过 Expression.setValue()方法 为表达式赋值

点击展开代码
@Test
public void test() {
    // 匿名内部类实例对象
    Object obj = new Object() {
        private Integer num1;
        private Integer num2;

        public Integer getNum1() {
            return num1;
        }

        public void setNum1(Integer num1) {
            this.num1 = num1;
        }

        public Integer getNum2() {
            return num2;
        }

        public void setNum2(Integer num2) {
            this.num2 = num2;
        }

        public Integer add() {
            return this.num1 + this.num2;
        }

        @Override
        public String toString() {
            return "$classname{" +
                "num1=" + num1 +
                ", num2=" + num2 +
                '}';
        }
    };
    {
        //user为root对象
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext(obj);
        // 会通过 setName()方法 存值
        parser.parseExpression("#root.num1").setValue(context, 2);
        parser.parseExpression("#root.num2").setValue(context, 2);
        // 输出对象 , 默认使用 toString()方法
        System.out.println("#root.num1 = " +
            parser.parseExpression("#root.num1").getValue(context, Integer.class));
        System.out.println("#root.num2 = " +
            parser.parseExpression("#root.num2").getValue(context, Integer.class));
        System.out.println("#root.add() = " +
            parser.parseExpression("#root.add()").getValue(context, Integer.class));
        System.out.println("#root = " +
            parser.parseExpression("#root").getValue(context, obj.getClass()));
    }
    {
        //user为变量
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("obj", obj);
        parser.parseExpression("#obj.num1").setValue(context, 2);
        parser.parseExpression("#obj.num2").setValue(context, 2);
        System.out.println("#obj.num1 = " +
            parser.parseExpression("#obj.num1").getValue(context, Integer.class));
        System.out.println("#obj.num2 = " +
            parser.parseExpression("#obj.num2").getValue(context, Integer.class));
        System.out.println("#obj.add() = " +
            parser.parseExpression("#obj.add()").getValue(context, Integer.class));
        System.out.println("#obj = " +
            parser.parseExpression("#obj").getValue(context, obj.getClass()));

    }
}
/* 结果
#root.num1 = 2
#root.num2 = 2
#root.add() = 4
#root = $classname{num1=2, num2=2}
#obj.num1 = 2
#obj.num2 = 2
#obj.add() = 4
#obj = $classname{num1=2, num2=2}
*/

# 安全导航表达式

但在表达式中获取属性 , 如 : user.name , 但这个过程不能确保user数据是有效的 , 因此需要安全导航来进行避免空指针异常问题 .

应用方式 : (对象/属性)?.属性 (这个使用方法和JS中的ES6语法类似)

点击展开代码
@Test
public void test() {
    Object user = new Object() {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "$classname{" +
                "name='" + name + '\'' +
                '}';
        }
    };
    //user为变量
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("user", user);
    parser.parseExpression("#user.name").setValue(context, "张三丰");
    try {
        System.out.println("#user.name = " +
            parser.parseExpression("#user.name").getValue(context, String.class));
    } catch (EvaluationException | ParseException e) {
        System.out.println("数据访问空指针异常 [#user.name]");
    }
    try {
        System.out.println("#obj.name = " +
            parser.parseExpression("#obj.name").getValue(context, String.class));
    } catch (EvaluationException | ParseException e) {
        System.out.println("数据访问空指针异常 [#obj.name]");
    }
    try {
        System.out.println("#user?.name = " +
            parser.parseExpression("#user?.name").getValue(context, String.class));
    } catch (EvaluationException | ParseException e) {
        System.out.println("数据访问空指针异常 [#user.name]");
    }
    try {
        System.out.println("#obj?.name = " +
            parser.parseExpression("#obj?.name").getValue(context, String.class));
    } catch (EvaluationException | ParseException e) {
        System.out.println("数据访问空指针异常 [#obj.name]");
    }
}
/* 结果
#user.name = 张三丰
数据访问空指针异常 [#obj.name]
#user?.name = 张三丰
#obj?.name = null
*/

# Bean引用

支持SpringBoot Ioc容器引入Bean , 通过 @符号 引入Bean , 引入需要 BeanResolver接口 实现

点击展开代码
static class Car {
    public String name;
    public Car(String name) {
        this.name = name;
    }
    public String run() {
        return name + " 跑起来了...";
    }
}

@Test
public void test() {
    // 存Bean工厂
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    Car car = new Car("保时捷");
    factory.registerSingleton("car", car);

    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setBeanResolver(new BeanFactoryResolver(factory));

    ExpressionParser parser = new SpelExpressionParser();

    System.out.println("@car.name = " + 
                       parser.parseExpression("@car.name").getValue(context, String.class));
    System.out.println("@car.run() = " + 
                       parser.parseExpression("@car.run()").getValue(context, String.class));
    
    Car carBean = parser.parseExpression("@car").getValue(context, Car.class);
    System.out.println("car = " + car);
    System.out.println("carBean = " + carBean);
    System.out.println("car == factory.getBean('car') = " + car == factory.getBean("car"));
    System.out.println("carBean == factory.getBean('car') = " + carBean == factory.getBean("car"));
}
/* 结果
carName = 保时捷
car.run() = 保时捷在奔跑
car = com.ruoyi.test.SpelTest$Car@1dac5ef
carBean = com.ruoyi.test.SpelTest$Car@1dac5ef
car == factory.getBean('car') = true
carBean == factory.getBean('car') = true
*/

# 集合应用

# List

List 定义使用方式 {表达式1 , 表达式2 , 表达式n} , 字面量表达式定义的List不可修改

点击展开代码
@Test
public void test14() {
    ExpressionParser parser = new SpelExpressionParser();
    // 对于字面量列表也将返回不可修改的List
    List<Integer> result = parser.parseExpression("{1,2,3}").getValue(List.class);
    System.out.println("result = " + result);
    // 访问List
    System.out.println("{1,2,3}[0] = " + 
        parser.parseExpression("{1,2,3}[0]").getValue(int.class));
    System.out.println("{1,2,3}.get(1) = " + 
        parser.parseExpression("{1,2,3}.get(1)").getValue(int.class));
    try {
        result.set(0, 2);
    } catch (Exception e) {
        System.out.println("set错误 , result不能更改");
    }
    
    // 对于列表中只要有一个不是字面量表达式,将只返回原始List (运算表达式)
    List<List<Integer>> result2 = parser.parseExpression("{{1+2,2+4},{3,4+4}}").getValue(List.class);
    result2.get(0).set(0, 1);
    System.out.println("result2 = " + result2);

    // 声明数组并初始化
    System.out.println("new int[2]{1,2} = " +
        parser.parseExpression("new int[2]{1,2}").getValue(int[].class)[0]);
    // 访问数组
    System.out.println("new int[2]{1,2}[1] = " +
        parser.parseExpression("new int[2]{1,2}[1]").getValue(int.class));
}
/* 结果
result = [1, 2, 3]
{1,2,3}[0] = 1
{1,2,3}.get(1) = 2
set错误 , result不能更改
result2 = [[1, 6], [3, 8]]
new int[2]{1,2} = 1
new int[2]{1,2}[1] = 2
*/

# Map

Map 定义通过 EvaluationContext.setVariable()方法 存Map字典 , 访问方式 map[key]

点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();

    //SpEL对Map字典元素访问的支持
    Map<String, Integer> map = new HashMap<>();
    map.put("a", 8);
    map.put("b", 10);
    context.setVariable("map", map);
    System.out.println("#map['b'] = " +
        parser.parseExpression("#map['b']").getValue(context, int.class));
}
/* 结果
#map['b'] = 8
*/

其他类型使用方式也同理

// SpEL目前支持所有集合类型的访问
Collection<Integer> collection = new HashSet<>();
collection.add(2);
collection.add(4);
collection.add(6);

context.setVariable("collection", collection);
System.out.println("#collection = " +
    parser.parseExpression("#collection").getValue(context, int.class));
System.out.println("#collection[1] = " +
    parser.parseExpression("#collection[1]").getValue(context, int.class));
/* 结果
#collection = 2
#collection[1] = 4
*/

# 集合选择

集合通过表达式筛选出满足条件的新集合 , 使用方式 (map|list).?[选择表达式], 表达式结果为布尔值控制筛选

  • list 获取当前值 #this
  • map 获取当前字典 key / value
点击展开代码
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();
    List<Integer> list = Arrays.asList(2, 4, 6, 8, 10);
    context.setVariable("list", list);
    System.out.println("#list = " +
        parser.parseExpression("#list").getValue(context, List.class));
    System.out.println("#list.?[#this>6] = " +
        parser.parseExpression("#list.?[#this>6]").getValue(context, List.class));
    System.out.println("#list.?[(#this>2 && #this<8)] = " +
        parser.parseExpression("#list.?[(#this>2 && #this<8)]").getValue(context, List.class));
}
/* 结果
#list = [2, 4, 6, 8, 10]
#list.?[#this>6] = [8, 10]
#list.?[(#this>2 && #this<8)] = [4, 6]
*/
@Test
void test() {
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();
    Map<String,Integer> map = new HashMap<String,Integer>(){{
       put("a",2);
       put("b",4);
       put("c",6);
       put("d",8);
       put("e",10);
    }};
    context.setVariable("map", map);
    System.out.println("#map = " +
        parser.parseExpression("#map").getValue(context, Map.class));
    System.out.println("#map.?[key!='a'] = " +
        parser.parseExpression("#map.?[key!='a']").getValue(context, Map.class));
    System.out.println("#map.?[value > 6] = " +
        parser.parseExpression("#map.?[value > 6]").getValue(context, Map.class));
    System.out.println("#map.![value + 2] = " +
        parser.parseExpression("#map.![value + 2]").getValue(context, List.class));
}
/* 结果
#map = {a=2, b=4, c=6, d=8, e=10}
#map.?[key!='a'] = {b=4, c=6, d=8, e=10}
#map.?[value > 6] = {d=8, e=10}
#map.![value + 2] = [4, 6, 8, 10, 12]
*/
#SpEL表达式

← PHP快速上手 velocity模板引擎→

最近更新
01
HTTPS自动续签
10-21
02
博客搭建-简化版(脚本)
10-20
03
ruoyi-vue-plus-部署篇
07-13
更多文章>
Theme by Vdoing | Copyright © 2019-2024 | 桂ICP备2022009417号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式