Spring 表達式語言(SpEL)深度解析:從基礎到高級實戰指南

目錄

一、SpEL是什么?為什么需要它?

核心價值:

典型應用場景:

二、基礎語法快速入門

1. 表達式解析基礎

2. 字面量表示

3. 屬性訪問

三、SpEL核心特性詳解

1. 集合操作

2. 方法調用

3. 運算符大全

4. 類型操作

四、Spring集成實戰

1. XML配置中的SpEL

2. 注解配置中的SpEL

3. Spring Data中的SpEL

五、高級技巧與最佳實踐

1. 自定義函數擴展

2. 模板表達式

3. 安全沙箱限制

六、性能優化指南

1. 表達式編譯

2. 緩存策略

3. 避免性能陷阱

七、SpEL在Spring生態系統中的應用

1. Spring Security

2. Spring Integration

3. Spring Batch

八、總結:SpEL最佳實踐


一、SpEL是什么?為什么需要它?

Spring Expression Language?(SpEL) 是Spring框架3.0引入的動態表達式引擎,它允許在運行時查詢和操作對象圖。與OGNL和MVEL類似,但深度集成Spring生態。

核心價值:

典型應用場景:

  1. Spring XML/注解配置中的動態值

  2. Spring Security的權限表達式

  3. Spring Data的查詢方法

  4. Thymeleaf模板引擎

  5. @Value注解注入

二、基礎語法快速入門

1. 表達式解析基礎

ExpressionParser parser = new SpelExpressionParser();// 數學運算
Expression exp = parser.parseExpression("100 * 2 + 50");
Integer result = (Integer) exp.getValue();  // 250// 字符串操作
exp = parser.parseExpression("'Hello ' + 'World'");
String str = exp.getValue(String.class);  // "Hello World"

2. 字面量表示

類型示例
整型42,?0x2A
浮點型3.14,?6.022e23
布爾型true,?false
字符串'Single Quote'
Nullnull

3. 屬性訪問

public class User {private String name;private Address address;// getters
}// 鏈式屬性訪問
exp = parser.parseExpression("address.city");
String city = exp.getValue(user, String.class); // 獲取user.address.city
 

三、SpEL核心特性詳解

1. 集合操作

列表/數組訪問:

List<String> list = Arrays.asList("a","b","c");// 索引訪問
exp = parser.parseExpression("[1]"); 
String elem = exp.getValue(list, String.class); // "b"// 集合投影(返回新集合)
exp = parser.parseExpression("![toUpperCase()]");
List<String> upper = exp.getValue(list, List.class); // ["A","B","C"]

Map訪問:

Map<String, Integer> scores = Map.of("Tom", 90, "Jerry", 85);// Key訪問
exp = parser.parseExpression("['Tom']");
Integer score = exp.getValue(scores, Integer.class); // 90

2. 方法調用

// 調用String方法
exp = parser.parseExpression("'abc'.substring(1, 2)");
String substr = exp.getValue(String.class); // "b"// 調用Bean方法
exp = parser.parseExpression("calculateDiscount(0.1)");
Double discount = exp.getValue(orderService, Double.class);

3. 運算符大全

類別運算符示例
算術+,?-,?*,?/,?%,?^2^3?→ 8
關系<,?>,?==,?<=,?>=,?!=price > 100 ? 'high' : 'low'
邏輯and,?or,?notisVip and age > 18
正則matchesemail matches '[a-z]+@domain\\.com'
三元?:status ?: 'default'
Elvis?:name ?: 'Unknown'
Safe導航?.user?.address?.city

4. 類型操作

// 類型判斷
exp = parser.parseExpression("'abc' instanceof T(String)");
Boolean isString = exp.getValue(Boolean.class); // true// 靜態方法調用
exp = parser.parseExpression("T(java.lang.Math).random()");
Double random = exp.getValue(Double.class);// 構造函數
exp = parser.parseExpression("new com.example.User('Alice')");
User user = exp.getValue(User.class);
 

四、Spring集成實戰

1. XML配置中的SpEL

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"><property name="jdbcUrl" value="#{systemProperties['db.url'] ?: 'jdbc:mysql://localhost:3306/default'}"/><property name="maximumPoolSize" value="#{T(java.lang.Runtime).getRuntime().availableProcessors() * 2}"/>
</bean>

2. 注解配置中的SpEL

@Value("#{systemEnvironment['APP_HOME']}")
private String appHome;@Scheduled(fixedDelay = #{@configService.getInterval() * 1000})
public void scheduledTask() {// 定時任務
}@PreAuthorize("hasRole('ADMIN') or #user.id == authentication.principal.id")
public void updateUser(User user) {// 方法安全控制
}

3. Spring Data中的SpEL

// 查詢方法中的SpEL
public interface UserRepository extends JpaRepository<User, Long> {@Query("SELECT u FROM User u WHERE u.status = :#{#status ?: 'ACTIVE'}")List<User> findByStatus(@Param("status") String status);
}// 實體中的默認值
@Entity
public class Article {@Id@GeneratedValueprivate Long id;@Columnprivate Date publishDate;@Transient@Value("#{T(java.time.LocalDate).now()}")private LocalDate currentDate;
}
 

五、高級技巧與最佳實踐

1. 自定義函數擴展

public class StringUtils {public static String reverse(String input) {return new StringBuilder(input).reverse().toString();}
}// 注冊自定義函數
StandardEvaluationContext context = new StandardEvaluationContext();
context.registerFunction("reverse", StringUtils.class.getDeclaredMethod("reverse", String.class));// 使用自定義函數
Expression exp = parser.parseExpression("#reverse('hello')");
String result = exp.getValue(context, String.class); // "olleh"

2. 模板表達式

ParserContext templateContext = new TemplateParserContext();// 解析模板
String template = "用戶#{#user.name}的余額是#{#account.balance}";
Expression exp = parser.parseExpression(template, templateContext);// 設置上下文變量
context.setVariable("user", currentUser);
context.setVariable("account", userAccount);String message = exp.getValue(context, String.class);

3. 安全沙箱限制

// 創建安全上下文
SecurityManager securityManager = new SecurityManager();
securityManager.setRestrict(true);
securityManager.setAllowedTypes(Collections.singleton(String.class));StandardEvaluationContext context = new StandardEvaluationContext();
context.setSecurityManager(securityManager);// 嘗試執行危險操作(將被阻止)
try {Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec('rm -rf /')");exp.getValue(context); // 拋出SecurityException
} catch (EvaluationException e) {System.err.println("危險操作被阻止!");
}
 

六、性能優化指南

1. 表達式編譯

// 解析并編譯表達式(提升10倍性能)
SpelCompiler compiler = SpelCompiler.getCompiler();
Expression compiledExp = compiler.compile(parser.parseExpression("amount * taxRate"));// 重復使用編譯后的表達式
for (Order order : orders) {Double tax = compiledExp.getValue(order, Double.class);
}

2. 緩存策略

// 使用ConcurrentHashMap緩存編譯后表達式
private static final Map<String, Expression> EXPR_CACHE = new ConcurrentHashMap<>();public Object evaluate(String exprStr, Object root) {Expression expr = EXPR_CACHE.computeIfAbsent(exprStr, key -> parser.parseExpression(key));return expr.getValue(root);
}

3. 避免性能陷阱

// 錯誤示例:每次解析新表達式
@Scheduled(fixedRate = 5000)
public void process() {Expression exp = parser.parseExpression(ruleEngine.getRule()); // 頻繁解析exp.getValue(context);
}// 正確方案:預編譯+緩存
private final Map<String, Expression> ruleCache = new ConcurrentHashMap<>();public void process() {Expression exp = ruleCache.computeIfAbsent(ruleEngine.getRule(), key -> parser.parseExpression(key));exp.getValue(context);
}
 

七、SpEL在Spring生態系統中的應用

1. Spring Security

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {// 方法級安全控制
}public interface BankService {@PreAuthorize("hasPermission(#accountId, 'ACCOUNT', 'WRITE')")void withdraw(Long accountId, BigDecimal amount);
}

2. Spring Integration

<int:router input-channel="input" expression="headers['type']"><int:mapping value="order" channel="orderChannel"/><int:mapping value="payment" channel="paymentChannel"/>
</int:router>

3. Spring Batch

<batch:job id="importJob"><batch:step id="processFile" next="#{jobParameters['skipStep'] ? 'skipStep' : 'nextStep'}"><!-- 步驟配置 --></batch:step>
</batch:job>
 

八、總結:SpEL最佳實踐

  1. 適用場景

    • ? 動態配置值

    • ? 條件化Bean創建

    • ? 安全表達式

    • ? 簡單業務規則

    • ? 復雜業務邏輯(應使用Java代碼)

  2. 性能黃金法則

    安全建議

    • 永遠不要執行不受信任的表達式

    • 生產環境啟用SecurityManager

    • 限制可訪問的類和包

終極提示:在Spring Boot中,可通過spring.expression.compiler.mode設置編譯器模式(IMMEDIATE, MIXED, OFF)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87971.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87971.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87971.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法導論第二十四章 深度學習前沿:從序列建模到創造式AI

第二十四章 深度學習前沿&#xff1a;從序列建模到創造式AI 算法的進化正在重新定義人工智能的邊界 深度學習作為機器學習領域最活躍的分支&#xff0c;正以驚人的速度推動著人工智能的發展。本章將深入探討五大前沿方向&#xff0c;通過原理分析、代碼實現和應用場景展示&…

抽象工廠設計模式

1.問題背景&#xff1a; 現在有兩個產品(Product)分別是手機殼(PhoneCase)和耳機(EarPhone)&#xff0c;但是他們會來自于各個生產廠商&#xff0c;比如說Apple和Android等等 那么至少會有四個產品&#xff0c;分別是安卓手機殼&#xff0c;安卓耳機&#xff0c;蘋果手機殼&a…

GESP 3級 C++ 知識點總結

根據GESP考試大綱 (2024年3月版)&#xff0c;幫大家總結一下GESP 3級 C語言的知識點&#xff1a; 核心目標&#xff1a; 掌握C程序的基本結構&#xff0c;理解并能運用基礎的編程概念解決稍復雜的問題&#xff0c;重點是函數、一維數組和字符串處理。 主要知識點模塊&#x…

騰訊云主動型云蜜罐技術解析:云原生威脅狩獵的革新實踐(基于騰訊云開發者社區技術網頁與行業實踐)

摘要 騰訊云主動型云蜜罐&#xff08;Active Cloud Honeypot&#xff09;通過動態誘捕機制和云原生架構&#xff0c;在威脅檢測效率、攻擊鏈還原深度、防御聯動實時性等維度實現突破。相較于傳統蜜罐&#xff0c;其核心優勢體現在&#xff1a; 部署效率&#xff1a;分鐘級完成…

企業微信wecom/jssdk的使用(入門)

概述 記錄一個企業微信jssdk的使用&#xff0c;因為要用到圖片上傳、掃描二維碼等工具。項目是uniapp開發的h5項目&#xff0c;fastadmin&#xff08;thinkphp5&#xff09;后端 先看官方文檔 https://developer.work.weixin.qq.com/document/path/90547#%E5%BA%94%E7%94%A8…

大零售生態下開源鏈動2+1模式、AI智能名片與S2B2C商城小程序的協同創新研究

摘要&#xff1a;在流量紅利消退、零售形態多元化的背景下&#xff0c;大零售生態成為商業發展的新趨勢。本文聚焦開源鏈動21模式、AI智能名片與S2B2C商城小程序在零售領域的協同應用&#xff0c;探討其如何打破傳統零售邊界&#xff0c;實現流量變現與用戶資產化。研究表明&am…

Scrapy全流程(一)

創建一個scrapy項目:scrapy startproject mySpider 生成一個爬蟲:scrapy genspider douban movie.douban.com 提取數據:完善spider&#xff0c;使用xpath等方法 保存數據:pipeline中保存數據 2 創建scrapy項目 下面以抓取豆瓣top250來學習scrapy的入門使用&#xff1a;豆瓣…

【Elasticsearch】TF-IDF 和 BM25相似性算法

在 Elasticsearch 中&#xff0c;TF-IDF 和 BM25 是兩種常用的文本相似性評分算法&#xff0c;但它們的實現和應用場景有所不同。以下是對這兩種算法的對比以及在 Elasticsearch 中的使用情況&#xff1a; TF-IDF - 定義與原理&#xff1a;TF-IDF 是一種經典的信息檢索算法&am…

【QT】控件二(輸入類控件、多元素控件、容器類控件與布局管理器)

文章目錄 1.輸入類控件1.1 LineEdit1.2 Text Edit1.3 Combo Box1.4 SpinBox1.5 Date Edit & Time Edit1.6 Dial1.7 Slider 2. 多元素控件2.1 List Widget2.2 Table Widget2.3 Tree Widget 3. 容器類控件3.1 Group Box3.2 Tab Widget 4. 布局管理器4.1 垂直布局4.2 水平布局…

【Docker基礎】Docker鏡像管理:docker pull詳解

目錄 1 Docker鏡像基礎概念 1.1 什么是Docker鏡像&#xff1f; 1.2 鏡像與容器的關系 1.3 鏡像倉庫(Registry) 2 docker pull命令詳解 2.1 基本語法 2.2 參數解釋 2.3 拉取鏡像的基本流程 2.4 鏡像分層結構解析 3 docker pull實戰指南 3.1 基本使用示例 3.2 指定鏡…

PixPin:一個強大且免費的截圖貼圖工具

PixPin 是一款國產免費的截圖工具&#xff0c;支持屏幕截圖、屏幕錄制&#xff08;GIF&#xff09;、文字識別&#xff08;OCR&#xff09;以及貼圖等功能。 高效截圖 PixPin 支持自由選擇或自動檢測窗口&#xff0c;自定義截圖區域&#xff0c;像素級精確捕捉&#xff0c;延時…

【測試報告】論壇系統

一、項目背景 1.1 測試目標及測試任務 測試目標旨在保障功能無漏洞、流程順暢&#xff0c;實現多端顯示交互一致&#xff0c;達成高并發場景下響應時間&#xff1c;2 秒等性能指標&#xff0c;抵御 SQL 注入等安全攻擊&#xff0c;提升 UI 易用性與提示友好度&#xff1b; 背…

30天pytorch從入門到熟練(day1)

一、總體工作思路 本項目采用“從零構建”的策略&#xff0c;系統性地開展了深度學習模型的開發與優化工作。其目標在于通過全流程自研方式&#xff0c;深入理解模型構建、訓練優化、推理部署的關鍵技術環節。整體路徑分為以下核心階段&#xff1a; 模型初步構建&#xff1a;以…

Subway Surfers Blast × 亞矩陣云手機:手游矩陣運營的終極變現方案

引爆全球&#xff1a;Subway Surfers Blast的流量紅利?? 隨著Sybo Games最新力作《Subway Surfers Blast》全球上線&#xff0c;這款休閑消除游戲迅速席卷各大應用商店榜單。對于手游推廣者而言&#xff0c;如何高效獲取這波流量紅利&#xff1f;??亞矩陣云手機專業手游推…

mysql join的原理及過程

連接過程 每獲得一條驅動表記錄&#xff0c;就立即到被驅動表尋找匹配的記錄。 對于兩表連接來說&#xff0c;驅動表只會被訪問一遍&#xff0c;但被驅動表卻要被訪問好多遍;具體訪問幾遍取決于對驅動表執行單表查詢后的結果集中有多少條記錄。 ? 對于內連接來說&#xff0…

Hologres的EXPLAIN和EXPLAIN ANALYZE簡介

文章目錄 一、執行計劃1、概念簡介2、使用方式①、EXPLAIN②、EXPLAIN ANALYZE 二、算子解讀1、SCAN2、Index Scan和 Index Seek3、Filter4、Decode5、Redistribution6、Join7、Broadcast8、Shard prune和Shards selected9、ExecuteExternalSQL10、Aggregate11、Sort12、Limit1…

49-Oracle init.ora-PFILE-SPFILE-啟動參數轉換實操

一早出現EMCC掛了&#xff0c;之后發現EMCC依賴的instance 掛了&#xff0c;重啟startup后發現spfile無法啟動。還是和小伙伴把基礎問題搞清。spfile是動態文件、動態文件、動態文件&#xff0c;linux下vi看起來部分亂碼部分是可編輯的&#xff0c;vi即使可以編輯也需要轉換成p…

spring碎片

包的掃描過程 判斷當前是否是文件夾獲取文件夾里面的所有內容判斷文件夾是否為空,為空的話直接返回如果文件夾不為空,則遍歷文件夾里面的所有內容 遍歷得到每個file對象,繼續進行判斷,如果還是文件,則進一步進行遞歸遍歷得到的file對象不是文件夾,是文件得到包路徑類名稱-字符…

如何形成項目經驗在多個項目間的高效復用?

要實現項目經驗的跨項目高效復用&#xff0c;核心在于建立系統化總結機制、標準化知識表達、平臺化共享工具。其中&#xff0c;標準化知識表達尤為關鍵&#xff0c;它通過統一模板和分類體系&#xff0c;確保不同項目的經驗可以被快速理解、輕松匹配到新場景&#xff0c;從而提…

目標檢測之YOLOV11談談OBB

引言&#xff1a;從軸對齊到定向邊界框的范式轉變 在計算機視覺領域&#xff0c;目標檢測算法長期受限于軸對齊邊界框&#xff08;AABB&#xff09;的固有缺陷——當面對航拍圖像中的艦船、遙感影像中的建筑物或工業質檢中的傾斜零件時&#xff0c;傳統邊界框會包含大量背景噪…