2.3.3 借助 JavaConfig 实现注入

我们前面所声明的 CompactDisc bean 是非常简单的,它自身没有其他的依赖。但现在,我们需要声明 CDPlayerbean,它依赖于 CompactDisc。

在 JavaConfig 中,要如何将它们装配在一起呢? 在 JavaConfig 中装配 bean 的最简单方式就是引用创建 bean 的方法。例如,下面就是一种声明 CDPlayer 的可行方案:

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

cdPlayer() 方法像 sgtPeppers() 方法一样,同样使用了 @Bean 注解,这表明这个方法会创建一个 bean 实例并将其注册到 Spring 应用上下文中。所创建的 bean ID为 cdPlayer,与方法的名字相同。

cdPlayer() 的方法体与 sgtPeppers() 稍微有些区别。在这里并没有使用默认的构造器构建实例,而是调用了需要传入 CompactDisc 对象的构造器来创建 CDPlayer 实例。

看起来,CompactDisc 是通过调用 sgtPeppers() 得到的,但情况并非完全如此。因为 sgtPeppers() 方法上添加了 @Bean 注解, Spring 将会拦截所有对它的调用,并确保直接返回该方法所创建的 bean,而不是每次都对其进行实际的调用。

比如说,假设你引入了一个其他的 CDPlayerbean,它和之前的那个 bean 完全一样:

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

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

假如对 sgtPeppers() 的调用就像其他的 Java 方法调用一样的话,那么每个 CDPlayer 实例都会有一个自己特有的 SgtPeppers 实例。如果我们讨论的是实际的 CD 播放器和 CD 光盘的话,这么做是有意义的。如果你有两台 CD 播放器,在物理上并没有办法将同一张 CD 光盘放到两个 CD 播放器中。

但是,在软件领域中,我们完全可以将同一个 SgtPeppers 实例注入到任意数量的其他 bean 之中。默认情况下,Spring 中的 bean 都是单例 的,我们并没有必要为第二个 CDPlayer bean 创建完全相同的 SgtPeppers 实例。所以,Spring 会拦截对 sgtPeppers() 的调用并确保返回的是 Spring 所创建的 bean,也就是 Spring 本身在调用 sgtPeppers() 时所创建的 CompactDiscbean。因此,两个 CDPlayer bean 会得到相同的 SgtPeppers 实例。

可以看到,通过调用方法来引用 bean 的方式有点令人困惑。其实还有一种理解起来更为简单的方式:

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

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

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

另外,需要提醒的是,我们在这里使用 CDPlayer 的构造器实现了 DI 功能,但是我们完全可以采用其他风格的 DI 配置。比如说,如果你想通过 Setter 方法注入 CompactDisc 的话,那么代码看起来应该是这样 的:

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

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

Last updated