@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_db
prod-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 应用。