写于:2018-12-17 08:52:37

# Spring Environment

Environment 是一种在容器内以 配置(Profile)和属性(Properties) 为模型的应用环境抽象整合。

这么说可能无法体现 Environment 的重要性。 我们可以吧 Spring 应用运行简单分为两部分:一、spring应用本身 二、spring 应用所处的环境 而 Environment 就是 spring 应用所处的环境的概念性建模。

  • 配置(Profile)

    在 Spring 容器中,Profile 是一种命名的 Bean 定义逻辑组

    一个 Spring 应用可以同时激活多个 Profile,常见的使用场景如:应用部署环境(test、stage、production)、单元测试等

    应用程序可以通过调用 ConfigurableEnvironment 接口控制 Profile 的激活

    public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
        void setActiveProfiles(String... var1);
    
        void addActiveProfile(String var1);
    
        void setDefaultProfiles(String... var1);
    
        MutablePropertySources getPropertySources();
    
        Map<String, Object> getSystemProperties();
    
        Map<String, Object> getSystemEnvironment();
    
        void merge(ConfigurableEnvironment var1);
    }
    
  • 属性(Properties)

    属性又称之为配置项,Key-Value 的形式。

    在 Spring 应用中常用作占用符(Placeolder),而在API 层面,Spring Framework 如下抽象来表述

    ​ 组合属性:PropertySources

    ​ 单一属性:PropertySource

# Evnironment 继承结构

PropertyResolver 读取属性,解析占位符,将读取到的属性转换成指定类型
Environment 继承自PropertyResolver,对环境属性访问和default/active profile访问的抽象
具备PropertyResolver提供的所有能力
ConfigurablePropertyResolver 接口,为PropertyResolver接口抽象的属性源访问做了配置方面的增强。
ConfigurableEnvironment 接口,在所继承的接口之上增加了设置defaut/active profile的能力,增加/删除环境对象中属性源的能力
ConfigurableWebEnvironment 接口,向接口ConfigurableEnvironment增强了根据Servlet上下文/配置初始化属性源的能力
AbstractEnvironment Environment抽象基类,实现了ConfigurableEnvironment
StandardEnvironment 实现类,针对标准Spring应用(非Web应用)环境,
AbstractEnvironment基础上提供了属性源systemEnvironment(来自System.getenv())和systemProperties(来自System.getProperties())
StandardServletEnvironment 实现类,针对标准Spring Servlet Web应用的环境,
StandardEnvironment的基础上增加了servletContextInitParams/servletConfigInitParams/jndiProperties三个属性源

# 配置文件在何时被读取的

application.properties 配置文件读取一样是通过 事件驱动机制 来实现的。

通过ConfigFileApplicationListener 监听 ApplicationEnvironmentPreparedEvent 进行处理。

我们先来看一下 ConfigFileApplicationListener

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    private static final String DEFAULT_PROPERTIES = "defaultProperties";
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
    private static final String DEFAULT_NAMES = "application";
    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    public static final int DEFAULT_ORDER = -2147483638;
    public static final String APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME = "applicationConfigurationProperties";
    private final DeferredLog logger = new DeferredLog();
    private String searchLocations;
    private String names;
    private int order = -2147483638;
    
    ......
        private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        Iterator var3 = postProcessors.iterator();

        while(var3.hasNext()) {
            EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }

    }
    ......
   
}

ConfigFileApplicationListener 源码中,我们可以知道很多信息,例如:默认读取的配置文件路径,默认读取配置文件名称 等信息。

在前面的,我们谈到了 SpringApplication 中 run() 方法:

 public ConfigurableApplicationContext run(String... args) {
     ......
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
     ......
    }

前面我们提到过,bootstrap 的读取优先于,但是 他们都是通过 监听时间来执行的,那么怎么来确认,说谁更优先呢。那就是对比 order (越小越优先)【或者直接简单粗暴的针对 ConfigFileApplicationListener BootstrapApplicationListener 中的 onApplication 打断点,看谁的限执行】

BootstrapApplicationListener : order = - 2147483643 ConfigFileApplicationListener: order = - 2147483638

# 模拟测试

# 尝试获取环境配置文件中的配置属性

@RestController
public class Application {
    
    @Autowired
    private Environment environment;

    @Autowired	
    private ApplicationContext context;	// context 中有 Environment 属性

    @RequestMapping("/server-port")
    public String getPort(){
        return environment.getProperty("server.port");
    }

    @RequestMapping("/server-port2")
    public String getPort2(){
        return context.getEnvironment().getProperty("server.port");
    }
}

# 模拟多个配置文件,根据加载顺序,根据覆盖的方式进行配置文件的加载

step1、在 resource 中增加 配置文件 application.properties

server.port=9527

step2、在 resource/config 中 增加配置文件 application.properties

## 关闭安全验证
management.security.enabled=false

## 端口配置
server.port=9528

结论: 两个配置文件都正常读取, resource/config 中配置文件读取优先级低于 resoruce 下文件,并对 resource/ 下的配置文件进行覆盖.

精彩内容推送,请关注公众号!
最近更新时间: 4/6/2020, 2:52:03 PM