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

     

  • 相关阅读:
    【oracle】查看表空间对应文件所在位置
    【oracle】查看表空间信息
    【java异常】java.lang.Integer cannot be cast to java.lang.String
    【oracle】DATE输出是什么东西
    数字万用表的精度和分辨率,ADC的位数
    二阶系统
    Verilog中实现电平检测
    模拟信号和数字信号,直流信号和交流信号
    噪声:强度,方差信噪比(待完善)
    Simulink模块之Band-Limited White Noise
  • 原文地址:https://www.cnblogs.com/liuenyuan1996/p/11133984.html
Copyright © 2020-2023  润新知