> For the complete documentation index, see [llms.txt](https://potoyang.gitbook.io/spring-in-action-v4/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://potoyang.gitbook.io/spring-in-action-v4/di-2-zhang-zhuang-pei-bean/2.4-tong-guo-xml-zhuang-pei-bean/2.4.4-she-zhi-shu-xing.md).

# 2.4.4　设置属性

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

{% code title="CDPlayer.java" %}

```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();
  }

}
```

{% endcode %}

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

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

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

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

```markup
<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 文件中与其他的命名空间一起对其进行声明：

```markup
<?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 属性：

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

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

![图 2.2　借助 Spring 的 p- 命名空间，将 bean 引用注入到属性中](/files/-Lmhb8c7tCspLTsvUqWO)

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

**将字面量注入到属性中**

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

{% code title="BlankDisc.java" %}

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

}
```

{% endcode %}

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

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

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

```markup
<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-` 命名空间的属性来完成该功能：

```markup
<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-` 命名空间及其模式：

```markup
<?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 之中，如下所示：

```markup
<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 属性中：

```markup
<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，其中包含值或引用  |
