轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

  • Spring

    • 基础

    • 框架

      • Spring容器初始化过程和Bean生命周期探究
      • Spring容器:从依赖管理到注解配置全解析
      • Spring事件探秘
      • Spring AOP的应用
      • Spring 事务管理
      • Spring中的资源访问:Resource接口
      • Spring中的验证、数据绑定和类型转换
      • Spring表达式语言(SpELl)
      • Spring中的属性占位符
        • Spring数据缓冲区与编解码器详解
        • Spring对于原生镜像和AOT的支持
        • Spring中的数据访问:JDBC、R2DBC、ORM、Object-XML
        • Spring中的Web访问:Servlet API支持
        • Spring中的Web访问:WebSocket支持
        • Spring中的Web访问:响应式栈 WebFlux
        • Spring中的集成测试与单元测试
        • Spring与多种技术的集成
        • Spring框架版本新特性
      • Spring Boot

      • 集成

    • 其他语言

    • 工具

    • 后端
    • Spring
    • 框架
    轩辕李
    2024-02-22
    目录

    Spring中的属性占位符

    属性占位符(Property Placeholder)是 Spring 框架中一个非常实用的特性,它主要用于从配置源中动态获取属性值,并将这些值注入到应用程序的组件中。

    下面将从基本概念、工作原理、配置方式、使用场景以及与 SpEL 的对比等方面详细介绍属性占位符。

    # 基本概念

    属性占位符使用 ${} 作为定界符,格式通常为 ${property.name:defaultValue}。其中,property.name 是要查找的属性名,defaultValue 是可选的默认值,当在配置源中找不到指定属性时,就会使用这个默认值。

    # 工作原理

    当 Spring 容器在处理带有属性占位符的注解(如 @Value)或配置文件时,会触发属性占位符解析机制。具体步骤如下:

    1. 解析占位符:Spring 会识别 ${} 包裹的内容,并提取出属性名。
    2. 查找属性值:从配置源中查找该属性名对应的值。配置源可以是多种类型,如 application.properties、application.yml 文件,系统环境变量,Java 系统属性等。
    3. 使用默认值(可选):如果在配置源中找不到该属性名对应的值,且占位符中指定了默认值,那么就会使用这个默认值。
    4. 注入属性值:将找到的属性值或默认值注入到对应的字段、方法参数或配置项中。

    # 配置方式

    # 1. 使用 application.properties 文件

    在 Spring Boot 应用中,最常见的配置方式是使用 application.properties 文件。例如:

    # application.properties
    database.url=jdbc:mysql://localhost:3306/mydb
    database.username=root
    database.password=secret
    

    在 Java 代码中可以使用 @Value 注解来注入这些属性值:

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component
    public class DatabaseConfig {
    
        @Value("${database.url}")
        private String url;
    
        @Value("${database.username}")
        private String username;
    
        @Value("${database.password}")
        private String password;
    
        // Getters and setters
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    # 2. 使用 application.yml 文件

    application.yml 文件也是常用的配置文件格式,它具有更清晰的层级结构。例如:

    # application.yml
    database:
      url: jdbc:mysql://localhost:3306/mydb
      username: root
      password: secret
    

    Java 代码的使用方式与 application.properties 相同。

    # 3. 系统环境变量和 Java 系统属性

    除了配置文件,属性占位符还可以从系统环境变量和 Java 系统属性中获取值。例如,设置系统环境变量 MY_APP_CONFIG=production,在 Java 代码中可以这样使用:

    @Value("${MY_APP_CONFIG:development}")
    private String appConfig;
    

    # 使用场景

    # 1. 配置数据库连接信息

    如上述示例所示,将数据库的 URL、用户名和密码等信息配置在属性文件中,通过属性占位符注入到应用程序中,方便在不同环境下进行配置切换。

    # 2. 多环境配置

    在开发、测试和生产环境中,很多配置项可能不同。可以使用不同的配置文件(如 application-dev.properties、application-test.properties、application-prod.properties),并通过 spring.profiles.active 属性指定当前使用的环境,实现不同环境下的配置隔离。

    # 3. 动态配置参数

    应用程序中可能有一些需要动态调整的参数,如缓存过期时间、日志级别等。将这些参数配置在属性文件中,通过属性占位符注入到相应的组件中,方便后续修改和管理。

    # 与 SpEL 的对比

    # 1. 语法区别

    • 属性占位符使用 ${} 作为定界符,主要用于从配置源中获取属性值。
    • SpEL 使用 #{} 作为定界符,可以进行更复杂的表达式计算,如属性访问、方法调用、算术和逻辑运算等。

    # 2. 功能区别

    • 属性占位符主要用于配置信息的注入,侧重于从配置源中获取静态的属性值。
    • SpEL 更侧重于在运行时进行动态计算和逻辑处理,可以结合属性占位符一起使用,实现更灵活的配置和计算。例如:
    @Value("#{${someValue} + 1}")
    private int calculatedValue;
    

    这里先使用属性占位符获取 someValue 的值,然后在 SpEL 表达式中对其进行运算。

    # 涉及的 Spring 内部类

    # 1. PropertySourcesPlaceholderConfigurer

    • 作用:在 Spring 传统配置中,PropertySourcesPlaceholderConfigurer 是一个非常重要的后置处理器(BeanFactoryPostProcessor)。它负责处理属性占位符,会在 Bean 定义加载完成后、Bean 实例化之前,对 Bean 定义中的属性占位符进行解析和替换。
    • 原理:该类会从多个属性源(如 application.properties、环境变量等)中查找属性值,将配置文件或注解中的 ${} 占位符替换为实际的属性值。

    # 2. PropertySources

    • 作用:PropertySources 是一个存储多个 PropertySource 的集合接口。它可以包含不同类型的属性源,如 PropertiesPropertySource(基于 java.util.Properties 的属性源)、SystemEnvironmentPropertySource(基于系统环境变量的属性源)等。
    • 原理:在属性占位符解析过程中,PropertySourcesPlaceholderConfigurer 会遍历 PropertySources 中的各个 PropertySource,依次查找所需的属性值。

    # 3. PropertySource

    • 作用:PropertySource 是一个抽象类,代表一个属性源,它定义了从属性源中获取属性值的基本方法。不同的具体实现类对应不同类型的属性源,例如 PropertiesPropertySource 用于从 Properties 对象中获取属性值,SystemEnvironmentPropertySource 用于从系统环境变量中获取属性值。
    • 原理:当 PropertySourcesPlaceholderConfigurer 需要查找某个属性值时,会调用 PropertySource 的 getProperty 方法,该方法会根据属性名返回对应的属性值。

    # 4. StandardEnvironment

    • 作用:StandardEnvironment 是 Spring 环境抽象的默认实现,它继承自 AbstractEnvironment 类。该类负责管理应用程序的属性源集合 PropertySources,并提供了一些方便的方法来访问和操作这些属性源。
    • 原理:在 Spring 应用启动时,会创建一个 StandardEnvironment 实例,并将默认的属性源(如系统属性、系统环境变量)添加到 PropertySources 中。在属性占位符解析过程中,PropertySourcesPlaceholderConfigurer 会使用 StandardEnvironment 中的 PropertySources 来查找属性值。

    # 内部原理

    # 1. 启动阶段

    • 环境初始化:在 Spring 应用启动时,会创建一个 StandardEnvironment 实例,并初始化其 PropertySources 集合,将系统属性和系统环境变量等默认属性源添加到集合中。
    • 配置文件加载:如果使用了 application.properties 或 application.yml 等配置文件,Spring 会在启动过程中加载这些文件,并将其中的属性信息封装成 PropertySource 对象,添加到 PropertySources 集合中。

    # 2. Bean 定义加载阶段

    • 占位符识别:当 Spring 加载 Bean 定义时,会识别其中的属性占位符(如 ${property.name})。这些占位符可能出现在 @Value 注解、XML 配置文件等地方。

    # 3. Bean 工厂后置处理阶段

    • PropertySourcesPlaceholderConfigurer 执行:PropertySourcesPlaceholderConfigurer 作为一个 BeanFactoryPostProcessor,会在 Bean 实例化之前被调用。它会遍历 Bean 定义中的所有属性,查找其中的属性占位符。
    • 属性值查找:对于每个属性占位符,PropertySourcesPlaceholderConfigurer 会从 StandardEnvironment 的 PropertySources 集合中依次查找对应的属性值。它会调用每个 PropertySource 的 getProperty 方法,直到找到属性值或遍历完所有的 PropertySource。
    • 占位符替换:如果找到了属性值,PropertySourcesPlaceholderConfigurer 会将属性占位符替换为实际的属性值。如果没有找到属性值,但占位符指定了默认值,则使用默认值进行替换;如果没有指定默认值,则会抛出异常。

    # 4. Bean 实例化阶段

    • 属性注入:经过占位符替换后,Spring 会根据更新后的 Bean 定义实例化 Bean,并将解析后的属性值注入到 Bean 的相应字段或方法参数中。

    以下是一个简单的示例代码,展示了属性占位符的使用和相关原理:

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    
    @Configuration
    public class AppConfig {
    
        @Value("${message:Default Message}")
        private String message;
    
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
            return new PropertySourcesPlaceholderConfigurer();
        }
    
        @Bean
        public MyBean myBean() {
            return new MyBean(message);
        }
    }
    
    class MyBean {
        private String message;
    
        public MyBean(String message) {
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
            MyBean myBean = context.getBean(MyBean.class);
            System.out.println(myBean.getMessage());
            context.close();
        }
    }
    

    在这个示例中,PropertySourcesPlaceholderConfigurer 负责解析 @Value 注解中的属性占位符,从属性源中查找 message 属性的值,并将其注入到 MyBean 中。

    祝你变得更强!

    编辑 (opens new window)
    上次更新: 2025/02/02
    Spring表达式语言(SpELl)
    Spring数据缓冲区与编解码器详解

    ← Spring表达式语言(SpELl) Spring数据缓冲区与编解码器详解→

    最近更新
    01
    Spring Boot版本新特性
    09-15
    02
    Spring框架版本新特性
    09-01
    03
    Spring Boot开发初体验
    08-15
    更多文章>
    Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式