spring1.3

1.3. Bean概述

Spring IoC 容器管理一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML <bean/>定义的形式)。

在容器本身内,这些 bean 定义表示为BeanDefinition对象,其中包含(除其他信息外)以下元数据:

  • 包限定的类名:通常,定义了 Bean 的实际的实现类。

  • Bean 行为配置元素,用于声明 Bean 在容器中的行为(作用域,生命周期回调等)。

  • 引用其他 bean 进行其工作所需的 bean。这些引用也称为协作者或依赖项。

  • 要在新创建的对象中设置的其他配置设置,例如,池的大小限制或在 Management 连接池的 bean 中使用的连接数。

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

表 1. bean 定义

Property Explained 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 definition 之外,ApplicationContext实现还允许注册 在容器外部(由用户)创建的现有对象。这是通过getBeanFactory()方法访问 ApplicationContext 的 BeanFactory 来完成的,该方法返回 BeanFactory 的 DefaultListableBeanFactory实现。 DefaultListableBeanFactory通过registerSingleton(..)registerBeanDefinition(..)方法支持此注册。然而,典型的应用程序只使用通过常规的 bean definition 元数据定义的 bean。

Note:
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 提供nameid。如果未明确提供nameid,则容器将为该 bean 生成一个唯一名称。但是,如果要通过名称引用该 bean(则通过使用ref元素或Service Locator样式查找),则必须提供一个名称。不提供'name'的原因与使用inner beansautowiring collaborators有关。

Bean 命名约定
约定是在命名 bean 时将标准 Java约定 用于实例字段名称。也就是说,bean 名称以小写字母开头,并从那里用驼峰式大小写。这样的名称的示例包括accountManager,accountService,userDao,loginController等。
一致地命名 Bean 使您的配置更易于阅读和理解。另外,如果您使用 Spring AOP,在应用 advice 通过名称设置bean 时,它会很有帮助

Note
通过在 Classpath 中进行 component 扫描,Spring 会按照前面描述的规则为未命名的 component 生成 Bean 名称:从本质上讲,采用简单的类名称并将其初始字符转换为小写。但是,在(不寻常的)特殊情况下,如果有多个字符并且第一个和第二个字符均为大写字母,则会保留原始大小写。这些规则与java.beans.Introspector.decapitalize(Spring 使用的)定义的规则相同。

在 Bean Definition 之外别名 Bean

在 bean 定义本身中,可以使用id属性指定的(最多一个)名称和name属性中任意数量的其他名称的组合来为 bean 提供多个名称。这些名称可以是同一个 bean 的等效别名,并且在某些情况下很有用,例如,通过使用特定于该 component 本身的 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"/>

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

Java-configuration
如果使用 Java 配置,则@Bean注解可用于提供别名。有关详情,请参见使用@Bean注解

1.3.2. 实例化 bean

bean definition 实质上是创建一个或多个对象的配方(recipe)。当被询问时,容器将查看命名 bean 的配方,并使用该 bean definition 封装的配置元数据来创建(或获取)实际对象。

如果使用基于 XML 的配置元数据,则可以在<bean/>元素的class属性中指定要实例化的对象的类型(或类)。通常,此class属性是必需的(在内部是BeanDefinition实例上的Class属性)。 (For exceptions, see Instantiation by Using an Instance Factory Method and Bean Definition Inheritance.)

可以通过以下两种方式之一使用Class属性:

  • 通常,容器通过反射调用其构造函数直接创建要构造的 Bean 类的 Bean 的情况下,在某种程度上等效于new运算符的 Java 代码。

  • 指定包含static工厂方法的实际类创建对象,在不太常见的情况下,容器将在类上调用static工厂方法以创建 Bean。从static工厂方法的调用返回的对象类型可以是同一类,也可以是完全不同的另一类。

内部类命名
如果要为static嵌套类配置 Bean 定义,则必须使用嵌套类的二进制名称。
例如,如果您在com.example包中有一个名为SomeThing的类,并且此SomeThing类具有一个名为OtherThing的static嵌套类,则 Bean 定义上class属性的值为com.example.SomeThing$OtherThing 或者 com.example.SomeThing.OtherThing.。
请注意,名称中使用了$字符以将嵌套的类名与外部类名分开。

用构造函数实例化

当通过构造方法创建一个 bean 时,所有普通类都可以被 Spring 使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 bean 类就足够了。但是,根据您用于具体的bean 的 IoC 的类型,您可能需要一个默认(空)构造函数。

Spring IoC 容器几乎可以管理您想管理的任何类。它不仅限于管理真正的 JavaBean。大多数 Spring 用户更喜欢实际的 JavaBean —— 它仅具有默认(无参数)构造函数,并具有根据容器中的属性建模的 setter 和 getter 方法。您还可以在容器中具有更多奇特的非 bean 样式类。例如,如果您需要使用绝对不符合 JavaBean 规范的旧式连接池,则 Spring 也可以对其进行管理。

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

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

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

有关向构造函数提供参数(如果需要) 和 构造对象之后设置对象实例属性的机制的详细信息,请参见Injecting Dependencies.

使用静态工厂方法实例化

定义使用静态工厂方法创建的 bean 时,请使用class属性来指定包含static工厂方法的类,并使用名为factory-method的属性来指定工厂方法本身的名称。您应该能够调用此方法(带有可选参数,如稍后所述)并返回一个活的对象,该对象将被视为已通过构造函数创建。这种 bean 定义的一种用法是调用在旧代码中static工厂。

以下 bean definition 指定通过调用工厂方法创建的 bean。该 definition 不指定返回对象的类型(类),仅指定包含工厂方法的类。在此的示例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;
    }
}

有关为工厂方法提供(可选)参数并在对象从工厂返回后设置对象实例属性的机制的详细信息,,请参见 Dependencies and Configuration in Detail

使用实例工厂方法实例化

与通过静态工厂方法实例化类似,使用实例工厂方法实例化从容器中调用现有 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)进行管理和配置。参见[依赖性和详细配置]Dependencies and Configuration in Detail

Note
在 Spring 文档中,“ factory bean”是指在 Spring 容器中配置并通过instancestatic工厂方法创建对象的 Bean。相比之下,FactoryBean(注意大小写)指的是特定于Spring的FactoryBean实现类。

确定Bean的运行时类型

具体bean的运行时类型很难确定。 bean metadata definition 中的指定类只是一个初始类引用,可能与声明的工厂方法结合,或者是一个能导致不同的bean运行时类型的'FactoryBean'类,或者在实例级工厂方法(通过指定的'factory-bean'名称来解析)的情况下根本不设置。另外,AOP代理可以用一个基于接口的代理来包装bean实例,在不指导目标bean的实际类型(just its implemented interfaces)情况下。
获得bean的实际运行时类型的推荐方法是BeanFactory.getType调用指定的bean名称。这将考虑所有上述案例并返回 调用 beanfactory.getBean 返回的对象的类型。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,888评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,677评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,386评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,726评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,729评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,337评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,902评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,807评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,349评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,439评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,567评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,242评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,933评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,420评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,531评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,995评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,585评论 2 359

推荐阅读更多精彩内容