Spring 中的 Environment 、Profile 与 PropertySource(上)

简介: 如何理解 Environment?Environment 由 Spring 3.1 版本提出,表示当前应用的运行时环境。用于管理 Spring 中的条件配置 Profiles 和配置属性源。

如何理解 Environment?


Environment 由 Spring 3.1 版本提出,表示当前应用的运行时环境。用于管理 Spring 中的条件配置 Profiles 和配置属性源。


Environment 的使用场景


Spring Boot 中,spring.profiles.active 属性可以指定激活的 profiles ,spring.profiles.default 属性可以指定无激活的 profiles 时使用的默认 profiles。属性可以配置在不同的数据源中,如配置文件,启动命令参数。


场景1:获取 Profiles


直接获取 Profiles 信息,不同运行环境激活不同的 Profiles,执行不同的逻辑。示例代码如下。

if(environment.acceptsProfiles("dev")){
    doSomething();
}else{
    doOtherSomething();
}


通过 @Profile 注解在不同环境注册不同的 bean。


@Profile 注解可以标注在表示组件的类或方法上,@Profile 注解 value 属性值和 Environment 中接受的 Profiles 值匹配时该组件才会被注册为 Spring 中的 bean。如下示例,可以在开发环境和生产环境使用不同的数据源。


@Configuration
public class SpringConfiguration {
    @Bean
    @Profile("dev")
    public DataSource devDataSource(){
        return new SimpleDataSource();
    }
    @Bean
    @Profile("prod")
    public DataSource prodDataSource(){
        return new SimpleDataSource();
    }
}


场景2:获取属性源中的属性


通过 Environment#getProperty(String) 获取

通过 @Value 注解注入属性值到 bean 的成员变量中。


如何获取 Environment ?


ApplicationContext#getEnvironment() 获取

可以通过 @Autowire 或实现 ApplicationContextAware 先获取 ApplicationContext。

@Autowire 注入、实现 EnvironmentAware 在 bean 生命周期中获取


Environment Profiles 管理


理解 Profiles


Profiles 表示条件配置,可用于在不同的环境中配置不同的 bean。Environment 对 Profile 管理的方法定义如下。


public interface Environment extends PropertyResolver {
  /**
   * 获取激活的 profiles
   */
  String[] getActiveProfiles();
  /**
   * 获取默认的 profiles
   */
  String[] getDefaultProfiles();
  /**
   * 给定的 profiles 是否被激活
   */
  @Deprecated
  boolean acceptsProfiles(String... profiles);
  /**
   * 给定的 profiles 是否被激活
   */
  boolean acceptsProfiles(Profiles profiles);
}


Environment 接口中只有获取和匹配的方法,对 profiles 设置的方法则由其子接口 ConfigurableEnvironment 提供。ConfigurableEnvironment 对 profiles 设置的方法如下。


public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
  /**
   * 设置激活的 profiles
   */
  void setActiveProfiles(String... profiles);
  /**
   * 添加激活的 profiles
   */
  void addActiveProfile(String profile);
  /**
   * 设置默认的 profiles
   */
  void setDefaultProfiles(String... profiles);
}


@Profile 注解


伴随着 Environment,Spring 在 3.1 提出了一个 @Profile 注解,标注了该注解的 Component,只有指定的值满足 Environment 接受的 Profiles,Component 才会注册为 Spring 的 bean。使用方式可参见前面的示例,这里不再举例。


Environment 配置属性源管理


配置属性获取


Environment 接口并未定义有关配置属性源的方法,配置属性相关的方法由其父接口 PropertyResolver 定义。PropertyResolver 相关方法如下。


public interface PropertyResolver {
  /**
   * 是否存在给定名称的属性
   */
  boolean containsProperty(String key);
  /**
   * 获取给定名称属性源的值
   */
  @Nullable
  String getProperty(String key);
  /**
   * 获取给定名称的属性,不存在时则使用默认值
   */
  String getProperty(String key, String defaultValue);
  /**
   * 获取给定名称和期望类型的属性值
   */
  @Nullable
  <T> T getProperty(String key, Class<T> targetType);
  /**
   * 获取给定名称和希望类型的属性值,不存在时则使用默认值
   */
  <T> T getProperty(String key, Class<T> targetType, T defaultValue);
  /**
   * 获取给定名称的属性值,不存在时则抛出异常
   */
  String getRequiredProperty(String key) throws IllegalStateException;
  /**
   * 获取给定名称和期望类型的属性值,不存在时则抛出异常
   */
  <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
  /**
   * 解析文本中的 ${...} 属性占位符,使用对应的属性值替换
   */
  String resolvePlaceholders(String text);
  /**
   * 解析文本中的 ${...} 属性占位符,使用对应的属性值替换,不存在对应的属性时则抛出异常
   */
  String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}


可以看到,PropertyResolver 接口中也只有属性的获取和匹配的方法,另外还提供了解析文本中属性占位符的方法。


配置属性源


理解配置属性源


属性可能来源不同的位置,如操作系统环境变量、启动参数、配置文件等。因此Environment 中具有不同的属性源。对属性源的获取相关方法则定义在 Environment 的子接口 ConfigurableEnvironment 中,相关方法定义如下。


public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
  /**
   * 获取可以修改的 PropertySources
   */ 
  MutablePropertySources getPropertySources();
  /**
   * 获取 System#getProperties() 的值,Environment 实现已经将该值作为默认的 PropertySource,建议不要直接调用该方法
   */
  Map<String, Object> getSystemProperties();
  /**
   * 获取 System#getenv() 的值,Environment 实现已经将该值作为默认的 PropertySource,建议不要直接调用该方法
   */ 
  Map<String, Object> getSystemEnvironment();
}


ConfigurableEnvironment 中遇到一个新的类 MutablePropertySources,这个类对所有的属性源进行管理,可以方便的添加删除替换属性源,并且可以指定属性源的添加位置,这样前面的属性源就会具有较高的优先级。


先看属性源 PropertySource 的定义,核心代码如下。


public abstract class PropertySource<T> {
  /**
   * 属性源名称
   */
  protected final String name;
  /**
   * 底层的属性源
   */
  protected final T source;
  public PropertySource(String name, T source) {
    ... 省略部分代码
    this.name = name;
    this.source = source;
  }
  @SuppressWarnings("unchecked")
  public PropertySource(String name) {
    this(name, (T) new Object());
  }
  /**
   * 获取当前属性源名称
   */
  public String getName() {
    return this.name;
  }
  /**
   * 获取属性源存储属性的底层类
   */
  public T getSource() {
    return this.source;
  }
  /**
   * 属性源是否包含给定的属性名称
   */
  public boolean containsProperty(String name) {
    return (getProperty(name) != null);
  }
  /**
   * 获取属性名称关联的值
   */
  @Nullable
  public abstract Object getProperty(String name);
}


可以认为,ProperySource 就表示某一个属性源,每个属性源都有一个名字,具体属性源的存储方式由子类指定,根据不同来源有所不同。常见的 PropertySource 如下。


ServletConfigPropertySource:Servlet 属性源

ServletContextPropertySource:ServletContext 属性源

CommandLinePropertySource:命令行属性源

SystemEnvironmentPropertySource:系统环境变量属性源

PropertiesPropertySource:Properties 属性源

ProperySources 是一个表示多个 PropertySource 的 Iterable,定义如下。


public interface PropertySources extends Iterable<PropertySource<?>> {
  /**
   * 是否存在给定名称的属性源
   */
  boolean contains(String name);
  /**
   * 获取给定名称的属性源
   */
  @Nullable
  PropertySource<?> get(String name);
}


MutablePropertySources 对 PropertySources 进行实现,添加了对 PropertySource 的添加、删除、替换方法。底层使用一个 List 来保存 PropertySource,这样不同的属性源就有了优先级,排在前面的具有较高的优先级。部分常用的方法如下。

\

public class MutablePropertySources implements PropertySources {
  // 使用 List 存储属性源
  private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
  // 是否存在给定名称的属性源
  @Override
  public boolean contains(String name) {
    return this.propertySourceList.contains(PropertySource.named(name));
  }
  // 获取给定名称的属性源
  @Override
  @Nullable
  public PropertySource<?> get(String name) {
    int index = this.propertySourceList.indexOf(PropertySource.named(name));
    return (index != -1 ? this.propertySourceList.get(index) : null);
  }
  // 添加属性源到第一个位置,添加的属性源具有最高的优先级
  public void addFirst(PropertySource<?> propertySource) {
    removeIfPresent(propertySource);
    this.propertySourceList.add(0, propertySource);
  }
  // 添加属性源到最后一个位置,添加的属性源就有最低的优先级
  public void addLast(PropertySource<?> propertySource) {
    removeIfPresent(propertySource);
    this.propertySourceList.add(propertySource);
  }
  // 移除给定名称的属性源
  @Nullable
  public PropertySource<?> remove(String name) {
    int index = this.propertySourceList.indexOf(PropertySource.named(name));
    return (index != -1 ? this.propertySourceList.remove(index) : null);
  }
  // 替换给定名称的属性源为新的属性源
  public void replace(String name, PropertySource<?> propertySource) {
    int index = assertPresentAndGetIndex(name);
    this.propertySourceList.set(index, propertySource);
  }
}


@PropertySource 注解


为了支持自定义的配置文件作为属性源,Spring 在 3.1 版本中同样添加了注解 @ProertySource ,使用方式为在配置类上添加该注解,然后指定配置文件的路径。示例代码如下。


@PropertySource(name = "default", value = "classpath:/META-INF/default.properties", encoding = "UTF-8",
    ignoreResourceNotFound = true, factory = PropertySourceFactory.class)
@Configuration
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
        Object source = context.getEnvironment().getPropertySources().get("default").getSource();
        System.out.println(source);
    }
}


打印结果如下。

{name=大鹏}


@PropertySource 各属性含义如下。


name:属性源的名称,如果不指定则会自动生成。

value:指定资源的路径。

encoding :表明资源文件的编码方式,如果不指定存在中文时可能会乱码。

ignoreResourceNotFound :找不到资源时是否忽略,默认为 false,找不到资源将抛出异常。

factory:创建 PropertySource 的工厂,默认的实现是 DefaultPropertySourceFactory 。


目录
相关文章
|
3月前
|
Java 测试技术 数据库
SpringBoot:@Profile注解和Spring EL
SpringBoot:@Profile注解和Spring EL
|
1月前
|
Java Spring
深入理解Spring Boot中的Profile配置
深入理解Spring Boot中的Profile配置
|
3月前
|
Java Shell 测试技术
环境切换大法:掌握Spring Boot多套配置与@Profile注解的高级技巧
环境切换大法:掌握Spring Boot多套配置与@Profile注解的高级技巧
73 2
环境切换大法:掌握Spring Boot多套配置与@Profile注解的高级技巧
|
3月前
|
存储 Java API
Spring揭秘:Environment接口应用场景及实现原理!
Environment接口提供了强大且灵活的环境属性管理能力,通过它,开发者能轻松地访问和配置应用程序运行时的各种属性,如系统属性、环境变量等。 同时,Environment接口还支持属性源的定制和扩展,使得开发者能根据实际需求灵活地定制属性的加载和解析方式。
146 1
Spring揭秘:Environment接口应用场景及实现原理!
|
3月前
|
XML Java 数据格式
掌握Spring Environment:配置管理的关键
掌握Spring Environment:配置管理的关键
193 1
|
3月前
|
Java Spring 容器
Spring注解驱动开发三切换环境Profile
Spring注解驱动开发三切换环境Profile
41 0
|
3月前
|
Java Spring 容器
Spring注解驱动开发二组件赋值-@Value和@PropertySource及XXXXAware接口
Spring注解驱动开发二组件赋值-@Value和@PropertySource及XXXXAware接口
37 0
|
3月前
|
XML Java 应用服务中间件
spring和maven(profile)的多环境部署
spring和maven(profile)的多环境部署
84 0
|
9月前
|
Java Spring
Spring Boot 启动报错解决:No active profile set, falling back to default profiles: default
Spring Boot 启动报错解决:No active profile set, falling back to default profiles: default
201 0
|
Java Maven 数据库
【Spring】Spring常用配置-Profile
【Spring】Spring常用配置-Profile
302 0
【Spring】Spring常用配置-Profile