Spring IoC容器之环境抽象

Environment是一个集成到容器中的特殊抽象,它针对应用环境建立了profile和properties。

\bullet profile:包含了多个bean定义的一个逻辑集合,只有当指定的profile倍激活时,其中的bean才会被激活。通过XML定义或通过注解,bean都可以配置到profile中。Environment与profile相关联,然后决定来激活哪个profile或哪个为默认的profile。

\bullet properties:它在应用中有着重要作用,如property文件、JVM系统的property、系统环境变量、JNDI、srevlet上下文参数、Map等。Environment与property相关联,给开发者一个方便的服务接口来配置这些数据源,并正确解析。

1、bean定义的profile

在容器中bean定义的profile是一种允许不同环境注册不同的bean的机制,适用以下场景:

1)解决内存中数据源问题,可以在不同环境中访问不同的数据源,如dev、sit、pre、prd环境等。

2)仅在开发环境中使用一些监视服务。

3)在不同环境中使用不同的bean实现。

如果泛化了一些特殊环境下引用的bean定义,可以将其中指定的bean注入特定的上下文中,而不是注入所有的上下文中。

2、@Profile

@Profile注解允许开发者表示一个组件是否适合在当前环境下进行注册,只有当前的profile被激活时,对应的bean才会被注册到上下文中。

@Configuration

@Profile("dev")

public class DataConfig {

    @Bean

    public DataSource dataSource() {

        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)

            .addScript("classpath:com/config/sql/schema.sql")

            .addScript("classpath:com/config/sql/test-data.sql").build();

    }

}

@Configuration

@Profile("prd")

public class JndiDataConfig {

    @Bean

    public DataSource dataSource() throws Exception {

        Context ctx =new InitialContext();

        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

    }

}

@Profile注解可以作为元注解来使用,如下自定义的@Prd注解可以替代@Profile("prd"):

@Profile注解也可以在方法级别使用,还可以声明在包含@Bean注解的方法上。如下:

@Configuration

public class AppConfig {

    @Bean

    @Profile("dev")

    public DataSource dataSource() {

        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)

            .addScript("classpath:com/config/sql/schema.sql")

            .addScript("classpath:com/config/sql/test-data.sql").build();

    }

    @Bean

    @Profile("prd")

    public DataSource dataSource() throws Exception {

        Context ctx =new InitialContext();

        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

    }

}

如果配置了@Configuration的类同时也配置了@profile,则所有配置了@Bean注解的方法和@Import注解的类都会被传递为该profile。除非这个profile被激活,否则其中的bean定义都不会被激活。如果配置为@Component或@Configuration的类标记了@profile({"p1","p2"}),那么这个类当且仅当profile为p1或p2时才会被激活。如果某个profile的前缀为“!”,则@profile注解的类只有在当前的profile没被激活时才能生效。如配置为@profile({"p1","!p2"})则注册的行为会在profile为p1或profile为非p2时被激活。

3、XML中定义的profile

在XML中配置<beans/>的profile属性。

<beans profile="dev"

    xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xsi:schemaLocation="...">

    <jdbc:embedded-database id="dataSource">

        <jdbc:script location="classpath:com/config/sql/schema.sql">

        <jdbc:script location="classpath:com/config/sql/test-data.sql">

    </jdbc:embedded-database>

</beans>

<beans profile="prd"

    xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:jee="http://www.springframework.org/schema/jee"

    xsi:schemaLocation="...">

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource" />

</beans>

或者通过嵌套<beans>标签来定义:

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:jee="http://www.springframework.org/schema/jee"

    xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xsi:schemaLocation="...">

    <beans profile="dev">

        <jdbc:embedded-database id="dataSource">

            <jdbc:script location="classpath:com/config/sql/schema.sql">

            <jdbc:script location="classpath:com/config/sql/test-data.sql">

        </jdbc:embedded-database>

    </beans>

    <beans profile="prd">

        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource" />

    </beans>

</beans>

4、激活profile

有多种方法来激活一个profile,最直接的方式是通过编程的方式来直接调用Environment API。ApplicationContext中包含以下接口:

AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext();

context.getEnvironment().setActiveProfiles("dev");

context.register(SomeConfig.class,DataConfig.class,PrdDataConfig.class);

context.refresh();

profile还可以通过spring.profiles.active中的属性来指定,可以通过系统环境变量、JVM系统变量、servlet上下文中的参数或JNDI的一个参数来写入。profile的定义并非是互斥关系,开发者可以在同一时间激活多个profile。

context.getEnvironment().setActiveProfiles("dev","sit");

也可以通过spring.profiles.active来指定逗号分隔的多个profile名称。

-Dspring.profiles.active=“profile1,profile2”

5、默认profile

@Configuration

@Profile("default")

public class DataConfig {

    @Bean

    public DataSource dataSource() {

        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)

            .addScript("classpath:com/config/sql/schema.sql")

            .addScript("classpath:com/config/sql/test-data.sql").build();

    }

}

如果没有其他profile被激活,则以上定义的dataSource将被创建,这种方式是默认情况下提供bean定义的一种方式。一旦任何一个profile被激活,默认的profile将不会被激活。默认的profile的名称可以通过Environment中的setActiveProfiles()或通过spring.profiles.default属性来更改。

6、@PropertySource

@PropertySource注解提供了一种方便的机制将PropertySource添加到spring的Environment中。给定一个文件app.properties包含了key-value对testbean.name=myTestBean,以下代码使用@PropertySource调用testbean.setName()将返回myTestBean。

@Configuration

@PropertySource("classpath:/com/xlws/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注解中如${...}的占位符,都可以被解析为Environment中的属性资源。如下:

@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中的资源,若有JVM系统属性或环境变量,占位符会解析成对象的值,若没有则default/path会作为默认值。如果没有指定默认值,并且占位符也解析不出来,将会抛出异常IllegalArgumentException。

7、占位符解析

开发者可以任意配置占位符:

1)可以自由调整系统变量和环境变量的优先级。

2)可以额外增加自己的属性源信息。

以下XML配置不关心customer属性在哪里定义,只要这个值在Environment中有效即可:

<beans>

    <import resource="com/xlws/service/${customer}-config.xml" />

</beans>



--参考文献《Srping5开发大全》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容