(07)Bean概述

Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的(例如,以XML<bean/>定义的形式)。
在容器本身中,这些bean定义表示为BeanDefinition对象,其中包含(除其他信息外)以下元数据:

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

该元数据转换为组成每个bean定义的一组properties。下表介绍了这些properties:

Table 1. The bean definition
PropertyExplained in…​

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来完成的,该方法返回DefaultListableBeanFactory实现。DefaultListableBeanFactory通过registerSingleton(..)和registerBeanDefinition(..)方法支持此注册。然而,典型的应用程序仅使用通过常规bean定义元数据定义的bean。

Bean元数据和手动提供的单例实例需要尽早注册,以便容器在自动布线和其他内省步骤期间正确地推断它们。虽然在某种程度上支持覆盖现有的元数据和现有的单例实例,但官方不支持在运行时注册新的bean(与对工厂的实时访问同时进行),这可能会导致并发访问异常、bean容器中的状态不一致,或两者兼而有之。

命名Beans

每个bean都有一个或多个标识符。这些标识符在承载bean的容器中必须是唯一的。一个bean通常只有一个标识符。但是,如果需要多个别名,则可以将多余的视为别名。
在基于XML的配置元数据中,可以使用id属性、name属性或两者来指定bean标识符。id属性允许您只指定一个id。通常,这些名称是字母数字('myBean'、'someService'等),但它们也可以包含特殊字符。如果您想为bean引入其他别名,也可以在name属性中指定它们,用逗号(,)、分号(;)或空格分隔。尽管id属性被定义为xsd:string类型,但bean id的唯一性是由容器强制执行的,而不是由XML解析器强制执行的。
您不需要为bean提供名称或id。如果您没有显式地提供名称或id,那么容器会为该bean生成一个唯一的名称。但是,如果您想通过使用ref元素或ServiceLocator样式的查找来按名称引用该bean,则必须提供一个名称。不提供名称的动机与使用内部bean和自动装配有关。

Bean命名约定

约定是在命名bean时使用标准Java约定作为实例字段名。也就是说,bean名称以小写字母开头,并从此处开始使用驼式大小写。此类名称的示例包括accountManager、accountService、userDao、loginController等。
一致地命名bean使您的配置更易于阅读和理解。此外,如果您使用SpringAOP,那么在将建议应用于一组按名称相关的bean时会有很大帮助。

 通过类路径中的组件扫描,Spring为未命名的组件生成bean名称,遵循前面描述的规则:本质上,采用简单的类名并将其初始字符改为小写。然而,在(不寻常的)特殊情况下,当有多个字符,并且第一个和第二个字符都是大写时,原始大小写会被保留。这些规则与java.beans.Interspector.decapital(Spring在这里使用)定义的规则相同。

在Bean定义外指定Bean别名

在bean定义本身中,通过使用id属性指定的最多一个名称和name属性中任意数量的其他名称的组合,可以为bean提供多个名称。这些名称可以是同一个bean的等效别名,在某些情况下很有用,例如让应用程序中的每个组件通过使用特定于该组件本身的bean名称来引用公共依赖项。
然而,指定实际定义bean的所有别名并不总是足够的。有时需要为其他地方定义的bean引入一个别名。这在大型系统中很常见,其中配置在每个子系统之间进行拆分,每个子系统都有自己的一组对象定义。在基于XML的配置元数据中,可以使用<alias/>元素来实现这一点。以下示例显示了如何执行此操作:

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

在这种情况下,在使用此别名定义后,名为fromName的bean(在同一容器中)也可能被称为toName。
例如,子系统A的configuration元数据可以通过子系统A DataSource的名称引用DataSource。子系统B的配置元数据可以通过子系统B DataSource的名称引用DataSource。在编写同时使用这两个子系统的主应用程序时,主应用程序以myApp DataSource的名称引用DataSource。要使所有三个名称都引用同一对象,可以将以下别名定义添加到配置元数据中:

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

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

Java配置

如果您使用Java配置,@Bean注释可以用于提供别名。有关详细信息,请参见使用@Bean注释。

 实例化Bean

Bean定义本质上是创建一个或多个对象的方法。当被询问时,容器会查看命名bean的配方,并使用该bean定义封装的配置元数据来创建(或获取)实际对象。
如果使用基于XML的configuration元数据,则在<bean/>元素的class属性中指定要实例化的对象的类型(或类)。这个类属性(在内部是BeanDefinition实例上的class属性)通常是强制性的。(有关例外情况,请参阅使用实例工厂方法实例化和Bean定义继承。)可以通过以下两种方式之一使用Class属性:

  • 通常,在容器本身通过反射调用其构造函数直接创建bean的情况下,指定要构建的bean类,这在某种程度上相当于带有新运算符的Java代码。
  • 指定包含被调用以创建对象的静态工厂方法的实际类,在不太常见的情况下,容器在类上调用静态工厂方法来创建bean。调用静态工厂方法返回的对象类型可能是同一个类,也可能完全是另一个类。

嵌套类名

如果要为嵌套类配置bean定义,可以使用嵌套类的二进制名称或源名称。
例如,如果com.example包中有一个名为SomeThing的类,而这个SomeThig类有一个称为Otherhing的静态嵌套类,它们可以用美元符号($)或点(.)分隔。因此,bean定义中class属性的值将是com.example.SomeThing$Otherhing或com.example.SemeThing.Otherhing。

 使用构造函数实例化

当您通过构造函数方法创建bean时,所有普通类都可以由Spring使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口,也不需要以特定的方式进行编码。只需指定bean类就足够了。然而,根据您为该特定bean使用的IoC类型,您可能需要一个默认(空)构造函数。
SpringIoC容器几乎可以管理您希望它管理的任何类。它并不局限于管理真正的JavaBeans。大多数Spring用户更喜欢实际的JavaBeans,只有一个默认的(无参数的)构造函数和以容器中的属性为模型的适当的setter和getter。您还可以在您的容器中拥有更多奇异的非bean风格的类例如,如果您需要使用一个完全不符合JavaBean规范的旧连接池,Spring也可以对其进行管理。

使用基于XML的配置元数据,您可以指定您的bean类,如下所示:

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

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

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

使用静态工厂方法进行实例化

定义使用静态工厂方法创建的bean时,使用class属性指定包含静态工厂方法的类,并使用名为factory方法的属性指定工厂方法本身的名称。您应该能够调用此方法(如后所述,带有可选参数)并返回一个活动对象,该对象随后将被视为是通过构造函数创建的。这种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属性留空,并在factorybean属性中指定当前(或父级或祖先级)容器中的bean的名称,该容器包含要调用以创建对象的实例方法。使用工厂方法属性设置工厂方法本身的名称。以下示例显示了如何配置这样的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"/>

以下示例显示了相应的类:

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"/>

以下示例显示了相应的类:

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文档中,“工厂bean”指的是在Spring容器中配置的bean,它通过实例或静态工厂方法创建对象。相比之下,FactoryBean(注意大写)指的是Spring特定的FactoryBean实现类。

 确定Bean的运行时类型

特定bean的运行时类型是很难确定的。bean元数据定义中的指定类只是一个初始类引用,可能与声明的工厂方法组合,或者是FactoryBean类,这可能会导致bean的运行时类型不同,或者在实例级工厂方法的情况下根本没有设置(而是通过指定的工厂bean名称解析)。此外,AOP代理可以使用基于接口的代理来包装bean实例,该代理对目标bean的实际类型(仅其实现的接口)具有有限的公开。
了解特定bean的实际运行时类型的推荐方法是对指定bean名称进行BeanFactory.getType调用。这将考虑以上所有情况,并返回BeanFactory.getBean调用将为相同的bean名称返回的对象类型。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值