Spring装配bean基础

Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,我们需要做的就是告诉Spring要创建哪些bean以及这些bean的依赖情况。
Spring提供了三种主要的装配机制,选择哪种方案很大程度上就是个人喜好的问题,你尽可以选择自己最喜欢的方式。

1.隐式的bean发现机制和自动装配

Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。
     首先,定义一个接口:
package soundsystem;
public interface CompactDisc {
        void play{};
}

以及,CompactDisc实现类SgtPeppers:

package soundsystem;
import org.springframework.stereotype.Component;
@Component("LonelyHeartsClub")  // or @Named
public class SgtPeppers implements CompactDisc {
          private String title = "Sgt. Pepper's Lonely Hearts Club Band";  
          private String artist = "The Beatles";

          public void play() {
              System.out.println("Playing " + title + " by " + artist);
          }
}

需要注意的是,SgtPeppers类使用了@Component注解,这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建一个id为LonelyHeartsClub的bean。不过,组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

   package soundsystem;
   import org.springframework.context.annotation.ComponentScan;
   import org.springframework.context.annotation.Configuration;

   @Configuration
   @ComponentScan
   public class CDPlayerConfig { 
}

CDPlayerConfig使用了@Configuration注解,表明这是一个spring的配置类,而@ComponentScan注解则能够在Spring中启用组件扫描功能。如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现CompactDisc,并且会在Spring中自动为其创建一个bean。如果你想要将config类放入一个独立的包中以方便管理,也可以为@ComponentScan传入你想要作为扫描基础包的包名,如:

 @ComponentScan("org.xxx.packagename")

或者

@ComponentScan(basePackages={"org.xxx.packagename","org.xxx.otherpackagename"})

来指定多个基础包。
 为了以后方便重构(有可能改变包名),那么可以采用更加安全的方式

   @ComponentScan(basePackageClasses={abc.class, 123.class})

不再使用String类型的名称来指定包,为basePackageClasses属性所设置的数组中包含了类。这些类所在的包将会作为组件扫描的基础包。
 如果你更倾向于使用XML来启用组件扫描的话,那么可以使用Spring context命名空间的<context:component-scan>元素。

   <?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:mvc="http://www.springframework.org/schema/mvc"
  xmlns:context="http://www.springframework.org/schema/context"   xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   <context:component-scan base-package="package" />

显然,在实际的项目开发中,bean之间的关系不会像上文所列举的那样简单,不同的组件往往相互依赖来完成复杂的任务,所以,我们需要了解一下Spring自动化配置的另外一方面内容,那就是自动装配。
 通过自动装配,将一个CD注入到CDPlayer之中:

   package soundsystem;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.stereotype.Component;

   @Component // or @Named
   public class CDPlayer implements MediaPlayer {
     private CompactDisc cd;

     @Autowired // or @Inject
     public CDPlayer(CompactDisc cd) {
       this.cd = cd;
     }

     public void play() {
       cd.play();
     }
   }

构造器上添加了@Autowired注解,这表明当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且会传入一个实现了CompactDisc的bean。
 @Autowired注解不仅能够用在构造器上,还能用在类的任何方法上。比如说,如果CDPlayer有一个insertDisc()方法,那么可以采用如下的注解形式进行自动装配:

   @Autowired
   public insertDisc(CompactDisc cd) {
       this.cd = cd;
   }

如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出异常。为了避免异常的出现,你可以将@Autowired的required属性设置为false:

   @Autowired(required=false)
   public insertDisc(CompactDisc cd) {
       this.cd = cd;
   }

将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。但是,把required属性设置为false时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException。

2.通过Java代码装配bean

大部分情况下组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通时,比如说,你想要将第三方库中
的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。

让我们修改一下CDPlayerConfig类:

   package soundsystem;
   import org.springframework.context.annotation.Configuration;

   @Configuration
   public class CDPlayerConfig { 
   }

我们去掉了@ComponentScan注解,意味着spring容器不再自动扫描我们带有@Component(或@Named)注解的类了,下面我们将在CDPlayerConfig类中显式地配置之前创建的类。
 我们在config类中加入以下代码:

   package soundsystem;
   import org.springframework.context.annotation.Configuration;

   @Configuration
   public class CDPlayerConfig { 
      @Bean
      public CompactDisc sgtPeppers() {
            return new SgtPeppers();
      }
   }

@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。bean的名字默认为方法名,如有需要,可以使用如下方法修改:

   @Bean(name="beanId")
   public CompactDisc sgtPeppers() {
          return new SgtPeppers();
   }

使用java配置类进行装配同样要考虑依赖注入的问题,下面就是一种声明CDPlayer的可行方案:

   @Bean
   public CDPlayer cdPlayer() {
        return new CDPlayer(sgtPeppers());
   }

看起来,cdPlayer所依赖的CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。因为sgtPeppers()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用,也就是说sgtPeppers仍然是单例的。
 但是上面的写法有一个不足,那就是如果sgtPeppers()方法没有定义在CDPlayerConfig中,而是定义在另一个config或xml配置文件中,cdPlayer方法是无法调用的,所以,这种方式并不合理,来看另一种方法:

   @Bean
   public CDPlayer cdPlayer(CompactDisc  compactDisc ) {
        return new CDPlayer(compactDisc );
   }

在这里,cdPlayer()方法请求一个CompactDisc作为参数。当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配(不管以何种方式)一个CompactDisc到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,cdPlayer()方法也能够将CompactDisc注入到CDPlayer的构造器中,而且不用明确引用CompactDisc的@Bean方法。

再次强调一遍,带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。

3.通过XML装配bean

// TODO

4.解决不同配置方式之间的依赖关系

现在,我们已经知道cdPlayer bean需要依赖于compactDisc bean,假如我们的cdPlayer bean依然由CDPlayerConfig来装配,而compactDisc bean则改由CDConfig来装配:

   package soundsystem;

   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.Configuration;

   @Configuration
   public class CDConfig {
     @Bean
     public CompactDisc compactDisc() {
       return new SgtPeppers();
     }
   }

一种方法就是在CDPlayerConfig中使用@Import注解导入CDConfig:

   package soundsystem;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.context.annotation.Import;
   import org.springframework.context.annotation.Bean;

   @Configuration
   @Import(CDConfig.class)
   public class CDPlayerConfig { 
       @Bean
       public CDPlayer cdPlayer(CompactDisc  compactDisc ) {
            return new CDPlayer(compactDisc );
       }
   }

或者采用一个更好的办法,也就是不在CDPlayerConfig中使用@Import,而是创建一个更高级别的SoundSystemConfig,在这个类中使用@Import将两个配置类组合在一起:

package soundsystem;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig {

}

同样的,假如还有一部分bean定义在bean-config.xml中,则需要用@ImportResource注解导入:

package soundsystem;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
@ImportResource("classpath:bean-config.xml")
public class SoundSystemConfig {

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,717评论 6 342
  • 本章内容: 声明Bean 构造器注入和Setter方法注入 装配Bean 控制bean的创建和销毁 任何一个成功的...
    谢随安阅读 1,627评论 0 9
  • 2.1 Spring配置的可选方案 Spring提供了三种主要的装配机制: 1)在XML中进行显示配置。2)在ja...
    如一诺然阅读 482评论 0 1
  • 经常听人说:想做就去做,别犹豫,否犹则豫一会就没了做这件事的兴趣了。 我曾经深以为是,可是随着时间的推移, 年龄的...
    第十页阅读 322评论 0 1