1. 什么是 Environment

Environment 是 spring 3.1 引入的一种概念,它内置的一种环境抽象,主要是用来描述 applicationContext 的运行时的一些配置信息和构件信息,比如包含 properties 和 profiles 的信息。通过${}占位符可以获取properties的属性信息,profile可以用来指定构件,构件可以理解为一组配置信息。ConfigurableEnvironment 即为可配置的环境,提供了set和get一系列方法用来操作当前所使用的profile。

2. 简单看下源码

image.png

2.1 PropertyResolver

这个接口它应该是处理占位符 ${} ,就是用来做配置属性值的获取和解析

  1. public interface PropertyResolver {
  2. // 检查所有的配置属性中是否包含指定key
  3. boolean containsProperty(String key);
  4. // 以String的形式返回指定的配置属性的值
  5. String getProperty(String key);
  6. // 带默认值的获取
  7. String getProperty(String key, String defaultValue);
  8. // 指定返回类型的配置属性值获取
  9. <T> T getProperty(String key, Class<T> targetType);
  10. // ......
  11. // 解析占位符
  12. String resolvePlaceholders(String text);
  13. // ......
  14. }

得出结论:**Environment** 可以获取配置元信息,同时也可以解析占位符的信息

2.2 ConfigurableEnvironment

Configurable 前缀,脑子里第一时间出现的就是 get set 果然,确实是这样,它就是用编程式设置 profile 。
还有一个方法可以注意下:

  1. MutablePropertySources getPropertySources();

点开 MutablePropertySources 它的源码,发现它的内部就是一个 List

  1. public class MutablePropertySources implements PropertySources {
  2. private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
  3. }

结论:Mutable 开头的类名,通常可能是一个类型的 List 组合封装。

2.3 StandardEnvironment

StandardEnvironment 是 SpringFramework 中默认使用的标准运行时环境的抽象实现,不过它里面的方法实现的非常少,基本都是由 AbstractEnvironment 负责实现。在后面的原理部分,我们还会再见到它的,这里先留意一下就好。

3. 使用 Enviroment(了解即可)

3.1 获得 Enviroment

  1. @Component
  2. public class EnvironmentHolder {
  3. @Autowired
  4. Environment environment;
  5. public void printEnvironment() {
  6. System.out.println(environment);
  7. }
  8. }
  1. public class EnvironmentQuickstartApplication {
  2. public static void main(String[] args) throws Exception {
  3. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
  4. "com.linkedbear.spring.environment.a_quickstart.bean");
  5. EnvironmentHolder environmentHolder = ctx.getBean(EnvironmentHolder.class);
  6. environmentHolder.printEnvironment();
  7. }
  8. }

执行:

StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}

3.2 Environment获取配置属性的值

@Configuration
@ComponentScan("com.ideal.bean")
@PropertySource("propertysource/jdbc.properties")
public class EnvironmentPropertyConfiguration {

}
@Component
public class EnvironmentHolder {
    @Autowired
    Environment environment;

    public void printEnvironment() {
 System.out.println(Arrays.toString(environment.getDefaultProfiles()));
        System.out.println(environment.getProperty("jdbc.url"));
    }
}

执行

[default]
jdbc:mysql://localhost:3306/demo

4. Environment 稍深入

上面控制台中,其实打印了 Profiles 信息,但是我们其实没有主动配置,这是为什么呢?

4.1 Environment的默认profiles

进入到 Environment 的抽象实现 AbstractEnvironment 中,getDefaultProfiles 调用了 doGetDefaultProfiles 方法,这个设计在 SpringFramework 中大量出现和使用

@Override
public String[] getDefaultProfiles() {
    return StringUtils.toStringArray(doGetDefaultProfiles());
}

这是什么意思呢?接着看

4.2 Spring 中的方法命名习惯

在 Spring 中,我们常能看到带do 和不带 do 的一组方法,不带 do 开头的方法一般负责前置校验处理、返回结果封装,带 do 开头的方法是真正执行逻辑的方法(如 getBean 方法的底层会调用 doGetBean 来真正的寻找 IOC 容器的 bean ,createBean 会调用 doCreateBean 来真正的创建一个 bean )。

4.2.1 doGetDefaultProfiles的实现

public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";

protected Set<String> doGetDefaultProfiles() {
    synchronized (this.defaultProfiles) {
        // 取框架默认的profiles,并与当前的对比
        if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
            // 如果一致,则尝试从Environment中获取显式声明的profiles
            String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
            // 如果有显式声明,则覆盖原有的默认值
            if (StringUtils.hasText(profiles)) {
                setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
                        StringUtils.trimAllWhitespace(profiles)));
            }
        }
        return this.defaultProfiles;
    }
}

看这个方法的实现,整体逻辑也不算复杂,关键是看它取框架默认的 profiles ,它其实就是取的 AbstractEnvironment 中内置的常量:

protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";

protected Set<String> getReservedDefaultProfiles() {
    return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}

4.2.2 覆盖默认的 profiles方法

上面的源码中,也可以看到,我们可以通过声明 **spring.profiles.default** 的配置,来覆盖 SpringFramework 中原有的默认 profiles ,一个比较常用的方法是在 jvm 的启动参数上添加:

-Dspring.profiles.default=aaa

4.3 补充

Environment 的解析配置属性值的底层是 PropertySourcesPropertyResolver 来处理