Spring-核心技术2

40次阅读

共计 5704 个字符,预计需要花费 15 分钟才能阅读完成。

1.3 Bean 概述

Spring IoC 容器管理一个或多个 bean,他们都是根据提供的配置元数据(例如 XML 中 <bean/> 定义)创建的。

在容器内部,这些 bean 定义以 BeanDefinition 对象进行表示,其中包含(以及其他信息)以下元数据:

  • 包限定的类名:通常是 bean 的实际实现类
  • Bean 行为配置元素,定义 bean 在容器中的行为方式(范围,生命周期回调等)
  • 引用 bean 执行工作所需的其他 bean,这些引用也称为协作者或依赖项
  • 要在新创建的对象中设置的其他配置。例如连接池中连接池的大小和连接数。

元数据可以转换为构成每个 bean 定义的一组属性。下表描述了这些属性:

属性转换目标
类 (Class) 实例化 Bean
名称 (Name)Bean 名称
范围 (Scope)Bean 作用范围
构造函数参数 (Constructor arguments) 依赖注入
属性 (Properties) 依赖注入
自动装配模式 (Autowiring mode) 自动装配协作者
延迟初始化模式 (Lazy initialization mode) 延迟初始化 Bean
初始化方法 (Initialization method) 初始化回调
销毁方法 (Destruction method) 销毁回调

除了包含如何创建特定 bean 的 bean 定义之外,ApplicationContext 实现类还允许用户已经在容器外创建的对象进行注册。这些任务通过使用 getBeanFactory() 方法访问应用程序上下文的 Bean 工厂来完成,该方法返回 Bean 工厂的实现类 DefaultListableBeanFactory。DefaultListableBeanFactory 通过 registerSingleton(..) 和 registerBeanDefinition(..) 方法支持此注册过程。然而一般情况下应用程序仅使用通过常规 bean 定义元数据定义的 bean。

Bean 元数据和手动提供的单例实例需要尽可能早的注册,以便容器在自动装配和其他内省步骤期间正确的推理。虽然在某种程度上支持覆盖现有元数据和现有单例实例,但是在运行时(与对工厂的实时访问同时)注册新 bean 并未得到官方支持,并且可能导致并发访问异常或 bean 容器中的状态不一致。

1.3.1 Bean 命名

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

在基于 XML 的配置元数据中,您可以使用 id 属性、name 属性或者两者都用来指定 bean 标识符。id 属性允许您指定一个准确的 id。一般来说这些名称包含字母和数字(’myBean’,’someService’ 等),但它们也可以包含特殊字符。如果你想要为 bean 引入其他别名,还可以在 name 属性中指定它们,使用逗号(,)、分号(;)或空格进行分隔。作为历史记录,在 Spring 3.1 之前的版本中,id 属性被定义为一种 xsd:ID 类型,它限制了可用的字符。从 3.1 开始,它被定义为一种 xsd:string 类型。请注意,bean id 在容器内必须唯一,但 XML 解析器不会进行限制。

bean 的 name 和 id 并不是必填的。如果你没有显式提供 name 或 id,则容器会为该 bean 生成唯一的名称。但是如果需要通过使用 ref 元素或 Service Locator 样式按名称引用该 bean 则必须提供名称。在使用内部 Bean 或自动装配协作者时可以不提供名称。

Bean 命名约定

在命名 bean 时约定使用 Java 实例字段命名规范。也就是说,bean 名称以小写字母开头并遵循驼峰法。这样的名字的例子包括 accountManager、accountService、userDao、loginController 等等。
命名 bean 始终使你的配置更易于阅读和理解。此外,如果使用 Spring AOP,那么在将切面织入使用 name 进行关联的一组 bean 时,它会有很大帮助。

通过类路径中的组件扫描,Spring 按照前面描述的规则为未命名的组件生成 bean 名称:实质上是简单的使用类名并将其初始字符转换为小写。但是,在特殊情况下,当类名中有多个字符且第一个和第二个字符都是大写字母时,原始格式将被保留。Spring 遵循 java.beans.Introspector.decapitalize 定义的规则。

在 Bean 定义之外为其创建别名

在 bean 定义本身中,可以组合使用单个 id shuxing 和任意数量的 name 属性为 bean 提供多个名称。这些名称可以是同一个 bean 的等效别名,在某些情况下很有用,例如让应用程序中的每个组件通过使用特定于该组件本身的 bean 名称来引用公共依赖项。

但是,只在 Bean 定义时指定别名是不够的,有时需要为其他地方定义的 bean 引入别名。在大型系统中,每个子系统之间的配置通常是分离的,每个子系统都有自己的一组对象定义。在基于 XML 的配置元数据中,您可以使用 <alias/> 元素来完成此任务。以下示例显示了如何执行此操作:

<alias name=”fromName” alias=”toName”/>

在示例中,同一容器中名称为 fromName 的 Bean 在使用这种别名定义之后也可以叫做 toName。

例如,子系统 A 的配置元数据通过名称 subsystemA-dataSource 引用数据源。子系统 B 的配置元数据通过名称 subsystemB-dataSource 引用数据源。在编写同时使用这两个子系统的主应用程序时,主应用程序通过名称 myApp-dataSource 引用数据源。要使这三个名称引用同一对象,可以将以下别名定义添加到配置元数据中:

<alias name=”myApp-dataSource” alias=”subsystemA-dataSource”/><alias name=”myApp-dataSource” alias=”subsystemB-dataSource”/>

现在,每个组件和主应用程序都可以通过一个唯一的名称引用 dataSource,并保证不与任何其他定义冲突(实际上创建了一个命名空间),但它们引用相同的 bean。

基于 Java 的配置

如果使用 Javaconfiguration,则 @Bean 注解可以提供别名。有关详细信息,请参阅使用 @Bean 注解。

1.3.2 初始化 Bean

bean 定义本质上是创建一个或多个对象的方法。容器在被请求时查找已经命名的 bean 的方法,并使用由该 bean 定义封装的配置元数据来创建(或获取)实际对象。

如果使用基于 XML 的配置元数据,则需要在 <bean/> 元素的 class 属性指定对象类型。此 class 属性通常是必需的(对应 BeanDefinition 实例的 Class 属性)。(有关例外的情况,请参阅使用实例工厂方法进行实例化和 Bean 定义继承)。你可以通过以下两种方式之一使用 Class 属性:

  • 容器通过反射调用 Bean 的构造函数(类似于使用 new 运算符的 Java 代码)直接创建 Bean 的情况下指定要构造的 bean 类型。
  • 调用指定包含静态工厂方法的实际类来创建对象,在不太常见的情况下,容器调用类中的静态工厂方法来创建 bean。通过调用静态工厂方法返回的对象类型可以完全是同一个类或另一个类。

内部类名
如果要为静态嵌套类配置 bean 定义则必须使用嵌套类的二进制名称。

例如,如果在 com.example 包中有一个类叫做 SomeThing,并且 SomeThing 类有一个静态嵌套类叫做 OtherThing,则 bean 定义中的 class 属性值将为 com.example.SomeThing$OtherThing。

请注意,类名中 $ 字符用于将嵌套类名与外部类名分开。

使用构造函数实例化

当您通过构造方法创建 bean 时,所有普通类都可以使用并与 Spring 兼容。也就是说,需要开发的类不需要实现任何特定接口或以某种特定方式进行编码。简单地指定 bean 的类就足够了。取决于指定 bean 使用的 IoC 类型,有时候你可能需要一个默认构造函数。

Spring IoC 容器几乎可以管理你希望它管理的任何类。它不仅限于管理真正的 JavaBean。大多数 Spring 用户更倾向于使用只有一个默认构造函数且在属性之后由合适的 setter 和 getter 的 JavaBean。你还可以在容器中包含一些外部的非 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”/>

以下示例显示了一个可以使用之前 bean 定义的类:

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)进行管理和配置。

正文完
 0