# 2.2.1　创建可被发现的 bean

在这个 MP3 和流式媒体音乐的时代，CD（compact disc）显得有点典雅甚至陈旧。它不像卡带机、八轨磁带、塑胶唱片那么普遍，随着以物理载体进行音乐交付的方式越来越少，CD 也变得越来越稀少了。

尽管如此，CD 为我们阐述 DI 如何运行提供了一个很好的样例。如果你不将 CD 插入（注入）到 CD 播放器中，那么 CD 播放器其实是没有太大用处的。所以，可以这样说，CD 播放器依赖于 CD 才能完成它的使 命。

为了在 Spring 中阐述这个例子，让我们首先在 Java 中建立 CD 的概念。以下展现了 CompactDisc，它是定义 CD 的一个接口：

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

```java
package soundsystem;

public interface CompactDisc {
  void play();
}
```

{% endcode %}

CompactDisc 的具体内容并不重要，重要的是你将其定义为一个接口。作为接口，它定义了 CD 播放器对一盘 CD 所能进行的操作。它将 CD 播放器的任意实现与 CD 本身的耦合降低到了最小的程度。

我们还需要一个 CompactDisc 的实现，实际上，我们可以有 CompactDisc 接口的多个实现。在本例中，我们首先会创建其中的一个实现，带有 @Component 注解的 CompactDisc 实现类 SgtPeppers：

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

```java
package soundsystem;

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

{% endcode %}

和 CompactDisc 接口一样，SgtPeppers 的具体内容并不重要。你需要注意的就是 SgtPeppers 类上使用了 @Component 注解。这个简单的注解表明该类会作为组件类，并告知 Spring 要为这个类创建 bean。没有必要显式配置 SgtPeppersbean，因为这个类使用了 @Component 注解，所以 Spring 会为你把事情处理妥当。

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

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

```java
package soundsystem;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig { 
}
```

{% endcode %}

类 CDPlayerConfig 通过 Java 代码定义了 Spring 的装配规则。在 2.3 节中，我们还会更为详细地介绍基于 Java 的 Spring 配置。不过，现在我们只需观察一下 CDPlayerConfig 类并没有显式地声明任何 bean，只不过它使用了 @ComponentScan 注解，这个注解能够在 Spring 中启用组件扫描。

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

如果你更倾向于使用 XML 来启用组件扫描的话，那么可以使用 Spring context 命名空间的元素。以下展示了启用组件扫描的最简洁 XML 配置：

{% code title="soundsystem.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:context="http://www.springframework.org/schema/context"
  xmlns:c="http://www.springframework.org/schema/c"
  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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="soundsystem" />

</beans>
```

{% endcode %}

尽管我们可以通过 XML 的方案来启用组件扫描，但是在后面的讨论中，我更多的还是会使用基于Java 的配置。如果你更喜欢 XML 的话，元素会有与 @ComponentScan 注解相对应的属性和子元素。

可能有点让人难以置信，我们只创建了两个类，就能对功能进行一番尝试了。为了测试组件扫描的功能，我们创建一个简单的 JUnit 测试，它会创建 Spring 上下文，并判断 CompactDisc 是不是真的创建出来 了。CDPlayerTest 就是用来完成这项任务的，测试组件扫描能够发现 CompactDisc：

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

```java
package soundsystem;

import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
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);
  }
  
}
```

{% endcode %}

CDPlayerTest 使用了 Spring 的 SpringJUnit4ClassRunner，以便在测试开始的时候自动创建 Spring 的应用上下文。注解 @ContextConfiguration 会告诉它需要在 CDPlayerConfig 中加载配置。因为 CDPlayerConfig 类中包含了 @ComponentScan，因此最终的应用上下文中应该包含 CompactDiscbean。

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

这个代码应该能够通过测试，并以测试成功的颜色显示（在你的测试运行器中，或许会希望出现绿色）。你第一个简单的组件扫描练习就成功了！尽管我们只用它创建了一个 bean，但同样是这么少的配置能够用来发现和创建任意数量的 bean。在 soundsystem 包及其子包中，所有带有 @Component 注解的类都会创建为 bean。只添加一行 @ComponentScan 注解就能自动创建无数个 bean，这种权衡还是很划算的。

现在，我们会更加深入地探讨 @ComponentScan 和 @Component， 看一下使用组件扫描还能做些什么。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://potoyang.gitbook.io/spring-in-action-v4/di-2-zhang-zhuang-pei-bean/untitled/2.2.1-chuang-jian-ke-bei-fa-xian-de-bean.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
