• Spring Environment(二)源码分析


    Spring Environment(二)源码分析

    Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

    Spring Environment 属性配置管理系列文章:

    1. Spring Environment(一)API 介绍
    2. Spring Environment(二)源码分析
    3. Spring Environment(三)生命周期

    一、Environment 接口

    Environment 类图

    public interface Environment extends PropertyResolver {
        String[] getActiveProfiles();
        String[] getDefaultProfiles();
    
        // @since 5.1 废弃,改用 Profiles(Profiles.of("dev"))
        @Deprecated
        boolean acceptsProfiles(String... profiles);
        boolean acceptsProfiles(Profiles profiles);
    }
    

    Environment 是对 JDK 环境、Servlet 环境的抽象,可以获取剖面相关的信息。同时实现了 PropertyResolver 接口用于解析属性占位符和类型转换,实际上是委托给 PropertySourcesPropertyResolver 完成的。

    在其子类 ConfigurableEnvironment 接口中还提供了设置剖面和相关数据的 API。

    public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
        // 1. 设置剖面
        void setActiveProfiles(String... profiles);
        void addActiveProfile(String profile);
        void setDefaultProfiles(String... profiles);
    
        // 2. 属性源
        MutablePropertySources getPropertySources();
        Map<String, Object> getSystemProperties();
        Map<String, Object> getSystemEnvironment();
    
        // 3. 合并两个环境信息
        void merge(ConfigurableEnvironment parent);
    }
    

    二、Environment 默认实现类

    Environment 的主要几个实现如下所示:

    • MockEnvironment:模拟的环境,用于测试时使用;
    • StandardEnvironment:标准环境,普通 Java 应用时使用,会自动注册 System.getProperties() 和 System.getenv()到环境;
    • StandardServletEnvironment:标准 Servlet 环境,其继承了 StandardEnvironment,Web 应用时使用,除了 StandardEnvironment 外,会自动注册 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 实例到环境;

    StandardEnvironment 标准的 JDK 环境实现如下,将 OS 和 JVM 相关的配置信息都交由 StandardEnvironment 管理。

    2.1 StandardEnvironment

    public class StandardEnvironment extends AbstractEnvironment {
        /** System environment property source name: {@value}. */
        public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
    
        /** JVM system properties property source name: {@value}. */
        public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
    
        @Override
        protected void customizePropertySources(MutablePropertySources propertySources) {
            propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
            propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
        }
    }
    

    2.2 StandardServletEnvironment

    StandardServletEnvironment 除了注册系统变量外,还会自动注册 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 实例到环境;

    public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
        /** Servlet context init parameters property source name: {@value}. */
        public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
        /** Servlet config init parameters property source name: {@value}. */
        public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
        /** JNDI property source name: {@value}. */
        public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
    
        protected void customizePropertySources(MutablePropertySources propertySources) {
            propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
            propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
            if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
                propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
            }
            super.customizePropertySources(propertySources);
        }
    
        // 调用 initPropertySources 替换对应的占位符
        @Override
        public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
            WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
        }
    

    其中 customizePropertySources 方法是在其父类 AbstractEnvironment 的构造方法中调用的,此时 ServletConfig 和 ServletContext 相关的信息还未初始化,因此需要一个占位符,在初始化 WEB 时自动替换。

    public static void initServletPropertySources(MutablePropertySources sources,
            @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    
        Assert.notNull(sources, "'propertySources' must not be null");
        String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
        if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
            sources.replace(name, new ServletContextPropertySource(name, servletContext));
        }
        name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
        if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
            sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
        }
    }
    

    三、AbstractEnvironment

    Environment 的源码非常简单,就不多说了。

    3.1 属性定义

    (1) 常量

    // 为 true 时 getSystemEnvironment() 返回空集合,屏蔽 OS 相关信息
    public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
    
    // 剖面相关的配置
    public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
    public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
    protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
    

    (2) 属性

    // 剖面信息,查找时先从 activeProfiles 再从 defaultProfiles 中查找
    private final Set<String> activeProfiles = new LinkedHashSet<>();
    private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
    
    // 属性源
    private final MutablePropertySources propertySources = new MutablePropertySources();
    // propertyResolver 用于解析属性占位符和类型转换
    private final ConfigurablePropertyResolver propertyResolver =
                new PropertySourcesPropertyResolver(this.propertySources);
    

    3.2 剖面相关的 API

    public boolean acceptsProfiles(String... profiles) {
        Assert.notEmpty(profiles, "Must specify at least one profile");
        for (String profile : profiles) {
            if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') {
                if (!isProfileActive(profile.substring(1))) {
                    return true;
                }
            } else if (isProfileActive(profile)) {
                return true;
            }
        }
        return false;
    }
    
    protected boolean isProfileActive(String profile) {
        // 只要 profile 不为空且不以 ! 开头
        validateProfile(profile);
        Set<String> currentActiveProfiles = doGetActiveProfiles();
        return (currentActiveProfiles.contains(profile) ||
                (currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile)));
    }
    

    获取剖面信息如下:

    @Override
    public String[] getActiveProfiles() {
        return StringUtils.toStringArray(doGetActiveProfiles());
    }
    
    // 获取 activeProfiles,defaultProfiles 类似
    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;
        }
    }
    

    3.3 获取属性

    private final ConfigurablePropertyResolver propertyResolver =
            new PropertySourcesPropertyResolver(this.propertySources);
    
    @Override
    public String getProperty(String key) {
        return this.propertyResolver.getProperty(key);
    }
    

    获取属性都委托给 propertyResolver 完成了,propertyResolver 持有 propertySources 数据源,可以解析占位符和进行类型转换。

    PropertyResolver 的使用参考:https://www.cnblogs.com/binarylei/p/10284826.html


    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    全国最全的省,市,县,电话号前缀,邮编数据
    数组的高级用法
    Maven Project configuration is not up-to-date with pom.xml错误解决方法
    HighCharts开发说明
    Java基础复习之二:运算符,键盘录入,流程控制语句,if语句,三元运算
    Java基础复习之一篇:关健字,标识符,注释,常量,进制转换,变量,数据类型,数据类型转换
    ehcache 缓存技术
    Write operations are not allowed in read-only mode
    浮点运算与boost.multiprecision
    关于OpenCASCADE数组序列的起始值
  • 原文地址:https://www.cnblogs.com/binarylei/p/10290712.html
Copyright © 2020-2023  润新知