Spring Expression Language (SpEL) 是一個強大的運行時表達式語言,支持查詢和操作對象圖。語法類似 Unified EL,但提供方法調用、構造函數調用、正則匹配、集合投影/選擇、模板表達式等高級特性。SpEL 獨立於 Spring 容器,可單獨使用。
// 1. 解析器
ExpressionParser parser = new SpelExpressionParser();
// 2. 解析表達式字符串
Expression exp = parser.parseExpression("'Hello ' + 'World!'");
// 3. 獲取值(可指定類型)
String result = exp.getValue(String.class); // "Hello World!"
// 4. 設置上下文(訪問 Bean/變量)
EvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable("user", new User(1, "Alice"));
Integer id = parser.parseExpression("#user.id + 100").getValue(ctx, Integer.class); // 101
| 類別 | 語法示例 | 說明 |
|---|---|---|
| 字面量 | 'Hello', 3.14, 0xFF, true, null | 字符串用單引號,轉義用反斜杠 |
| 屬性訪問 | user.name, user['name'], list[0], map['key'] | 點號/中括號,支持嵌套 |
| 方法調用 | user.getName(), 'abc'.substring(1,2), T(Math).random() | T() 訪問類靜態方法/常量 |
| 構造調用 | new String('hello'), new ArrayList() | 直接實例化 |
| 運算符 | + - * / % ^, == eq != ne < lt > gt, and or not, matches, between | 不支持 && ||,用 and or |
| 三元/Elvis | condition ? a : b, name ?: 'default' | Elvis 操作符簡化非空判斷 |
| 正則 | '5.00' matches '^-?\\d+(\\.\\d{2})?$' | 返回 boolean |
| 集合字面量 | {1,2,3} (List), {{'a','b'},{'c','d'}} (List<List>) | 不支持多維數組字面量、Map 字面量 |
| 集合投影 | users.![name] → List<String> 姓名集合 | 對每個元素應用表達式收集結果 |
| 集合選擇 | users.?[age > 18] → 篩選成年用戶 | 返回子集合 |
| 首尾選擇 | users.^[age > 18] (首個), users.$[age > 18] (最後) | 返回單個元素 |
| 變量賦值 | #var = 'value', #varName 引用 | 在 EvaluationContext 中操作 |
| 模板表達式 | parser.parseExpression("Hello #{name}", new TemplateParserContext()) | #{...} 為表達式塊 |
<bean id="user" class="com.example.User" p:name="Alice" p:age="25"/>
<bean id="order" class="com.example.Order">
<!-- 引用其他 Bean 屬性 -->
<property name="customerName" value="#{user.name}"/>
<!-- 調用方法/靜態方法 -->
<property name="orderCode" value='#{T(java.util.UUID).randomUUID().toString()}'/>
<property name="amount" value="#{T(java.lang.Math).random() * 100}"/>
<!-- 系統屬性 -->
<property name="env" value="#{systemProperties['user.region']}"/>
<!-- Bean 間引用 -->
<property name="relatedOrder" value="#{anotherOrder}"/>
</bean>
@Component
public class AppConfig {
@Value("#{systemProperties['app.version']}")
private String version;
@Value("#{T(java.lang.Math).random() * 100}")
private double randomScore;
@Value("#{userService.defaultUser.name}") // 引用其他 Bean
private String defaultUserName;
@Value("#{'${app.prefix}' + '_' + T(java.util.UUID).randomUUID()}")
private String generatedId; // 結合 ${...} 佔位符
}
List<User> users = Arrays.asList(
new User(1, "Alice", 20),
new User(2, "Bob", 17),
new User(3, "Carol", 25)
);
EvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable("users", users);
// 投影:提取所有名字
List<String> names = parser.parseExpression("#users.![name]").getValue(ctx, List.class);
// ["Alice", "Bob", "Carol"]
// 選擇:篩選成年用戶
List<User> adults = parser.parseExpression("#users.?[age >= 18]").getValue(ctx, List.class);
// [Alice, Carol]
// 首個匹配
User firstAdult = parser.parseExpression("#users.^[age >= 18]").getValue(ctx, User.class); // Alice
模板表達式
SpEL vs 其他表達式語言
| 特性 | SpEL | OGNL | JUEL |
|---|---|---|---|
| 方法調用 | ✓ | ✓ | ✗ (僅屬性) |
| 構造調用 | ✓ | ✓ | ✗ |
| 集合投影/選擇 | ✓ | ✓ | ✗ |
| 模板表達式 | ✓ | ✗ | ✗ |
| 類型訪問 T() | ✓ | ✓ (@) | ✗ |
| Spring 生態集成 | 原生 | 需適配 | 標準 |
@Value、XML 中使用 SpEL 注入配置,減少硬編碼StandardEvaluationContext 緩存解析後的 Expression 對象,避免重複解析
5. 注意 SpEL 表達式注入風險:不可信輸入嚴禁直接作為表達式解析
SpEL 為 Spring 生態提供了統一、強大的表達式能力,掌握其核心語法與集成方式,能顯著提升配置靈活性與開發效率。