spring bean


Spring Bean

Spring容器管理了一个或者多个bean,这些bean是更具bean定义元数据进行创建的。

在Spring容器内部,bean定义元数据以BeanDefinition对象呈现,包含以下信息:

  • 类的全类名
  • 定义bean在容器中的行为的配置元素,比如:bean作用域、生命周期回掉方法等
  • 被当前bean所引用的bean
  • 其他配置-比如:连接池的大小

这些元数据会翻译为一系列属性组成BeanDefinition

属性官方说明链接
ClassInstantiating Beans
NameNaming Beans
ScopeBean Scopes
Constructor argumentsDependency Injection
PropertiesDependency Injection
Autowiring modeAutowiring Collaborators
Lazy initialization modeLazy-initialized Beans
Initialization methodInitialization Callbacks
Destruction methodDestruction Callbacks

BeanDefinition包含了如何准确创建一个bean的信息,ApplicationContext也允许用户注册在容器外部创建的对象。可以通过getBeanFactory()来获取ApplicationContext内部聚合的BeanFactory来做到,其返回BeanFactory实现类为DefaultListableBeanFactoryDefaultListableBeanFactory支持通过registerSingleton(..)registerBeanDefinition(..)方法进行注册

TIPS: 为了能让Spring容器在自动装盘时正确的理解bean的意图,Bean元数据和手动注册的单实例对象应该被尽可能早地执行。虽然在某种程度上支持覆盖现有Bean元数据和现有单例实例,但是在运行时(与对工厂的实时访问同时)对新bean的注册不被Spring支持,并且可能导致并发访问异常,bean容器中的状态不一致。

bean的命名

任何一个bean都有一个或多个标识符,这些标识符在持有这些bean的容器中必须唯一,通常一个bean只有一个标识符,如果要求有多个,可以考虑起别名。

使用XML进行配置时,我们使用idname属性进行指定bean的标识符,id能指定唯一的标识,这些都是由字母数字组成的(‘myBean’, ‘someService’等),也能包含特殊的字符,如果需要起一些别名,则使用name属性进行定义,使用逗号,分号或空格进行分隔。但id必须唯一。

如果没有指定idname,容器会为bean自动生成一个唯一的标识符。

bean命名惯例:bean的名称默认使用首单词的首字母小写,其余的单词首字母都是大写,比如:accountManager,accountService,userDao,loginController等。

bean的命名能够使得应用的配置更加简单和易读,如果使用Spring Aop,能够更方便的通过bean的名称进行的切面定义。

TIPS:通过在类路径中进行组件扫描,Spring会按照前面描述的规则为未命名的组件生成Bean名称:从本质上讲,将采用简单的类名称并将其首字符转换为小写。 但是,在(不寻常的)特殊情况下,如果有多个字符且第一个和第二个字符均为大写字母,则会保留原始大小写。

在BeanDefinition之外给bean取别名

我们可以通过BeanDefinition给bean定义一个或多个名称,通过id属性定义唯一标识符和name属性定义多个别名。

但是,在实际定义bean的地方指定所有别名并不能满足需求。 有时需要为在别处定义的bean引入别名。 在大型系统中通常是这种情况,在大型系统中,配置在每个子系统之间分配,每个子系统都有自己的对象定义集。

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

使用XML的标签进行起别名给fromName的bean起一个toName的别名。

比如:A系统通过subsystemA-dataSource来引用一个Datasource对象,B系统通过subsystemB-dataSource来引用DataSource对象,当组合这2个子系统为一个系统时,主应用使用myApp-dataSource来进行引用,这种情况可以使用以下配置:

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

实例化bean

一个beanDefinition可以创建一个或多个对象,容器在被查询时会查看命名bean的定义,并使用该bean定义封装的配置元数据来创建(或获取)实际对象。

如果使用XML定义Bean元数据,通常需要指定标签中的class属性class属性对应BeanDefinition中的Class字段。Class字段有2种用法:

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

内部类:如果需要配置类的内部静态类的bean定义,必须要使用类的组合名称,比如:在 com.example 包下有一个叫SomeThing的类,在其内部有一个叫OtherThing的内部静态类,则其beanDefinition的class属性将会是com.example.SomeThing$OtherThing,$被用来作为内部类和外部类的分隔。

使用构造器初始化

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

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

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

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

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

可以使用class属性来指定包含静态工厂方法的的类,使用factory-method属性来指定工厂方法的名字。

<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:

<!-- 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配置。

决定bean的运行时类型

确定特定bean的运行时类型并非易事。 Bean元数据定义中的指定类只是初始类引用,可能与声明的工厂方法结合使用,或者是FactoryBean类,这可能导致Bean的运行时类型不同,或者在实例的情况下根本不进行设置 工厂方法(通过指定的factory-bean名称解析)。 此外,AOP代理可以使用基于接口的代理包装Bean实例,而目标Bean的实际类型(仅是其实现的接口)的暴露程度有限。

建议使用BeanFactory.getType获取运行时bean的类型。


文章作者: Ubi-potato
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ubi-potato !
评论
  目录