2.4.4 设置属性

到目前为止,CDPlayer 和 BlankDisc 类完全是通过构造器注入的,没有使用属性的 Setter 方法。接下来,我们就看一下如何使用 Spring XML 实现属性注入。假设属性注入的 CDPlayer 如下所示:

CDPlayer.java
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;

public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

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

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

}

该选择构造器注入还是属性注入呢?作为一个通用的规则,我倾向于对强依赖使用构造器注入,而对可选性的依赖使用属性注入。按照这个规则,我们可以说对于 BlankDisc 来讲,唱片名称、艺术家以及磁道列表是强依赖,因此构造器注入是正确的方案。不过,对于 CDPlayer 来讲,它对 CompactDisc 是强依赖还是可选性依赖可能会有些争议。虽然我不太认同,但你可能会觉得即便没有将 CompactDisc 装入进去,CDPlayer 依然还能具备一些有限的功能。

现在,CDPlayer 没有任何的构造器(除了隐含的默认构造器),它也没有任何的强依赖。因此,你可以采用如下的方式将其声明为 Spring bean:

<bean id="cdPlayer" class="soundsystem.CDPlayer" />

Spring 在创建 bean 的时候不会有任何的问题,但是 CDPlayerTest 会因为出现 NullPointerException 而导致测试失败,因为我们并没有注入 CDPlayer 的 compactDisc 属性。不过,按照如下的方式修改 XML,就能解决该问题:

<bean id="cdPlayer" class="soundsystem.CDPlayer" >
  <property name="compactDisc" ref="compactDisc" />
</bean>

<property> 元素为属性的 Setter 方法所提供的功能与 <constructor-arg> 元素为构造器所提供的功能是一样的。在本例中,它引用了 ID 为 compactDisc 的 bean(通过 ref 属性),并将其注入到 compactDisc 属性中(通过setCompactDisc() 方法)。如果你现在运行测试的话,它应该就能通过了。

我们已经知道,Spring 为 <constructor-arg> 元素提供了 c- 命名空间作为替代方案,与之类似,Spring 提供了更加简洁的 p- 命名空间,作为 <property> 元素的替代方案。为了启用 p- 命名空间,必须要在 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"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd">
  ...
</beans>

我们可以使用 p- 命名空间,按照以下的方式装配 compactDisc 属性:

<bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc" />

p- 命名空间中属性所遵循的命名约定与 c- 命名空间中的属性类似。图 2.2阐述了 p- 命名空间属性是如何组成的。

首先,属性的名字使用了 p: 前缀,表明我们所设置的是一个属性。接下来就是要注入的属性名。最后,属性的名称以 -ref 结尾,这会提示 Spring 要进行装配的是引用,而不是字面量。

将字面量注入到属性中

属性也可以注入字面量,这与构造器参数非常类似。作为示例,我们重新看一下 BlankDisc bean。不过,BlankDisc 这次完全通过属性注入进行配置,而不是构造器注入。新的 BlankDisc 类如下所示:

BlankDisc.java
package soundsystem;

import java.util.List;

public class BlankDisc implements CompactDisc {

  private String title;
  private String artist;
  private List<String> tracks;

  public BlankDisc(String title, String artist, List<String> tracks) {
    this.title = title;
    this.artist = artist;
    this.tracks = tracks;
  }

  public void play() {
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }

}

现在,它不再强制要求我们装配任何的属性。你可以按照如下的方式创建一个 BlankDiscbean,它的所有属性全都是空的:

<bean id="reallyBlankDisc" class="soundsystem.BlankDisc" />

当然,如果在装配 bean 的时候不设置这些属性,那么在运行期 CD 播放器将不能正常播放内容。play() 方法可能会遇到的输出内容是 “Playing null by null”,随之会抛出 NullPointerException 异常,这是因为我们没有指定任何的磁道。所以,我们需要装配这些属性,可以借助 <property> 元素的 value 属性实现该功能:

<bean id="compactDisc" class="soundsystem.BlankDisc">
  <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
  <property name="artist" value="The Beatles">
  <property name="tracks">
    <list>
      <value>Sgt. Pepper's Lonely Hearts Club Band</value>
      <value>With a Little Help from My Friends</value>
      <value>Lucy in the Sky with Diamonds</value>
      <value>Getting Better</value>
      <value>Fixing a Hole</value>
      <!-- ...other tracks omitted for brevity... -->
    </list>
  </property>
</bean>

在这里,除了使用 <property> 元素的 value 属性来设置 title 和 artist,我们还使用了内嵌的 <list> 元素来设置 tracks 属性,这与之前通过 <constructor-arg> 装配 tracks 是完全一样的。

另外一种可选方案就是使用 p- 命名空间的属性来完成该功能:

<bean id="compactDisc" class="soundsystem.BlankDisc"
      p:title="Sgt. Pepper's Lonely Hearts Club Band"
      p:artist="The Beatles" >
  <property name="tracks">
    <list>
      <value>Sgt. Pepper's Lonely Hearts Club Band</value>
      <value>With a Little Help from My Friends</value>
      <value>Lucy in the Sky with Diamonds</value>
      <value>Getting Better</value>
      <value>Fixing a Hole</value>
      <!-- ...other tracks omitted for brevity... -->
    </list>
  </property>
</bean>

c- 命名空间一样,装配 bean 引用与装配字面量的唯一区别在于是否带有 -ref 后缀。如果没有 -ref 后缀的话,所装配的就是字面量。

但需要注意的是,我们不能使用 p- 命名空间来装配集合,没有便利的方式使用 p- 命名空间来指定一个值(或 bean 引用)的列表。但是,我们可以使用 Spring util- 命名空间中的一些功能来简化 BlankDisc bean。

首先,需要在 XML 中声明 util- 命名空间及其模式:

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

util- 命名空间所提供的功能之一就是元素,它会创建一个列表的 bean。借助,我们可以将磁道列表转移到 BlankDisc bean 之外,并将其声明到单独的 bean 之中,如下所示:

<util:list id="trackList">
  <value>Sgt. Pepper's Lonely Hearts Club Band</value>
  <value>With a Little Help from My Friends</value>
  <value>Lucy in the Sky with Diamonds</value>
  <value>Getting Better</value>
  <value>Fixing a Hole</value>
  <!-- ...other tracks omitted for brevity... -->
</util:list>

现在,我们能够像使用其他的 bean 那样,将磁道列表 bean 注入到 BlankDisc bean 的 tracks 属性中:

<bean id="compactDisc" class="soundsystem.BlankDisc"
      p:title="Sgt. Pepper's Lonely Hearts Club Band"
      p:artist="The Beatles"
      p:tracks-ref="trackList" />

<util:list> 只是 util- 命名空间中的多个元素之一。表 2.1 列出了 util- 命名空间提供的所有元素。

在需要的时候,你可能会用到 util- 命名空间中的部分成员。但现在,在结束本章前,我们看一下如何将自动化配置、JavaConfig 以及 XML 配置混合并匹配在一起。

元素

描述

<util:constant>

引用某个类型的 public static 域,并将其暴露为 bean

util:list

创建一个 java.util.List 类型的 bean,其中包含值或引用

util:map

创建一个 java.util.Map 类型的 bean,其中包含值或引用

util:properties

创建一个 java.util.Properties 类型的 bean

util:property-path

引用一个 bean 的属性(或内嵌属性),并将其暴露为 bean

util:set

创建一个 java.util.Set 类型的 bean,其中包含值或引用

Last updated