Spring小记(4):Spring的bean的装配(高级)

Bean Profile

对于一份代码,我们可能需要它在不同的环境都进行不同的调用或者配置,为了不对代码进行多次修改,我们可以预设多个条件,然后在代码执行的时候根据环境的不同来使用不同的配置,这种做法就是使用Bean Profile进行配置,在Spring中,我们使用@Profile注解进行表示,看下面一个例子
这是我们在开发时所需要的一个数据库的操作源

@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {
    @Bean(destroyMethod = "shutdown")
    public DataSource dataSource(){
        return (DataSource) new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }
}

假如我们生产中同样也需要一个操作源,那么我们可以这样写

@Configuration
@Profile("prod")
public class ProductionProfileConfig {
    @Bean
    public DataSource dataSource(){
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/myDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

这两个类的意思是只有分别在profile文件是dev或者prod被激活的时候,才会创建相应的bean
不过Spring3.2之后可以在方法级别上使用这个profile文件,如下所示

@Configuration
public class DataSourceConfig {
    @Bean(destroyMethod = "shutdown")
    public DataSource embeddeddataSource(){
        return (DataSource) new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }

    @Bean
    public DataSource jndidataSource(){
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/myDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

bean file不仅可以使用JavaConfig进行配置,而且还可以使用XML进行配置,比如我们需要配置一个开发时的数据库,那么我们可以这样进行配置

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
        profile="dev">
       <jdbc:embedded-database id="dataSource">
              <jdbc:script location="classpath:schema.sql"/>
              <jdbc:script location="classpath:test-data.sql"/>
       </jdbc:embedded-database>
</beans>

也可以为了多个环境把配置写在同一个文件里面而不是为每一个环境写一个xml文件,写的时候需要用beans进行包裹,如下

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       <beans profile="dev">
              <jdbc:embedded-database id="dataSource">
                     <jdbc:script location="classpath:schema.sql"/>
                     <jdbc:script location="classpath:test-data.sql"/>
              </jdbc:embedded-database>
       </beans>
       <beans profile="prod">
              <jee:jndi-lookup jndi-name="jdbc/myDatabase"
                               id="jndiDataSource"
                               resource-ref="true"
                               proxy-interface="javax.sql.DataSource"/>
       </beans>
       <beans profile="qa">
              <bean id="qaDataSource"
                    class="org.apache.commons.dbcp.BasicDataSource"
                    destroy-method="close"
                    p:url="jdbc:h2:tcp://dbserver/~/test"
                    p:driverClassName="org.h2.Driver"
                    p:username="sa"
                    p:password="password"
                    p:initialSize="20"
                    p:maxActive="30"/>
       </beans>
</beans>

激活Bean profile的方式有两种,分别是spring.profiles.active和spring.profile.default,一般以active为主,没有这个属性则使用default属性,如果二者都不存在,则不激活带profile参数的文件,可以有多种方式来设置这两个属性

1、作为DispatcherServlet的初始化参数
2、作为web的应用上下文参数
3、做为JNDI条目
4、作为环境变量
5、作为JVM的系统属性
6、在集成的环境上,使用@ActiveProfile注解设置
条件化的Bean

有时候我们需要根据某些条件是否满足来决定是否创建bean,我们在这时候就需要使用@Conditional注解来表示我们的条件,注解中的内容是实现Condition的类,举例如下

@Bean
    @Conditional(MagicExistsCondition.class)
    public CompactDisc randomBeatlesCD(){
        int choice = (int) Math.floor(Math.random() * 4);
        if(choice == 0){
            return new SgtPeppers();
        }else {
            return new Revolver();
        }
    }

对Condition接口实现的类如下

public class MagicExistsCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment env = conditionContext.getEnvironment();
        return env.containsProperty("magic");
    }
}

当matches返回值为true的时候,我们创建这个bean,当返回值为false的时候,则不创建

处理自动装配的歧义性

当对应一种接口有多种实现的时候,假如此时我们使用自动装配,那么就会出现自动装配的歧义,在这种情况下,我们有3种方式来消除歧义

1、对默认的bean使用@Primary标识,但是假如多个bean都使用@Primary标识,那么就没有办法进行区分
2、使用@Qualifier加限定符的方式来进行标识
3、使用自定义的限定标识符
Bean的作用域

默认的情况下,bean是单例的形式创建的,也就是说在整个应用程序的生命周期内,bean的实例只有一个,但是有时候单例的bean并不能解决所有问题,所以我们又添加了几种作用域的bean,分别是

1、单例:整个应用中只存在一个bean的实例
2、原型:每次注入或通过应用上下文获取的时候都会创建一个新的bean实例
3、会话:每个会话创建一个bean实例
4、请求:每次请求时创建一个bean实例
运行时注入

有时候我们对创建的bean的值是在运行时确定的而不是在编译时就确定的,我们有两种方式来做到这一点,分别是

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

推荐阅读更多精彩内容

  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,796评论 6 342
  • 本章内容: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表...
    谢随安阅读 1,187评论 0 5
  • 本章内容: 声明Bean 构造器注入和Setter方法注入 装配Bean 控制bean的创建和销毁 任何一个成功的...
    谢随安阅读 1,639评论 0 9
  • 我们有没有在逃避什么? 我有的,生活的压力越来越大,与你的关系并没有越来越好,工作上并没有越来越顺心,身体没有越来...
    诗人的远方阅读 139评论 0 0
  • 2016年8月25日 周四 8月24日早晨6:00我们在大庆市政府广场集合,我才知道是乘大巴车去北京参加“和合康源...
    魅力春天阅读 208评论 0 1