Environment
是集成在容器中的抽象耍缴,他包含了兩個(gè)方面: profiles 和 properties。
一組 bean 可以定義為一個(gè)擁有名字的 profile
懊渡,只有給定的 profile
被激活的時(shí)候蝗肪,這些 bean 才會(huì)注冊(cè)到容器中斜友。不管是 xml词顾,還是注解定義到bean 都可以被指定給 profile八秃,可以指定多個(gè) profile 。Environment
對(duì)象會(huì)判斷哪個(gè) profile
被激活了肉盹。
java -jar rabbitmq-tutorials.jar --spring.profiles.active=hello-world,receiver
Properties屬性扮演一個(gè)非常重要的角色昔驱,可能來源于:properties文件,JVM系統(tǒng)屬性垮媒,系統(tǒng)環(huán)境變量舍悯,JNDI航棱,servlet上下文參數(shù)睡雇,點(diǎn)對(duì)點(diǎn)的屬性對(duì)象萌衬,Maps等。 Environment
對(duì)象給用戶提供方便接口它抱,方便撰寫配置秕豫、方便解析配置。
1.13.1 bean定義profiles
bean定義profiles是核心容器內(nèi)的一種機(jī)制观蓄,該機(jī)制能在不同環(huán)境中注冊(cè)不同的bean混移。環(huán)境的意思是,為不同的用戶做不同的事兒侮穿,該功能在很多場(chǎng)景中都非常有用歌径,包括:
- 開發(fā)期使用內(nèi)存數(shù)據(jù)源,在QA或者產(chǎn)品上則使用來自JNDI的相同的數(shù)據(jù)源
- 開發(fā)期使用監(jiān)控組件亲茅,當(dāng)部署以后則關(guān)閉監(jiān)控組件回铛,讓應(yīng)用更高效
- 為用戶各自注冊(cè)自定義bean實(shí)現(xiàn)
考慮一個(gè)實(shí)際應(yīng)用中的場(chǎng)景,現(xiàn)在需要一個(gè)DataSource克锣。開測(cè)試環(huán)境中茵肃,這樣配置:
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("my-schema.sql")
.addScript("my-test-data.sql")
.build();
}
現(xiàn)在讓我們考慮如何將此應(yīng)用程序部署到QA或生產(chǎn)環(huán)境中,假設(shè)應(yīng)用程序的數(shù)據(jù)源將注冊(cè)到生產(chǎn)應(yīng)用程序服務(wù)器的JNDI目錄袭祟。 我們的dataSource bean現(xiàn)在看起來像這樣:
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
看起來很完美验残,但是現(xiàn)在的問題變成了,如何在當(dāng)前環(huán)境中如何切換這兩個(gè)配置巾乳∧唬總結(jié)一下
概括一下上面的場(chǎng)景:你需要在場(chǎng)景A中注冊(cè)一組bean定義,在場(chǎng)景B中注冊(cè)另外一組胆绊。
@Profile注解
@Profile
注解用于當(dāng)一個(gè)或多個(gè)配置文件激活的時(shí)候紊婉,用來指定組件是否有資格注冊(cè)。 使用上面的例子辑舷,我們可以重寫dataSource
配置如下:
@Configuration
@Profile("dev")
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");
}
}
如前所述喻犁,使用@Bean方法,通常會(huì)選擇使用JNDI查找:使用Spring的JndiTemplate /JndiLocatorDelegatehelper或上面顯示的直接JNDIInitialContext用法何缓,但不是JndiObjectFactoryBean 這將使你聲明返回類型必須為FactoryBean類型
自定義組合注解 @Production
肢础,該注解用于替換@Profile("production")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
@Profile也能注解方法,用于配置一個(gè)配置類中的指定bean:
@Configuration
public class AppConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
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
@Profile("production")
public DataSource productionDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
如果一個(gè)@Component或@Configuration類被標(biāo)記為@Profile({“p1”碌廓,“p2”})传轰, 那么除非profile 'p1' 或 'p2' 被激活,否則該類將不會(huì)注冊(cè)/處理谷婆。如果是@Profile({“p1”慨蛙,“!p2”}), 只要p1 激活或者p2沒有激活辽聊,才會(huì)被注冊(cè)。
思考
如果 @Configuration 有 @Porfile('p1', 'p2')期贫,@bean 有 @Porfile('p3')的時(shí)候跟匆,激活的是 p1 時(shí),此 bean會(huì)注冊(cè)嗎通砍?
1.13.2 XML bean定義profile
XML中的<beans>元素有一個(gè)profile屬性
<beans profile="dev"
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>
也可以不用分開2個(gè)文件
<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="dev">
<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>
spring-bean.xsd強(qiáng)制允許將profile元素定義在文件的最后面玛臂,這有助于在XML文件中提供靈活的方式而又不引起混亂。
啟用 profile
有幾種方式可以啟用 profile封孙。
最直接的方式
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
setActiveProfiles()
方法提供多個(gè)配置文件的姿勢(shì):
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
聲明式的使用spring.profiles.active迹冤,值可以為逗號(hào)分隔的配置文件名列表:
-Dspring.profiles.active="profile1,profile2"
默認(rèn)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就會(huì)被創(chuàng)建虎忌。這提供了一種默認(rèn)的方式泡徙。如果有任何一個(gè)配置文件啟用,default配置就不會(huì)生效膜蠢。
默認(rèn)配置文件的名字(default)可以通過Environment的setDefaultProfiles方法或者spring.profiles.default屬性修改堪藐。
1.13.3 PropertySource abstraction
Spring的Environment抽象提供用于一系列的操作 property sources 的接口
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsFoo = env.containsProperty("foo");
System.out.println("Does my environment contain the 'foo' property? " + containsFoo);
StandardEnvironment
有兩個(gè) PropertySource 對(duì)象,一個(gè)表示JVM系統(tǒng)屬性(ystem.getProperties())狡蝶,一個(gè)表示系統(tǒng)環(huán)境變量 (System.getenv())庶橱。
StandardEnvironment 是默認(rèn)的。
StandardServletEnvironment
包括servlet配置和servlet上下文參數(shù)贪惹。
查詢是有優(yōu)先級(jí)苏章。默認(rèn)情況下,系統(tǒng)屬性優(yōu)先于環(huán)境變量奏瞬,因此如果在調(diào)用env.getProperty(“foo”)時(shí)枫绅,兩個(gè)地方都設(shè)置了foo屬性,系統(tǒng)屬性值 返回優(yōu)先于環(huán)境變量硼端。
StandardServletEnvironment
的屬性順序:ServletConfig參數(shù) > JNDI環(huán)境變量 > JVM系統(tǒng)屬性 > JVM系統(tǒng)環(huán)境 > 系統(tǒng)環(huán)境變量
這個(gè)機(jī)制是可以配置的
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
在上面的代碼中并淋,“MyPropertySource”在搜索中以最高優(yōu)先級(jí)添加。 如果它包含一個(gè)foo屬性珍昨,县耽,它將會(huì)被探測(cè)并返回,優(yōu)先于其他PropertySource中的fooproperty屬性镣典。
1.13.4 @PropertySource
@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;
}
}
任何的存在于@PropertySource中的${...}占位符兔毙,將會(huì)被解析為定義在環(huán)境中的屬性配置文件中的屬性值:
@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;
}
}
假設(shè)“my.placeholder”存在于已經(jīng)注冊(cè)的一個(gè)屬性源中,例如 系統(tǒng)屬性或環(huán)境變量兄春,占位符將被解析為相應(yīng)的值澎剥。 如果沒有,那么default/path將被用作默認(rèn)值赶舆。 如果未指定默認(rèn)值哑姚,那么property將不能解析祭饭,,則將拋出IllegalArgumentException叙量。
1.13.5 Placeholder resolution in statements
以前倡蝙,元素中占位符的值只能針對(duì)JVM系統(tǒng)屬性或環(huán)境變量進(jìn)行解析。 現(xiàn)在不再是這種情況宛乃。 因?yàn)榄h(huán)境抽象集成在整個(gè)容器中悠咱,所以很容易通過它來來對(duì)占位符進(jìn)行解析蒸辆。 這意味著你可以以任何你喜歡的方式來配置這個(gè)解析過程:可以改變是優(yōu)先查找系統(tǒng)properties或者是有限查找環(huán)境變量征炼,或者刪除它們;增加自定義property源躬贡,使之成為更合適的谆奥。
具體來說,無論“自定義”屬性定義在何處拂玻,以下語句都會(huì)工作酸些,只要它在“Environment”中可用:
<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>