Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.3 Bean概述
6.3 Bean概述
Spring IoC容器管理一个或多个bean。这些bean是使用你提供给容器的配置元数据去创建的,例如,以XML <bean />定义的形式。
在容器内部,这些bean的定义以BeanDefinition对象呈现出来,其中包含了(以及其他信息)以下元数据:
- 限定包的类名:典型的是定义的bean的实际实现类。
- bean的行为配置元素,说明bean在容器中的行为方式(范围,生命周期回调等)。
- bean执行工作所需的其他bean的引用; 这些引用也称为协作者或者依赖项。
- 创建新对象需要设置的其他配置,例如,Bean管理连接池而要用到的池的连接数或池的大小限制。
元数据会转换为构成每个bean的定义的一组属性。
表6.1 Bean的定义
属性 |
解释 |
类 |
6.3.2节,“Bean的实例化” |
名称 |
6.3.1节,“Bean的命名” |
作用域 |
6.5节,“Bean的作用域” |
构造函数参数 |
6.4.1节,“依赖注入” |
属性 |
6.4.1节,“依赖注入” |
自动装配模式 |
6.4.5节,“自动装配协作者” |
延迟初始化模式 |
6.4.4节,“延迟初始化的Bean” |
初始化方法 |
名为“初始化回调”章节 |
销毁方法 |
名为“销毁回调”章节 |
另外,Bean的定义也包含如何创建特定Bean的信息。ApplicationContext实现了允许对用户在容器外部创建的存在对象进行注册,是通过getBeanFactory()方法访问ApplicationContext的BeanFactory来完成的,该方法返回BeanFactory的实现DefaultListableBeanFactory。DefaultListableBeanFactory通过registerSingleton(..)和registerBeanDefinition(..)方法支持此注册。 然而,应用程序要使用的Bean,一般地仅通过元数据进行定义。
注意:需要尽早注册Bean元数据和手动提供的单例实例,以便容器在自动装配和其他内置步骤期间正确推理它们。虽然在某种程度上支持覆盖现有元数据和现有单例实例,但是,在运行时注册新的bean(并发实时地访问工厂)并未得到官方支持,并且可能导致在bean容器中出现并发访问异常和/或不一致状态。
6.3.1 Bean的命名
每个bean都有一个或多个标识符。这些标识符在托管bean的容器中必须是唯一的。 bean通常只有一个标识符,但如果它需要多个标识符,则额外的标识符可以被视为别名。
在基于XML的配置元数据中,使用id和/或name属性指定bean标识符。id属性允许您指定一个id。通常,这些名称是字母数字('myBean','fooService'等),但也可能包含特殊字符。如果要向bean引入其他别名,还可以在name属性中指定它们,用逗号(,),分号(;)或空格分隔。作为历史记录,在Spring 3.1之前的版本中,id属性被定义为xsd:ID类型,它约束了可能的字符。从3.1开始,它被定义为xsd:string类型。由于bean ID的独特性,它仍然由容器执行,不再由XML解析器执行。
你不需要为bean提供名称或ID。如果没有显式提供名称或标识,容器则会为该bean生成唯一的名称。但是,如果要通过名称引用该bean,则必须通过使用ref元素或服务定位器样式查找来提供名称。不提供名称的动机与使用内部bean和自动装配协作者有关。
Bean的命名惯例
惯例是在命名bean时使用标准Java约定作为实例字段名称。也就是说,bean名称以小写字母开头,接着以驼峰形式。像这些名称示例(不带引号),'accountManager','accountService','userDao','loginController'等等。
Bean的命名一致性使您的配置更易于阅读和理解,如果你在使用Spring AOP,那么在将advice应用于与名称相关的一组bean时,会有很大帮助。
在Bean的定义之外为Bean指定别名
在bean定义本身中,您可以为bean提供多个名称,通过将指定最多一个名称的id属性和允许任意数量的其他名称的name属性组合起来使用。 这些名称可以是同一个bean的等效别名,并且在某些情况下很有用,例如,允许应用程序中的每个组件通过使用指定该组件本身的bean名称来引用公共依赖项。
然而,指定实际定义bean的所有别名在什么地方并不总是合适的,有时需要为其他地方定义的bean引入别名。 在大型系统中通常就是这种情况,每个子系统具有其自己的一组对象定义,配置会在每个子系统之中被拆分。 在基于XML的配置元数据中,您可以使用<alias />元素来完成此任务。
<alias name="fromName" alias="toName"/>
在这种情况下,在使用此别名定义之后,同一个容器中,一个名为fromName的bean会被指为toName。
例如,子系统A的配置元数据可以通过名称subsystemA-dataSource引用一个DataSource。子系统B的配置元数据可以通过名称subsystemB-dataSource引用一个DataSource。在编写使用这两个子系统的主应用程序时,主应用程序通过名称myApp-dataSource引用DataSource。 要让这三个名称引用你添加到MyApp配置元数据的同一对象,请使用以下别名定义:
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/> <alias name="subsystemA-dataSource" alias="myApp-dataSource" />
现在,每个组件和主应用程序都可以通过一个唯一的名称引用dataSource,并保证不与任何其他定义冲突(有效地创建命名空间),它们仍然引用同一个bean。
Java配置
如果你使用的是Java配置,则@Bean注解可用于提供别名,请参见第6.12.3节“使用@Bean注释”了解详细信息。
6.3.2 实例化Bean
bean定义本质上是用于创建一个或多个对象的配方。容器在被询问时查看命名bean的配方,并使用由该bean定义封装的配置元数据来创建(或获取)实际对象。
如果使用基于XML的配置元数据,则要在<bean />元素的class属性中指定要实例化的对象的类型(或类)。class属性通常是必需的,其内在是BeanDefinition实例的Class属性。 (有关异常,请参阅名为“使用实例工厂方法实例化”一节和第6.7节“Bean的定义的继承”。)你可以通过以下两种方式之一使用Class属性:
通常,指定要构造的bean类,容器本身通过反射调用其构造函数直接创建bean的这种情况,稍微等同于使用new运算符的Java代码。
指定包含了用来创建对象的静态工厂方法的实际类,在少有的情况下,容器调用类上的静态工厂方法来创建bean。从调用静态工厂方法返回的对象类型完全可以是同一个类或另一个类。
内部类名。如果要为静态嵌套类配置bean定义,则必须使用嵌套类的二进制名称。
例如,如果你在com.example包中有一个名为Foo的类,并且这个Foo类有一个名为Bar的静态嵌套类,那么bean的定义中的'class'属性的值将是......
com.example.Foo$Bar
请注意,在名称中使用$字符可以将嵌套类名与外部类名分隔开。
用构造函数实例化
当你通过构造函数方案创建bean时,所有普通类都可以使用并与Spring兼容。也就是说,被开发的类不需要实现任何特定接口或以特定方式进行编码。简单地指定bean类就足够了。然而,根据你为该特定bean使用的IoC类型,你可能需要一个默认(空)构造函数。
Spring IoC容器几乎可以管理你想管理的任何类; 它不仅限于管理真正的JavaBeans。 大多数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的定义指定了通过调用factory-method去创建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属性设置工厂方法本身的名称。
<!-- 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(); private DefaultServiceLocator() {} 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(); private DefaultServiceLocator() {} public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } }
这种方法表明工厂bean本身可以通过依赖注入(DI)进行管理和配置。 请参阅详细信息中的依赖关系和配置。
注意:在Spring文档中,工厂bean指的是在Spring容器中配置的,可以通过实例或静态工厂方法创建对象的bean。相比之下,FactoryBean(注意大小写)是指Spring规范的FactoryBean。