Spring的核心技术(三)---Bean简介

Bean简介

Spring的IoC容器管理着很多个Bean,这些Bean是通过提供给容器的配置元数据来创建的,例如XML样式的<bean/>定义。

1. 在容器的Bean定义表示为BeanDefinition对象,它包含了以下元数据:

2. 限定包的类名:通常是实际的定义Bean的实现类;

3. Bean的行为配置元素,定义了容器中的Bean应该如何工作(如范围、生命周期的回调等等);

4. Bean工作是所要引用的其他的Bean,这些引用也叫做合作者或依赖;

5. 要设置给新创建对象的其他配置设置,例如,管理连接池Bean中使用的连接的数量,或连接池的大小限定。

这些元数据会转换成一组构成每个Bean定义的属性。

Bean的定义属性

序号

属性

解释说明

1

class

请参照Bean的示例化

2

name

请参照Bean的命名

3

scope

请参照Bean的范围

4

构造器参数

请参照依赖注入

5

属性

请参照依赖注入

6

自动组装模式

请参照自动组装的合作器

7

初始化lazy模式

请参照Bean的Lazy初始化

8

初始化方法

请参照初始化回调方法

9

析构方法

请参照析构回调方法

 

ApplicationContext除了包含如何创建指定的Bean的定义信息之外,它还允许登记由用户在容器外部创建的既存的对象。通过访问由getBeanFactory()方法返回的ApplicationContext的BeanFactory实现的DefaultListableBeanFactory对象来完成对象的注册,DefaultListableBeanFactory对象通过registerSingleton()和registerBeanDefinition()方法来支持Bean的登记功能。但是,通常应用程序只跟通过元数据的Bean定义所定义的Bean一起工作。

提示:Bean的元数据和手动提供的单例实例需要尽可能早的注册,以便在自动装配和执行其他的内部操作期间,容器能够正确的推导出相关的Bean。虽然在某种程度上支持重写既存的元数据和单例的实例,但是官方不支持在运行时注册新的Bean,这样可能导致Bean容器内的并发访问异常或/和状态的不一致。

Bean的命名

每个Bean都有一个或多个标识符,这些标识符必须是主机相应的Bean容器内部唯一的。一个Bean通常只有一个标识符,但是如果需要多个,额外的那些都被认为是别名。

在基于XML的配置元数据中,使用id和/或name属性来指定Bean的标识符。id属性只允许明确的指定一个,通常名称是字符数字混合的(如”myBean”,”fooService”等),而且还可以包含特殊字符。如果想要给Bean引入其他的别名,可以在name属性中使用逗号(”,”)、分号(”;”)或空格(” “)来分割名称。由于历史原因,在Spring3.1之前的版本张,id属性被定义为xsd:ID类型,它限制了可能的字符。版本3.1之后,它被定义为一个xsd:string类型。需要注意的是,虽然不再有XML解析器来处理,但容器依然强制要求Bean的id的唯一性。

给一个Bean提供名称或id不再是必须的,如果没有明确的提供名称或id,容器会给相应的Bean提供一个唯一的名称。但是,如果想要通过名称来引用这个Bean(如使用ref元素或Service Locator样式来查找),就必须提供一个名称。起因不会给所使用的内部Bean自助装配器提供一个名称。

Bean的命名规范

在给Bean命名时,使用Java标准的给字段命名的规范。也就是说,Bean的名称使用小写字母开头,采用驼峰式的命名方式。例如”accountManager”、”accountService”、”userDao”、”loginController”等。

命名的Bean会让配置更加容易阅读和理解,当通过名称来使用一组相关的Bean的时候,使用Spring的AOP会有更大的帮助。

在Bean的定义的外部给一个Bean定义别名

在一个Bean自己的定义里,可以使用组合的方式给这个Bean提供多个名称,这些名称被放在name属性里。这些名称等价于相同Bean的别名,这对某些场景是有用的,如允许应用程序中的每个组件通过使用组件自己所指定的Bean的名称来应用共通的依赖。

但是Bean实际定义的所有的别名不总是够用的,某些时候期望引入一个在其他地方定义的别名。在一些大型系统中,配置通常被分割到每个子系统中,每个子系统都有它自己的对象定义集合,在基于XML的配置元数据中,可以使用<alias/>元素来实现这个目的。

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

在这个案例中,同一个容器中的一个Bean被命名为fromName,使用了这个别名定义之后,也可以应用toName。

例如,子系统A的配置元数据可以通过名称subsystemA-dataSource来引用一个数据源。子系统B可以通过名称subsystemB-dataSource来引用一个数据源。当使用这两个子系统的主应用程序在组装时,主应用程序通过名称myApp-dataSource来引用数据源。这三个名称都指向由下列别名定义的统一个对象。

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

现在,每个组件和主应用程序都能够通过一个唯一的名称指向同一个数据源,这就保证了跟其他的定义不发生冲突,但是它们依然指向了相同的Bean。

基于Java代码的配置

如果使用基于Java代码的配置,@Bean注解可以用来提供别名,详细请看“使用@Bean注解”。

 

Bean的实例化

一个Bean的定义本质上是创建一个或多个对象的配方。在被要求实例化时,容器使用Bean的名称来查找这个配方,并且使用定义该Bean的配置元数据来创建(或获取)一个实际的对象。

如果使用基于XML的配置元数据,在<bean/>元素的class属性中指定要实例化的对象的类型(或类)。这个class属性对应了BeanDefinition对象实例上的Class属性,通常是强制要求的(例外的情况,请看“使用实例工厂方法来实例化”和“Bean定义的继承”)。有以下两种使用Class属性的方法:

A. 通常情况下,容器通过反射的方式调用相关Bean的构造器来直接创建指定的Bean,这有点像Java代码中使用的new操作符。

B. 调用指定的包含static工厂方法的类来创建对象,容器调用类上的static工厂方法来创建Bean的情况非常少见。从调用的static工厂方法返回的的对象类型可以是相同的类或其他的类。

内部类的命名

如果要给一个静态的内部类配置一个Bean定义,必须使用内部类的二进制名称。

例如,在com.example包张有一个Foo类,这个类有一个内部类Bar,在Bean定义中的”class”属性的值应该是com.example.Foo$Bar。

注意,在上面的命名中使用了$符把内部类的名称跟外部类的名称分开。

使用构造器来实例化

通过构造器的方法来创建Bean适用于所有的普通的类,并且与Spring兼容。也就是说,开发的类不需要实现任何特殊的接口或特定格式的编码。只需简单的指定Bean的类就足够了。但是对于有些特殊的Bean会依赖于你所使用的IoC的类型,可能需要一个默认的构造器。

实际上Spring的IoC容器可以管理所需要的任意类型的类。它并不限定于管理纯粹的JavaBean。大多数的Spring用户都偏好容器中包含只有默认构造器(没有参数)和相应的get、set方法的JavaBean,当然也可以在容器中也可以包含很多非Bean样式的类。例如,如果要使用历史遗留的不符合JavaBean规范的连接池,Spring也能够很好的管理它。

使用基于XML的配置元数据,可以像下面这样指定Bean的类:

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

关于给构造器提供参数,以及在创建对象后设置该对象实例属性的机制,情况注入依赖

 

使用静态工厂方法来实例化

在定义一个由静态的工厂方法创建的Bean的时候,要使用class属性来指定包含的static工厂方法所对应的类,并且使用factory-method属性来指定工厂方法自己的名字。调用这个方法会返回对应类的对象(随后介绍带可选参数的静态工厂方法),这跟通过构造器来创建的处理过程是一样的。这样的Bean定义的一种用途是在遗留代码中调用static工厂方法。

下面的Bean定义指定了通过调用工厂方法来创建这个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的名称,这个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文档中, factorybean指在Spring容器中配置的一个Bean,它会通过实例或静态工厂方法来创建对象。而FactoryBean(注意大写)特指Spring的 FactoryBean接口。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页