Spring Environment(二)源碼分析

Spring Environment(二)源碼分析

Spring 系列目錄(http://www.javashuo.com/article/p-kqecupyl-bm.html)html

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

  1. Spring Environment(一)API 介紹
  2. Spring Environment(二)源碼分析
  3. Spring Environment(三)生命週期

1、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 完成的。spring

在其子類 ConfigurableEnvironment 接口中還提供了設置剖面和相關數據的 API。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);
}

2、Environment 默認實現類

Environment 的主要幾個實現以下所示:ide

  • 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 時自動替換。this

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));
    }
}

3、AbstractEnvironment

Environment 的源碼很是簡單,就很少說了。spa

3.1 屬性定義

(1) 常量code

// 爲 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 的使用參考:http://www.javashuo.com/article/p-pgbhqkkt-bs.html


天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索