0%

spring引入外部属性文件

Spring 引入外部属性文件的完整指南:从 XML 到注解配置

在 Spring 开发中,将数据库连接信息、API 密钥等配置项硬编码到配置文件中会导致维护困难(如切换环境时需修改大量配置)。通过引入外部属性文件(如 .properties.yml),可以实现 “配置与代码分离”,提升项目的可维护性。本文将详细讲解 Spring 引入外部属性文件的多种方式(XML 配置、注解配置)、底层原理及最佳实践。

外部属性文件的核心作用

外部属性文件(通常为 .properties 格式)用于存储键值对形式的配置信息,如:

1
2
3
4
5
# db.properties
db.url=jdbc:mysql://localhost:3306/test
db.username=root
db.password=123456
db.driver=com.mysql.cj.jdbc.Driver

引入外部属性文件的优势:

  1. 分离配置与代码:避免在 Spring 配置文件(XML / 注解)中硬编码环境相关信息;
  2. 多环境适配:不同环境(开发 / 测试 / 生产)可使用不同的属性文件,无需修改核心配置;
  3. 安全性:敏感信息(如密码)可单独管理,避免提交到代码仓库。

XML 配置方式:<context:property-placeholder>

这是传统 Spring 项目中引入外部属性文件的经典方式,通过 XML 标签 <context:property-placeholder> 实现。

1. 基本用法(单文件引入)

步骤 1:创建属性文件

src/main/resources 目录下创建 db.properties

1
2
3
4
5
# 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)中添加命名空间并引入属性文件:

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"> <!-- 声明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)引入多个属性文件

通过逗号分隔多个文件路径,支持通配符(*)批量引入:

1
2
3
4
5
6
<!-- 引入多个属性文件 -->
<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:忽略无法解析的占位符(适用于多配置中心场景)。
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"/> <!-- 忽略无法解析的占位符 -->

<!-- 使用默认值:若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:在配置类中引入并使用
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
// 引入外部属性文件:classpath:db.properties
@PropertySource(value = "classpath:db.properties", encoding = "UTF-8") // encoding解决中文乱码
public class DataSourceConfig {

// 通过@Value("${key}")获取属性值
@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
@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;

// 自定义YAML文件解析工厂
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
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);
}
}

// 在配置类中使用
@Configuration
@PropertySource(value = "classpath:app.yml", factory = YamlPropertySourceFactory.class)
public class YamlConfig {
@Value("${app.name}")
private String appName;
// ...
}

底层原理:PropertySourcesPlaceholderConfigurer

无论是 XML 标签 <context:property-placeholder> 还是注解 @PropertySource,其底层都依赖 PropertySourcesPlaceholderConfigurer 类(BeanFactoryPostProcessor 的实现类),核心逻辑如下:

  1. 加载属性文件:读取 location 指定的属性文件,将键值对存入 PropertySources
  2. 替换占位符:遍历容器中的 BeanDefinition,将 ${key} 形式的占位符替换为属性文件中的对应值;
  3. 优先级处理:若多个文件存在相同 key,后加载的文件会覆盖先加载的(就近原则)。

注意:PropertySourcesPlaceholderConfigurerBeanFactoryPostProcessor 的实现类,在 Bean 实例化前 执行(参考 “Spring 扩展接口” 中的 BeanFactoryPostProcessor 阶段),确保占位符在 Bean 初始化前被替换。

Spring Boot 中的扩展:更灵活的配置管理

Spring Boot 对外部属性文件的支持更强大,默认支持:

  1. 多环境配置:通过 application-{profile}.properties 区分环境(如 application-dev.properties 对应开发环境);
  2. 配置优先级:外部配置(如命令行参数、系统环境变量)可覆盖内部属性文件;
  3. 类型安全绑定:通过 @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;

// 绑定前缀为"db"的属性
@Component
@ConfigurationProperties(prefix = "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
}

// 在配置类中使用
@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;
}
}

最佳实践与注意事项

  1. 属性命名规范:使用小写字母 + 下划线(如 db_url)或驼峰式(如 dbUrl),保持一致性;
  2. 敏感信息加密:密码等敏感信息需加密存储(如使用 Jasypt 加密),避免明文暴露;
  3. 多环境管理:
    • 非 Spring Boot 项目:通过 @ProfileEnvironment 动态切换属性文件;
    • Spring Boot 项目:优先使用 spring.profiles.active=dev 指定环境;
  4. 避免循环依赖:属性文件中不要引用其他占位符形成循环(如 a=${b}b=${a});
  5. 编码问题:属性文件若含中文,需指定编码(如 @PropertySource(encoding = "UTF-8"))。

总结

Spring 引入外部属性文件的核心是通过 PropertySourcesPlaceholderConfigurer 加载并替换占位符,具体方式根据项目类型选择:

  • 传统 XML 项目:使用 <context:property-placeholder> 标签;
  • 注解驱动项目:使用 @PropertySource + @Value
  • Spring Boot 项目:推荐 @ConfigurationProperties 实现类型安全绑定

欢迎关注我的其它发布渠道