菜单

Spring 表达式语言 (SpEL)

相关源文件

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)。每个AST节点都实现了SpelNodeImpl,并提供了解释和可选的编译功能。

关键AST节点类型

节点类型目的示例表达式
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类为各种数据结构提供了全面的索引功能。

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接口支持自定义数据结构。

内置索引支持

自定义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管理

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()
BeanResolverBean引用解析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