Java: SpEL表达式
名词解释
SpEL(Spring Expression Language),即Spring表达式语言,能在运行时构建复杂表达式、存取对象属性、对象方法调用等等,并且能与Spring功能完美整合,如能用来配置Bean定义。
实现原理
- 创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现
- 解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象
- 构造上下文:准备比如变量定义等等表达式需要的上下文数据。
- 求值:通过Expression接口的getValue方法根据上下文(EvaluationContext,RootObject)获得表达式值。
语法
基本表达式
字面量表达式
ExpressionParser parser = new SpelExpressionParser();
String str1 = parser.parseExpression("'Hello World!'").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+2").getValue(Double.class);
int hex1 = parser.parseExpression("0xa").getValue(Integer.class);
long hex2 = parser.parseExpression("0xaL").getValue(long.class);
boolean true1 = parser.parseExpression("true").getValue(boolean.class);
Object null1 = parser.parseExpression("null").getValue(Object.class);
算数运算表达式
- 加减乘除 + - * /
- 取余 %
- 幂 ^
关系表达式
- 常规 == ; != ; > ; >= ; < ; <=
- 特殊 between
运算符右边操作数必须是列表类型,且只能包含2个元素。第一个元素为开始,第二个元素为结束,区间运算是包含边界值的,即 xxx>=list.get(0) && xxx<=list.get(1)
parser.parseExpression("1 between {1, 2}").getValue(boolean.class); // true
逻辑表达式
- && ; || ; !
三目运算表达式
- 1 > 2 ? true : false -> false
类相关表达式
使用“T(Type)”来表示java.lang.Class实例,“Type”必须是类全限定名,“java.lang”包除外,即该包下的类可以不指定包名;使用类类型表达式还可以进行访问类静态方法及类静态字段。
ExpressionParser parser = new SpelExpressionParser();
String expression = "T(Math).random()";
Double value = parser.parseExpression(expression).getValue(Double.class);
String dateExpression = "new java.util.Date().toInstant()";
Instant instant = parser.parseExpression(dateExpression).getValue(Instant.class);
// 这边#this就是指带传进去的instant实例,把它作为rootObject
String dateClassExpression = "T(java.util.Date).from(#this)";
Date date = parser.parseExpression(dateClassExpression).getValue(instant, Date.class);
0.4828331204176707
2023-04-12T12:50:06.429Z
Wed Apr 12 20:50:06 CST 2023
StandardEvaluationContext相关表达式
- Variable
Order order = new Order();
order.setOrderPrice(new OrderPrice(88));
OrderItem orderItem = new OrderItem();
orderItem.setId(2);
StandardEvaluationContext context = new StandardEvaluationContext();
ExpressionParser parser = new SpelExpressionParser();
context.setVariable("order", order);
context.setVariable("orderItem", orderItem);
// 使用“#variable”来引用在EvaluationContext定义的变量
String expression = "#order.orderPrice.price * #orderItem.id";
Integer value = parser.parseExpression(expression).getValue(context, Integer.class);
176
- RootObject
StandardEvaluationContext context = new StandardEvaluationContext();
ExpressionParser parser = new SpelExpressionParser();
context.setRootObject(this);
// “#this”引用当前上下文对象,使用“#root”引用根对象
Object thisObj = parser.parseExpression("#this").getValue(context);
Object thisRoot = parser.parseExpression("#root").getValue(context);
com.th.test.SpELTest@67d18ed7
com.th.test.SpELTest@67d18ed7
- BeanResolver
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
Order order = new Order();
order.setId(55);
factory.registerSingleton("test", order);
StandardEvaluationContext context = new StandardEvaluationContext();
ExpressionParser parser = new SpelExpressionParser();
context.setBeanResolver(new BeanFactoryResolver(factory));
// 使用“@”符号来引用Bean
Integer id = parser.parseExpression("@test.id").getValue(context, Integer.class);
55
集合相关表达式
ExpressionParser parser = new SpelExpressionParser();
Collection list = parser.parseExpression("{4,5,6,7}").getValue(Collection.class);
Integer first = parser.parseExpression("#this[0]").getValue(list, Integer.class);
[4, 5, 6, 7]
4
表达式模版
每个表达式块由“前缀+表达式+后缀”形式组成
SpelExpressionParser parser = new SpelExpressionParser();
ParserContext context = new TemplateParserContext("%{", "}");
Expression expression = parser.parseExpression("Hello, My name is %{#name}, age is %{#age}", context);
EvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setVariable("name", "Aaron");
evaluationContext.setVariable("age", 23);
String value = expression.getValue(evaluationContext, String.class);
Hello, My name is Aaron, age is 23
整合Spring替换模版
使用BeanFactoryPostProcessor接口提供postProcessBeanFactory回调方法,它是在IoC容器创建好实例化Bean对象之前被ApplicationContext实现调用,因此在这个阶段把SpEL前缀及后缀修改掉是安全的
@Component
public class SpelBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanExpressionResolver beanExpressionResolver = beanFactory.getBeanExpressionResolver();
if (beanExpressionResolver instanceof StandardBeanExpressionResolver) {
StandardBeanExpressionResolver resolver = (StandardBeanExpressionResolver) beanExpressionResolver;
resolver.setExpressionPrefix("%{");
resolver.setExpressionSuffix("}");
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器