Spring容器和bean的基本原理

本文由石头发表于:http://changshi.chen.blog.163.com/blog/static/2542389520077532736199/

 

控制反转: Inversion of Control,IoC

Spring框架所提供的众多功能之所以能成为一个整体正是建立在IoC的基础之上,因此对这一内涵简单、外延丰富的技术我们有必要进行详细的介绍。

org.springframework.beans及org.springframework.context包是Spring IoC容器的基础。BeanFactory提供的高级配置机制,使得管理任何性质的对象成为可能。ApplicationContext是BeanFactory的扩展,功能得到了进一步增强,比如更易与Spring AOP集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。

简而言之,BeanFactory提供了配制框架及基本功能,而ApplicationContext则增加了更多支持企业核心内容的功能。ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。

容器和bean的基本原理:

在Spring中,那些组成应用的主体(backbone)及由Spring IoC容器所管理的对象被称之为bean。简单地讲,bean就是由Spring容器初始化、装配及被管理的对象,除此之外,bean就没有特别之处了(与应用中的其他对象没有什么区别)。而bean定义以及bean相互间的依赖关系将通过配置元数据来描述。

(Notic:接口选择之惑
在实际应用中,用户有时候不知道到底是选择BeanFactory接口还是ApplicationContext接口。但是通常在构建J2EE应用时,使用ApplicationContext将是更好的选择,因为它不仅提供了BeanFactory的所有特性,同时也允许使用更多的声明方式来得到我们想要的功能。)

(Notice:为什么使用bean?
使用'bean'这个名字而不是'组件'(component) 或'对象'(object)的动机源于Spring框架本身(部分原因则是相对于复杂的EJB而言的)

容器:org.springframework.beans.factory.BeanFactory是Spring IoC容器的实际代表者,IoC容器负责容纳此前所描述的bean,并对bean进行管理。
在Spring中,BeanFactory是IoC容器的核心接口。它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是最常用的一个。该实现将以XML方式描述组成应用的对象以及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。

配置元数据:
Spring IoC容器将读取配置元数据;并通过它对应用中各个对象进行实例化、配置以及组装。通常情况下我们使用简单直观的XML来作为配置元数据的描述格式。在XML配置元数据中我们可以对那些我们希望通过Spring IoC容器管理的bean进行定义。
Note:
到目前为止,基于XML的元数据是最常用到的配置元数据格式。然而,它并不是唯一的描述格式。Spring IoC容器在这一点上是完全开放的。
在本文写作时,Spring支持三种配置元数据格式:XML格式、Java属性文件格式或使用Spring公共API编程实现。由于XML元数据配置格式简单明了,因而本章采用该格式来表达Spring IoC容器的主要理念和特性。
 
在大多数的应用程序中,并不需要用显式的代码去实例化一个或多个的Spring IoC容器实例。例如,在web应用程序中,我们只需要在web.xml中添加(大约)8 行简单的XML描述符即可(参见Section 3.8.4, “ApplicationContext在WEB应用中的实例化”)。(监听器)

多种资源
对IoC容器基本原理的掌握将有利于我们对Chapter 4, 资源中Resource抽象机制的理解。
Spring IoC容器可以通过多种途径来加载配置元数据,比如本地文件系统、Java CLASSPATH等。

bean定义与应用程序中实际使用的对象一一对应。通常情况下bean的定义包括:服务层对象、数据访问层对象(DAO)、类似Struts Action的表示层对象、Hibernate SessionFactory对象、JMS Queue对象等等。项目的复杂程度将决定bean定义的多寡。

以下是一个基于XML的配置元数据的基本结构:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
  <bean id="..." class="...">
    <!-- collaborators and configuration for this bean go here -->
  </bean>
  <bean id="..." class="...">
    <!-- collaborators and configuration for this bean go here -->
  </bean>
  <!-- more bean definitions go here... -->
</beans>

实例化容器
Spring IoC容器的实例化非常简单,如下面的例子:
Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
... 或...
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
... 或...
ApplicationContext context = new ClassPathXmlApplicationContext(
        new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) context;

将XML配置文件分拆成多个部分是非常有用的。为了加载多个XML文件生成一个ApplicationContext实例,可以将文件路径作为字符串数组传给ApplicationContext构造器。而bean factory将通过调用bean defintion reader从多个文件中读取bean定义。
通常情况下,Spring团队倾向于上述做法,因为这样各个配置并不会查觉到它们与其他配置文件的组合。另外一种方法是使用一个或多个的<import/>元素来从另外一个或多个文件加载bean定义。所有的<import/>元素必须放在<bean/>元素之前以完成bean定义的导入。 让我们看个例子:
<beans><import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>
      <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
  </beans>

bean定义:
名称  链接
class  Section 3.2.3.2, “实例化bean”
 
name  Section 3.2.3.1, “命名bean”
 
scope  Section 3.4, “bean的作用域”
 
constructor  arguments Section 3.3.1, “注入依赖”
 
properties   Section 3.3.1, “注入依赖”
 
autowiring   mode Section 3.3.6, “自动装配(autowire)协作者”
 
dependency   checking mode Section 3.3.7, “依赖检查”
 
lazy-initialization mode Section 3.3.5, “延迟初始化bean”
 
initialization method Section 3.5.1, “Lifecycle接口”
 
destruction  method Section 3.5.1, “Lifecycle接口”
 
bean命名约定
bean的命名采用标准的Java命名约定,即小写字母开头,首字母大写间隔的命名方式。如accountManager、 accountService、userDao及loginController,等等。
对bean采用统一的命名约定将会使配置更加简单易懂。而且在使用Spring AOP时,如果要发通知(advice)给与一组名称相关的bean时,这种简单的命名方式将会令你受益匪浅。

每个bean都有一个或多个id(或称之为标识符或名称,在术语上可以理解成一回事)。这些id在当前IoC容器中必须唯一。如果一个bean有多个id,那么其他的id在本质上将被认为是别名。

当使用基于XML的配置元数据时,将通过id或name属性来指定bean标识符。id属性具有唯一性,而且是一个真正的XML ID属性,因此其他xml元素在引用该id时,可以利用XML解析器的验证功能。通常情况下最好为bean指定一个id。尽管XML规范规定了XML ID命名的有效字符,但是bean标识符的定义不受该限制,因为除了使用指定的XML字符来作为id,还可以为bean指定别名,要实现这一点可以在name属性中使用逗号、冒号或者空格将多个id分隔。

值得注意的是,为一个bean提供一个name并不是必须的,如果没有指定,那么容器将为其生成一个惟一的name。对于不指定name属性的原因我们会在后面介绍(比如内部bean就不需要)。

在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,需要通过alias属性来加以指定。而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易的对公共组件进行引用。
然而,在定义bean时就指定所有的别名并不是总是恰当的。有时我们期望能在当前位置为那些在别处定义的bean引入别名。在XML配置文件中,可用单独的<alias/> 元素来完成bean别名的定义。如:
<alias name="fromName" alias="toName"/>

实例化bean
就Spring IoC容器而言,bean定义基本上描述了创建一个或多个实际bean对象的内容。当需要的时候,容器会从bean定义列表中取得一个指定的bean定义,并根据bean定义里面的配置元数据使用反射机制来创建一个实际的对象。因此这一节将讲解如何告知Spring IoC容器我们将要实例化的对象的类型以及如何实例化对象。

当采用XML描述配置元数据时,将通过<bean/>元素的class属性来指定实例化对象的类型。class 属性 (对应BeanDefinition实例的Class属性)通常是必须的(不过也有两种例外的情形,见Section 3.2.3.2.3, “使用实例工厂方法实例化”和Section 3.6, “bean定义的继承”)。

用构造器来实例化
当采用构造器来创建bean实例时,Spring对class并没有特殊的要求,我们通常使用的class都适用。也就是说,被创建的类并不需要实现任何特定的接口,或以特定的方式编码,只要指定bean的class属性即可。不过根据所采用的IoC类型,class可能需要一个默认的空构造器。

此外,IoC容器不仅限于管理JavaBean,它可以管理任意的类。不过大多数使用Spring的人喜欢使用实际的JavaBean(具有默认的(无参)构造器及setter和getter方法),但在容器中使用非bean形式(non-bean style)的类也是可以的。比如遗留系统中的连接池,很显然它与JavaBean规范不符,但Spring也能管理它。

使用 静态工厂方法实例化
当采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method属性来指定创建bean实例的工厂方法。Spring将调用此方法(其可选参数接下来介绍)返回实例对象,就此而言,跟通过普通构造器创建类实例没什么两样。
下面的bean定义展示了如何通过工厂方法来创建bean实例。注意,此定义并未指定返回对象的类型,仅指定该类包含的工厂方法。在此例中, createInstance()必须是一个static方法。
<bean id="exampleBean"
      class="examples.ExampleBean2"
      factory-method="createInstance"/>

从本质上讲,BeanFactory仅仅只是一个维护bean定义以及相互依赖关系的高级工厂接口。通过BeanFactory我们可以访问bean定义。下面的例子创建了一个bean工厂,此工厂将从xml文件中读取bean定义:

InputStream is = new FileInputStream("beans.xml");
BeanFactory factory = new XmlBeanFactory(is);
基本上就这些了,接着使用getBean(String)方法就可以取得bean的实例;BeanFactory提供的方法极其简单。它仅提供了六种方法供客户代码调用:

boolean containsBean(String):如果BeanFactory包含给定名称的bean定义(或bean实例),则返回true

Object getBean(String):返回以给定名字注册的bean实例。根据bean的配置情况,如果为singleton模式将返回一个共享的实例,否则将返回一个新建的实例。如果没有找到指定的bean,该方法可能会抛出BeansException异常(实际上将抛出NoSuchBeanDefinitionException异常),在对bean进行实例化和预处理时也可能抛出异常

Object getBean(String, Class):返回以给定名称注册的bean实例,并转换为给定class类型的实例,如果转换失败,相应的异常(BeanNotOfRequiredTypeException)将被抛出。上面的getBean(String)方法也适用该规则。

Class getType(String name):返回给定名称的bean的Class。如果没有找到指定的bean实例,则抛出NoSuchBeanDefinitionException异常。

boolean isSingleton(String):判断给定名称的bean定义(或bean实例)是否为singleton模式(singleton将在bean的作用域中讨论),如果bean没找到,则抛出NoSuchBeanDefinitionException异常。

String[] getAliases(String):返回给定bean名称的所有别名。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值