问题
使用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先执行,如果放在后面,
可能其他处理器已修改了环境