type
Post
status
Published
date
Nov 16, 2022
slug
summary
tags
Java
category
技术分享
icon
password
Property
Apr 4, 2023 08:58 AM
Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于Unified EL,但提供了额外的功能,特别是方法调用和基本的字符串模板功能。同时因为SpEL是以API接口的形式创建的,所以允许将其集成到其他应用程序和框架中。
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEl可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。

环境demo

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.3.15</version> </dependency> </dependencies>
package spel; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class demo{ public static void main(String[] args) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec(\"calc.exe\")"); Object value = exp.getValue().toString(); } }

调用链分析

获取解析器配置

ExpressionParser parser = new SpelExpressionParser()
notion image
SpelExpressionParser中的configuration存放的是SpEL表达式解析器的配置选项,包括自定义函数、变量和类型注册等选项。这些配置选项可以用来扩展表达式语言的功能,使其能够更好地满足应用程序的需求。例如,可以使用配置选项向表达式语言中添加自定义函数,以便在表达式中调用自定义逻辑。

初始化spel解析对象

接着来到获取Expression对象并将spel表达式填入,在new SpelExpression(expressionString, ast, this.configuration)将结合我们给的表达式和解析器配置信息返回一个已经初始化好的SpelExpression对象
notion image
此前的调用栈如下:
doParseExpression:138, InternalSpelExpressionParser (org.springframework.expression.spel.standard) doParseExpression:61, SpelExpressionParser (org.springframework.expression.spel.standard) doParseExpression:33, SpelExpressionParser (org.springframework.expression.spel.standard) parseExpression:52, TemplateAwareExpressionParser (org.springframework.expression.common) parseExpression:43, TemplateAwareExpressionParser (org.springframework.expression.common) main:10, demo (spel)

执行spel表达式

Object value = exp.getValue()
先通过 ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration); 创建获取一个新的表达式状态实例对象
notion image
步入下一段代码,Object result = this.ast.getValue(expressionState);
这里调用了SpelExpression对象的ast属性(CompoundExpression对象)的getValue方法,并传入ExpressionState对象。持续步入到getValueRef:61, CompoundExpression (org.springframework.expression.spel.ast) 可以看到其中有个循环遍历this.children数组,拿到数组中的getRuntime等(记住这个遍历)
notion image
步入至getValueInternal:103, MethodReference (org.springframework.expression.spel.ast)并传入以下四个参数
notion image
接下来来到个次重点的地方,findAccessorForMethod:202, MethodReference (org.springframework.expression.spel.ast) 该方法用于SpEL解析对象属性表达式时可引用、调用给定的类、方法和参数类型所匹配的方法,将返回的MethodExecutor对象赋给executorToUse
notion image
notion image
最后跟进到 executorToUse.execute(evaluationContext, value, arguments); 最后通过反射调用getRuntime的exec方法实现命令执行
notion image
此前的调用栈为:
execute:130, ReflectiveMethodExecutor (org.springframework.expression.spel.support) getValueInternal:139, MethodReference (org.springframework.expression.spel.ast) access$000:55, MethodReference (org.springframework.expression.spel.ast) getValue:387, MethodReference$MethodValueRef (org.springframework.expression.spel.ast) getValueInternal:92, CompoundExpression (org.springframework.expression.spel.ast) getValue:112, SpelNodeImpl (org.springframework.expression.spel.ast) getValue:141, SpelExpression (org.springframework.expression.spel.standard) main:11, demo (spel)

小结

SpEL表达式的命令注入命令执行实际上也用反射去获取Class对象、methodToInvoke方法和arguments执行参数,最后还是用反射的invoke方法去执行命令
EL表达式注入源码分析defineClass动态加载类构造回显