spring 和 spring boot 中的属性配置方式
作者:梦想画家
本文我们介绍如何在spring中配置和应用属性——通过xml的 或java Configuration 的@PropertySource。
在Spring 3.1之前,将新的属性文件添加到Spring中及使用属性值并不是那么灵活和健壮。从Spring 3.1开始,新的Environment 和 PropertySource 抽象已经简化整个过程。
在xml中注册属性文件
通过在xml增加
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <context:property-placeholder location="classpath:foo.properties" /> </beans>
foo.properties文件可以放在/src/main/resources文件夹中,即运行时类路径。
多个
如果在Spring上下文中出现了多个 元素,那么应该遵循以下几个最佳实践:
- 需指定order属性来确定spring处理顺序
- 需要引用其他原始属性元素应该增加ignore-unresolvable= “true”,使解析机制先不抛出异常的情况下继续加载其他配置。
通过java注解方式注册属性文件
Spring 3.1 引入新的 @PropertySource 注解,可以方便地给spring environment中添加property source。该注解与基于Java Configuration 方式配置的@Configuration注解一起使用:
@Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { //... }
另一个非常有用的注册方式为使用占位符方式实现运行时动态选择属性文件,示例如下:
@PropertySource({ "classpath:persistence-${envTarget:mysql}.properties" }) …
使用及注入属性
直接通过 @Value 注解注入属性:
@Value( "${jdbc.url}" ) private String jdbcUrl;
也可以指定缺省值:
@Value( "${jdbc.url:aDefaultUrl}" ) private String jdbcUrl;
在 Spring XML configuration使用属性:
<bean id="dataSource"> <property name="url" value="${jdbc.url}" /> </bean>
旧的 PropertyPlaceholderConfigurer 和新的 PropertySourcesPlaceholderConfigurer(Spring 3.1 中引入)都可以解析xml bean定义和@value注解中的 ${…} 占位符 。
最后,使用新的Environment API可以获取属性值:
@Autowired private Environment env; ... dataSource.setUrl(env.getProperty("jdbc.url"));
特别需要注意的是,使用不会暴露属性给 Spring Environment,这意味这下面代码会返回null:
env.getProperty("key.something")
属性搜索优先级
spring4中,默认local properties文件最后加载,environment Properties和system Properties之后。local properties是通过PropertiesLoaderSupport 类的API方法 (setProperties, setLocation, etc)手工或编程方式配置的。
这种机制可以通过设置PropertySourcesPlaceholderConfigurer类的localOverride 属性进行修改,值为true允许local properties覆盖spring系统加载的。
spring3.0之前,PropertyPlaceholderConfigurer 类尝试在手工定义源和System Properties两个地方查找,查找顺序也可以通过设置systemPropertiesMode属性进行配置:
- never – 总不检查 system properties
- fallback (default) – 如果指定的properties files查找不到,则检查 system properties
- override – 先检查system properties,然后再尝试指定的properties files。这允许system properties覆盖任何其他属性源。
最后需注意,如果在两个或多个通过@PropertySource定义了属性,那么最后一个定义优先级最高并覆盖以前的定义。这使得准确的属性值难以预测,所以如果覆盖不满足需求,那么可以重写PropertySource API。
spring boot 属性加载
在我们进入更高级的属性配置之前,让我们先看看Spring Boot中属性加载的新特性。
总的来说与标准Spring相比,这种新支持的配置更少,当然这是Spring Boot的主要目标之一。
application.properties – 缺省属性文件
spring boot 应用是典型基于配置文件规范约定。我们可以简单地放“application.properties” 文件在“src/main/resources”目录中,spring boot会自定监测,我们能在其中放入任何属性。
通过使用缺省文件,我们无须显示注册PropertySource并指定属性文件路径。我们也可以在运行时使用环境变量属性指定不同的属性配置文件:
java -jar app.jar --spring.config.location=classpath:/another-location.properties
特定环境属性文件
如果我们需要针对不同环境,spring boot内置机制可以满足。我们可以在“src/main/resources”目录中定义“application-environment.properties” 文件,然后设置spring profile与environment名称一致。
例如,如果我们定义“staging” 环境变量,则我们必须定义staging profile,然后定义application-staging.properties属性文件。
特定环境属性文件加载优先级高于缺省属性文件。注意,默认文件仍然会被加载,只是当有属性冲突时,特定环境属性文件优先。
特定测试属性文件
在应用测试阶段,我们可能需要不同的属性值。Spring Boot通过在测试运行期间查找“src/test/resources”目录中的属性文件来处理这个问题。同样,默认属性仍然会被加载,但是如果发生冲突,将会覆盖这些属性。
@TestPropertySource注解
如果需要更细粒度控制测试属性,我们可以使用@TestPropertySource注解。其可以设置给测试上下文设置测试属性,比缺省属性源优先级高:
@ContextConfiguration @TestPropertySource("/my-test.properties") public class IntegrationTests { // tests }
如果我们不想使用文件,也直接指定名称和值:
@ContextConfiguration @TestPropertySource("foo=bar", "bar=foo") public class IntegrationTests { // tests }
也可以通过@SpringBootTest注解,指定相应properties参数值达到同样效果:
@SpringBootTest(properties = {"foo=bar", "bar=foo"}) public class IntegrationTests { // tests }
层次属性
如果属性按分组形式配置,可以使用 @ConfigurationProperties注解,其会按照对象图方式映射这些分层组织属性。下面示例看看数据库连接配置属性:
database.url=jdbc:postgresql:/localhost:5432/instance database.username=foo database.password=bar
然后使用注解映射至数据库对象:
@ConfigurationProperties(prefix = "database") public class Database { String url; String username; String password; // standard getters and setters }
spring boot 在配置方法中再次应用了基于约定原则,自动映射属性值对象字段,我们仅需提供属性前缀即可。
YAML 文件
YAML文件也支持。
同样名称规则可以应用至测试规范、environmet规范以及缺省属性文件。仅文件扩展名不同以及需提供SnakeYAML依赖。
YAML对层次属性存储特别方便,下面的属性文件:
database.url=jdbc:postgresql:/localhost:5432/instance database.username=foo database.password=bar secret: foo
对应的YAML文件为:
database: url: jdbc:postgresql:/localhost:5432/instance username: foo password: bar secret: foo
需要注意的是YAML文件不支持使用@PropertySource注解,所以如果使用该注解则必须使用属性文件。
命令行传入属性
相对于使用文件,属性也可以直接通过命令行进行传递:
java -jar app.jar --property="value"
你也能通过系统属性实现,需要在-jar命令之前提供:
java -Dproperty.name="value" -jar app.jar
环境变量属性
spring boot也能监测环境变量,效果与属性一样:
export name=value java -jar app.jar
随机属性值
如果属性值不确定,RandomValuePropertySource 可以实现给属性赋随机值:
random.number=${random.int} random.long=${random.long} random.uuid=${random.uuid}
其他类型的属性源
spring boot 支持很多属性源,实现较好顺序及合理覆盖。其官方文档可以参阅。
spring配置实现
- spring3.1之前
spring3.1引入注解,可以方便地定义属性源,在此之前,xml配置是必须的。 xml元素自动在spring上下文中注册新的PropertyPlaceholderConfigurer bean。为了向后兼容,在spring3.1及之后版本中,如果XSD schemas不升级到新的3.1 XSD版本,仍然会创建相应bean。
- spring3.1之后
从spring3.1起,XML 元素不再注册旧的PropertyPlaceholderConfigurer 类,代替引入PropertySourcesPlaceholderConfigurer类,新的类可以实现更灵活地和Environment 和 PropertySource机制进行交互。
对3.1之后版本,应该应用新的标准。
多层级上下文中属性加载
当web应用有父和子上下文时,属性如何加载是很常见的问题。父上下文有一些通用的核心功能和bean,并包括一个或多个子上下文,可能包含servlet特定的bean。
这种场景下,如何最佳方式定义属性文件并引入到各自的上下文中?以及如何在spring中以最佳方式获取这些属性,下面分类进行说明:
属性文件通过定义xml中
如果文件定义在父上下文:
- @Value 在子上下文 : 否
- @Value 在父上下文 : 是
如果文件定义在子上下文:
- @Value 在子上下文 : 是
- @Value 在父上下文 : 否
总之如上所述,没有暴露属性给environment,所以environment.getProperty在上下文中不工作。
属性文件通过@PropertySource定义在java中
如果文件定义在父上下文:
- @Value 在子上下文 : 是
- @Value 在父上下文 : 是
- environment.getProperty 在子上下文: 是
- environment.getProperty 在父上下文: 是
如果文件定义在子上下文:
- @Value 在子上下文 : 是
- @Value 在父上下文 : 否
- environment.getProperty 在子上下文: 是
- environment.getProperty 在父上下文: 否
总结
本文通过几个示例说明了spring中加载属性机制。希望能给大家一个参考,也希望大家多多支持脚本之家。