@PropertySource 和 @PropertySources 是 Spring 框架中用于从外部属性文件加载配置属性的核心注解,它们将属性文件中的键值对注入到 Spring 的 Environment 中,使得应用可以通过 ${property.key} 的方式访问这些配置。以下从注解定义、源码解析、核心功能、使用场景及注意事项展开详细说明。
一、注解定义与源码解析
1. @PropertySource 注解
@PropertySource 位于 org.springframework.context.annotation 包中,用于声明单个属性源(如一个属性文件)。其源码定义如下(简化版):
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class) // 支持重复注解(通过 @PropertySources 容器)
public @interface PropertySource {
/**
* 属性文件的名称(支持类路径、文件系统路径)
*/
String name() default "";
/**
* 属性文件的路径(必填,除非通过 `name` 指定)
*/
String value() default "";
/**
* 是否忽略资源不存在(默认 false,不存在时抛异常)
*/
boolean ignoreResourceNotFound() default false;
/**
* 属性文件的编码(默认 UTF-8)
*/
String encoding() default "UTF-8";
/**
* 工厂类(用于自定义属性源的创建,如加密属性解密)
*/
Class<? extends PropertySourceFactory> factory() default DefaultPropertySourceFactory.class;
}
关键属性说明:
value:属性文件的路径(如classpath:config/db.properties或file:/etc/app/config.properties)。name:属性源的逻辑名称(可选,若未指定则使用value的值)。ignoreResourceNotFound:若属性文件不存在,是否忽略(默认false,抛IllegalArgumentException)。encoding:属性文件的编码(如UTF-8、ISO-8859-1)。
2. @PropertySources 注解
@PropertySources 是 @PropertySource 的容器注解,用于声明多个属性源(解决 @PropertySource 只能声明一个的限制)。其源码定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PropertySources {
/**
* 多个 @PropertySource 注解的数组
*/
PropertySource[] value();
}
核心作用:允许在一个配置类上声明多个属性源,按顺序加载(后加载的属性源会覆盖先加载的同名属性)。
二、核心功能:外部属性文件的加载与访问
1. 属性加载的底层流程
Spring 启动时,ConfigurationClassParser 会扫描所有 @Configuration 类上的 @PropertySource 和 @PropertySources 注解,并通过 PropertySourceAnnotationBeanDefinitionRegistrar 将这些属性源注册到 Environment 中。具体流程如下:
- 解析
@PropertySource注解:获取属性文件的路径、编码、是否忽略不存在等参数。 - 创建
PropertySource实例:根据factory属性(默认DefaultPropertySourceFactory)创建具体的属性源(如ResourcePropertySource)。 - 注册到
Environment:将属性源添加到Environment的属性源列表中(AbstractEnvironment维护一个PropertySource列表)。
2. 属性的访问方式
加载后的属性可以通过以下方式访问:
(1)通过 Environment 接口
在 Spring 管理的 Bean 中注入 Environment,使用 getProperty 方法获取属性值:
@Service
public class UserService {
@Autowired
private Environment env;
public void printDbUrl() {
String dbUrl = env.getProperty("db.url"); // 从属性文件中获取 db.url 的值
System.out.println("数据库 URL:" + dbUrl);
}
}
(2)通过 @Value 注解直接注入
在字段、方法参数上使用 @Value("${property.key}") 直接注入属性值:
@Service
public class UserService {
@Value("${db.username}")
private String dbUsername;
public void printUsername() {
System.out.println("数据库用户名:" + dbUsername);
}
}
(3)通过 @ConfigurationProperties 绑定对象
将属性批量绑定到 Java 对象(需配合 @EnableConfigurationProperties):
@ConfigurationProperties(prefix = "db")
public class DbConfig {
private String url;
private String username;
// getter/setter...
}
@Configuration
@EnableConfigurationProperties(DbConfig.class)
@PropertySource("classpath:config/db.properties")
public class AppConfig {
}
三、典型使用场景与示例
1. 加载外部配置文件(最常用)
将数据库连接、第三方 API 密钥等敏感或环境相关的配置存储在外部文件中,避免硬编码在代码中。
示例:db.properties 文件
# src/main/resources/config/db.properties
db.url=jdbc:mysql://localhost:3306/mydb
db.username=root
db.password=123456
配置类声明
@Configuration
@PropertySource(value = "classpath:config/db.properties", encoding = "UTF-8")
public class DbConfig {
// 可选:通过 @ConfigurationProperties 绑定属性
}
使用属性
@Service
public class JdbcService {
@Value("${db.url}")
private String dbUrl;
public void connect() {
System.out.println("连接数据库:" + dbUrl); // 输出:jdbc:mysql://localhost:3306/mydb
}
}
2. 多环境配置隔离
通过多个 @PropertySource 加载不同环境的配置(如开发、测试、生产),结合 spring.profiles.active 动态切换。
示例:多环境属性文件
dev-db.properties(开发环境):db.url=jdbc:mysql://dev-host:3306/dev_dbprod-db.properties(生产环境):db.url=jdbc:mysql://prod-host:3306/prod_db
配置类声明
@Configuration
// 根据激活的 Profile 动态加载对应的属性文件
@PropertySources({
@PropertySource(value = "classpath:config/${spring.profiles.active}-db.properties", ignoreResourceNotFound = true)
})
public class DbConfig {
}
启动命令(激活生产环境):
java -jar app.jar --spring.profiles.active=prod
3. 覆盖默认配置
Spring Boot 的 application.properties 是默认配置源,通过 @PropertySource 可以加载外部文件覆盖其中的属性。
示例:覆盖 application.properties 中的 server.port
custom.properties文件:server.port=8081- 配置类声明:
@Configuration @PropertySource("classpath:custom.properties") public class AppConfig { }
效果:应用启动时,server.port 会被 custom.properties 中的 8081 覆盖(默认是 8080)。
四、源码实现细节与关键类
1. PropertySourceAnnotationBeanDefinitionRegistrar
Spring 处理 @PropertySource 的核心类,负责将 @PropertySource 注解转换为 PropertySource 实例并注册到 Environment 中。其关键逻辑如下:
(1)解析 @PropertySource 注解
通过 AnnotationMetadata 获取 @PropertySource 的属性(如 value、encoding、factory)。
(2)创建 PropertySource 实例
根据 factory 属性(默认 DefaultPropertySourceFactory)创建 PropertySource:
DefaultPropertySourceFactory:通过ResourceLoader加载属性文件,生成ResourcePropertySource(基于Resource的属性源)。
(3)注册到 Environment
将创建的 PropertySource 添加到 AbstractEnvironment 的 propertySources 列表中(通过 MutablePropertySources 管理)。
2. PropertySource 接口的实现类
Spring 支持多种 PropertySource 实现,常见的有:
| 实现类 | 描述 |
|---|---|
ResourcePropertySource | 基于 Resource(如类路径、文件系统路径)的属性源,最常用。 |
SystemEnvironmentPropertySource | 基于 JVM 系统属性(System.getProperties())的属性源。 |
CommandLinePropertySource | 基于命令行参数的属性源(如 --key=value)。 |
五、注意事项与常见问题
1. 属性的覆盖顺序
Spring 的 Environment 按以下顺序加载属性源(后加载的覆盖先加载的同名属性):
- 命令行参数(
--key=value)。 @PropertySource声明的属性源(按@PropertySources中的顺序)。application.properties(或application.yml)。- 系统属性(
System.getProperties())。
示例:若 @PropertySource 加载的文件中定义了 db.url=jdbc:mysql://a,而 application.properties 中定义了 db.url=jdbc:mysql://b,最终生效的是 application.properties 中的值(因为 application.properties 加载顺序更晚)。
2. 忽略资源不存在
若 @PropertySource 的 ignoreResourceNotFound 设为 true,当属性文件不存在时,Spring 不会抛异常,而是静默跳过该属性源。
示例:
@PropertySource(value = "classpath:config/optional.properties", ignoreResourceNotFound = true)
3. 编码问题
若属性文件包含非 ASCII 字符(如中文),需显式指定 encoding(如 UTF-8),否则可能出现乱码。
4. 与 @ConfigurationProperties 的区别
| 特性 | @PropertySource | @ConfigurationProperties |
|---|---|---|
| 核心功能 | 加载属性到 Environment | 将属性绑定到 Java 对象 |
| 作用范围 | 全局(所有 Bean 可访问) | 仅绑定的 Java 对象可访问 |
| 动态刷新 | 不支持(需重启应用) | 支持(结合 @RefreshScope) |
| 典型场景 | 加载外部配置文件 | 批量绑定结构化配置(如数据库连接池) |
5. 性能优化
- 避免在
@PropertySource中加载过大的属性文件(如数万行),可能导致启动变慢。 - 对于频繁变更的配置,建议使用
@RefreshScope(结合 Spring Cloud Config)实现动态刷新。
六、总结
@PropertySource 和 @PropertySources 是 Spring 中管理外部配置的核心注解,通过它们可以将属性文件中的键值对加载到 Environment 中,实现配置与代码的解耦。其核心机制依赖 PropertySourceAnnotationBeanDefinitionRegistrar 和 PropertySource 接口的实现类,支持多属性源加载、覆盖默认配置等场景。理解其源码和使用规范(如属性覆盖顺序、编码问题),有助于开发者编写更灵活、可维护的 Spring 应用。
@PropertySource和@PropertySources注解详解
917

被折叠的 条评论
为什么被折叠?



