1.13 Environment抽象

Environment是集成在容器中的抽象耍缴,他包含了兩個(gè)方面: profilesproperties

一組 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>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市檐蚜,隨后出現(xiàn)的幾起案子魄懂,更是在濱河造成了極大的恐慌,老刑警劉巖闯第,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件市栗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡咳短,警方通過查閱死者的電腦和手機(jī)填帽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咙好,“玉大人篡腌,你說我怎么就攤上這事」葱В” “怎么了嘹悼?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長层宫。 經(jīng)常有香客問我杨伙,道長,這世上最難降的妖魔是什么卒密? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任缀台,我火速辦了婚禮,結(jié)果婚禮上哮奇,老公的妹妹穿的比我還像新娘膛腐。我一直安慰自己睛约,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布哲身。 她就那樣靜靜地躺著辩涝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪勘天。 梳的紋絲不亂的頭發(fā)上怔揩,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音脯丝,去河邊找鬼商膊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宠进,可吹牛的內(nèi)容都是我干的晕拆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼材蹬,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼实幕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起堤器,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤昆庇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后闸溃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體整吆,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年圈暗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掂为。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡员串,死狀恐怖勇哗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寸齐,我是刑警寧澤欲诺,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站渺鹦,受9級(jí)特大地震影響扰法,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毅厚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一塞颁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦祠锣、人聲如沸酷窥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蓬推。三九已至,卻和暖如春澡腾,著一層夾襖步出監(jiān)牢的瞬間沸伏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國打工动分, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毅糟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓刺啦,卻偏偏與公主長得像留特,于是被迫代替她去往敵國和親纠脾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玛瘸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容