在Profile管理環境一文中介紹了環境的概念以及Spring Profile特性控制Bean的添加。本文將進一步介紹Spring管理和控制操做系統變量、JVM變量和Java標準資源(properties文件)的相關功能。html
文章的代碼僅僅用於說明問題,可執行代碼請到個人gitee庫clone,本文的代碼在chkui.springcore.example.hybrid.propertsource包中。java
在整個Jvm運行期間,咱們能夠隨時隨地獲取到2個與環境相關的參數:git
package chkui.springcore.example.hybrid.propertsource; //env是與操做系統相關的參數 Map<String, String> env = System.getenv(); //properties中是Jvm相關的參數 Properties p = System.getProperties(); System.out.println("env :" + env); System.out.println("properties :" + p);
若是沒有人爲的添加額外信息,System::getEnv獲取的數據都與當前的操做系統相關(如下稱爲「操做系統參數」),而System::getProperties獲取的內容都與Jvm相關(如下稱爲「JVM參數」)。spring
Spring會將操做系統參數和Jvm參數都整合到本身的環境管理接口Environment中,例以下面的代碼:api
package chkui.springcore.example.hybrid.propertsource; //向系統級的properties設置一個參數 System.setProperty("wow", "World of Warcraft"); ApplicationContext ctx = new AnnotationConfigApplicationContext(PropertySourcesApp.class); //經過spring的Environment獲取參數 Environment springEnv = ctx.getEnvironment(); System.out.println(springEnv.getProperty("wow")); System.out.println(springEnv.getProperty("PATH"));
除了咱們自定義的"wow",操做系統參數"PATH"也能夠在Spring的Environment中獲取。bash
一般狀況下,在Environment內部維護了2個PropertySources的實例:一個是操做系統參數,另一個是JVM參數。若是2者有一樣的參數,那麼咱們在調用Environment::getProperty方法時,獲得的是JVM參數(System::getProperties),也就是說 Jvm參數具備更高的優先級。app
除了經過外部設置,咱們也能夠直接使用Spring提供的接口來設置:post
package chkui.springcore.example.hybrid.propertsource; //咱們要對環境進行配置,須要使用ConfigurableApplicationContext接口 ConfigurableApplicationContext configAbleCtx = new AnnotationConfigApplicationContext(PropertySourcesApp.class); //ConfigurableApplicationContext接口提供對應的可編輯Environment和PropertySources MutablePropertySources ps = configAbleCtx.getEnvironment().getPropertySources(); Map<String, Object> map = new HashMap<String, Object>(); map.put("wow", "Origin = World of Warcraft!But Edit it already!"); //添加到Spring的環境參數中 ps.addFirst(new MapPropertySource("myPropertySource", map)); System.out.println(springEnv.getProperty("wow"));
代碼添加到PropertySource中,Environment會額外維護一個PropertySources,而本身添加的PropertySources優先級是最高的,因此最後Environment::getProperty獲取到的值是最後設置的值。ui
若是須要添加多個PropertySources,能夠經過MutablePropertySources::addFirst或MutablePropertySources::addLast方法來控制他們之間的優先級。spa
*.properties是Java的標準資源文件,在Java的各類項目中經常使用來記錄各類配置參數。Spring提供了註解和XML配置將*.properties文件中的數據整合到Spring的環境參數(Environment)中。
在@Configuration標記的類上使用@PropertySource註解能夠引入0~n個*.properties配置文件。以下面的例子:
package chkui.springcore.example.hybrid.propertsource; @Configuration @PropertySource("classpath:/hybrid/propertysource/config.properties") public class PropertySourcesApp { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(PropertySourcesApp.class); System.out.println("Properties file params: " + springEnv.getProperty("Gdi")); } }
對應的config.properties文件:
#hybrid.propertysource.config.properties Gdi=StarCraft
同一個工程中支持使用多個@PropertySource註解來引入配置文件,也支持Ant風格(Ant-style,例如"classpath:a/b/**/config.properties")以及Spring擴展的(好比"classpath*:")的路徑規則,資源路徑控制會在後續的文章中介紹。
XML配置在以前介紹容器後置處理器——BeanFactoryPostProcessor的文章中已經介紹了,他就是 PropertyPlaceholderConfigurer ,咱們在XML配置文件中進行一下設置便可。
引入Bean:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- 指定*.properties文件的路徑 --> <property name="locations" value="classpath:/hybrid/propertysource/config.properties"/> </bean>
直接使用context進行全局設置:
<context:property-placeholder location="classpath:/hybrid/propertysource/config.properties"/>
PropertyPlaceholderConfigurer繼承了抽象類PropertyPlaceholderConfigurer,而*.properties文件的讀寫就是在PropertyResourceConfigurer類中實現的。PropertyPlaceholderConfigurer進一步實現了配置文件中佔位符(${...})替換功能。
在Spring IoC容器執行Bean的掃描、加載以前添加一個環境變量(也能夠動態添加而後再執行ConfigurableApplicationContext::refresh方法),就能夠在不少資源路徑的位置使用這個佔位符,對上面的例子進行一些修改:
@Configuration //經過佔位符來設置路徑 @PropertySource("classpath:${resource.propertiesPath}/config.properties") public class PropertySourcesApp { public static void main(String[] args) { //容器啓動以前設置環境變量 System.setProperty("resource.propertiesPath", "/hybrid/propertysource"); ApplicationContext ctx = new AnnotationConfigApplicationContext(PropertySourcesApp.class); //獲取環境對象實例 Environment springEnv = ctx.getEnvironment(); System.out.println("Properties : " + springEnv.getProperty("Gdi")); } }
一樣的,只要環境變量存在,也可使用佔位符替換配置文件中的數據,例如:
<context:property-placeholder location="classpath:${resource.propertiesPath:/config}/config.properties"/>
XML中的佔位符使用的格式是${resource.propertiesPath:/config},它表示使用環境變量resource.propertiesPath進行替換,若是resource.propertiesPath不存在則使用值"/config"。
咱們能夠在任何Bean中使用@Value註解來獲取環境變量。以下面的例子:
package chkui.springcore.example.hybrid.propertsource; @Configuration public class PropertySourcesApp { @Value("${resource.propertiesPath}") private String value; @Value("#{systemProperties['resource.propertiesPath']}") private String elValue; @Value("Resource PropertiesPath") private String staticValue; public static void main(String[] args) { System.setProperty("resource.propertiesPath", "/hybrid/propertysource"); ApplicationContext ctx = new AnnotationConfigApplicationContext(PropertySourcesApp.class); PropertySourcesApp app = ctx.getBean(PropertySourcesApp.class); System.out.println("Value: " + app.value); System.out.println("EL Value: " + app.elValue); System.out.println("Static Value: " + app.staticValue); } }
@Value能夠注入一個純字面量,如上面示例代碼中的staticValue,也可使用佔位符使用環境變量中的任何值。除了使用佔位符${},@Value還支持"#{systemProperties['resource.propertiesPath']}"這樣具有代碼執行功能的複雜表達式來獲取數據,這部分功能會在後續介紹EL表達式的文章中進行分享。