码迷,mamicode.com
首页 > 编程语言 > 详细

SpringInAction读书笔记--第2章装配Bean

时间:2016-12-03 11:50:34      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:配置文件   多个   细节   null   完全   方法   string   代码区   exce   

  实现一个业务需要多个组件相互协作,创建组件之间关联关系的传统方法通常会导致结构复杂的代码,这些代码很难被复用和单元测试。在Spring中,对象不需要自己寻找或创建与其所关联的其它对象,Spring容器负责把需要相互协作的对象引用赋予各个对象。创建对象之间协作关系的行为称为装配,这也是依赖注入的本质。Spring为装配bean提供了三种主要的装配机制。      

1.自动化装配bean

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

  • 组件扫描:Spring会自动发现应用上下文中所创建的bean
  • 自动装配:Spring自动满足bean之间的依赖。

  组件扫描和自动装配组合起来能够将显式配置降低到最少。@Component注解表明该类会作为组件类,并告知Spring要为这个类创建bean。不过,组件扫描默认是不启用的,我们还需要显式配置一下Spring,命令它去寻找带有@Component注解的类,并为其创建bean。我们可以通过JavaConfig定义Spring的装配规则,创建一个类,只需加上注解@Configuration表明它是一个JavaConfig,再使用@ComponentScan注解启用组件扫描。如果没有其它配置的话,@ComponentScan默认扫描与配置类相同的包及其子包,查找带有@Component注解的类,在Spring中自动为其创建一个bean。也可以使用XML的方案启用组件扫描,<context:component-scan>元素会有与@ComponentScan注解相对应的属性和子元素。

      Spring应用上下文中所有的bean都会给定一个ID,如果没有明确地为bean设置ID,Spring会根据类名为其指定一个ID,如果想为这个bean设置不同的 ID,将ID作为值传递给@Component注解,如@Component("lonelyHeartsClub"),还有另一种为bean命名的方式,这种方式使用Java依赖注入规范中提供的@Named注解来为bean设置ID,如@Named("lonelyHeartsClub")。@Named和@Component在大多数场景中可以互相替换,但是@Component更清楚地表明它是做什么的。

      @ComponentScan默认扫描该类所在的包及其子包,但是我们想要将配置类放在单独的包中,使其与其它的应用代码区分开来。此时我们需要扫描多个包,将包名传给@ComponentScan可以指明扫描的包的名称,如@ComponentScan("soundsystem"),如果想更清晰表明所设置的是基础包,可以通过basePackages属性配置,如@ComponentScan(basePackages="soundsystem"),也可以同时配置多个基础包,如@ComponentScan(basePackages={"soundsystem","video"})。在上面的例子中,所设置的基础包是以String类型表示的,这种方法类型不安全,如果重构代码,所指定的基础包可能会出现错误。@ComponentScan还提供了另外一种方法,将其指定为包中所包含的类或接口,如@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class})。这些类所在的包会作为组件扫描的基础包。可以在包中创建一个用来进行扫描的空标记接口,因为在稍后重构中,应用代码可能会被移除掉。

      Spring自动配置的另外一方面内容就是自动装配。自动装配是让Spring自动满足bean依赖的一种方法,在满足依赖过程中,会在Spring应用上下文中寻找匹配某个bean需求的其它bean。为了声明自动装配,Spring提供了@Autowired注解,它可以在构造器上添加,表明当Spring创建该类的bean时,通过这个构造器进行实例化并传入可设置给构造器参数类型的bean。@Autowired注解不仅能够用在构造器上,还可以用在属性的Setter方法上,在Spring初始化bean之后,它会尽可能的去满足bean的依赖。实际上,Setter方法没有什么特殊之处,@Autowired注解可以用在类的任何方法上。无论使用以上哪种方法,Spring都会尝试满足方法参数上声明的依赖,如果只有一个bean匹配依赖需求的话,这个bean将会被装配进来。如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常,为了避免异常的出现,可以@Autowired(required=false)。将required属性设置为false时,Spring会尝试自动装配,没有匹配的bean时,Spring将会让这个bean处于未装配的状态。但是这个处于未装配状态的属性可能会在接下来的代码运行中出现NullPointerException。如果有多个bean都能满足依赖关系,Spring将会抛出一个异常,表明没有明确选择哪个bean进行装配。如果不愿使用Spring特有注解@Autowired,可以使用来源于Java依赖注入规范的@Inject注解替换。

2.通过Java代码装配bean

      有时候自动化配置的方案行不通,如需要将第三方库中的组件装配到自己的应用中时,无法在它的类上添加@Component和@Autowired注解,这时必须要采用显式装配的方式。进行显式装配时,JavaConfig是更好的方案,因为它更强大、类型安全并且对重构友好。因为它就是Java代码,和应用中的其它Java代码一样,同时它与其它的Java代码又有区别,它不应该不含业务逻辑,也不应该侵入到业务逻辑代码中。通常会将JavaConfig放到单独的包中,使它与其它的应用程序逻辑分离开。

      创建JavaConfig类的关键在于为其添加@Configuration注解,表明这是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。要在JavaConfig中声明bean,我们需要编写一个方法,这个方法创建所需类型的实例,然后给这个方法加上@Bean注解,如

 @Bean
  public CompactDisc sgtPeppers(){
     return new SgtPeppers():
  }

      bean的ID与带有@Bean注解的方法名是一样的,也可以通过name属性指定一个不同的名字,如@Bean(name="lonelyHeartsClubBand"),方法体可以使用Java提供的所有功能,只要最终返回一个CompactDisc实例即可。 在JavaConfig中装配bean的最简单方式是引用创建bean的方法,如

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

      看起来,CompactDisc是通过调用sgtPeppers()得到,但因为sgtPeppers()方法上添加了@Bean注解,实际上Spring将会拦截所有对它的调用,确保直接返回该方法所创建的bean,而不是每次都对其进行实际调用。默认情况下,Spring中的bean都是单例的。还有一种更为简单的方式,如

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

      当Spring调用该方法创建bean的时候,会自动装配一个CpmpactDisc到配置方法之中,不用明确引用CompactDisc的@Bean方法,这种方式引用其它的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类中,甚至它可以通过组件扫描自动发现或XML来进行配置。不管CompactDisc是什么方式创建出来的,Spring都会将其传入到配置方法中,用来创建CDPlayer bean。另外,除了使用构造器实现DI功能,也可以使用其它风格的DI配置,如通过Setter方法注入。

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

      总之,带有@Bean注解的方法可以采用任何必要的Java功能产生bean实例。

3.通过XML装配bean

      在XML配置中,要创建一个XML文件,并且以<beans>元素为根。最为简单的Spring 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"
              xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context" >
      </beans>
      基本的XML配置已经比同等功能的JavaConfig类复杂得多,在JavaConfig中所需要的只是@Configuration,但在使用XML时,需要在配置文件的顶部声明多个XML模式(XSD)文件,这些文件定义了配置Spring的XML元素。创建和管理Spring XML配置文件的一种简便方式是使用Spring Tool Suite。
      要在基于XML的Spring配置中声明一个bean,要使用spring-beans模式的另外一个元素<bean>,它类似于JavaConfig中的@Bean注解。<bean class="soundsystem.SgtPeppeers"/>,这个bean 的类通过class属性指定,它会根据全限定类名进行命名,这里bean的ID将会是"soundsystem.SgtPeppeers#0"。其中#0是一个计数形式,用来区分相同类型的其它bean。通常来讲更好的办法是借助id属性,为每个bean设置名字,<bean id="compactDisc" class="soundsystem.SgtPeppeers"/>。当这个bean需要装配到其它bean时,会用到这个具体的名字,通常只对这些需要按名字引用的bean进行明确地命名。Spring的XML配置更加被动,不能像JavaConfig那样使用任何方法创建bean实例,也不能在编译器检查类型,可以使用感知Spring功能的IDE,如Spring Tool Suite。
      在Spring XML配置中,只有一种方法声明bean,不过有多种方法声明DI。具体到构造器注入,有两种基本的方案。(1)<constructor-arg>元素(2)使用c-命名空间。两者区别很大程度就在于是否冗长繁琐,但是有些事使用c-空间无法实现。使用<constructor-arg>元素如下:
      <bean id="cdPlayer" class="soundsystem.CDPlayer">
           <constructor-arg ref="compactDisc" />
      </bean>
      Spring会创建一个CDPlayer实例,将ID为 compactDisc的bean引用传递到CDPlayer的构造器中。作为替代的方案,c-命名空间更为简洁地描述构造器参数的方式,在XML顶部声明其模式。
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
              xmlns:c="http://www.springframework.org/schema/c"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context" >
      </beans>
      之后,就可以使用它来声明构造器参数,如下
      <bean id="cdPlayer" class="soundsystem.CDPlayer">
           <c:cd-ref="compactDisc" />
      </bean>
      它直接引用了构造器参数的名称(cd),可能无法正常执行,替代的方案是使用参数在参数列表中的位置。
       <bean id="cdPlayer" class="soundsystem.CDPlayer">
           <c:_0-ref="compactDisc" />
      </bean>
      当我们使用一个字面量值配置对象时,可以使用constructor-arg的value属性,<constructor-arg value="The Beatles" />或者c-命名空间。当装配集合时,只能用constructor-arg,不能用c-命名空间。最简单的方法是将集合设置为null,<constructor-arg><null/></constructor-arg>,但是这样会遇到NullPointerException。更好的方法是使用<list>元素将集合声明。
      <constructor-arg>
          <list>
               <value>Sgt.Peppers</value>
               <value>With a Little</value>
          </list>
      </constructor-arg>
      也可以使用<ref>代替<value>,实现bean引用列表的装配。可以按照同样的方式使用<set>元素,它们也可以装配数组。以上完全是通过构造器注入,我们也可以使用属性的Setter方法实现属性注入。通常来说,对强依赖使用构造器注入,对可选性的依赖使用属性注入。
      <bean id="cdPlayer" class="soundsystem.CDPlayer">
           <property name="compactDisc" ref="compactDisc" />
      </bean>
      <property>元素为属性的Setter方法提供的功能与<constructor-arg>元素为构造器提供的功能一样,上例中,它引用了ID为compactDisc的bean,通过setCompactDisc()方法将其注入到compactDisc属性中。类似的,Spring提供了更为简洁的p-命名空间,作为<property>元素的替代方案。同样不能使用p-命名空间装配集合,但是我们可以使用util-命名空间的一些功能来简化。
      <util:list id="trackList">
            <value>Sgt.Peppers</value>
            <value>With a Little</value>
      </util:list>
      现在我们可以使用p-命名空间注入bean,<p:tracks-ref="trackList">

      我们可能会同时使用自动化和显式配置,在Spring中支持混合配置,在自动装配时它并不在意要装配的bean来自哪里。
      Spring的配置风格是可以互相搭配的,建议尽可能地使用自动配置的机制。当你必须要显式配置bean的时候,推荐使用类型安全并且比XML更加强大的JavaConfig。最后,只有当想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。

SpringInAction读书笔记--第2章装配Bean

标签:配置文件   多个   细节   null   完全   方法   string   代码区   exce   

原文地址:http://www.cnblogs.com/yumo/p/6128025.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!