User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Spring   ›   Spring Notes   ›   正文
Spring Notes

Spring—SpEL表达式总结

2020-07-23 21:20:08
2879  0 2
参考目录 隐藏
1) 一. 用法
2) 1. @Value注解
3) 2. XML配置
4) 3. Expression​​​​​​
5) 二. SpEL表达式语法
6) 1. 直接量表达式
7) 2. 直接使用java代码
8) 3. 使用T(Type)
9) 4. 变量
10) 5. 方法调用
11) 6. 运算符表达式
12) 7. Elvis运算符
13) 8. 安全保证
14) 9. 集合定义
15) 10. 集合访问
16) 11. 集合修改
17) 12. 集合选择
18) 13. 集合投影
19) 案例

阅读完需:约 13 分钟

SpEL(Spring Expression Language),即Spring表达式语言。它是一种类似JSP的EL表达式、但又比后者更为强大有用的表达式语言。

为什么要用SpEL:因为它可以在spring容器内实时查询和操作数据,尤其是操作List列表型、Array数组型数据。所以使用SpEL可以有效缩减代码量,优化代码结构,笔者认为很有用。

  1. #{…} 用于执行SpEl表达式,并将内容赋值给属性
  2. ${…} 主要用于加载外部属性文件中的值
  3. #{…} 和${…} 可以混合使用,但是必须#{}外面,${}在里面,#{ ‘${}’ } ,注意单引号,注意不能反过来

一. 用法

常规SpEL有三种用法:

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

1. @Value注解

@Value可以加在class的成员变量和形参上。用法如下

    //@Value能修饰成员变量和方法形参
    //#{}内就是SpEL表达式的语法
    //Spring会根据SpEL表达式语法,为变量arg赋值
    @Value("#{表达式}")
    public String arg;

如果修饰成员变量,Spring容器会根据SpEL表达式语法筛选修改数据,然后赋值给所@Value修饰的变量;

如果修饰方法形参,则是过滤传进来的参数值。

2. XML配置

XML配置用在Spring的applicationContext.xml配置文件内的<bean>元素上,用法如下:

<bean id="xxx" class="com.java.XXXXX.xx">
    <!-- 同@Value,#{}内是表达式的值,可放在property或constructor-arg内 -->
    <property name="arg" value="#{表达式}">
</bean>

用法跟注解@Value修饰形参类似  

3. Expression​​​​​​ 

在代码中创建Expression对象,利用Expression对象来执行SpEL

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 SpELTest {
 
    public static void main(String[] args) {
 
    	//创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL表达式语法设置在parseExpression()入参内
        Expression exp = parser.parseExpression("表达式");
        //执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
        Object value = exp.getValue();
        
 
        /**也可以使用非Spring的ApplicationContext容器,则用下面的方法*/
        //创建一个虚拟的容器EvaluationContext
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //向容器内添加bean
        BeanA beanA = new BeanA();
        ctx.setVariable("bean_id", beanA);
        //setRootObject并非必须;一个EvaluationContext只能有一个RootObject,引用它的属性时,可以不加前缀
        ctx.setRootObject(XXX);        
        //getValue有参数ctx,从新的容器中根据SpEL表达式获取所需的值
        Object value = exp.getValue(ctx);
    }
}

Expression用法可以在代码中使用SpEL进行数据的读取过滤和修改,十分方便。

由上面可以看出,SpEL与Spring容器本身紧密相关,且用法特别灵活,可以直接操作Spring管理的各种bean、变量、properties配置文件等数据。

二. SpEL表达式语法

知道SpEL的使用场景范围和用法后,我们来看下SpEL表达式的具体语法。

SpEL语法决定了程序员能取到通过编写SpEL,从容器内取到什么样的数据,以及可以把数据处理成什么结果。有时用java代码写很多行的逻辑,用SpEL表达式一句话就可以搞定。

具体如下:

1. 直接量表达式

    @Value("#{'Hello World'}")        
    String word;        //变量word赋值直接量:字符串"Hello World"

2.  直接使用java代码

如在SpEL中直接试用new/instance of,像写Java代码一样。注意:在SpEL中直接使用某个类名时,此类必须是java.lang 包中的类,才可以在SpEL中省略包名;否则需要写全名

Expression exp = parser.parseExpression("new Spring('Hello World')"); 

3.  使用T(Type)

使用“T(Type)”来表示java.lang.Class类的实例,即如同java代码中直接写类名。同样,只有java.lang 下的类才可以省略包名。此方法一般用来引用常量或静态方法

parser.parseExpression("T(Integer).MAX_VALUE");    //等同于java代码中的:Integer.MAX_VALUE

4.  变量

        获取容器内的变量,可以使用“#bean_id”来获取。有两个特殊的变量,可以直接使用。

  •     #this 使用当前正在计算的上下文
  •     #root 引用容器的root对象,即applicationContext本身
 //从ctx容器内,获取rootObject,并转换为String类型
        String result1 = parser.parseExpression("#root").getValue(ctx, String.class);  
        
        //在ctx容器内,设置abc的值为"abcdef"
        String s = new String("abcdef");
        ctx.setVariable("abc",s);
        //取id为abc的bean,然后调用其中的substring方法,得到结果赋值给result2
        String result2 = parser.parseExpression("#abc.substring(0,1)").getValue(ctx, String.class);

5.  方法调用

SpEL的方法调用与直接编写Java代码没有什么区别。具体可见上例abc.substring(0,1)与java代码”abcdef”.substring(0,1)效果一样

SpEL也可以自定义方法,如下:

        //创建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //获取java自带的Integer类的parseInt(String)方法
        Method parseInt = Integer.class.getDeclaredMethod("parseInt", String.class);
        //将parseInt方法注册在ctx容器内
        ctx.registerFunction("parseInt", parseInt);
        //再将parseInt方法设为parseInt2
        ctx.setVariable("parseInt2", parseInt);
 
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL语法,比对两个方法执行完成后,结果是否相同
        String expreString = "#parseInt('2') == #parseInt2('3')";
        Expression expression = parser.parseExpression(expreString);
        return expression.getValue(ctx, Boolean.class);    //执行结果为false
 
        /** 如果String expreString = "#parseInt('2') == #parseInt2('3')",执行结果为true */
        /** 可见SpEL正常执行*/

“registerFunction”和“setVariable”都可以注册自定义函数,但是两个方法的含义不一样,推荐使用“registerFunction”方法注册自定义函数。

6. 运算符表达式

  1. 算数表达式(“1+2-3*4/2″)
  2. 比较表达式(“1>2”)
  3. 逻辑表达式(“2>1 and (!true or !false)”)
  4. 赋值表达式(“#variableName=value”)
  5. 三目表达式(“表达式1?表达式2:表达式3”)
  6. 正则表达式(“123′ matches ‘\d{3}”)

等运算符,都可以直接放在SpEL中,执行结果为运算符表达式的结果

7.  Elvis运算符

是三目运算符的特殊写法,可以避免null报错的情况

    //SpEL可简写为:
    name?:"other"
 
    //等同于java代码
    name != null? name : "other"

8.  安全保证

为了避免操作对象本身可能为null,取属性时报错,可以使用SpEL检验语法

语法: “对象?.变量|方法”

    //SpEL表达式简写
    list?.length
 
    //等同于java代码
    list == null? null: list.length

 当对象为null时,直接返回“null”,不会抛出NullPointerException

9.   集合定义

使用“{表达式,……}”定义List,如“{1,2,3}”

    //SpEL的@Value注解设置List
    @Value("1,2,3")
    private List<Integer> f1;
 
    @RequestMapping(value = "/a", method = RequestMethod.POST)
    public List<Integer> a() throws NoSuchMethodException {
 
        //SpEL
        List<Integer> result1 = parser.parseExpression("{1,2,3}").getValue(List.class);
 
        //等同于如下java代码
        Integer[] integer = new Integer[]{1,2,3};
        List<Integer> result2 = Arrays.asList(integer);
 
        return result1;
    }

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

10.  集合访问

SpEL目前支持所有集合类型和字典类型的元素访问

语法:“集合[索引]”、“map[key]”

EvaluationContext context = new StandardEvaluationContext();
 
//即list.get(0)
int result1 = parser.parseExpression("{1,2,3}[0]").getValue(int.class); 
 
//list获取某一项
Collection<Integer> collection = new HashSet<Integer>();
collection.add(1);
collection.add(2);
 
context.setVariable("collection", collection);
int result2 = parser.parseExpression("#collection[1]").getValue(context, int.class); 
 
//map获取
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
 
context.setVariable("map", map);
int result3 = parser.parseExpression("#map['a']").getValue(context, int.class);

11.  集合修改

可以使用赋值表达式或Expression接口的setValue方法修改;

//赋值语句
int result = parser.parseExpression("#array[1] = 3").getValue(context, int.class); 
 
//serValue方法
parser.parseExpression("#array[2]").setValue(context, 4);

12.  集合选择

通过一定的规则对及格进行筛选,构造出另一个集合

语法:“(list|map).?[选择表达式]”

选择表达式结果必须是boolean类型,如果true则选择的元素将添加到新集合中,false将不添加到新集合中。

parser.parseExpression("#collection.?[#this>2]").getValue(context, Collection.class); 

上面的例子从数字的collection集合中选出数字大于2的值,重新组装成了一个新的集合

13.  集合投影

根据集合中的元素中通过选择来构造另一个集合,该集合和原集合具有相同数量的元素

语法:“SpEL使用“(list|map).![投影表达式]”

public class Book {
 
	public String name;         //书名
	public String author;       //作者
	public String publisher;    //出版社
	public double price;        //售价
	public boolean favorite;    //是否喜欢
}
public class BookList {
 
    @Autowired
    protected ArrayList<Book> list = new ArrayList<Book>() ;
	
    protected int num = 0;
}

将BookList的实例映射为bean:readList,在另一个bean中注入时,进行投影

 //从readList的list下筛选出favorite为true的子集合,再将他们的name字段投为新的list
	@Value("#{list.?[favorite eq true].![name]}")
	private ArrayList<String> favoriteBookName;

案例

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 *
 */
@Slf4j
@RestController
@RequestMapping("/SpEL")
public class SpELController {
 
    @Value("#{'Hello World1'}")
    String helloWorld1;        //变量word赋值直接量:字符串"Hello World"
 
    @Value("Hello World2")
    String helloWorld2;        //变量word赋值直接量:字符串"Hello World"
 
    //注入list
    @Value("7,2,3,5,1")
    private List<Integer> fList;
 
 
    /**
     * {@code @Value} 注入String
     *
     * @return
     */
    @RequestMapping(value = "/valueAnnoString", method = RequestMethod.POST)
    public String valueAnnoString() {
        return helloWorld1 + " & " + helloWorld2;
    }
 
 
    /**
     * {@code @Value} 注入List
     *
     * @return
     * @throws NoSuchMethodException
     */
    @RequestMapping(value = "/valueAnnoList", method = RequestMethod.POST)
    public List<Integer> valueAnnoList() {
        return fList;
    }
 
 
    /**
     * 测试通过ExpressionParser调用SpEL表达式
     * @return
     */
    @RequestMapping(value = "/expressionParse", method = RequestMethod.POST)
    public List<Integer> expressionParse() {
 
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        List<Integer> result1 = parser.parseExpression("{4,5,5,6}").getValue(List.class);
        return result1;
    }
 
 
 
    /**
     * 使用java代码
     * @return
     */
    @RequestMapping(value = "/javaCode", method = RequestMethod.POST)
    public Integer javaCode() {
 
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
 
        //等同于直接用java代码,还有方法调用
        String str = parser.parseExpression("new String('Hello World').substring(3)").getValue(String.class);
        log.info("str=={}", str);
 
        //TType 等同于java的Integer.MAX_VALUE
        Integer integer = parser.parseExpression("T(Integer).MAX_VALUE").getValue(Integer.class);
        log.info("integer=={}", integer);
        return integer;
    }
 
 
 
 
    /**
     * 注入并调用method方法
     * @return
     * @throws NoSuchMethodException
     */
    @RequestMapping("methodInvoke")
    private boolean methodInvoke() throws NoSuchMethodException {
        //创建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //获取java自带的Integer类的parseInt(String)方法
        Method parseInt = Integer.class.getDeclaredMethod("parseInt", String.class);
        //将parseInt方法注册在ctx容器内, 推荐这样使用
        ctx.registerFunction("parseInt", parseInt);
        //再将parseInt方法设为parseInt2
        ctx.setVariable("parseInt2", parseInt);
 
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL语法,比对两个方法执行完成后,结果是否相同
        String expreString = "#parseInt('2') == #parseInt2('3')";
        //执行SpEL
        Expression expression = parser.parseExpression(expreString);
        Boolean value = expression.getValue(ctx, Boolean.class);
        return value;
    }
 
 
 
    /**
     * 运算符
     * @return
     */
    @RequestMapping(value = "/operator", method = RequestMethod.POST)
    public boolean operator() {
 
        //创建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //将字符串defg放在 ctx容器内
        ctx.setVariable("abc", new String("defg"));
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        String abc = parser.parseExpression("#abc").getValue(ctx, String.class);
        log.info("abc=={}", abc);
 
        //运算符判断
        Boolean result = parser.parseExpression("#abc.length() > 3").getValue(ctx, Boolean.class);
        log.info("result=={}", result);
        return result;
    }
 
 
    /**
     * Elvis等用法
     * @return
     */
    @RequestMapping(value = "/elvis", method = RequestMethod.POST)
    public void elvis(){
        //创建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //将字符串defg放在 ctx容器内
        ctx.setVariable("name", null);
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        String name = parser.parseExpression("#name?:'other'").getValue(ctx, String.class);
        log.info("name=={}",name);
        log.info("saved length() == {}", parser.parseExpression("#name?.lenth()").getValue(ctx));
 
        //将字符串defg放在 ctx容器内
        ctx.setVariable("name", "abc");
        name = parser.parseExpression("#name?:'other'").getValue(ctx, String.class);
        log.info("changed name=={}",name);
        log.info("changed saved length() == {}", parser.parseExpression("#name?.length()").getValue(ctx));
 
 
        //map获取
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("a", 1);
        ctx.setVariable("map", map);
        int mapA = parser.parseExpression("#map['a']").getValue(ctx, int.class);
        log.info("map['a']=={}", mapA);
        //修改
        parser.parseExpression("#map['a']").setValue(ctx, 6);
        mapA = parser.parseExpression("#map['a']").getValue(ctx, int.class);
        log.info("changed map['a']=={}", mapA);
 
        return ;
    }
 
 
    @RequestMapping("/listFunction")
    private void listFunction() {
        //创建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
 
        //list过滤
        ctx.setVariable("aList",fList);
        List<Integer> cList = parser.parseExpression("#aList.?[#this>3]").getValue(ctx, List.class);
        log.info("filter list=={}", cList);
 
 
        List<Book> books = new ArrayList<>();
        books.add(new Book("JAVA Program", 2000, 102.5));
        books.add(new Book("C Program", 1985, 36));
        books.add(new Book("scala", 2015, 60));
 
        //object过滤
        ctx.setVariable("books", books);
        List<Book> filterBooks1 = (List<Book>) parser.parseExpression("#books.?[year>2000]").getValue(ctx);
        log.info("filterBooks1=={}", filterBooks1);
 
        //投影
        List<String> filterBooksName = parser.parseExpression("#books.?[price<100].![name]").getValue(ctx, List.class);
        log.info("filterBooksName=={}", filterBooksName);
 
        return;
    }
 
 
 
    @Data
    class Book{
        private String name;
        private int year;
        private double price;
 
        public Book(String name, int year, double price) {
            this.name = name;
            this.year = year;
            this.price = price;
        }
    }
 
}

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

2 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 594172浏览

随机文章
SpringCloud—Zuul(三)
5年前
Kotlin-内置类型—数组(三)
4年前
SpringBoot——RabbitMQ初步整合
5年前
SpringMVC笔记17—拦截器
5年前
MyBatis笔记11—resultMap+分页
5年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 594172 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付