在继续讲解基于注解(@Value)的属性配置之前,我们先介绍Spring的Environment,否则@Value的属性来源会讲不清楚。这与官方文档的顺序有所不同。本章内容对应官方文档地址。
Environment是对Spring运行的外部环境抽象,Environment主要管理两个概念:profiles和properties。
Profile
要准确给Pofile下一个定义还挺难的,官方文档认为Pofile可以理解为若干bean集合的逻辑组名称,而我认为可理解为Spring容器的运行时标签。我们不妨先了解了Profile如何使用,然后自就在头脑中建立起对它的认知。
激活Profile
Spring可以同时激活一个或多个Profile,他们记录在Environment里,激活一个Profile最直接的是调用Environment接口:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
另外就是通过设定spring.profiles.active属性,如果通过命令行参数的话,类似这样:-Dspring.profiles.active="profile1,profile2"
默认Profile
如果没有激活任何Proifle,那么名叫“default”的profile会被激活。可以通过ctx.getEnvironment().setDefaultProfiles方法或者java系统属性spring.profiles.default
来修改。
Profile机制
Profile给Spring提供了一种机制,在不同的环境里注册不同的Bean,比如:
- 在开发环境使用In-memory数据库,在QA以及生产环境从JNDI查找数据源;
- 在性能测试环境在注册那些性能检测的bean;
- 企业软件对不同的客户部署定制化的bean。
满足上面第一个需求的代码类似:
@Configuration
@Profile("development")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
@Profile可以放在@Configuration类上面,也可以放在@Bean方法上面。一旦附加了@Profile,那么只有指定的Profile被激活时,该@Configuration或@Bean才有效。
逻辑操作符
@Profile注解支持逻辑操作:
-
@Profile(!profile)
, 逻辑非,代表当profile没有激活; -
@Profile(profileA&profileB)
,逻辑与,代表当profileA和profileB都激活; -
@Profile(profileA|profileB)
,逻辑或,代表当profileA或profileB被激活; -
@Profile({profileA,profileB})
,逗号分隔的多个profile,相当于逻辑或
和java的逻辑操作符类似,可以组合这些操作符,通过括号来界定优先级。
Properties
Property是具名的值,也即name&value名值对。Spring容器定义了一个全局统一的Property查询接口:
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
String pvalue = env.getProperty("my-property");
虽然用统一的方式查询,但属性有多种来源,实际上,Environment抽象了一个可配置的、包含多种属性源层级结构。属性源对应的java定义是PropertySource。
PropertySource
Spring包含多种Environment实现,分别预设了不同的PropertySource,这些PropertySource是有序的,排在前面的有更高的优先级。比如StandardEnvironment,配置了两个源:JVM系统属性和系统环境变量,它的源码一看就明白:
@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参数
- ServletContext配置参数
- JNDI环境变量
- JVM系统属性
- JVM环境变量
添加PropertySource
我们可以添加自己的PropertySource到Environment,下面的代码将MyPropertySource添加为Environment的第一个PropertySource,使其具备最高的优先级:
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
在配置类上通过注解@PropertySource可以添加读取属性文件的PropertySource:
@Configuration
@PropertySource(value = {"classpath:propertySource.properties"})
public class AppConfig {
}
总结
Sping的环境被抽象为Environment,Environment主要管理两个概念,Profile和Properties。Profile是Spring的容器的标签机制,通过激活不同的Profile,我们可以基于同一套代码在运行时激活不同的bean。Properties是Spring容器范围内共享的名值对,它来自多个PropertySource,我们可以添加自定义的PropertySource。