Environment 概念
Environment
接口抽象出2个关键概念:profiles 和 properties.
profile是在XML和注解定义的bean定义元数据的逻辑组,Environment
能决定当前使用哪一个逻辑组的bean定义元数据,那些是默认使用的。
Properties在所有的应用程序中占有非常重要的地位,它可以来自多种来源:属性文件,JVM系统属性,系统环境变量,JNDI,Servlet上下文参数,临时属性对象,Map对象等。Environment
提供了方便的接口来供开发者来配置属性源以及从中获取属性。
BeanDefinition Profiles
Beandefinition profiles 提供了一种能够在不同环境注册不同的bean的机制,Environment
对不同的用户来说可以是不同的意义:
- 在开发环境注入本地数据源
- 在性能检测开发环境注入监控组件
- 为客户A和客户B部署注册bean的自定义实现
使用 @Profile
@Profile
注解能够在多环境下指定那些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的名称可能包含环境名称(比如:production
),同时还支持复杂的表达式:production & us-east
,支持以下表达式:
!
:逻辑非&
: 逻辑与|
: 逻辑或
混合使用
&
和|
必须要用括号区分,比如:production & us-east | eu-central
不能通过验证,需要使用如下:production & (us-east | eu-central)
可以使用@Profile
作为原注解来开发组合注解,一下案例开发了一个@Production
注解,其功能和@Profile("production")
一致:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
@Configuration
标注的配置类也被@Profile
标注,这个配置类下的所有@Bean
以及配置类上的@Import
注解倒入的bean都只会在指定的环境中生效,如果@Component
和@Configuration
被@Profile({"p1", "p2"})
标注,则只有在p1
或p2
环境被启用的时候它们才会被注入,如果被@Profile({"p1", "!p2"})
标注,则只有当p1
激活或p2
不被激活时被注入。
@Profile
也能被标注到配置类的方法级别:
@Configuration
public class AppConfig {
@Bean("dataSource")
@Profile("development")
public DataSource standaloneDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
@Bean("dataSource")
@Profile("production")
public DataSource jndiDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
激活Profile
激活当前应用配置有许多方式:但是最直接的方式是通过ApplicationContext
获取Environment
通过api来直接指定Profile
public void start(){
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
}
还可以通过spring.profiles.active
属性来指定,还能通过系统的环境变量、JVM系统属性等方式配置该属性。如果在单元测试中,需要使用@ActiveProfiles
来指定
tip:profile可以指定多个,它们并不是互相冲突的,比如可以通过
setActiveProfiles()
一次注册多个环境:ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
通过运行时参数:
-Dspring.profiles.active="profile1,profile2"
默认的Profile
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
如果没有指定环境,则上述默认的DataSource
将会被注入,指定则不会注入。
通过setDefaultProfiles()
方法可以指定默认的环境,或者通过spring.profiles.default
属性指定
PropertySource
Environment
提供获取属性的api:
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
上述代码通过Environment
查询是否包含my-property
属性,其内部是通过遍历 PropertySource
集合,PropertySource
是key-value的简单抽象,Spring’的StandardEnvironment
配置了2个PropertySource
对象–一个是JVM系统属性(System.getProperties()
),另一个是操作系统环境变量(System.getenv()
)。
对于独立的应用,默认的
PropertySource
实现类是StandardEnvironment
,StandardServletEnvironment
可以获取到额外的一些熟悉,比如servlet配置和servlet上下文参数
当使用StandardEnvironment
时,当调用env.containsProperty("my-property")
时,如果系统属性或者环境变量包含my-property
属性则返回true
查询属性的过程时:系统属性优先于环境变量,所以。如果
my-property
同时在系统属性和环境变量中设置,会优先使用系统属性,而不是合并相同的属性
如果需要自定义属性源可以实现PropertySource
并添加到Environment
中:
public void addPropertiySource(){
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
}
MutablePropertySources
提供了一系列的API对PropertySource
进行操作
使用@PropertySource
注解
@PropertySource
提供一种声明式的方式添加PropertySource
到Environment
如果有一个叫app.properties
的文件包含:testbean.name=myTestBean
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
通过上述配置之后,调用testBean.getName()
返回myTestBean
@PropertySource
还支持${…}
表达式:
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
my.placeholder
需要预先注册到Environment
中,如果未找到则默认使用default/path
@PropertySource
是可重复使用的。