@PropertySource和@PropertySources 详解及详细源码展示

@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.propertiesfile:/etc/app/config.properties)。
  • name:属性源的逻辑名称(可选,若未指定则使用 value 的值)。
  • ignoreResourceNotFound:若属性文件不存在,是否忽略(默认 false,抛 IllegalArgumentException)。
  • encoding:属性文件的编码(如 UTF-8ISO-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 中。具体流程如下:

  1. 解析 @PropertySource 注解:获取属性文件的路径、编码、是否忽略不存在等参数。
  2. 创建 PropertySource 实例:根据 factory 属性(默认 DefaultPropertySourceFactory)创建具体的属性源(如 ResourcePropertySource)。
  3. 注册到 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 的属性(如 valueencodingfactory)。

(2)创建 PropertySource 实例

根据 factory 属性(默认 DefaultPropertySourceFactory)创建 PropertySource

  • DefaultPropertySourceFactory:通过 ResourceLoader 加载属性文件,生成 ResourcePropertySource(基于 Resource 的属性源)。
(3)注册到 Environment

将创建的 PropertySource 添加到 AbstractEnvironmentpropertySources 列表中(通过 MutablePropertySources 管理)。

2. PropertySource 接口的实现类

Spring 支持多种 PropertySource 实现,常见的有:

实现类描述
ResourcePropertySource基于 Resource(如类路径、文件系统路径)的属性源,最常用。
SystemEnvironmentPropertySource基于 JVM 系统属性(System.getProperties())的属性源。
CommandLinePropertySource基于命令行参数的属性源(如 --key=value)。

五、注意事项与常见问题

1. 属性的覆盖顺序

Spring 的 Environment 按以下顺序加载属性源(后加载的覆盖先加载的同名属性):

  1. 命令行参数(--key=value)。
  2. @PropertySource 声明的属性源(按 @PropertySources 中的顺序)。
  3. application.properties(或 application.yml)。
  4. 系统属性(System.getProperties())。

示例:若 @PropertySource 加载的文件中定义了 db.url=jdbc:mysql://a,而 application.properties 中定义了 db.url=jdbc:mysql://b,最终生效的是 application.properties 中的值(因为 application.properties 加载顺序更晚)。

2. 忽略资源不存在

@PropertySourceignoreResourceNotFound 设为 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 中,实现配置与代码的解耦。其核心机制依赖 PropertySourceAnnotationBeanDefinitionRegistrarPropertySource 接口的实现类,支持多属性源加载、覆盖默认配置等场景。理解其源码和使用规范(如属性覆盖顺序、编码问题),有助于开发者编写更灵活、可维护的 Spring 应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值