Spring是如何實現屬性占位符解析

Spring屬性占位符解析

      • 核心實現思路
      • 1?? 定義占位符處理器類
      • 2?? 處理 BeanDefinition 中的屬性
      • 3?? 替換具體的占位符
      • 4?? 加載配置文件
      • 5?? Getter / Setter 方法

源碼見:mini-spring

在這里插入圖片描述

在使用 Spring 框架開發過程中,為了實現配置的靈活性,通常會借助 .properties.yml 等文件來支持動態參數注入。屬性占位符 ${} 的出現,正是為了完成對這些配置值的動態替換。

在動手編碼之前,不妨先思考一個問題:Bean 的創建依賴于 BeanDefinition,那么屬性替換的動作,自然應當發生在 BeanDefinition 完成初始化之前。換句話說,我們需要找到一個能在 BeanDefinition 加載完成后、Bean 實例化前介入處理的時機。這時候,BeanFactoryPostProcessor 便是最合適的切入點。


核心實現思路

我們需要定義一個類來實現 BeanFactoryPostProcessor 接口,在 Spring 容器啟動時,利用其 postProcessBeanFactory 方法,介入 BeanDefinition 的構建過程,提前解析并替換其中的占位符內容。


1?? 定義占位符處理器類

public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {public static final String PLACEHOLDER_PREFIX = "${";public static final String PLACEHOLDER_SUFFIX = "}";private String location;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {Properties properties = loadProperties();processProperties(beanFactory, properties);}...
}

這段代碼展示了核心類的定義和處理流程的入口。通過實現接口方法 postProcessBeanFactory,在 Bean 初始化前加載配置并處理。


2?? 處理 BeanDefinition 中的屬性

private void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties) {String[] beanNames = beanFactory.getBeanDefinitionNames();for (String name : beanNames) {BeanDefinition definition = beanFactory.getBeanDefinition(name);resolvePropertyValues(definition, properties);}
}

該方法遍歷所有 Bean 的定義,并逐一處理其中的屬性值,檢測是否包含占位符格式。


3?? 替換具體的占位符

private void resolvePropertyValues(BeanDefinition beanDefinition, Properties properties) {PropertyValues values = beanDefinition.getPropertyValues();for (PropertyValue pv : values.getPropertyValueList()) {Object val = pv.getValue();if (val instanceof String) {String strVal = (String) val;int start = strVal.indexOf(PLACEHOLDER_PREFIX);int end = strVal.indexOf(PLACEHOLDER_SUFFIX);if (start != -1 && end != -1 && start < end) {String key = strVal.substring(start + 2, end);String resolved = properties.getProperty(key);StringBuffer buffer = new StringBuffer(strVal);buffer.replace(start, end + 1, resolved);values.addPropertyValue(new PropertyValue(pv.getName(), buffer.toString()));}}}
}

這是占位符替換的具體邏輯,目前僅處理格式為 ${xxx} 的情況。解析出 key 后,從已加載的配置文件中獲取對應值進行替換。


4?? 加載配置文件

public Properties loadProperties() {try {DefaultResourceLoader loader = new DefaultResourceLoader();Resource resource = loader.getResource(location);Properties props = new Properties();props.load(resource.getInputStream());return props;} catch (IOException e) {throw new BeansException(e.getMessage(), e);}
}

此方法負責從指定路徑讀取 .properties 文件并轉換為 Properties 對象,為后續替換操作提供數據支撐。


5?? Getter / Setter 方法

public String getLocation() {return location;
}
public void setLocation(String location) {this.location = location;
}

通過這些方法配置屬性文件的路徑,確保配置器能讀取到外部參數。

完整代碼

/**  * PropertyPlaceholderConfigurer 類實現 BeanFactoryPostProcessor 接口,  * 用于解析并替換 Bean 定義中的占位符。  *  * 該類主要功能是加載屬性文件,并在 BeanFactory 中的所有 Bean 定義屬性中替換相應的占位符。  *  * @author jixu * @title PropertyPlaceholderConfigurer * @date 2025/5/31 00:35 */
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {  // 占位符前綴  public static final String PLACEHOLDER_PREFIX = "${";  // 占位符后綴  public static final String PLACEHOLDER_SUFFIX = "}";  // 屬性文件路徑  private String location;  /**  * 對 BeanFactory 進行后處理的方法。該方法在 Spring 容器實例化所有 bean 之后,但在 bean 初始化之前被調用。  * 實現類可以通過該方法對 BeanFactory 進行自定義的修改或擴展。  *  * @param beanFactory 可配置的 BeanFactory 實例,允許對 bean 定義進行修改或擴展。  */  @Override  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {  // 加載屬性配置文件  Properties properties = loadProperties();  // 屬性值替換占位符  processProperties(beanFactory, properties);  }  /**  * 處理屬性,替換 BeanFactory 中所有 Bean 定義中的占位符。  *  * @param beanFactory 包含 Bean 定義的 BeanFactory 實例。  * @param properties  加載的屬性配置文件。  */  private void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties) {  String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();  for (String beanDefinitionName : beanDefinitionNames) {  BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);  // 判斷屬性當中是否有占位符存在,如果有則進行替換  resolvePropertyValues(beanDefinition, properties);  }  }  /**  * 解析并替換 Bean 定義屬性中的占位符。  *  * @param beanDefinition Bean 定義。  * @param properties     加載的屬性配置文件。  */  private void resolvePropertyValues(BeanDefinition beanDefinition, Properties properties) {  PropertyValues propertyValues = beanDefinition.getPropertyValues();  for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {  Object value = propertyValue.getValue();  if (value instanceof String) {  // TODO 僅簡單支持一個占位符的格式  String strVal = (String) value;  StringBuffer buf = new StringBuffer(strVal);  int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);  int endIndex = strVal.indexOf(PLACEHOLDER_SUFFIX);  if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {  String propKey = strVal.substring(startIndex + 2, endIndex);  String propVal = properties.getProperty(propKey);  buf.replace(startIndex, endIndex + 1, propVal);  propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buf.toString()));  }  }  }  }  /**  * 加載屬性配置文件。  *  * @return 加載的屬性配置文件。  */  public Properties loadProperties() {  try {  DefaultResourceLoader loader = new DefaultResourceLoader();  Resource resource = loader.getResource(location);  Properties properties = new Properties();  properties.load(resource.getInputStream());  return properties;  } catch (IOException e) {  throw new BeansException(e.getMessage(), e);  }  }  public String getLocation() {  return location;  }  public void setLocation(String location) {  this.location = location;  }  
}

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

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

相關文章

【大模型面試每日一題】Day 31:LoRA微調方法中低秩矩陣的秩r如何選取?

【大模型面試每日一題】Day 31&#xff1a;LoRA微調方法中低秩矩陣的秩r如何選取&#xff1f; &#x1f4cc; 題目重現 &#x1f31f;&#x1f31f; 面試官:LoRA微調方法中低秩矩陣的秩r如何選取&#xff1f;&#xff1a; #mermaid-svg-g5hxSxV8epzWyP98 {font-family:"…

字節golang后端二面

前端接口使用restful格式&#xff0c;post與get的區別是什么&#xff1f; HTTP網絡返回的狀態碼有哪些&#xff1f; go語言切片與數組的區別是什么&#xff1f; MySQL實現并發安全避免兩個事務同時對一個記錄寫操作的手段有哪些&#xff1f; 如何實現業務的冪等性&#xff08;在…

Spring Security安全實踐指南

安全性的核心價值 用戶視角的數據敏感性認知 從終端用戶角度出發,每個應用程序都涉及不同級別的數據敏感度。以電子郵件服務與網上銀行為例:前者內容泄露可能僅造成隱私困擾,而后者賬戶若被操控將直接導致財產損失。這種差異體現了安全防護需要分級實施的基本原則: // 偽…

Leetcode第451場周賽分析總結

題目鏈接 競賽 - 力扣&#xff08;LeetCode&#xff09;全球極客摯愛的技術成長平臺 題目解析 A. 3560. 木材運輸的最小成本 AC代碼 class Solution { public:long long minCuttingCost(int n, int m, int k) {if (n > m) swap(n, m); // n < m;using ll long lon…

Linux中的shell腳本

什么是shell腳本 shell腳本是文本的一種shell腳本是可以運行的文本shell腳本的內容是由邏輯和數據組成shell腳本是解釋型語言 用file命令可以查看文件是否是一個腳本文件 file filename 腳本書寫規范 注釋 單行注釋 使用#號來進行單行注釋 多行注釋 使用 : " 注釋內容…

PHP與MYSQL結合中中的一些常用函數,HTTP協議定義,PHP進行文件編程,會話技術

MYSQL&#xff1a; 查詢函數: 執行查詢語句: 1.mysql_query("SQL語法"); 凡是執行操作希望拿到數據庫返回的數據進行展示的(結果返回: 數據結果); 2.執行結果的處理:成功為結果集&#xff0c;失敗為false; 成功返回結果:SQL指令沒有錯誤&#xff0c;但是查詢結果…

數學分析——一致性(均勻性)和收斂

目錄 1. 連續函數 1.1 連續函數的定義 1.2 連續函數的性質 1.2.1 性質一 1.2.2 性質二 1.2.3 性質三 1.2.4 性質四 2. 一致連續函數 2.1 一致連續函數的定義 2.2 一致連續性定理(小間距定理)(一致連續函數的另一種定義) 2.3 一致連續性判定法 2.4 連…

湖北理元理律師事務所:企業債務優化的科學路徑與人文關懷

湖北理元理律師事務所&#xff1a;企業債務優化的科學路徑與人文關懷 在中小企業經營壓力增大的背景下&#xff0c;如何平衡債務清償與員工生計成為關鍵課題。湖北理元理律師事務所聯合計劃集團公司&#xff0c;為服務企業設計了一套兼顧法律合規性與民生保障的債務解決方案&a…

樹莓派安裝openwrt搭建軟路由(ImmortalWrt固件方案)

&#x1f923;&#x1f449;我這里準備了兩個版本的openwrt安裝方案給大家參考使用&#xff0c;分別是原版的OpenWrt固件以及在原版基礎上進行改進的ImmortalWrt固件。推薦使用ImmortalWrt固件&#xff0c;當然如果想直接在原版上進行開發也可以&#xff0c;看個人選擇。 &…

一鍵凈化Excel數據:高性能Python腳本實現多核并行清理

摘要 本文分享兩個基于Python的Excel數據凈化腳本&#xff0c;通過多進程并行技術清除工作表內不可見字符、批注、單元格樣式等冗余內容&#xff0c;利用OpenPyXL實現底層操作&#xff0c;結合tqdm進度條和進程級任務分配&#xff0c;可快速處理百萬級單元格數據。適用于數據分…

【Netty】EventLoopGroup

在Netty的ServerBootstrap中設置兩個EventLoopGroup的作用是將網絡操作的兩個關鍵階段分離到不同的線程組中處理&#xff0c;從而優化性能并簡化并發控制。具體來說&#xff1a; 1. 兩個EventLoopGroup的角色 第一個EventLoopGroup&#xff08;通常稱為bossGroup&#xff09;&…

【前端】Vue中使用CKeditor作為富文本編輯器

官網https://ckeditor.com/ 此處記錄一下我在使用的時候具體初始化的代碼。 <template><div><textarea :id"id"></textarea></div> </template><script> export default {name: CkEditor,data: function () {return {id:…

前端面經 websocket

應用層協議&#xff0c;實現一個TCP連接上的全雙工通信&#xff0c;實時通訊 之前的實時WEB 實現輪詢 增加輪詢頻率 ws wss 明文版本 和 密文版本 特點 # 1 頭部小 2 更注重實時性

【筆記】suna部署之獲取 Supabase API key 和 project URL

#工作記錄 Supabase | The Open Source Firebase Alternative 一、注冊與登錄 方式一&#xff1a;GitHub 授權登錄 在登錄頁面選擇 “繼續使用 GitHub” &#xff0c;跳轉到 GitHub 授權頁面&#xff08;如圖 5 所示&#xff09;。確認 “Supabase 的想要訪問您的 [賬戶名] 帳…

爬蟲工具鏈的詳細分類解析

以下是針對爬蟲工具鏈的詳細分類解析&#xff0c;涵蓋靜態頁面、動態渲染和框架開發三大場景的技術選型與核心特性&#xff1a; &#x1f9e9; 一、靜態頁面抓取&#xff08;HTML結構固定&#xff09; 工具組合&#xff1a;Requests BeautifulSoup 適用場景&#xff1a;目標數…

STM32F407寄存器操作(ADC非連續掃描模式)

1.前言 書接上回&#xff0c;在看手冊的時候我突然發現手冊上還描述了另一種ADC掃描模式&#xff0c;即非連續掃描模式&#xff0c;想著連續掃描模式都已經探索過了&#xff0c;那就順手把非非連續模式研究一下吧。 2.理論 我們先看看手冊&#xff0c;這里我就以規則通道舉例…

spring切面

概念 兩個特點&#xff1a; IOC控制反轉AOP主要用來處理公共的代碼 例如一個案例就是添加用戶&#xff0c;重復的代碼包含了記錄日志、事務提交和事務回滾等&#xff0c;都是重復的&#xff0c;為了簡單&#xff0c;交給AOP來做。 即將復雜的需求分解出不同方面&#xff0c…

[Python] Python中的多重繼承

文章目錄 Lora中的例子 Lora中的例子 https://github.com/michaelnny/QLoRA-LLM/blob/main/qlora_llm/models/lora.py#L211C1-L243C10如果繼承兩個父類&#xff0c;并且父類的__init__參數不一樣&#xff0c;則可以顯式的調用父類init&#xff1b;如果用super().__init__()則需…

rsync服務的搭建

目錄 一、rsync介紹 rsync的安裝 二、rsync的語法 三、rsync命令使用 1. 本機同步 2. 遠程同步 四、rsync作為服務使用 1、嘗試啟動rsync程序 2、rsync的配置文件介紹 注意事項&#xff1a; 3. rsyncinotify實時同步 3.依賴服務托管xinetd&#xff08;CentOS 6中rs…

【C/C++】面試基礎題目收集

C 軟件開發面試中常見的刷題題目通常可分為以下幾大類&#xff1a;數據結構與算法、系統編程、面向對象設計、C 語言特性、并發編程等。 &#x1f9e0; 一、數據結構與算法&#xff08;力扣/牛客經典題&#xff09; 掌握 STL 和底層結構實現能力&#xff1a; &#x1f4cc; 數…