spring引入外部属性文件
Spring 引入外部属性文件的完整指南:从 XML 到注解配置
在 Spring 开发中,将数据库连接信息、API 密钥等配置项硬编码到配置文件中会导致维护困难(如切换环境时需修改大量配置)。通过引入外部属性文件(如 .properties 或 .yml),可以实现 “配置与代码分离”,提升项目的可维护性。本文将详细讲解 Spring 引入外部属性文件的多种方式(XML 配置、注解配置)、底层原理及最佳实践。
外部属性文件的核心作用
外部属性文件(通常为 .properties 格式)用于存储键值对形式的配置信息,如:
# db.properties
db.url=jdbc:mysql://localhost:3306/test
db.username=root
db.password=123456
db.driver=com.mysql.cj.jdbc.Driver
引入外部属性文件的优势:
- 分离配置与代码:避免在 Spring 配置文件(XML / 注解)中硬编码环境相关信息;
- 多环境适配:不同环境(开发 / 测试 / 生产)可使用不同的属性文件,无需修改核心配置;
- 安全性:敏感信息(如密码)可单独管理,避免提交到代码仓库。
XML 配置方式:<context:property-placeholder>
这是传统 Spring 项目中引入外部属性文件的经典方式,通过 XML 标签 <context:property-placeholder> 实现。
1. 基本用法(单文件引入)
步骤 1:创建属性文件
在 src/main/resources 目录下创建 db.properties:
# db.properties
db.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
db.username=root
db.password=123456
db.driver=com.mysql.cj.jdbc.Driver
步骤 2:在 XML 中引入并使用
在 Spring 配置文件(如 applicationContext.xml)中添加命名空间并引入属性文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" <!-- 引入context命名空间 -->
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 声明schema位置 -->
<!-- 引入外部属性文件:classpath表示从类路径下查找 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 使用${key}引用属性值 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${db.url}"/> <!-- 引用db.url -->
<property name="username" value="${db.username}"/> <!-- 引用db.username -->
<property name="password" value="${db.password}"/> <!-- 引用db.password -->
<property name="driverClassName" value="${db.driver}"/> <!-- 引用db.driver -->
</bean>
</beans>
2. 进阶用法(多文件引入与配置)
(1)引入多个属性文件
通过逗号分隔多个文件路径,支持通配符(*)批量引入:
<!-- 引入多个属性文件 -->
<context:property-placeholder
location="classpath:db.properties, classpath:redis.properties"/>
<!-- 通配符批量引入(匹配所有以config开头的properties文件) -->
<context:property-placeholder location="classpath:config*.properties"/>
(2)设置默认值与忽略未找到的文件
default-value:当属性未找到时使用默认值;ignore-resource-not-found:忽略未找到的文件(避免启动报错);ignore-unresolvable:忽略无法解析的占位符(适用于多配置中心场景)。
<context:property-placeholder
location="classpath:db.properties, classpath:app.properties"
ignore-resource-not-found="true" <!-- 忽略未找到的文件 -->
ignore-unresolvable="true"/> <!-- 忽略无法解析的占位符 -->
<!-- 使用默认值:若db.maxActive未配置,默认值为10 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="maxActive" value="${db.maxActive:10}"/> <!-- 冒号后为默认值 -->
</bean>
注解配置方式:@PropertySource
在基于注解的 Spring 项目(如 Spring Boot)中,推荐使用 @PropertySource 注解引入外部属性文件,配合 @Value 注解获取属性值。
1. 基本用法(单文件引入)
步骤 1:创建属性文件
同 XML 方式,在 src/main/resources 下创建 db.properties。
步骤 2:在配置类中引入并使用
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.beans.factory.annotation.Value;
import com.alibaba.druid.pool.DruidDataSource;
// 引入外部属性文件:classpath:db.properties
// encoding解决中文乱码
public class DataSourceConfig {
// 通过@Value("${key}")获取属性值
private String url;
private String username;
private String password;
private String driverClassName;
// 定义数据源Bean
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
return dataSource;
}
}
2. 进阶用法(多文件与动态配置)
(1)引入多个属性文件
通过 @PropertySources 组合多个 @PropertySource:
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.annotation.PropertySource;
public class AppConfig {
// ...
}
(2)支持 YAML 文件(需额外配置)
@PropertySource 默认不支持 .yml 文件,需自定义 PropertySourceFactory 实现:
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.yaml.snakeyaml.Yaml;
import java.io.IOException;
import java.util.Properties;
// 自定义YAML文件解析工厂
public class YamlPropertySourceFactory implements PropertySourceFactory {
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
Properties properties = new Properties();
// 解析YAML文件为Properties
properties.loadFromXML(new Yaml().loadAs(resource.getInputStream(), String.class).getBytes());
return new PropertiesPropertySource(resource.getResource().getFilename(), properties);
}
}
// 在配置类中使用
public class YamlConfig {
private String appName;
// ...
}
底层原理:PropertySourcesPlaceholderConfigurer
无论是 XML 标签 <context:property-placeholder> 还是注解 @PropertySource,其底层都依赖 PropertySourcesPlaceholderConfigurer 类(BeanFactoryPostProcessor 的实现类),核心逻辑如下:
- 加载属性文件:读取
location指定的属性文件,将键值对存入PropertySources; - 替换占位符:遍历容器中的
BeanDefinition,将${key}形式的占位符替换为属性文件中的对应值; - 优先级处理:若多个文件存在相同 key,后加载的文件会覆盖先加载的(就近原则)。
注意:
PropertySourcesPlaceholderConfigurer是BeanFactoryPostProcessor的实现类,在 Bean 实例化前 执行(参考 “Spring 扩展接口” 中的BeanFactoryPostProcessor阶段),确保占位符在 Bean 初始化前被替换。
Spring Boot 中的扩展:更灵活的配置管理
Spring Boot 对外部属性文件的支持更强大,默认支持:
- 多环境配置:通过
application-{profile}.properties区分环境(如application-dev.properties对应开发环境); - 配置优先级:外部配置(如命令行参数、系统环境变量)可覆盖内部属性文件;
- 类型安全绑定:通过
@ConfigurationProperties将属性绑定到 Java 类(替代@Value)。
示例:Spring Boot 中使用 @ConfigurationProperties
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
// 绑定前缀为"db"的属性
public class DbConfig {
private String url;
private String username;
private String password;
private String driver;
// getter和setter(必须提供,否则无法绑定)
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
// ... 其他getter/setter
}
// 在配置类中使用
public class DataSourceConfig {
private DbConfig dbConfig;
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(dbConfig.getUrl());
dataSource.setUsername(dbConfig.getUsername());
// ...
return dataSource;
}
}
最佳实践与注意事项
- 属性命名规范:使用小写字母 + 下划线(如
db_url)或驼峰式(如dbUrl),保持一致性; - 敏感信息加密:密码等敏感信息需加密存储(如使用 Jasypt 加密),避免明文暴露;
- 多环境管理:
- 非 Spring Boot 项目:通过
@Profile或Environment动态切换属性文件; - Spring Boot 项目:优先使用
spring.profiles.active=dev指定环境;
- 非 Spring Boot 项目:通过
- 避免循环依赖:属性文件中不要引用其他占位符形成循环(如
a=${b}且b=${a}); - 编码问题:属性文件若含中文,需指定编码(如
@PropertySource(encoding = "UTF-8"))。
总结
Spring 引入外部属性文件的核心是通过 PropertySourcesPlaceholderConfigurer 加载并替换占位符,具体方式根据项目类型选择:
- 传统 XML 项目:使用
<context:property-placeholder>标签; - 注解驱动项目:使用
@PropertySource+@Value; - Spring Boot 项目:推荐
@ConfigurationProperties实现类型安全绑定