Spring实战--(二)装配Bean

Spring提供了三种主要的bean装配机制:

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

使用优先级:自动化装配>Java>XML

尽可能地使用自动配置的机制。显式配置越少越好。当必须要显式配置bean的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用JavaConfig。因为JavaConfig更强大、类型安全并且对重构友好。最后,只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。

自动化装配

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

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

创建可被发现的bean

用CD和CD播放器来举例,首先定义一个CD接口:

package soundsystem;

public interface CompactDisc {
  void play();
}

CD接口的一个实现类:

package soundsystem;

@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);
  }

}
  • @Component注解:表明该类会作为组件类,并告知Spring要为这个类创建bean。

没有必要显式配置SgtPeppers bean,因为这个类使用了@Component注解,所以Spring会为你把事情处理妥当。

不过,组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。以下配置类展现了完成这项任务的最简洁配置。

package soundsystem;

@Configuration
@ComponentScan
public class CDPlayerConfig { 
}
  • @ComponentScan注解:在配置类上使用该注解,能够在Spring中启用组件扫描。

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

为了测试组件扫描的功能,我们创建一个简单的JUnit测试,它会创建Spring上下文,并判断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,因此最终的应用上下文中应该包含CompactDisc bean。

为了证明这一点,在测试代码中有一个CompactDisc类型的属性,并且这个属性带有@Autowired注解,以便于将CompactDiscbean注入到测试代码之中。最后,会有一个简单的测试方法断言cd属性不为null。如果它不为null的话,就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码之中。

为组件扫描的bean命名

Spring应用上下文中所有的bean都会给定一个ID。没有显示配置的话默认为类名第一个字母小写。(前面的例子:sgtPeppers)。

如果想为这个bean设置不同的ID,你所要做的就是将期望的ID作为值传递给@Component注解(@Component(“期望的ID”))。比如说,如果想将SgtPeppers bean标识为lonelyHeartsClub,那么你需要将SgtPeppers类的@Component注解配置为如下所示:

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

@Name注解和@Component注解有一些细微差异,在大部分情况下可以相互替换。

设置组件扫描的基础包

在@ComponentScan的value属性中指明要扫描的包的名称:

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

如果想更加清晰地表明所设置的是基础包,那么可以通过basePackages属性进行配置:

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

设置多个基础包:设置为要扫描包的数组

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

除了String类型外,可以指定为包中所包含的类或接口,这些类所在的包将会作为组件扫描的基础包。此时不再是basePackages而是basePackageClasses:

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

可以考虑在包中创建一个用来进行扫描的空标记接口(marker interface)。通过标记接口的方式,你依然能够保持对重构友好的接口引用,但是可以避免引用任何实际的应用程序代码(在稍后重构中,这些应用代码有可能会从想要扫描的包中移除掉)。

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

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

  • 构造器上添加@Autowired注解:这表明当Spring创建该bean的时候,会通过这个构造器来进行实例化并且会传入一个其所依赖的bean。
package soundsystem;

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

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

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

}

当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。

@Autowired注解不仅能够用在构造器上,还能用在类方法(如setter方法)上。比如说,如果CDPlayer有一个setCompactDisc()方法,那么可以采用如下的注解形式进行自动装配:

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

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

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

@AutoWired(required = false)
public void setCompactDisc(CompactDisc cd){
    this.cd = cd;
}

将required属性设置为false,自动装配时没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。在代码中应当进行null检查,否则可能会出现NullPointerException。

如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配。

@Inject和@Autowired有一些细微差别,但在大多数情况下可以互相替换。

通过Java代码装配bean

当想要将第三方库中的组件装配到应用中时,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。在这种情况下,必须要采用显式装配的方式。

JavaConfig与其他的Java代码有所区别。在概念上,它与应用程序中的业务逻辑和领域代码是不同的。尽管它与其他的组件一样都使用相同的语言进行表述,但JavaConfig是配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了。

创建配置类

创建JavaConfig类的关键在于为其添加@Configuration注解。

  • @Configuration注解:表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。
@Configuration
public class CDPlayerConfig{
}

尽管可以同时使用组件扫描和显式配置,但是本节更加关注于显式配置,因此将CDPlayerConfig的@ComponentScan注解移除掉了。

声明简单的bean

要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。比方说,下面的代码声明了CompactDisc 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();
}

不管你采用什么方法来为bean命名,bean声明都是非常简单的。方法体返回了一个新的SgtPeppers实例。这里是使用Java来进行描述的,因此我们可以发挥Java提供的所有功能,只要最终生成一个CompactDisc实例即可。比如在一组CD中随机选择一个CompactDisc来播放:

@Bean
public CompactDisc randomBeatlesCD(){
    int choice = (int) Math.floor(Math.random() * 3);
    if(choice == 0){
        return new SgtPeppers();
    }else if(choice == 1){
        return new HardDayNight();
    }else{
        return new Revolver();
    }
}

借助JavaConfig实现注入

通过构造器实现:

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

通过Setter方法实现:

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

再次强调,带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。构造器和Setter方法只是@Bean方法的两个简单样例。这里所存在的可能性仅仅受到Java语言的限制。

通过这种方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。你可以将配置分散到多个配置类、XML文件以及自动扫描和装配bean之中,只要功能完整健全即可。不管CompactDisc是采用什么方式创建出来的,Spring都会将其传入到配置方法中,并用来创建CDPlayer bean。

默认情况下,Spring中的bean都是单例的。假设此时有第二个CDPlayer,我们并没有必要为第二个CDPlayer bean创建完全相同的CompactDisc bean。所以,Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用sgtPeppers()时所创建的CompactDiscbean。因此,两个CDPlayer bean会得到相同的SgtPeppers实例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值