在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表达式的文章中进行分享。