1:概述

Spring中EnvironmentSpring3.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属性解析委托给该类。包括对属性类型之间必要转换。ConverterConverterService。实际的占位符解析委托给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标注的方法、类就会生效,就会被加入到容器内。

最新文章

  1. 【代码笔记】iOS-显示图片的各种方式
  2. iOS开发之Xcode 如何使用API帮助
  3. iPhone、iPad默认按钮样式问题
  4. 逆向思维Stock Maximize
  5. HDU 2544 最短路 SPFA 邻接表 模板
  6. MVC @Html控件
  7. [CJOJ2410]数列操作d
  8. select * 和 select 所有字段的区别
  9. ABP学习之路--切换mysql数据库
  10. 阿里云服务器使用镜像市场上的环境以后sql不能远程问题
  11. Spring_xml和注解混合方式开发
  12. SSM整合Activiti
  13. 简单利用jQuery,让前端开发不再依赖于后端的接口
  14. 记hangfire后台任务运行一段时间后不运行了。
  15. Linux 文件 目录结构探索
  16. sql 表中删除字段重复的行
  17. adobe
  18. postgresql 主从 patroni
  19. flexbox子盒子flex属性
  20. vmware 共享文件夹(win10下的vmware安装了centos7)

热门文章

  1. 卷积神经网络(CNN)的理解与总结
  2. 零元学Expression Design 4 - Chapter 2 熟悉Design并且快速设计出Silverlight网页
  3. 概率分布的 perplexity
  4. nginx 502错 failed (13: Permission denied)
  5. ASP .NET Views文件夹下面的文件找不到
  6. WPF生命周期
  7. EF相关报错
  8. 【C#】wpf查找父子节点
  9. Linux参数调优
  10. error C2760: 语法错误: 意外的令牌“标识符”,预期的令牌为“类型说明符”