Spring IoC 容器管理一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML <bean/>
定义的形式)。
在容器本身中,这些 bean 定义表示为 BeanDefinition 对象,其中包含(以及其他信息)以下元数据:
- 一个包限定的类名:通常是被定义的 bean 的实际实现类。
- Bean 行为配置元素,用于说明 bean 在容器中的行为方式(范围、生命周期回调等)。
- 对 bean 完成工作所需的其他 bean 的引用。这些引用也称为协作者或依赖项。
- 要在新创建的对象中设置的其他配置设置——例如,池的大小限制或在管理连接池的 bean 中使用的连接数。
这些元数据转换为每个 beanDefinition 的一组属性,
Property | 解释 |
---|---|
Class (类) | 实例化 Bean |
Name (Bean 名称) | Bean 的命名 |
Scope (Bean 的范围) | Bean 的范围 |
Constructor arguments (构造参数) | 依赖注入 |
Properties (属性) | 依赖注入 |
Autowiring mode(自动装配模式) | 自动装配协作者 |
Lazy initialization mode (懒加载模式) | 延迟初始化的 Bean |
Initialization method (初始化方法) | 初始化回调 |
Destruction method (销毁方法) | 销毁回调 |
除了通过 BeanDefinition 定义元数据让 Spring 创建 Bean 之外,ApplicationContext 还允许注册由用户自己创建的 Bean 到容器中
命名 Bean
每个 Bean 都有一个或多个唯一标识符(容器中唯一),一个 Bean 通常只有一个标识符,如果有多个,可以将多余的视为别名
在 XML 文件中可以使用 id 或 name 属性(或则两者来指定),一般使用字母数字来表示一个 Bean 的名称,如 myBean、someService 等。如果要指定多个名称的话可以使用英文 ,
、;
分隔。
如果想通过名称应用这个 Bean ,可以通过 ref 元素或则服务定位器查找;也可以不提供 id 或 name,容器会为该 Bean 生成一个唯一名称(一般采用简单的类名,并将首字母小写),不提供名称的动机一般是在 内部 Bean 和 自动装配协作者 的场景中
在 Bean 定义之外为 Bean 起别名
前面说到,在 Bean 定义中可以为 Bean 提供多个名称,但是还可以在 Bean 定义外为 Bean 提供任意个等效的别名,比如下面这样
<alias name="fromName" alias="toName"/>
这个使用场景一般是在大型系统中,或则在一些应用程序中的每个组件通过使用特定于该组件本身的 bean 名称来引用公共的依赖项。
比如下面这个,A 组件使用别名 subsystemA-dataSource 来引用一个公共的数据源,每个组件和主应用程序都可以通过一个唯一的名称来引用数据源,并且保证不会与任何其他定义冲突(有效地创建命名空间),但它们引用的是同一个 bean。
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
:::tips JAVA 配置中的 @Bean 中也可以配置别名 :::
实例化 Bean
bean 定义本质上是创建一个或多个对象的方法,当需要创建实例时,容器会根据 bean 定义中的配置元数据来创建(或获取)实际的对象
也就是 bean 标签中指定的 class 属性。指定的是一个类的限定名称,如果是嵌套类名,如下面这种
package com.example;
class SomeThing {
static class OtherThing {}
}
那么 OtherThing 的类名可以使用美元符号 ($
) 或点 (.
) 分隔: com.example.SomeThing$OtherThingor
、com.example.SomeThing.OtherThing
。
使用构造函数实例化
使用构造函数进行实例化时,所有普通类都可以被 Spring 所使用并兼容,一般会使用默认的空参构造函数实例化。使用默认的无惨构成函数时,下面这样定义就可以了
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
当然也可以使用有参构造函数实例化,这个需要参阅后续的 注入依赖项。
使用静态工厂方法
如下面这个类
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
想要使用 createInstance 这个静态方法实例化 ClientService,可以使用 bean 标签中的 factory-method 属性
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
有关在工厂返回对象后向工厂方法提供(可选)参数和设置对象实例属性的机制的详细信息,请参阅 依赖项和配置详述。
使用实例工厂方法进行实例化
与静态工厂方法类似,不过使用实例工厂方法进行实例化是从容器中调用现有 bean 的非静态方法来创建新 bean。
比如要调用 createClientServiceInstance 获取实例
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
就可以这样写
<!-- 空参构造实例化 DefaultServiceLocator -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- 注意这里没有 class 属性,使用 factory-bean 指向容器中已有的 bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
这样一来,你就可以在一个实例对象中定义多个工厂方法了,如下所示
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 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"/>
这种方法表明, factory bean 本身可以通过依赖注入 (DI) 进行管理和配置 :::tips 在 Spring 文档中,「factory bean」是指在 Spring 容器中配置并通过 实例 或 静态工厂方法 创建对象的 bean。相比之下, FactoryBean(注意大写)指的是特定于 Spring 的 FactoryBean 实现类。 :::
确定 Bean 的运行时类型
特定 bean 的运行时类型很难确定。bean 元数据定义中的指定类只是一个初始类引用,可能与声明的工厂方法结合,或者是一个 FactoryBean 类,可能导致 bean 的不同运行时类型的类,或则像上面那样使用实例工厂方法进行实例化的场景,仅通过 factory-bean 指定的名称解析。此外,AOP 代理可以使用基于接口的代理来包装 bean 实例,并有限地公开目标 bean 的实际类型(仅其实现的接口)。
了解特定 bean 的实际运行时类型的推荐方法是 BeanFactory.getType 调用指定的 bean 名称。这将考虑上述所有情况,并返回 BeanFactory 所需的对象类型。getBean调用将返回相同的bean名称。
所以可以使用:
BeanFactory.getType("beanName")
:获取对应 bean 的 Class 对象BeanFactory.getBean("beanName")
:则是获取对应 bean 的 实例对象