文章参考来源:Spring Framework官方文档
关于Environment(org.springframework.core.env.Environment) ,它是集成在容器中的一个抽象接口,它的使用体现在应用程序环境的两个方面:概要文件和属性(profiles and properties)。
-
概要文件是一个命名的、逻辑的bean定义组,只有在给定的概要文件是active活动的情况下才向容器注册。无论是用XML定义的还是用注释定义的bean,都会被分配给一个概要文件。与概要文件相关的Environment对象的作用是确定哪些概要文件(如果有的话)当前是活动的,以及哪些概要文件(如果有的话)在默认情况下应该是活动的。
-
属性在几乎所有的应用程序中都扮演着重要的角色,它可能起源于各种来源:属性文件、JVM系统属性、系统环境变量、JNDI、servlet上下文参数、特别的Properties对象、Map对象等等。与属性相关的Environment对象的作用是为用户提供方便的服务接口,用于配置属性源并从它们解析属性。
概括来说,Environment 的作用就是:区分active环境,并配置解析对应属性。
1. Profile
(1)@Profile的使用
@Profile注解,用来标注active环境。比如以下例子中,不同环境下使用的数据库不同:
@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注解括号中的string,可以是profile name也可以是profile 表达式。profile 表达式允许一些复杂的表达式逻辑,比如“production & us-east”,其中:
- !表示“非”,not
- &表示“与”,and
- |表示“或”,or
以上表达式支持混合使用,比如production & us-east | eu-central或者production & (us-east | eu-central)
甚至可以支持自定义注解来标注环境,比如使用@Profile注解生成一个@Production注解,标注其环境名为:production
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
@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不仅可以在java代码中以注解方式起作用(@Profile),也可以在XML配置profile属性:
<beans profile="development"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="...">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
profile属性也可以在同一个文件中体现:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<!-- other bean definitions -->
<beans profile="development">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>
(2)激活Profile
激活方式:
- (1)利用ApplicationContext
- (2)配置文件中设置属性:spring.profiles.active
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
甚至可以激活多个profile:
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();
}
}
如果没有profile出于active状态,那么dataSource会被创建。哪怕只有一个profile出于active,那么默认的profile将不会起效。
如果想修改默认profile,可以选择Environment的setDefaultProfiles()方法,或者设置spring.profiles.default属性。
2. PropertySource
(1)关于PropertySource的简单理解
PropertySource是对任何键值对源的一个简单抽象。
Spring的StandardEnvironment配置了两个PropertySource对象——一个表示JVM系统属性集(system . getproperties()),另一个表示系统环境变量集(system .getenv())。
对于独立应用程序而言,StandardEnvironment能够提供默认属性源。像StandardServletEnvironment,它使用了额外的默认属性源,包括servlet配置和servlet上下文参数。它甚至可以选择性启用一个JndiPropertySource。
可以自定义添加自己的PropertySource,比如:
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
(2)@PropertySource注解的使用
@PropertySource为将PropertySource添加到Spring环境提供了一种简单方便的声明性机制。
在下面的例子中,假设文件app.properties中包含键值对属性为:testbean.name=myTestBean,以下@Configuration配置类使用@PropertySource注解,将testbean.name引入并注入到testBean的name上:
@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;
}
}
关于语句中的占位符:
- 之前,占位符的解析只能根据JVM系统属性或环境变量。(也就是只有在JVM系统属性或环境变量中设置过的占位符值,才可以被解析到)
- 现在情况不再是这样了。因为Environment抽象方法在整个容器中集成,所以很容易通过它来路由占位符的解析。
- 这意味着可以以任何方式配置解析过程。甚至可以更改搜索系统属性和环境变量的优先级,或者完全删除它们。还可以根据需要将自己的属性源添加到该组合中。
也就是说,无论自定义属性在哪里定义,只要它在Environment环境中可用,那么声明都是可解析有效的:
<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>