Spring Environment抽象

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标注的方法、类就会生效,就会被加入到容器内。

 

 

posted @ 2019-07-04 17:55  海渊  阅读(646)  评论(1编辑  收藏  举报