Spring系列之二:装配Bean

注:本文大部分内容出自《Spring实战(第4版)》

一、Spring配置的可选方案

Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系。但是,作为开发人员,你需要告诉Spring要创建哪些bean并且如何将其装配在一起。

当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:

  • 在XML中进行显式配置。
  • 在Java中进行显式配置。
  • 隐式的bean发现机制和自动装配。

注:本文只介绍Java配置方式,XML方式请参阅原书

Spring的配置风格是可以互相搭配的, 所以你可以选择使用XML装配一些bean, 使用Spring基于Java的配置(JavaConfig) 来装配另一些bean, 而将剩余的bean让Spring去自动发现。

建议尽可能地使用自动配置的机制,显式配置越少越好。 

二、自动化装配bean

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

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

1、创建可被发现的bean

CD为我们阐述DI如何运行提供了一个很好的样例。 如果你不将CD插入(注入) 到CD播放器中, 那么CD播放器其实是没有太大用
处的。 所以, 可以这样说, CD播放器依赖于CD才能完成它的使命。下面的代码定义了一个CD接口

CompactDisc接口在Java中定义了CD的概念

package soundsystem;
public interface CompactDisc{
      void play();
}

作为接口,它定义了CD播放器对一盘CD所能进行的操作。它将CD播放器的任意实现与CD本身的耦合降低到了最小的程度。

我们首先会创建CompactDisc其中的一个实现,也就是如下所示的SgtPeppers类:

带有@Component注解的CompactDisc实现类SgtPeppers

package sounddsystem;
import org.springframework.stereotype.Component;
@Component
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要为这个类创建bean。没有必要显式配置SgtPeppersbean,因为这个类使用了@Component注解,所以Spring会为你把事情处理妥当。
 

组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

@ComponentScan注解启用了组件扫描

package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig{
}

 

类CDPlayerConfig通过Java代码定义了Spring的装配规则,并没有显式地声明任何bean,只不过它使用了@ComponentScan注解,这个注解能够在Spring中启用组件扫描。

如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现CompactDisc,并且会在Spring中自动为其创建一个bean。

创建一个简单的JUnit测试,它会创建Spring上下文,测试组件扫描能够发现CompactDisc程序清单:

测试组件扫描能够发现CompactDisc

package soundsystem;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest{
      @Autowired
      private CompactDisc cd;
  
      @Test
      public void cdShouldNotBeNull(){
            assertNotNull(cd);
      }
}

 

CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。

注解@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDiscbean。
带有@Autowired注解,以便于将CompactDiscbean注入到测试代码之中。最后,会有一个简单的测试方法断言cd属性不为null。 如果它不为null的话,就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码之中。

2、为组件扫描的bean命名

Spring应用上下文中所有的bean都会给定一个ID。尽管我们没有明确地为SgtPeppersbean设置ID,但Spring会根据类名为其指定一个ID。这个bean所给定的ID为sgtPeppers,也就是将类名的第一个字母变为小写。如果想为这个bean设置不同的ID,你所要做的就是将期望的ID作为值传递给@Component注解。

@Component注解配置为如下所示:

@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc{
    ...
}

 

还有另外一种为bean命名的方式,这种方式不使用@Component注解,而是使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解来为bean设置ID:

@Named("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc{
      ......
}

 

Spring支持将@Named作为@Component注解的替代方案。 两者之间有一些细微的差异, 但是在大多数场景中, 它们是可以互相替换的。

3、设置组件扫描的基础包

按照默认规则, 会以配置类所在的包作为基础包,若想扫描不同的包,可通过设置@ComponentScan的value属性指明基础包来扫描组件:

@Configuration
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig{}

 

basePackages属性使用的是复数形式,可以设置多个基础包:

@Configuration
@ComponentScan(basePackages="soundsystem","video")
public class CDPlayerConfig{}

 

在上面的例子中,所设置的基础包是以String类型表示的,但这种方法是类型不安全(not type-safe)的。
@ComponentScan还可以通过basePackageclasses将其指定为包中所包含的类或接口:

@Configuration
@ComponentScan(basePackageclasses={CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig{}

 

4、通过为bean添加注解实现自动装配

简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的@Autowired注解。

如下面代码中的CDPlayer类,它的构造器上添加了@Autowired注解, 这表明当Spring创建CDPlayerbean的时候, 会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。

通过自动装配, 将一个CompactDisc注入到CDPlayer之中

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
  
@Component
public class CDPlayer implements MediaPlayer{
      private CompactDisc cd;
  
      @Autowired
      public CDPlayer(CompactDisc cd){
            this.cd = cd;
      }
  
      public void play(){
            cd.play();
      }
}

 

@Autowired注解不仅能够用在构造器上,还能用在类的任何方法上。

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

 

不管是什么方法,Spring都会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。

下面来看一下在Spring中如何显式地装配bean, 以Java代码编写配置为例。

三、通过Java代码装配bean

在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式, 但有时候自动化配置的方案行不通, 比如,你想要将第三方库中的组件装配到你的应用中, 但是没有办法在它的类代码上添加@Component和@Autowired注解。这种情况下, 你必须要采用显式装配的方式(Java、XML)。

JavaConfig是配置代码,这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来。

1、创建配置类

在前面见过的CDPlayerConfig中:

package soundsystem;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDPlayerConfig{
}

 

创建JavaConfig类的关键在于为其添加@Configuration注解,表明这是一个配置类。

2、声明简单的bean

要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解:

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

 

@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。

默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在本例中,bean的名字将会是sgtPeppers。可以通过name属性指定一个不同的名字:

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

 

3、借助JavaConfig实现注入

假设我们需要声明CDPlayerbean,它依赖于CompactDisc:

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

 

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

它不会要求将CompactDisc声明到同一个配置类之中。实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。你可以将配置分散到多个配置类、XML文件以及自动扫描和装配bean之中,只要功能完整健全即可。

 

通过XML装配bean、在JavaConfig中引用XML配置、在XML配置中引用JavaConfig未讲解

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值