Spring 系列目錄(http://www.javashuo.com/article/p-kqecupyl-bm.html)html
Spring Environment 屬性配置管理系列文章:java
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); }
Environment 的主要幾個實現以下所示:ide
MockEnvironment
:模擬的環境,用於測試時使用;StandardEnvironment
:標準環境,普通 Java 應用時使用,會自動註冊 System.getProperties() 和 System.getenv()到環境;StandardServletEnvironment
:標準 Servlet 環境,其繼承了 StandardEnvironment,Web 應用時使用,除了 StandardEnvironment 外,會自動註冊 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 實例到環境;StandardEnvironment 標準的 JDK 環境實現以下,將 OS 和 JVM 相關的配置信息都交由 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())); } }
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)); } }
Environment 的源碼很是簡單,就很少說了。spa
(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);
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; } }
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
天天用心記錄一點點。內容也許不重要,但習慣很重要!