Spring 引入外部属性文件的完整指南:从 XML 到注解配置
在 Spring 开发中,将数据库连接信息、API 密钥等配置项硬编码到配置文件中会导致维护困难(如切换环境时需修改大量配置)。通过引入外部属性文件(如 .properties 或 .yml),可以实现 “配置与代码分离”,提升项目的可维护性。本文将详细讲解 Spring 引入外部属性文件的多种方式(XML 配置、注解配置)、底层原理及最佳实践。
外部属性文件的核心作用
外部属性文件(通常为 .properties 格式)用于存储键值对形式的配置信息,如:
1 2 3 4 5
| 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:
1 2 3 4 5
| 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)中添加命名空间并引入属性文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?xml version="1.0" encoding="UTF-8"?> <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">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> <property name="driverClassName" value="${db.driver}"/> </bean> </beans>
|
2. 进阶用法(多文件引入与配置)
(1)引入多个属性文件
通过逗号分隔多个文件路径,支持通配符(*)批量引入:
1 2 3 4 5 6
| <context:property-placeholder location="classpath:db.properties, classpath:redis.properties"/>
<context:property-placeholder location="classpath:config*.properties"/>
|
(2)设置默认值与忽略未找到的文件
default-value:当属性未找到时使用默认值;
ignore-resource-not-found:忽略未找到的文件(避免启动报错);
ignore-unresolvable:忽略无法解析的占位符(适用于多配置中心场景)。
1 2 3 4 5 6 7 8 9
| <context:property-placeholder location="classpath:db.properties, classpath:app.properties" ignore-resource-not-found="true" <!-- 忽略未找到的文件 --> ignore-unresolvable="true"/>
<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:在配置类中引入并使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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;
@Configuration
@PropertySource(value = "classpath:db.properties", encoding = "UTF-8") public class DataSourceConfig {
@Value("${db.url}") private String url;
@Value("${db.username}") private String username;
@Value("${db.password}") private String password;
@Value("${db.driver}") 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:
1 2 3 4 5 6 7 8 9 10 11
| import org.springframework.context.annotation.PropertySources; import org.springframework.context.annotation.PropertySource;
@Configuration @PropertySources({ @PropertySource("classpath:db.properties"), @PropertySource("classpath:redis.properties") // 引入第二个文件 }) public class AppConfig { }
|
(2)支持 YAML 文件(需额外配置)
@PropertySource 默认不支持 .yml 文件,需自定义 PropertySourceFactory 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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;
public class YamlPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { Properties properties = new Properties(); properties.loadFromXML(new Yaml().loadAs(resource.getInputStream(), String.class).getBytes()); return new PropertiesPropertySource(resource.getResource().getFilename(), properties); } }
@Configuration @PropertySource(value = "classpath:app.yml", factory = YamlPropertySourceFactory.class) public class YamlConfig { @Value("${app.name}") private String appName; }
|
无论是 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;
@Component @ConfigurationProperties(prefix = "db") public class DbConfig { private String url; private String username; private String password; private String driver;
public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
@Configuration public class DataSourceConfig { @Autowired private DbConfig dbConfig;
@Bean 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 指定环境;
- 避免循环依赖:属性文件中不要引用其他占位符形成循环(如
a=${b} 且 b=${a});
- 编码问题:属性文件若含中文,需指定编码(如
@PropertySource(encoding = "UTF-8"))。
总结
Spring 引入外部属性文件的核心是通过 PropertySourcesPlaceholderConfigurer 加载并替换占位符,具体方式根据项目类型选择:
- 传统 XML 项目:使用
<context:property-placeholder> 标签;
- 注解驱动项目:使用
@PropertySource + @Value;
- Spring Boot 项目:推荐
@ConfigurationProperties 实现类型安全绑定