Spring表达式语言(SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。它在Spring框架内提供了一个统一的表达式求值框架,能够进行动态属性访问、方法调用、集合操作以及带有可选字节码编译的复杂表达式求值以优化性能。
有关核心实用工具和类型转换的信息,请参阅核心实用工具和类型系统与转换。有关注解处理功能,请参阅注解处理。
SpEL遵循传统的解释器模式,并具有可选的编译功能。该系统通过标记化、解析和AST构造将文本表达式转换为可执行代码。
来源: spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java1-50
来源: spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpressionParser.java1-100 spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java1-100
解析系统将表达式字符串转换为可执行的抽象语法树(AST)。每个AST节点都实现了SpelNodeImpl,并提供了解释和可选的编译功能。
| 节点类型 | 目的 | 示例表达式 |
|---|---|---|
Indexer | 数组、列表、映射和对象索引 | array[0], map['key'], obj['property'] |
MethodReference | 方法调用 | object.method(args) |
PropertyOrFieldReference | 属性/字段访问 | object.property |
FunctionReference | 函数调用 | #functionName(args) |
ConstructorReference | 对象构造 | new java.lang.String('text') |
Indexer类为各种数据结构提供了全面的索引功能。
Indexer使用?.[]运算符支持空安全操作,并通过配置的TypeConverter进行类型自动转换。
来源: spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java48-87 spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java501-1067
求值系统在EvaluationContext中执行AST节点,该上下文提供变量、函数、类型转换器以及属性/方法解析器的访问权限。
在求值期间,AST节点使用ExpressionState来维护上下文,并使用ValueRef实现来进行读/写操作。
来源: spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java200-250
SpEL通过可配置的访问器接口提供灵活的属性和方法访问,默认实现支持基于反射的访问。
ReflectivePropertyAccessor是属性访问的主要实现,支持基于字段和方法的属性访问。
ReflectiveMethodResolver处理方法发现和调用,支持可变参数、类型转换和方法缓存。
| 方法解析功能 | 实现 |
|---|---|
| 方法重载 | ArgumentsMatchKind评分系统 |
| 可变参数支持 | ReflectionHelper.convertAllArguments() |
| 类型转换 | 通过TypeConverter自动进行 |
| 缓存 | CachedMethodExecutor用于性能优化 |
来源: spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java72-115 spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java49-135
SpEL的索引系统不仅限于基本的数组/集合访问,还通过IndexAccessor接口支持自定义数据结构。
ReflectiveIndexAccessor通过委托给指定的getter/setter方法来实现对自定义类型的索引。
来源: spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java217-304 spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveIndexAccessor.java1-50
SpEL包含一个复杂的编译系统,可以将解释过的AST节点转换为高效的字节码,以满足性能关键型场景的需求。
| AST节点类型 | 编译支持 | 要求 |
|---|---|---|
Indexer | 完全支持 | 公共类型,兼容的访问器 |
MethodReference | 完全支持 | 公共方法,无参数转换 |
PropertyOrFieldReference | 完全支持 | 公共属性/字段 |
FunctionReference | 部分支持 | 仅限静态方法 |
ConstructorReference | 完全支持 | 公共构造函数 |
CodeFlow类负责管理字节码生成状态,包括类型描述符、变量分配和指令生成。
来源: spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java47-106 spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java307-336
SpEL提供了几个关键接口,支持自定义和扩展表达式求值能力。
| 接口 | 目的 | 关键方法 |
|---|---|---|
PropertyAccessor | 自定义属性访问 | canRead(), read(), canWrite(), write() |
MethodResolver | 自定义方法解析 | resolve() |
IndexAccessor | 自定义索引支持 | canRead(), read(), canWrite(), write() |
TypeConverter | 类型转换 | canConvert(), convertValue() |
BeanResolver | Bean引用解析 | resolve() |
对于性能关键型场景,扩展接口可以实现支持编译的变体。
来源: spring-expression/src/main/java/org/springframework/expression/spel/CompilablePropertyAccessor.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/CompilableIndexAccessor.java1-50 spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java100-200