【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用

简介: 【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用

简介

Spring3引入了Spring表达式语言(SpEL),作为一种强大而简洁的Bean装配方式。它可以通过运行时执行的表达式将值动态地注入到我们的属性或构造函数中,并且支持调用JDK提供的静态常量以及获取外部Properties文件中的配置。让我们深入了解SpEL的魔法!K中提供的静态常量,获取外部Properties文件中的的配置。

用法

Spring EL表达式的解析器—SpelExpressionParser

java

复制代码

ExpressionParser parser = new SpelExpressionParser();

SpEL的文本表达式支持

SpEL的文本表达式支持多种类型,包括字符串(需要用单引号声明)、日期、数字、布尔类型和null。对于数字类型,SpEL支持负数、指数和小数,并默认使用Double.parseDouble()进行表达式类型转换。

java

复制代码

parser.parseExpression("'hello'").getValue(String.class); // hello , 注意单引号
parser.parseExpression("1.024E+3").getValue(Long.class);  // 1024  , 指数形式
parser.parseExpression("0xFFFF").getValue(Integer.class); // 65535 , 十六进制
parser.parseExpression("true").getValue(Boolean.class);   // true
parser.parseExpression("null").getValue();

SpEL对变量的处理模式

java

复制代码

// 定义变量
String name = "Tom";
EvaluationContext context = new StandardEvaluationContext();  //表达式的上下文,
context.setVariable("myName", name);                          //为了让表达式可以访问该对象, 先把对象放到上下文中
ExpressionParser parser = new SpelExpressionParser();
// 访问变量
parser.parseExpression("#myName").getValue(context, String.class);   // Tom , 使用变量
// 直接使用构造方法创建对象
parser.parseExpression("new String('aaa')").getValue(String.class);   // aaa

SpEL的属性访问和方法调用

在SpEL中,属性可以直接使用其名称进行访问,不区分大小写。对于数组和列表,可以通过下标形式(例如list[index])进行访问。而对于Map类型,可以将其键值用作索引(例如map[key])进行访问,非常方便。同时,属性名的首字母大小写均可,只有首字母才不区分大小写。

方法可以直接访问

java

复制代码

// 准备工作
EvaluationContext context = new StandardEvaluationContext();  // 表达式的上下文,
ExpressionParser parser = new SpelExpressionParser();

对象的属性访问控制

主要面向于对象的属性进行访问操作处理控制。

java

复制代码

// 一个普通的POJO
EvaluationContext context = new StandardEvaluationContext();  // 表达式的上下文,
context.setVariable("person", person);                        // 为了让表达式可以访问该对象, 先把对象放到上下文中
ExpressionParser parser = new SpelExpressionParser();
Person person = new Person("Tom", 18); 
// Tom , 属性访问
parser.parseExpression("#person.name").getValue(context, String.class); 
// Tom , 属性访问, 但是首字母大写了      
parser.parseExpression("#person.Name").getValue(context, String.class);

集合/对象访问

首先,针对于集合类型的操作访问

java

复制代码

EvaluationContext context = new StandardEvaluationContext();  // 表达式的上下文,
List<String> list = Lists.newArrayList("a", "b");
Map<String, String> map = Maps.newHashMap();
map.put("A", "1");
map.put("B", "2");
context.setVariable("person", person);                        // 为了让表达式可以访问该对象, 先把对象放到上下文中
context.setVariable("map", map);
context.setVariable("list", list);
// 列表
parser.parseExpression("#list[0]").getValue(context, String.class)           // a , 下标
// map
parser.parseExpression("#map[A]").getValue(context, String.class);           // 1 , key
// 方法
parser.parseExpression("#person.getAge()").getValue(context, Integer.class); // 18 , 方法访问

SpEL对类型以及原生类的操作和控制

T 操作符可以用于获取类型信息,并调用对象的静态方法。

java

复制代码

// 获取类型
parser.parseExpression("T(java.util.Date)").getValue(Class.class); // class java.util.Date
// 访问静态成员(方法或属性)
parser.parseExpression("T(Math).abs(-1)").getValue(Integer.class); // 1
// 判断类型
parser.parseExpression("'asdf' instanceof T(String)").getValue(Boolean.class); // true;

操作符

Spring EL 支持大多数的数学、逻辑和关系操作符。

  • 关系操作符, 包括: eq(==), ne(!=), lt()<, le(<=), gt(>), ge(>=)
  • 逻辑运算符, 包括: and(&&), or(||), not(!)
  • 数学操作符, 包括: 加(+), 减(-), 乘(*), 除(/), 取模(%), 幂指数(^)
  • 其他操作符, 如: 三元操作符, instanceof, 赋值(=), 正则匹配

另外三元操作符有个特殊的用法, 一般用于赋默认值, 比如: parseExpression("#name?:'defaultName'"), 如果变量name为空时设置默认值.

java

复制代码

parser.parseExpression("1 > -1").getValue(Boolean.class);         // true
parser.parseExpression("1 gt -1").getValue(Boolean.class);        // true
parser.parseExpression("true or true").getValue(Boolean.class);   // true
parser.parseExpression("true || true").getValue(Boolean.class);   // true
parser.parseExpression("2 ^ 3").getValue(Integer.class);          // 8
parser.parseExpression("true ? true : false").getValue(Boolean.class); // true
parser.parseExpression("#name ?: 'default'").getValue(context, String.class); // default
parser.parseExpression("1 instanceof T(Integer)").getValue(Boolean.class); // true
parser.parseExpression("'5.00' matches '^-?\d+(\.\d{2})?$'").getValue(Boolean.class); // true
parser.parseExpression("#person.name").getValue(context, String.class);  // Tom , 原来的值
parser.parseExpression("#person.name = 'Jim'").getValue(context, String.class); // Jim , 赋值之后
parser.parseExpression("#person.name").getValue(context, String.class);  // Jim, 赋值起了作用

避免空指针

当访问一个对象的属性或方法时, 若该对象为null, 就会出现空指针异常. 安全导航会判断对象是否为null,如果是的话, 就返回null而不是抛出空指针异常. 使用方式就是在对象后面加个 ?, 如下:

java

复制代码

// 使用这种表达式可以避免抛出空指针异常
parser.parseExpression("#name?.toUpperCase()").getValue(context, String.class); // null

this变量

有个特殊的变量#this来表示当前的对象. 常用于集合的过滤

java

复制代码

// this 使用示例
parser.parseExpression("{1, 3, 5, 7}.?[#this > 3]").getValue(); // [5, 7]

集合选择

可以使用选择表达式对集合进行过滤或一些操作,从而生成一个新的符合选择条件的集合, 有如下一些形式:

  • ?[expression]: 选择符合条件的元素
  • ^[expression]: 选择符合条件的第一个元素
  • $[expression]: 选择符合条件的最后一个元素
  • ![expression]: 可对集合中的元素挨个进行处理

对于集合可以配合#this变量进行过滤, 对于map, 可分别对keySet及valueSet分别使用key和value关键字;

java

复制代码

// 集合
parser.parseExpression("{1, 3, 5, 7}.?[#this > 3]").getValue(); // [5, 7] , 选择元素
parser.parseExpression("{1, 3, 5, 7}.^[#this > 3]").getValue(); // 5 , 第一个
parser.parseExpression("{1, 3, 5, 7}.$[#this > 3]").getValue(); // 7 , 最后一个
parser.parseExpression("{1, 3, 5, 7}.![#this + 1]").getValue(); // [2, 4, 6, 8] ,每个元素都加1
// map
Map<Integer, String> map = Maps.newHashMap();
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
map.put(4, "D");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("map", map);
parser.parseExpression("#map.?[key > 3]").getValue(context);             // {4=D}
parser.parseExpression("#map.?[value == 'A']").getValue(context);        // {1=A}
parser.parseExpression("#map.?[key > 2 and key < 4]").getValue(context); // {3=C}

模板表达式

模板表达式允许文字和表达式混合使用, 一般选择使用#{}作为一个定界符:

java

复制代码

EvaluationContext context = new StandardEvaluationContext();  //表达式的上下文,
ExpressionParser parser = new SpelExpressionParser();
parser.parseExpression("他的名字为#{#person.name
相关文章
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
1179 22
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
3116 0
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
3404 1
|
12月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
7472 117
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
910 2
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
1345 1
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
446 32
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
557 0
|
Java 开发者 Spring
Spring框架 - 深度揭秘Spring框架的基础架构与工作原理
所以,当你进入这个Spring的世界,看似一片混乱,但细看之下,你会发现这里有个牢固的结构支撑,一切皆有可能。不论你要建设的是一座宏大的城堡,还是个小巧的花园,只要你的工具箱里有Spring,你就能轻松搞定。
619 9

热门文章

最新文章

推荐镜像

更多
  • DNS