# 1.3 Bean概述

Spring IOC容器管理一个或多个bean。这些bean是使用你提供给容器的配置元数据创建的（例如，以xml定义的形式）。

在容器本身中，这些bean表示为BeanDefinition对象，其中包含（除其他信息外）以下元数据：

* 包限定类名：通常是定义bean的实际实现类。
* bean行为配置元素，用于说明bean在容器中的行为（范围、生命周期回调等）。
* 对bean执行其工作所需的其他bean的引用。这些引用也称为合作者或依赖。
* 要在新创建的对象中设置的其他配置设置，例如一个管理连接池的Bean中连接池的大小限制或正在使用的连接数目。

这个些元数据包含了构造Bean定义的一组属性。下表描述了这些属性：

| 属性名                      | 解释于...                   |
| ------------------------ | ------------------------ |
| Class                    | Instantiating Beans      |
| Name                     | Naming Beans             |
| Scope                    | Bean Scopes              |
| Constructor arguments    | Dependency Injection     |
| Properties               | Dependency Injection     |
| Autowiring mode          | Autowiring Collaborators |
| Lazy initialization mode | Lazy-initialized Beans   |
| Initialization method    | Initialization Callbacks |
| Destruction method       | Destruction Callbacks    |

除了包含有关如何创建特定bean的信息的bean定义之外，ApplicationContext实现还允许注册容器外部（由用户）创建的现有对象。 通过getBeanFactory（）方法访问ApplicationContext的BeanFactory，该方法返回BeanFactory接口的DefaultListableBeanFactory实现方法。 DefaultListableBeanFactory通过registerSingleton（..）和registerBeanDefinition（..）这两个方法来注册外部Bean。 但是，应用程序通常只使用常规bean定义元数据来定义bean。

> bean元数据和手动提供的单例实例需要尽早注册，以便容器在自动装配和其他自省步骤中正确地解析它们。
>
> 虽然在Spring某种程度上支持覆盖现有元数据和现有的单例实例，但是官方并不支持在运行时注册新bean（与工厂的实时访问同时进行），因为这可能会导致并发访问异常或者bean容器中的状态不一致。

## 1.3.1. 为Bean命名

每个bean都有一个或多个标识符。这些标识符在承载bean的容器中必须是唯一的。bean通常只有一个标识符。但是，如果它需要多个标志符，则可以将额外的标志符视为别名。

在基于XML的配置元数据中，可以使用id属性或name属性或者同时使用来指定bean标识符。ID属性允许你仅指定一个ID。通常来说，这些名称是字母数字（“mybean”、“someservice”等），但它们也可以包含特殊字符。如果要为bean引入其他别名，还可以在name属性中指定它们，用逗号（，），分号（；）或空格分隔。

在Spring3.1之前的版本中，id属性被定义为xsd:id类型，这限制了可能的字符。从3.1开始，它被定义为xsd:string类型。注意，bean id的唯一性仍然由容器强制实现，尽管不再由XML解析器实现。

你不需要强制的为bean提供名称或ID。如果不显式地提供名称或ID，容器将为该bean生成一个唯一的名称。但是，如果你希望通过使用ref元素或服务定位器风格来查找该bean，则必须提供一个名称。不提供名称仅用在使用内部bean和自动装配协作者的情况。

> Bean命名规范
>
> 在命名bean时使用标准Java规范，例如字段名。也就是说，bean名称以一个小写字母开头的驼峰写法。比如：accountManager, accountService, userDao, loginController等。
>
> bean命名的一致性使你的配置更易于阅读和理解。另外，如果你使用SpringAOP，那么一致性的Bean命名会在通知一组名字相关的Bean时很有用。
>
> 通过在类路径中扫描组件，Spring为未命名的组件生成bean名称，遵循前面描述的规则：本质上，取简单的类名并将其初始字符改为小写。但是，在特殊情况下，当有多个字符并且第一个和第二个字符都是大写时，原始的大小写将被保留。这些规则与java.beans.introspector.decapitalize定义的规则相同。

**在Bean的定义外使用别名**

在bean定义中，你可以为bean提供多个名称，可以通过id属性指定最多一个名称或者通过name属性指定任何数量的其他名称。 这些名称是同一Bean的等效别名，在某些情况下非常有用，例如，可以为一个通用的组件指定名字，并在应用程序的其他每个组件引用它。

只在bean实际定义的地方指定所有别名并不够用，有时需要为在别处定义的bean引入别名。在大型系统中，通常把系统拆分成多个子系统，每个子系统都有自己的对象定义集。在基于XML的配置元数据中，可以使用元素来实现这一点。以下示例显示了如何执行此操作：

```
<alias name="fromName" alias="toName"/>
```

在这种情况下，名为fromName的bean（在同一容器中）在使用这个别名定义之后，也可以称为toName。

例如，子系统A的配置元数据引用了名为subsystemA-dataSource的DataSource。子系统B的配置元数据引用了名为subsystemB-dataSource的DataSource。 当组成使用这两个子系统的主应用程序时，主应用程序以myApp-dataSource的名称引用DataSource。要使所有三个名称都指向同一对象，可以将以下别名定义添加到配置元数据中：

```
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
```

现在，每个组件和主应用程序都可以通过唯一的名称引用数据源，并保证不会与任何其他定义冲突（有效地创建了一个名称空间），但它们引用的是同一个bean。

```
JAVA配置
如果你使用JAVA配置，@Bean可以用来提供别名。
```

## 1.3.2. 实例化Bean

bean定义本质上是创建一个或多个对象的方法。当需要创建Bean的时候，容器会查看命名bean的创建方法，并使用由该bean定义封装的配置元数据来创建（或获取）实际对象。

如果使用基于XML的配置元数据，则需要指定要在< bean/>元素的class属性中实例化的对象的类型（或类名）。这个class属性（在内部是BeanDefinition实例上的Class属性）通常是强制的。（或者请参见使用实例工厂方法和bean定义继承进行实例化。）可以通过以下两种方式之一使用Class属性：

* 多数情况下，在容器本身直接创建bean，通过调用bean的构造函数来创建bean的实例，相当于Java代码的new。
* 少数情况下，容器调用类上的静态工厂方法来创建bean。需要要指定包含调用创建对象的静态工厂方法的实际类，静态工厂方法调用返回的对象类型可以是同一个类或完全是另一个类。

> 内部类名
>
> 如果要为静态内部类配置bean定义，则必须使用嵌套类的二进制名称。
>
> 例如，如果在com.example包中有一个名为SomeThing的类，而这个SomeThing类有一个名为OtherThing的静态内部类，bean定义中class属性的值将是com.example.SomeThing$OtherThing。
>
> 注意，在名称中使用$字符将内部类名与外部类名分开。

**通过构造函数实例化**

当使用构造函数创建bean时，所有普通类都可以被Spring使用并兼容。也就是说，正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。仅仅指定bean类就足够了。但是，根据你对特定bean使用的IOC类型，你可能需要一个默认（空）构造函数。

SpringIOC容器几乎可以管理你想要管理的任何类。它不仅限于管理真正的JavaBeans。大多数Spring用户更喜欢实际的JavaBean，它只有一个默认（无参数）构造函数，以及根据容器中的属性创建的setter和getter方法。你还可以在容器中拥有更多其他非bean风格的类。例如，如果你需要使用完全不符合JavaBean规范的历史的连接池，那么Spring也可以对其进行管理。

使用基于XML的配置元数据，可以按如下方式指定bean类：

```
<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
```

有关向构造函数提供参数（如果需要）和在构造对象后设置对象实例属性的机制的详细信息，请参阅注入依赖。

**使用静态工程方法实例化**

定义使用静态工厂方法创建的bean时，使用class属性指定包含静态工厂方法的类，使用名为factory-method的属性指定工厂方法本身的名称。你应该能够调用这个方法（使用可选的参数，如后文所述），并返回一个活动对象，然后将其视为是通过构造函数创建的。这种bean定义的一个用途是调用遗留代码中的静态工厂。

下面的bean定义指定通过调用工厂方法来创建bean。定义没有指定返回对象的类型（类），只指定包含工厂方法的类。在本例中，createInstance（）方法必须是静态方法。以下示例显示如何指定工厂方法：

```
<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
```

下面是这个工厂类：

```
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
```

有关向工厂方法提供（可选）参数和在从工厂返回对象后设置对象实例属性的机制的详细信息，请参阅依赖和配置。

**使用实例里面的工厂方法创建实例**

与通过静态工厂方法进行实例化类似，使用实例工厂方法进行实例化会从容器中调用现有bean的非静态方法来创建新bean。要使用此机制，请将class属性保留为空，并在factory-bean属性中，在包含要调用以创建对象的实例方法的当前（或父或父）容器中指定bean的名称。使用factory-method属性设置工厂方法本身的名称。下面的示例演示如何配置这样的bean：

```
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
```

下面列出了相应的Java类：

```
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
```

如下所示，一个工厂类可以包括多个工厂方法：

```
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
```

下面列出了相应的Java类：

```
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}
```

这种方法表明，工厂bean本身可以通过依赖注入（DI）进行管理和配置。请参阅依赖和配置。

> 在Spring文档中，“factory bean”是指在Spring容器中配置的bean，它通过实例或静态工厂方法创建对象。 相比之下，FactoryBean（注意大写）是指Spring中的FactoryBean类。
