文章目录

  • 1. 简单介绍
    • 1.1. 什么是 Spring EL
    • 1.2. 为什么要使用 Spring EL
    • 1.3. 如何使用 Spring EL
  • 2. 简单使用
  • 3. EL 表达式解析引擎
    • 3.1. 带缓存的 EL 表达式工具方法:
    • 3.2. 设置上下文的 EL 表达式
  • 4. 总结
  • 5. 补充示例
    • 5.1. 使用 Spring EL 进行赋值
    • 5.2. 使用 Spring EL 进行计算

本文主体部分来自于 KILLKISS的 SpringEL详解及应用。对文中部分代码做了校验和补充,并添加了自己的部分代码。


1. 简单介绍

1.1. 什么是 Spring EL

Spring3 中引入了 Spring 表达式语言 — Spring EL,SpEL 是一种强大,简洁的装配 Bean 的方式,它可以通过运行期间执行的表达式将值装配到我们的属性或构造函数当中,更可以调用 JDK 中提供的静态常量,获取外部 Properties 文件中的的配置。

1.2. 为什么要使用 Spring EL

我们平常通过配置文件或注解注入的 Bean,其实都可以称为静态性注入,试想一下,如果我的 Bean A 中有变量 A,它的值需要根据 Bean B 的 B 变量为参考,在这个场景下静态注入就显得非常无力,而 Spring3 增加的 Spring EL 就可以完全满足这种需求,而且还可以对不同 Bean 的字段进行计算再进行赋值,功能非常强大。

1.3. 如何使用 Spring EL

Spring EL 从名字来看就能看出,和 EL 是有关系的,Spring EL 的使用和 EL 表达式的使用非常相似,EL 表达式在 JSP 页面更方便的获取后台中的值,而 Spring EL 就是为了更方便获取 Spring 容器中的 Bean 的值,EL 使用${},而 Spring EL 使用#{}进行表达式的声明。

2. 简单使用

辅助类:

package com.example.spel.bean;import org.springframework.stereotype.Component;
import java.util.*;@Component
public class TestConstant {public static final String STR = "测试SpEL";public String nickname = "一线大码";public String name = "笑傲江湖";public int num = 5;public List<String> testList = Arrays.asList("aaa", "bbb", "ccc");public Map testMap = new HashMap() {{put("aaa", "元宇宙算法");put("hello", "world");}};public List cityList = new ArrayList<City>() {{add(new City("aaa", 500));add(new City("bbb", 600));add(new City("ccc", 1000));add(new City("ddd", 1000));add(new City("eee", 2000));add(new City("fff", 3000));}};public String showProperty() {return "Hello";}public String showProperty(String str) {return "Hello " + str + "!";}
}
package com.example.spel.bean;import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
public class City {private String name;private long population;
}

测试代码:

package com.example.spel.el;import com.example.spel.bean.City;
import com.example.spel.bean.TestConstant;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;@ToString
@Component
public class TestSpringEL {/*** 注入简单值,输出num为5*/@Value("#{5}")private Integer num;@Value("#{'rain'.toUpperCase()}")private String name;//注入bean,访问属性和方法/*** 注入ID为testConstant的Bean*/@Value("#{testConstant}")private TestConstant testConstant;/*** 注入ID为testConstant的Bean中的STR常量/变量*/@Value("#{testConstant.STR}")private String str;/*** 调用无参方法*/@Value("#{testConstant.showProperty()}")private String method1;/*** 调用有参方法,接收字符串*/@Value("#{testConstant.showProperty('World')}")private String method2;/*** 方法返回的String为大写*/@Value("#{testConstant.showProperty().toUpperCase()}")private String method3;/*** 使用method3这种方式,如果showProperty返回为null,将会抛出NullPointerException,可以使用以下方式避免。* 使用?.符号表示如果左边的值为null,将不执行右边方法*/@Value("#{testConstant.showProperty()?.toUpperCase}")private String method4;//注入JDK中的工具类常量或调用工具类的方法/*** 获取Math的PI常量*/@Value("#{T(java.lang.Math).PI}")private double pi;/*** 调用random方法获取返回值*/@Value("#{T(java.lang.Math).random()}")private double ramdom;/*** 获取文件路径符号*/@Value("#{T(java.io.File).separator}")private String separator;//使用SpringEL进行运算及逻辑操作/*** 拼接字符串*/@Value("#{testConstant.nickname + ' ' + testConstant.name}")private String concatString;/*** 对数字类型进行运算*/@Value("#{ 3 * T(java.lang.Math).PI + testConstant.num}")private double operation;/*** 进行逻辑运算*/@Value("#{testConstant.num > 100 and testConstant.num <= 200}")private boolean logicOperation;/*** 进行或非逻辑操作*/@Value("#{not(testConstant.num == 100 or testConstant.num <= 200)}")private Boolean logicOperation2;/*** 使用三元运算符*/@Value("#{testConstant.num > 100 ? testConstant.num : testConstant.num + 100}")private Integer logicOperation3;//SpringEL使用正则表达式/*** 验证是否邮箱地址正则表达式*/@Value("#{testConstant.STR matches '\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+'}")private boolean regularExpression;//TestConstant类中有名为testList的List变量,和名为testMap的Map变量/*** 获取下标为0的元素*/@Value("#{testConstant.testList[0]}")private String firstStr;/*** 获取下标为0元素的大写形式*/@Value("#{testConstant.testList[0]?.toUpperCase()}")private String upperFirstStr;/*** 获取map中key为hello的value*/@Value("#{testConstant.testMap['hello']}")private String mapValue;/*** 根据testList下标为0元素作为key获取testMap的value*/@Value("#{testConstant.testMap[testConstant.testList[0]]}")private String mapValueByTestList;//声明City类,有population(人口)属性。testConstant拥有名为cityList的City类List集合/*** 过滤testConstant中cityList集合population属性大于1000的全部数据注入到本属性*/@Value("#{testConstant.cityList.?[population > 1000]}")private List<City> cityList;/*** 过滤testConstant中cityList集合population属性等于1000的第一条数据注入到本属性*/@Value("#{testConstant.cityList.^[population == 1000]}")private City city;/*** 过滤testConstant中cityList集合population属性小于1000的最后一条数据注入到本属性*/@Value("#{testConstant.cityList.$[population < 1000]}")private City city2;/** 首先为city增加name属性,代表城市的名称*//*** 假如我们在过滤城市集合后只想保留城市的名称,可以使用如下方式进行投影*/@Value("#{testConstant.cityList.?[population > 1000].![name]}")private List<String> cityName;
}

执行测试:

package com.example.spel;import com.example.spel.el.TestSpringEL;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;/*** @author wangbo* @date 2021/11/26*/
@SpringBootTest
class Test1 {@Autowiredprivate TestSpringEL testSpringEL;@Testvoid test(){System.out.println(testSpringEL);}
}

测试结果:

TestSpringEL(num=5, name=rain, name1=RAIN, testConstant=com.example.spel.bean.TestConstant@35cd68d4, str=测试SpEL, method1=Hello, method2=Hello World!, method3=HELLO, method4=HELLO, pi=3.141592653589793, ramdom=0.38512203414759527, separator=\, concatString=一线大码 笑傲江湖, operation=14.42477796076938, logicOperation=false, logicOperation2=false, logicOperation3=105, regularExpression=false, firstStr=aaa, upperFirstStr=AAA, mapValue=world, mapValueByTestList=元宇宙算法, cityList=[City(name=eee, population=2000), City(name=fff, population=3000)], city=City(name=ccc, population=1000), city2=City(name=bbb, population=600), cityName=[eee, fff])

3. EL 表达式解析引擎

3.1. 带缓存的 EL 表达式工具方法:

package gtcom.governance.impl.util;import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** SpEL表达式解析** @author wangbo* @date 2021/11/26*/
public class ExpressionUtils {private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();private static final Map<String, Expression> EXPRESSION_CACHE = new ConcurrentHashMap<>();/*** 获取解析后的表达式** @param expression EL表达式字符串* @return 解析后的表达式,如果之前已经解析过,则返回缓存的表达式*/public static Expression getExpression(String expression) {if (StringUtils.isBlank(expression)) {return null;}expression = expression.trim();return EXPRESSION_CACHE.computeIfAbsent(expression, EXPRESSION_PARSER::parseExpression);}
}

上面工具的测试代码:

package gtcom.governance.impl.util;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.expression.Expression;import java.util.Objects;/*** 测试SpEL表达式解析** @author wangbo* @date 2021/11/26*/
public class TestExpression {public static void main(String[] args) throws JsonProcessingException {ObjectMapper mapper = new ObjectMapper();ObjectNode root = mapper.createObjectNode();root.put("pubTime", "11111");ObjectNode supplier = root.putObject("supplier");supplier.put("sourceType", "TestSourceType");supplier.put("comFrom", "QX");System.out.println(mapper.writeValueAsString(root));String sourceType = root.get("supplier").path("sourceType").asText();System.out.println(sourceType);String expression1 = "get('supplier').path('sourceType').asText";Expression expr1 = ExpressionUtils.getExpression(expression1);System.out.println(expr1);String sourceType1 = Objects.requireNonNull(expr1).getValue(root, String.class);System.out.println(sourceType1);String expression2 = "get('supplier').path('sourceType').asText";Expression expr2 = ExpressionUtils.getExpression(expression2);System.out.println(expr2);String sourceType2 = Objects.requireNonNull(expr2).getValue(root, String.class);System.out.println(sourceType2);}
}

3.2. 设置上下文的 EL 表达式

package com.example.spel;import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;public class TestStringSubExpression {public static void main(String[] args) {String expressionStr = "'hello world'.toUpperCase().substring(1,5)";//指定SpelExpressionParser解析器实现类ExpressionParser parser = new SpelExpressionParser();//解析表达式Expression expression = parser.parseExpression(expressionStr);System.out.println(expression.getValue());}
}

等价设置了上下文的代码:

package com.example.spel;import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class TestStringSubExpression {public static void main(String[] args) {String expressionStr = "'hello world'.toUpperCase().substring(#start, #end)";//指定SpelExpressionParser解析器实现类ExpressionParser parser = new SpelExpressionParser();//解析表达式Expression expression = parser.parseExpression(expressionStr);//设置对象模型基础EvaluationContext context = new StandardEvaluationContext();context.setVariable("start", 1);context.setVariable("end", 5);System.out.println(expression.getValue(context));}
}

另一个设置了上下文的 SpEL 测试代码:

package com.example.spel;import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class Test {public static void main(String[] args) {//测试SpringEL解析器。//设置文字模板,其中#{}表示表达式的起止,#user是表达式字符串,表示引用一个变量。String template = "#{#user},早上好";//创建表达式解析器。ExpressionParser parser = new SpelExpressionParser();//通过evaluationContext.setVariable可以在上下文中设定变量。EvaluationContext context = new StandardEvaluationContext();context.setVariable("user", "黎明");//解析表达式,如果表达式是一个模板表达式,需要为解析传入模板解析器上下文。Expression expression = parser.parseExpression(template, new TemplateParserContext());//使用Expression.getValue()获取表达式的值,这里传入了Evaluation上下文,第二个参数是类型参数,表示返回值的类型。System.out.println(expression.getValue(context, String.class));}
}

代码执行结果:

黎明,早上好

4. 总结

优点:

Spring EL 功能非常强大,在 Annotation 的方式开发时可能感觉并不强烈,因为可以直接编写到源代码来实现 Spring EL 的功能,但如果是在 XML 文件中进行配置,Spring EL 可以弥补 XML 静态注入的不足,从而实现更强大的注入。

缺点:

Spring EL 在使用时仅仅是一个字符串,不易于排错与测试,也没有 IDE 检查我们的语法(目前 DIEA 可以检测 EL 语言),当出现错误时较难检测。

笔者实际应用:

笔者开发的项目当中比较频繁的使用 Spring EL,例如通过 Spring EL 获取外部 properties 中的值,又或者项目当中的数据字典亦是使用 Spring EL 的一个场景,我们抽象出一个 Param 类的集合,通过 Spring EL 集合筛选和投影获取我们想要的字段参数添加到我们的程序逻辑当中(笔者项目中的 Spring Security 亦使用 Spring EL,但本文章不加以叙述)。

总结:

Spring3.0 让人为之惊艳的非 Spring EL 莫属,为我们的注入提供了另一种强大的形式,传统注入能做到的事情,和做不到的事情,Spring EL 一概能完成,但在项目当中并不适宜大量使用 Spring EL,适当的技术方在适当的位置,才能更好的完成事情。

5. 补充示例

@Value中的${...}表示占位符,它会读取上下文的属性值装配到属性中。
@Value中的#{...}表示启用 Spring 表达式,具有运算功能。
T(...)表示引入类,Systemjava.lang.*包下的类,是 Java 默认加载的包,因此可以不用写全限定名,如果是其它的包,则需要写出全限定名才能引用类。

@Value("${database.driverName}")
private String driver;@Value("#{T(System).currentTimeMillis()}")
private Long initTime = null;

5.1. 使用 Spring EL 进行赋值

//赋值字符串
@Value("#{'使用 Spring EL 赋值字符串'}")
private String str = null;//科学计数法赋值
@Value("#{9.3E3}")
private double d;//赋值浮点数
@Value("#{3.14}")
private float pi;
//赋值 bean 的属性
@Value("#{beanName.str}")
private String otherBeanProp = null;//?表示判断是否为 null,不为空才会执行后面的表达式
@Value("#{beanName.str?.toUpperCase()}")
private String otherBeanProp1 = null;

5.2. 使用 Spring EL 进行计算

//数学运算
@Value("#{1+2}")
private int run;//浮点数比较运算
@Value("#{beanName.pi == 3.14f}")
private boolean piFlag;//字符串比较运算
@Value("#{beanName.str eq 'Spring Boot'}")
private boolean strFlag;//字符串连接
@Value("#{beanName.str + ' 连接字符串'}")
private String strApp = null;//三元运算
@Value("#{beanName.d > 1000 ? '大于' : '小于'}")
private String resultDesc = null;

Spring EL 表达式的简单介绍和使用相关推荐

  1. java中的el表达式_简单介绍Java中的EL表达式(图)

    EL全名为Expression Language,这篇文章主要给大家介绍EL表达式的主要作用及内容简介,感兴趣的朋友一起看看 JSP页面中支持使用EL表达式,EL全名为Expression Langu ...

  2. Spring EL表达式使用详解

    Spring EL表达式使用详解 什么是Spring EL表达式 注入字面值 注入操作系统(OS)的属性 注入properties配置文件中数据 Bean属性调用 Bean方法调用 T运算符 构造器 ...

  3. Spring EL表达式

    1,Spring EL 表达式简介 Spring EL 表达式是Spring提供的最新的灵活的注入方式,相比于传统的注解注入以及xml配置文件的注入远远地更加的强大. 2,Spring EL的功能 ( ...

  4. 【SpringBoot】【Thyemeleaf 】【Spring EL表达式】 SPEL调用静态类、静态方法

    前言 spring 5.0.6.RELEASE Thyemeleaf 3.0 SpringBoot 2.3.4.RELEASE Spring EL表达式 调用静态类.静态方法 参考这里:https:/ ...

  5. spring el 表达式的上下文关联到 ApplicationContext

    前言 spring 5.0.6.RELEASE 需求:让 spring el 表达式的上下文关联到 ApplicationContext .这样就能在 EL 表达式中调用Bean. 代码 // cre ...

  6. php有lambda表达式吗,Python中lambda表达式的简单介绍(附示例)

    本篇文章给大家带来的内容是关于Python中lambda表达式的简单介绍(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 一:匿名函数的定义 lambda parameter ...

  7. 什么是Spring EL表达式

    本文来说下什么是Spring EL表达式 文章目录 概述 概述

  8. Spring 在 xml配置文件 或 annotation 注解中 运用Spring EL表达式

    Spring  EL 一:在Spring xml 配置文件中运用   Spring EL Spring EL 采用 #{Sp Expression  Language} 即 #{spring表达式} ...

  9. spring el表达式解析_Spring之旅第一篇-初识Spring

    目录 一.概述 二.模块 三.动手创建 一.概述 只要用框架开发java,一定躲不过spring,Spring是一个轻量级的Java开源框架,存在的目的是用于构建轻量级的J2EE应用.Spring的核 ...

最新文章

  1. go环境变量配置 (GOROOT和GOPATH)
  2. flex布局:子子元素过大撑开了设定flex:1的子元素的解决方案
  3. 正则表达式的威力--轻松消除HTML代码
  4. 给我往死里贪——HRBUST - 1167-每种面值的货币要多少
  5. 实现物联网项目,你需要提前知道的6件事情
  6. ZooKeeper系列(四)
  7. matlab中弹性碰撞课程设计,完全弹性碰撞matlab.doc
  8. 【cuda】——npp/cuda图像预处理resize+norm对比
  9. 人脸关键点检测face_landmark
  10. 二叉树:前序与后序、前序与中序以及中序与后序相同、相反的特征
  11. android 监测usb有线网卡的网线连接状态 详细一点的输出信息解析
  12. netbeans php下载,Netbeans7下载和安装
  13. 系统维护模式修改/etc/fstab
  14. 通过路由器高速使用校园网(sdust)
  15. python结束子进程_如何清除python中的子进程
  16. windows bat脚本教程
  17. pageContext
  18. 用源码论述Eclipse学习体会
  19. Inpaint Version6.2分析
  20. 电脑重启出现蓝屏提示“beginning dump of physical memory”解决方法

热门文章

  1. 用html编写一个红绿灯,如何用html+css+javascript写一个简易红绿灯
  2. python上安装reuquest_八爪鱼增值API文档
  3. Spring5源码系列-02-源码整体脉络
  4. ios和android测试面试题目,App测试面试题总结
  5. math的向上取整_Javascript中Math常用操作,向上取整、向下取整、四舍五入
  6. CTF综合靶场渗透系列-Billu_b0x
  7. C语言中的 static变量(全局和局部)、static函数总结
  8. 音乐计算机生日快乐歌曲,生日快乐
  9. “华米OV”罕见联手,“一头多充”照进现实
  10. C++ map中使用pair构造键值对小记