Spring Beans 详解

目录

1、如何命名 Beans

2、如何实例化 Beans

3、确定 Bean 的运行时类型


        Spring IoC 容器用来管理一个或多个 bean。这些 bean 通过用户提供的配置文件创建(例如,xml 格式的 <bean/> 定义等)。

在容器中,bean 定义表示为 BeanDefinition 对象,BeanDefinition 对象包含以下元数据:

  • 包限定(完整包路径)类名:通常是定义的 bean 的实际实现类。// 类名
  • Bean 的行为配置,声明 Bean 在容器中的行为方式(如范围、生命周期回调等等)。
  • 对 bean 完成其工作所需的其他 bean 的引用。这些引用也称为协作或依赖关系。// 依赖
  • 对象创建的其他配置。例如,对连接池的大小限定或者配置连接池 bean 中使用的连接数等。// 属性

每个元数据都对应 bean 定义的一组属性。下表中描述了这些属性:

属性说明
Class类(实例化 Bean )
NameBean 命名
Scope Bean 范围(singleton/prototype...)
Constructor arguments构造器参数
PropertiesBean 属性
Autowiring mode自动注入模型(by Name、by Type)
Lazy initialization mode懒加载模型( lazy-init="true" )
Initialization method初始化方法
Destruction method销毁方法

        除了加载通过 Bean 定义创建的特定 Bean ,Spring 容器还允许加载在容器外部创建的对象。通过 getBeanFactory() 方法访问 ApplicationContext 的 BeanFactory ,该方法会返回DefaultListableBeanFactory 的实现。DefaultListableBeanFactory 通过 registerSingleton(..) 和 registerBeanDefinition(..) 方法支持外部创建对象的注册。但是,一般应用程序仅使用通过常规 bean 定义创建的 Bean。// 容器通过Bean定义创建Bean,也可以加载外部创建的 Bean

        Bean 元数据和手动提供的单实例需要尽早注册,容器才能在自动装配和其他步骤中正确地使用这些 Bean。虽然 Spring 支持覆盖现有元数据和现有单实例的操作,但并不支持在运行时注册新的 bean(创建和访问 BeanFactory 不能同时进行),因为这可能会导致并发访问异常,或者 bean 容器中的状态不一致问题。// 不能同时对 BeanFactory 进行读写

1、如何命名 Beans

        每个 bean 都有一个或多个标识符。这些标识符在 bean 容器中必须是唯一的。通常,一个 bean 只有一个标识符。但是,如果 bean 需要定义多个标识符,那么额外的标识符就需要使用别名。// bean 具备唯一标识

        在基于 xml 的配置文件中,可以使用 id 属性、name 属性或两者都使用来指定 bean 的标识符。id 属性可以让你精确的指定一个 id 。按照惯例,name 属性的值都是字母或数字(如 'myBean'、'someService' 等),但是它也可以包含一些特殊字符。如果希望为 bean 引入其他的别名,也可以在 name 属性中指定它们,用逗号(,)、分号(;)或空格进行分隔。在 Spring 3.1 之前的版本中,id 属性被定义为 xsd: id 类型,它限制了可使用的字符。从 3.1 开始,它被定义为 xsd:string 类型。但是,bean id 的惟一性仍然由容器强制执行,但不再通过 XML 解析器强制执行。// bean 的命名规则

        为 bean 提供名称或 id 不是必须的。如果不显式提供名称或 id,容器将为该 bean 生成惟一的名称。但是,如果想通过名称引用该 bean,通过 ref 元素或 Service Locator 方式对 bean 进行查找,那么必须提供 bean 的名称。不提供 bean 名称的机制与使用内部 bean 和自动装配有关。// 为 bean 提供名称或 id 不是必须的?有尝试过这种情况吗?匿名内部类?

在 bean 定义之外使用别名命名 bean

        在 bean 定义中,通过 id 属性和任何数量的 name 属性组合可以为 bean 提供多个名称。这些名称可以作为同一个 bean 的别名,它们在某些情况下很有用。例如,不同的应用程序可以使用不同的别名去引用同一个公共的 bean 。

        当定义 bean 时,并不一定能一次性指定所有别名,有时候,在其他地方也需要定义 bean 的别名。大型系统中,在不同文件中定义别名是常见的情况,其中配置会被划分到每个子系统中,而每个子系统都有自己的 bean definitions 配置。在 xml 配置中,可以使用 <alias/> 标签来实现这种操作。下面如何做到这一点的例子展示:

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

        在这种情况下,同一个容器中命名为 fromName 的 bean ,除了可以使用 fromName 进行引用,也可以使用 toName 进行引用。

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

// 数据源 A
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
// 数据源 B
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

        现在,主应用程序和每个组件都可以通过唯一的名称引用 dataSource,且引用的都是同一个 bean,并且不会与其他的 Bean 定义进行冲突。

2、如何实例化 Beans

        bean 定义本质上是一个用来创建一个或多个对象的菜单,当 Spring 容器需要使用 bean 定义创建或者获取实际的对象时,容器会通过查找菜单,获取特定名称的 bean 信息。

        如果使用的是 xml 配置,那么需要在 <bean/> 标签的 class 属性中指定需要实例化的对象的类型(或类)。这个 class 属性通常是必须配置的(在内部是 BeanDefinition 实例上的 class 属性)。可以通过以下两种方式使用 class 属性:// 配置class 属性

  • 指定有构造方法的 bean 类。容器通过反射性调用构造函数可以直接创建 bean 的实例,这种情况与 Java 中的 new 操作有些相同。// 使用构造函数
  • 指定有静态工厂方法(用来创建对象)的 bean 类。在实际情况中,Spring 容器很少通过类中的静态工厂方法去创建一个实例。因为,通过调用静态工厂方法返回的对象类型可能是同一个类,也可能是另一个完全不同的类。// 使用静态工厂

嵌套类的类名

        如果想要为嵌套类配置 bean 定义,可以使用嵌套类的二进制名称或源名称。

        例如,如果在 com.example 包中有一个名为 SomeThing 的类。在 SomeThing 类中有一个静态嵌套的类 OtherThing ,它们之间可以用美元符号 ($) 或点 (.) 来进行分隔。所以,嵌套类在 bean 定义中 class 属性的值应该是 com.example.SomeThing$OtherThing 或者 com.example.SomeThing.OtherThing。// 嵌套类/内部类

(1)使用构造方法进行实例化

        当通过构造函数方法创建 bean 实例时,Spring 可以使用并兼容所有的普通类。也就是说,正在开发中的类不需要实现任何特定的接口,也不需要以特定的方式编码,只需指定 bean 的类就足够了。不过,这也取决于 Bean 注入时使用的 IOC 类型,有些情况下会需要提供一个默认(空)的构造函数。// 提供默认的构造函数

        Spring IoC 容器可以管理任何类,并不仅仅局限于管理 JavaBeans。大多数 Spring 用户更喜欢使用 JavaBeans,它只有一个默认的(无参数的)构造函数以及一些根据属性创建的 setter 和 getter 方法。此外,你也可以在容器中定义一些非 Bean 样式的类。比如,使用一个完全不符合 JavaBean 规范的遗留连接池(a legacy connection pool ),Spring 同样也能很好的管理它。// 管理非 Bean 样式的类,从来没有用过?a legacy connection pool 是啥?

使用 xml 的配置,可以按照以下方式指定 bean 类:

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

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

        如果想要详细了解如何向构造函数提供参数,以及在对象构造后,如何为对象实例设置属性,可以去查看依赖注入相关的资料。

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

        当定义的类中有创建 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;
    }
}

(3)使用实例工厂方法进行实例化

        实例工厂方法与静态工厂方法方式有些类似。不同的是,实例工厂方法是调用类中的非静态方法来实例化 bean 。要使用这种方式,属性 class 需要保留为空,在属性 factory-bean 中,需要指定容器中具备创建对象实例方法的 bean 的名称,同时,属性 factory-method 需要指定工厂方法的名称,下面的例子展示了如何配置这样一个 bean:// 通过一个类的普通方法去创建另一个对象,并使用 Spring 容器对对象进行管理

<!-- 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;
    }
}

        注意:在 Spring 文档中, "factory bean" 指的是在 Spring 容器中配置的 bean,它通过实例或者静态工厂方法创建。相比之下,FactoryBean(注意大小写)是 Spring 中特定的接口 FactoryBean 的一个实现类。// FactoryBean 和 "factory bean" 的区别,FactoryBean 也是一个特殊的 Bean

3、确定 Bean 的运行时类型

        确定一个 bean 的运行时类型并不容易。bean 配置中指定的类只是一个初始的类引用,该类可能与已经声明的工厂方法相结合,又或者该类是一个 FactoryBean 类型的类,也会导致 bean 的运行时类型可能不同,还有,在使用实例工厂方法的情况下,根本不设置 class 属性(通过指定的工厂 bean 名称解析)。此外,AOP 代理可以通过基于接口的代理包装 bean 实例,限制了目标bean 实际类型的公开(仅仅公开其实现的接口的类型)。

        如果需要了解一个 bean 的实际运行时类型,推荐使用 BeanFactory.getType该方法在考虑了上述所有的情况下,可以通过指定 bean 名称获取对应的对象类型// 不去通过 class 属性获取对象的类型,而是通过 name 属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值