問題
使用spring DI注入外部properties文件屬性時,讀取到userName變量值和properties文件的值不一致。
bean屬性注入:
<!--加載配置文件-->
<context:property-placeholder location="classpath:*.properties"/><bean id="userDao" class="com.fjd.dao.impl.UserDaoImpl"><property name="userName" value="${userName}"/><property name="password" value="${password}"/>
</bean>
properties文件內容:
輸出結果:
原因分析
問題的根源在于:系統環境變量與屬性文件中的變量名沖突。在Spring 中,${} 占位符會按特定順序解析屬性源,而系統環境變量優先級高于 properties 文件。
- 沖突的變量名:
- userName 是常見系統環境變量(存儲當前登錄用戶名)
- properties 文件也定義了同名變量
- Spring 屬性解析順序:
- 當使用 ${userName} 時,Spring 按此順序查找:
- JVM 系統屬性 (-D 參數)
- 操作系統環境變量 👉 這里找到了系統用戶名!
- properties 文件中的屬性
解決方法
方案 1:重命名屬性(推薦)
- 修改 properties 文件,添加前綴避免沖突:
# 修改后
db.userName=root
db.password=123
- 修改 Spring 配置:
<bean id="userDao" class="com.fjd.dao.impl.UserDaoImpl"><property name="userName" value="${db.userName}"/><property name="password" value="${db.password}"/>
</bean>
方案 2:調整屬性源優先級(Spring 4.3+)
在 PropertySourcesPlaceholderConfigurer 中顯式設置優先級:
<bean <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"><property name="locations"><list><value>classpath*:data.properties</value></list></property><!-- 關鍵:讓本地屬性覆蓋系統環境變量 --><property name="localOverride" value="true"/></bean>
注意:確保上述bean是在applicationContext.xml文件中第一個定義的bean
- 為什么要作為第一個定義的bean(額外知識點!)
1.Spring 容器初始化順序
2.關鍵機制解析
BeanFactoryPostProcessor 的特殊性:
PropertySourcesPlaceholderConfigurer實現了BeanFactoryPostProcessor 接口
后置處理器優先:BeanFactoryPostProcessor 必須先于普通 bean
這類 bean 會在普通 bean 實例化之前執行
但多個 BeanFactoryPostProcessor 之間仍有執行順序
????????3.工作流程
Spring 首先初始化 PropertySourcesPlaceholderConfigurer
它立即加載 data.properties 并注冊到環境
設置 localOverride=true 使這些屬性覆蓋系統環境變量
當初始化 userDao 時,${userName} 已使用文件中的值
????????4.位置決定執行順序:
XML中先定義的BeanFactoryPostProcessor先執行,如果放在后面,
可能其他處理器已修改了環境