Spring Environment抽象
Spring中Environment
是Spring3.1
版本引入的,是Spring
核心框架定义的一个接口,用来表示整个应用运行时环境
。该环境模型只接受两种应用环境profiles(配置文件)
和properties(属性)
。与属性访问相关的方法通过PropertyResolver
超接口访问。
建模关键
profile(配置文件)
-
一个
profile
是一组Bean
定义的逻辑分组,只有当配置文件被激活的时候,才会将对应逻辑上组织的Bean定义注册到容器中。 -
Bean
添加到profile
可以通过XML或者Annotation
方式。 -
Environment
对象对于profile
机制所扮演的角色是用来指定哪些profile
是当前活跃或者缺省活跃
。可以通过getActiveProfiles
或者getDefaultProfiles
获取。
proprety(属性)
-
一个应用属性有很多来源:属性文件(properties files),JVM系统属性(
getSystemProperties
),系统变量属性(getSystemEnvironment
),JNDI
,servlet上下文参数
,临时属性对象,Maps
等。 -
Environment
对于property
所扮演的角色提供给使用一个方便服务接口用于-
配置属性源
-
从属性源解析和获取属性
-
容器上下文(ApplicationContext
)所获取的bean
,如果想直接使用Environment
对象访问profile
状态或者获取属性。有以下方式。
-
EnvironmentAware
接口 -
@Inject
或者@Autowired
注入一个Environment
对象
绝大多数情况,bean都不需要直接访问Environment
对象,而是通过类似@Value
注解方式把属性值注入进来。
这个接口定义在包 org.springframework.core.env
中。下面是Spring
围绕环境抽象Environment
各个接口/类之间的继承关系:
2:Environment
接口相关类介绍
接口|类 | 介绍 |
---|---|
PropertyResolver |
接口,抽象对属性源的访问比如是否包含某个属性,读取属性,解析占位符,将读取到的属性转换成指定类型 (提供读操作)默认实现PropertySourcesPropertyResolver |
Environment |
接口,继承自PropertyResolver ,对环境属性访问和default/active profile 访问的抽象 。 |
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应用的环境, 增加了servletContextInitParams /servletConfigInitParams /jndiProperties |
3:外部化配置抽象相关类
接口|类 | 介绍 |
---|---|
PropertySource |
用来抽象属性键值对(外部化配置,即属性源)配置基类 。例如Map,Properties,ServletConfig,ServletContext |
PropertySources |
对PropertySource 抽象属性键值对外部化配置提供集合操作。 |
MutablePropertySources |
PropertySources 默认实现。 |
MapPropertySource |
Map对象中读取属性键值对 |
PropertiesPropertySource |
Properties对象中读取属性键值对 |
ResourcePropertySource |
Resource对象读取中读取属性键值对。只支持.xml和.properties文件。 底层实现使用了工具类PropertiesLoaderUtils 。 |
CompositePropertySource |
聚合一组PropertySource 。 |
Web环境实现类和JNDI实现类和随机数实现类 | ServletConfigPropertySource ,ServletContextPropertySource , JndiPropertySource ,RandomValuePropertySource |
命令行参数实现类 | CommandLinePropertySource |
4:混淆定义
-
上下文:用来处理分层传递抽象,代表着
应用
。 -
环境:
当前上下文运行环境
,存储各种全局变量
。比如JDK信息,内存信息等等。
5:核心API
-
PropertySource:属性源。key-value属性对抽象
-
PropertyResolver:属性解析器。用于解析相应key的value
-
Profile:配置。只有激活的配置profile的组件/配置才会注册到Spring容器,类似于maven中profile。
-
Environment:环境,本身也是个属性解析器
PropertyResolver
。
6:属性解析器相关类详细介绍
PropertySourcesPropertyResolver
该类是Spring内建提供的PropertyResolver
唯一实现类。环境抽象Environment
属性解析委托给该类。包括对属性类型之间必要转换。Converter
和ConverterService
。实际的占位符解析委托给PropertyPlaceholderHelper
。
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { ... @Nullable private final PropertySources propertySources; //内部持有一组PropertySource // 由此可以看出propertySources的顺序很重要~~~ // 并且还能处理占位符~~~~~ resolveNestedPlaceholders支持内嵌、嵌套占位符 @Nullable protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource<?> propertySource : this.propertySources) { Object value = propertySource.getProperty(key); if (value != null) { if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); } logKeyFound(key, propertySource, value); return convertValueIfNecessary(value, targetValueType); } } } return null; } ... } public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver { ... @Nullable private volatile ConfigurableConversionService conversionService; @Nullable private PropertyPlaceholderHelper nonStrictHelper; @Nullable private PropertyPlaceholderHelper strictHelper; private boolean ignoreUnresolvableNestedPlaceholders = false; private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX; private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX; @Nullable private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR; private final Set<String> requiredProperties = new LinkedHashSet<>(); ... }
7:应用环境抽象Environment
Environment
接口:环境的读操作
public interface Environment extends PropertyResolver { /** * Return the set of profiles explicitly made active for this environment. Profiles * are used for creating logical groupings of bean definitions to be registered * conditionally, for example based on deployment environment. Profiles can be * activated by setting {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME * "spring.profiles.active"} as a system property or by calling * {@link ConfigurableEnvironment#setActiveProfiles(String...)}. * <p>If no profiles have explicitly been specified as active, then any * {@linkplain #getDefaultProfiles() default profiles} will automatically be activated. * @see #getDefaultProfiles * @see ConfigurableEnvironment#setActiveProfiles * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME */ String[] getActiveProfiles(); /** * Return the set of profiles to be active by default when no active profiles have * been set explicitly. * @see #getActiveProfiles * @see ConfigurableEnvironment#setDefaultProfiles * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME */ String[] getDefaultProfiles(); /** * Return whether one or more of the given profiles is active or, in the case of no * explicit active profiles, whether one or more of the given profiles is included in * the set of default profiles. If a profile begins with '!' the logic is inverted, * i.e. the method will return true if the given profile is <em>not</em> active. * For example, <pre class="code">env.acceptsProfiles("p1", "!p2")</pre> will * return {@code true} if profile 'p1' is active or 'p2' is not active. * @throws IllegalArgumentException if called with zero arguments * or if any profile is {@code null}, empty or whitespace-only * @see #getActiveProfiles * @see #getDefaultProfiles */ boolean acceptsProfiles(String... profiles); }
ConfigurableEnvironment
:增加环境的写操作
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver { // 指定该环境下的 profile 集 void setActiveProfiles(String... profiles); // 增加此环境的 profile void addActiveProfile(String profile); // 设置默认的 profile void setDefaultProfiles(String... profiles); // 返回此环境的 PropertySources MutablePropertySources getPropertySources(); // 尝试返回 System.getenv() 的值,若失败则返回通过 System.getenv(string) 的来访问各个键的映射 Map<String, Object> getSystemEnvironment(); // 尝试返回 System.getProperties() 的值,若失败则返回通过 System.getProperties(string) 的来访问各个键的映射 Map<String, Object> getSystemProperties(); void merge(ConfigurableEnvironment parent); }
AbstractEnvironment
:作为环境接口抽象实现,主要实现了profile
相关功能
public abstract class AbstractEnvironment implements ConfigurableEnvironment { public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore"; // 请参考:ConfigurableEnvironment#setActiveProfiles public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active"; // 请参考:ConfigurableEnvironment#setDefaultProfiles public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default"; private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles()); // 默认的profile名称 protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default"; ... protected Set<String> doGetActiveProfiles() { synchronized (this.activeProfiles) { if (this.activeProfiles.isEmpty()) { String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { setActiveProfiles(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(profiles))); } } return this.activeProfiles; } } ... }
如果 activeProfiles
为空,则从 Properties
中获取 spring.profiles.active
配置;如果不为空,则调用 setActiveProfiles()
设置 profile,最后返回。
从这里可以知道,API设置的activeProfiles优先级第一,其次才是属性配置。
8:应用环境配置激活(@Profile和ProfileCondition)
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(ProfileCondition.class) public @interface Profile { String[] value(); }
从Spring4.0
开始提供Conditional
接口,该注解实现原理基于Condition
条件接口,Condition
条件接口计算结果实现类为ConditionEvaluator
,该类是个内部类。
ProfileCondition
class ProfileCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 因为value值是个数组,所以此处有多个值 用的MultiValueMap MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) { // 多个值中,但凡只要有一个acceptsProfiles了,那就返回true~ if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) { return true; } } return false; } return true; } }
@Profile
的value可以指定多个值,并且只需要有一个值符合了条件,@Profile
标注的方法、类就会生效,就会被加入到容器内。