Environment顾名思义用于表示spring应用运行的环境,它主要抽象了两个关键的概念: profiles(配置场景)和properties(属性),甚至可以说Environment=profiles+properties。
Spring对Profiles的解释是:一组仅在profile为活跃状态下才会被注册的命名的、逻辑的BeanDefinition,而后者则是Bean的抽象存在,可以access属性值,ctor的参数值等Bean信息。
而properties则是更广泛的概念,它指从一切属性源(如配置文件、系统属性等)解析出的配置键值对,环境本身存在的意义也是提供给用户更方便的获取与修改properties。
Environment及其父类
PropertyResolver
定义了对Properties的基本存取操作,提供了2个特性:
- 对requiredProperties的存取操作,即声明了配置信息中可以有必要配置,如无法获取必要配置将出错
- 对placeHolder的解析:spring官方给出的placeHolder形式为
${...},是将带placeHolder的值转换为正常值。Profiles
at:org.springframework.core.env.Profiles
其本身可用Profiles.of(String... profiles)的方法创建,而它有意义的方法只有一个:boolean matches(Predicate<String> activeProfiles)
它的作用就是判断当前Profiles是否通过指定的判别方法判别为active。所以Profiles在底层接口方面实际上并没有实际属性,只是抽象地定义了profiles的判定方法。
Profiles实际上对应了BeanDefinition组所处的配置环境集合,简单理解就是dev,pre,prod,cloud这种不同的部署环境以及它们的组合,它如开篇中说道,是构成spring语境下“环境”的重要组成部分之一。
of方法可以通过括号、与或非的运算符以及上述代表不同执行环境的词(token)生成对应的Profiles,如prod|test就指的是生产环境和测试环境。然后它就可以对给出的pattern进行判断,并通过判断结果来决定BeanDefinition能否载入
Environment接口本身
它继承了PropertiesResolver,同时声明了获取Profile列表,以及验证Profiles的方法,所以说,它本质上成为了一个有属性的Profiles集合,这构成了spring语境下抽象的Environment。
Environment只提供只读方法,所以它是在EnvironmentAware中被暴露的接口,Bean可以读取到自身所在的Environment,但并没有能力去修改它。
ConfigurableEnvironment
该接口提供了基本的对Environment中Properties与Profiles的修改和添加功能。setActiveProfiles(String... profiles)、setDefaultProfiles(String... profiles)方法提供了对环境中Profiles的操作,而MutablePropertySources getPropertySources()提供了可操作的PropertySource集合,进而将所有可操作的配置源提供给调用者,同时也可以添加不同优先级的新配置源。
另外,ConfigurableEnvironment继承了ConfigurablePropertyResolver,这代表它对Properties的解析以及配置这种解析的能力。
提到了这么多次的Properties,其实体存在形式主要是键值对集合(不一定是Map,它只是键值对的一种形式)形式的PropertySource
MutablePropertySources
它实现了PropertySources,因此持有PropertySource
在此类规定中,PropertySource
MutablePropertySources是一个重要的元素,在AbstractEnvironment及其以下的层级中,一个MutablePropertySources类型的field就代表环境中的Properties集合。
AbstractEnvironment
AbstractEnvironment对spring中“环境”的语义做了较为详细的规定,之后的StandardEnvironment以及再之后的各类specified的Environment类整体上都基于这个抽象类。所以它是理解spring的“环境”语义的关键之一。
在这个类中,基本实现了对Environment两大内容:Profiles和Properties的具体操作,这个抽象类并没有抽象方法,因此它事实上是场景独立但实现完整的Environment。
重要Field
- activeProfiles与defaultProfiles,均使用LinkedHashSet存储。
- MutablePropertySources propertySources
- ConfigurablePropertyResolver propertyResolver
PropertyResolver的继承树也是复杂体系,但本质上它为了做一件事:将PropertySource中V的任意类型转换为所需(大部分情况下,这个所需类型是String)类型,进而提供可读取与修改的配置。带有Configurable前缀后,就能为propertyResolver添加修改配置属性的功能,并且还有规定prefix,suffix的功能。
总之,propertyResolver代理了所有对properties的操作,它在AbstractEnvironment中接受数据源propertySources并通过对其进行解析实现其方法,AbstractEnvironment提供的public方法是对其方法的简单封装。
完成了对Properties和Profiles两个关键因素的封装,AbstractEnvironment呈现出:能够获取与修改当前环境的Profiles(这实际上也是通过解析Properties实现的),包括active profiles与default profiles;能够获取与修改目前持有的数据源下所有的配置,包括普通配置和required配置。
StandardEnvironment
它是对AbstractEnvironment的简单封装,重写了customizePropertySources方法,其实这是重写AbstractEnvironment的主要方法(我甚至认为AbstractEnvironment应当把这个方法标记为abstract),他能够为当前指定的环境做定制化的配置,当我们看spring库中对这个方法的重写实现,可以看到,它主要用于为当前环境的properties增加一些个性化配置,如servlet的环境就需要添加一些关于servlet的占位配置(因为创建环境时,这些配置可能并没有被加载好,但它们确实需要存在于环境中,因而可以先创建一些占位PropertySource),这是costomize的一种方法,本质上就是提供一个环境创建好后的回调钩子。
注意到,如果Environment类系列继承的不是接口而是如StandardEnvironment这样的实体类,就要保证每个实体类的customizePropertySources都被执行,所以继承实体类的Environment类(如StandardServletEnvironment)会调用super.customizePropertySources(propertySources);
总结
至此,spring环境的继承树大致分析完成,常用的环境就是StandardEnvironment的子类,并重写/进一步实现各种功能,总之,Environment=profiles+properties。
另外,spring对环境的设计也是十分值得学习的设计模式,它在接口层面(Environment)只提供了只读方法,而在具体的实现类中才添加了对环境的mutation操作,这样在实际操作时,不需要且不应该改变环境的bean只需要实现EnvironmentAware,就能够获取只读的Environmen了。
