Spring 框架 总结

上一篇 / 下一篇  2011-04-08 16:34:46 / 天气: 阴雨 / 心情: 平静 / 精华(3) / 置顶(3) / 个人分类:JAVA

一部分 Spring基础
         介绍Spring框架的两个核心特性:控制反转和面向方面编程。
第一章 快速进入Spring世界
         一切都起源于Bean。然而复杂的应用通常需要诸如事务支持,安全和分布式计算之类的服务,JavaBean规范并没有直接支持这些服务。因此Sun发布了EJB规范。尽管有很多基于EJB的成功的应用,然而EJB并没有取得其预期的目的:简化企业应用开发。新技术,如AOP和IOC使JavaBean同样可以具有EJB的强大功能,这就是Spring出现的背景。
1.1 为什么选择Spring?
简而言之,Spring使开发企业应用更容易。
1.1.1 J2EE开发者的一天
         为开发一个组件(component)必需编写几个类,还要为JavaBean创建部署描述子。为节省力气,引入XDoclet。XDoclet是一个代码生成工具,可以从一个源文件生成所有需要的EJB文件。为验证代码逻辑的正确性,需要书写几个测试用例,问题是测试代码必需运行在EJB容器中。为解决这个问题,创建一个Servlet来运行这些测试用例。因为所有的J2EE容器都支持Servlet,所以测试代码可以与EJB运行于同一个容器。但是如果测试用例没有通过,必需修改代码,重新启动容器,再次测试,重启容器的时间就被浪费掉了。
1.1.2 Spring的承诺
         EJB之所以复杂是因为它是用来解决复杂问题的,如分布式对象和远程事务。不幸的是,很多企业级项目并没有达到这种的复杂程度,但仍然要承担EJB的多个 Java文件和部署描述子,以及重量级容器。使用EJB,无论你要解决的问题是否复杂,你的应用程序都很复杂,然而对于Spring来说,应用程序的复杂程度是与要解决的问题的复杂程度成正比的。Spring遵循的逻辑是:J2EE应该易于使用,为保持这种逻辑,Spring的设计遵循以下信念:
<!--[if !supportLists]-->n  <!--[endif]-->好的设计比起底层的实现技术更重要。
<!--[if !supportLists]-->n  <!--[endif]-->通过接口松散耦合的JavaBean是很好的模型。
<!--[if !supportLists]-->n  <!--[endif]-->代码应该易于测试。
好的设计比起底层的实现技术更重要。
         不必为了使用EJB技术而使用它,真正需要它提供的服务才去使用它。
通过接口松散耦合的JavaBean是很好的模型
         使用Spring,JavaBean通过接口依赖于协作。你所需要做的就是创建彼此通过接口通信的类,其他的都交给Spring来做。
代码应该易于测试
         测试不需启动J2EE容器,因为你测试的是POJO。
1.2 Spring是什么?
         Spring是一个开源的框架,由Rod Johnson创建。Spring的创建用来解决企业级应用开发的复杂性。Spring使我们能够使用普通的JavaBean来取得之前使用EJB达到的效果。然而,Spring的作用并不限于服务器端开发。任何应用程序都可得益于Spring的简单性,易测试性和松散耦合性。
         简单地讲,Spring是一个轻量级的控制反转和面向方面编程的框架。以下是这个描述的详细解释:
<!--[if !supportLists]-->n  <!--[endif]-->轻量级。从两个方面:大小和预处理。Spring发布版本的就是一个JAR包,大小就是1M多一点。经Spring预处理的时间几乎可以忽略。
<!--[if !supportLists]-->n  <!--[endif]-->控制反转。Spring通过控制反转来达到松散耦合。通过IOC,对象被动地接受他们的依赖,而不是主动地查找和创建他们的依赖。
<!--[if !supportLists]-->n  <!--[endif]-->面向方面。Spring对AOP的支持使业务逻辑从系统服务中分离出来。
<!--[if !supportLists]-->n  <!--[endif]-->容器。从包容和管理应用程序对象生命周期的意义上来讲,Spring是一个容器。你可以配置你的每一个 JavaBean如何被创建,以及他们之间的关联。不应当把Spring同重量级的EJB容器混淆,因为EJB容器是重量级的而且工作起来很笨重。
<!--[if !supportLists]-->n  <!--[endif]-->框架。Spring组合简单的JavaBean成复杂的的应用程序。在Spring中应用程序对象通常通过 XML文件声明式地组合。Spring提供很多基础的功能,如事务管理,持久化框架集成等,你要做的就是开发业务逻辑。
1.2.1 Spring模块
         Spring框架由七个良好定义的模块组成。这些模块为你提供了企业级应用程序开发的所有东西,但是你也不必全盘接受,你可以只选择适合你的应用程序的模块而忽略其他模块。所有的Spring模块都建立在核心容器之上,容器定义JavaBeans如何被创建,配置和管理。核心容器的类会被隐式地调用来配置你的应用程序。
核心容器
         Spring的核心容器提供Spring框架的基础功能。在这个容器中有一个BeanFactory,它是任何基于Spring的应用程序的核心。 BeanFactory是工厂模式的一个实现,他应用IOC将应用程序配置和依赖说明从实际的应用程序代码中分离出来。
应用程序上下文模块
         核心模块的BeanFactory使Spring成为一个容器,而应用程序上下文模块使Spring成为一个框架。这个模块扩展了BeanFactory,增加了对I18N消息,应用程序生命周期事件和验证的支持。
         此外,这个模块提供了一些企业级服务,如email,JNDI访问,EJB集成,远程访问和计划任务。还包括对模板框架如Velocity和FreeMaker集成的支持。
Spring的AOP模块
         Spring在AOP模块对AOP提供了丰富的支持。这个模块提供了开发自己的基于Spring的应用程序的方面的基础。
         为确保Spring和其他AOP框架之间的互操作性,Spring的很多对AOP的支持都是基于AOP Alliance定义的API。AOP Alliance是一个开源的项目,它的目标是通过定义组通用的接口和组件,提高对AOP的采用率和不同AOP实现之间的互操作性。
Spring模块还为Spring引入了元数据编程。通过Spring的元数据支持,你可以向源代码中加入标注(Annotation)来指示在何处通过何种方式来应用方面(Aspects)。
JDBC抽象和DAO模块
         Spring的JDBC和DAO模块使数据库代码更加整洁和简单,并且防止一些由于关闭数据库资源引起失败带来的问题。这个模块还在由多个数据库服务器给出的错误消息之上建立了一层有意义的异常。
         另外,这个模块使用了Spring的AOP模块为Spring应用程序中的对象提供了事务管理服务。
ORM集成模块
         Spring为那些喜欢使用ORM功能的人提供了ORM模块。Spring没有试图实现自己的ORM解决方案,只是为几个流行的ORM框架,如 Hibernate,JDO和iBATIS SQL Maps提供了外挂功能。Spring的事务管理支持这些ORM框架和JDBC。
Spring的Web模块
         Web上下文模块建立于应用程序上下文模块,提供了一个适合于基于Web的应用的上下文。此外,这个模块提供了对一些基于Web的任务的支持,如对于文件上传的multipart请求的透明处理和将请求参数程序化绑定到业务对象。还包含对Struts的集成的支持。
SpringMVC框架
         Spring为构建Web应用程序带来了一个完整特性的MVC框架。尽管Spring可以容易地与其他MVC框架如Struts集成,但是Spring的 MVC框架使用IOC提供控制逻辑与业务逻辑的清晰分离。它允许你声明式地将请求参数绑定到业务逻辑。还有就是Spring的MVC框架可以利用 Spring的其他服务,如I18N消息机制和验证机制。
1.3 快速上手
         让基于Spring的应用程序不同的是类之间是如何配置和相互引入的。典型地,Spring应用程序使用一个XML文件来描述如何配置类,这个XML文件叫做Spring配置文件。
         GreetingServiceImpl类的greeting字段有两个方法可以设置,通过构造函数或属性的setter方法。不明显的是谁来调用构造函数或setter方法,答案是通过Spring配置文件让Spring容器来调用。
         Spring配置文件的根元素是<beans>元素。<bean>元素告诉Spring容易一个类的信息和如何配置这个类。<bean>元素的子元素<property>元素用来设定类的属性。当然也可以通过构造函数的入参来设定属性。
1.4 理解控制反转IOC
         IoC处于Spring框架的核心。IoC听上去可能比较复杂,实际上,通过在项目中使用IoC,你会发现你的代码变得更简单,更易于理解和易于测试。
1.4.1 注入依赖
         Martin Fowler为IoC造了一个更好的名字:依赖注入。
         任何稍大一点的应用程序都由多于两个类组成,他们相互协作完成一些业务逻辑。通常每一个对象都负责取得与它协作的那些对象的引用。你会发现,这会导致高耦合,难测试的代码。
         通过应用IoC,对象在创建的时候,通过调度系统中每一个对象的外部实体给予他们的依赖。即依赖被注入对象。即IoC的意思是关于对象如何取得他们协作的对象的依赖责任的反转。
1.4.2 IoC实践
         对于自己取得自己引用的对象的对象,无法单独进行测试。被引用的对象被间接测试,而且被引用的对象如果没有通过测试,可能会影响调用对象的测试结果。
         问题的所在是由于耦合。降低耦合的一个通用技术是将实现细节隐藏在接口后面,这样可以交换具体实现而不影响客户类。但重要的是对象如何取得它引用的对象,如果是给予的方式,那么在测试用例中只要给予Mock对象,而在产品系统中给予真实的对象,就解决了不能单独测试的问题。
         这就是IoC要做的事情。
         创建应用程序组件之间关联的动作被成为wiring,通常的方法是通过XML文件。
1.4.3 企业级应用程序中的IoC
         在EJB程序中,需要自己从JNDI上下文环境中查找EJB Home,然后创建Service,使用Spring后,由Spring容器负责设置Service。
1.5 应用面向方面编程
         面向方面编程使你能够在应用程序中以可复用组件的形式捕获功能。
1.5.1 AOP简介
         AOP通常定义为一种编程技术,它将软件系统的不同关注方面分离。系统由多个组件组成,每一个组件负责一个特定的功能。像写log,事务管理和安全之类的系统服务通常存在于一些核心职责是别的东西的组件。这些系统服务通常被称作cross-cutting关注方面,因为它们趋向于存在系统的各个地方。
         将这些关注方面分散于系统的多个组件,会对代码引入两个级别的复杂性:
<!--[if !supportLists]-->n  <!--[endif]-->实现系统范围关注方面的代码,在多个组件间存在重复。这意味着如果你需要修改这些关注方面的话,你需要修改多个组件。即使你将这些关注方面抽象到单独的模块,对组件的影响只是一个方法调用,这个方法调用也会在多个组件中重复。
<!--[if !supportLists]-->n  <!--[endif]-->组件中会散布一些与核心功能无关的代码。
AOP能够让这些服务模块化,然后以声明的形式应用于它们应该影响的组件。这样能够使组件能够更具有聚合性,专注于他们的关注方面,可以完全忽略需要涉及的系统服务。
通过AOP,可以用多层功能覆盖核心的应用。这些层可以灵活的声明式地应用于应用程序,核心应用程序甚至不知道他们的存在。
1.5.2 AOP实践
         实现MethodBeforeService接口,可以在一个方法调用时做一些事情,而被调用的类和方法甚至不知道被做的这些事情。
织补Aspect
         在Spring中,Aspect通过Spring的XML文件织入对象,同Bean被绕在一起的方式差不多。
1.5.3 企业级的AOP
         EJB可以通过部署描述子来声明事务和安全策略。但是EJB比较复杂。
         尽管Spring的AOP可以用来分布于应用程序核心逻辑各处的关注方面,但它的主要工作是作为Spring对声明式事务支持的基础。Spring具有几个方面,使对JavaBeans声明事务策略成为可能。而Acegi安全系统提供对JavaBeans的声明式安全。同所有Spring配置一样,事务和安全策略都在Spring配置文件中描述。
         使用Spring的TransactionProxyFactoryBean,使我们能够对既存的class监听函数调用和应用事务上下文。
1.6 Spring的代替品
1.6.1 比较Spring与EJB
         选择Spring与EJB并不是可以直接用谁取代谁。而且,你也不必要么选择Spring,要么选择EJB,Spring可以用来支持既有的EJBs。
EJB是一个标准
         EJB是有JCP定义的规范,作为标准具有一些重要的启示:
<!--[if !supportLists]-->n  <!--[endif]-->广泛的业界支持—有很多重要的厂商都支持这项技术,如Sun,IBMOracle和BEA。这意味着很多年之内EJB都会被支持和积极开发,这使很多公司感觉到选择EJB作为J2EE框架比较安全。
<!--[if !supportLists]-->n  <!--[endif]-->广泛的采用—EJB技术已经被成千上万的公司部署。因此EJB是大部分J2EE开发者的工具,拥有EJB技术更容易找到工作,公司使用EJB技术也更容易招聘到技术人员。
<!--[if !supportLists]-->n  <!--[endif]-->有工具支持—EJB规范是一个固定的东西,这更容易地使得厂商更快地生产工具来帮助开发者创建EJB应用程序。EJB工具具有更广泛的选择。
Spring和EJB的共同点
         作为J2EE容器,EJB和Spring都为开发者开发应用程序提供了强大的特性。下表列出了两个框架的共同特性和它们的实现比较。
特性
 EJB
 Spring
 
事务管理
 <!--[if !supportLists]-->n  <!--[endif]-->必需使用JTA事务管理。
<!--[if !supportLists]-->n  <!--[endif]-->支持跨越远程方法调用的事务。
 <!--[if !supportLists]-->n  <!--[endif]-->通过PlatformTransactionManager接口支持多事务环境,包括JTA,Hibernate,JDO和JDBC。
<!--[if !supportLists]-->n  <!--[endif]-->自身不支持分布式事务—必需与JTA事务管理器一起使用。
 
声明式事务支持
 <!--[if !supportLists]-->n  <!--[endif]-->可以通过部署描述子声明式地定义事务。
<!--[if !supportLists]-->n  <!--[endif]-->可以使用通配符*来针对每一个方法或每一个类定义事务行为。
<!--[if !supportLists]-->n  <!--[endif]-->不能声明式地定义回滚行为,必需程序实现。
 <!--[if !supportLists]-->n  <!--[endif]-->可以通过Spring配置文件或类的元数据声明式地定义事务。
<!--[if !supportLists]-->n  <!--[endif]-->可以显示地或通过正则表达式定义对哪些方法应用事务行为。
<!--[if !supportLists]-->n  <!--[endif]-->可以针对每一个方法或每一个异常类型声明式地定义回滚行为。
 
持久化
 <!--[if !supportLists]-->n  <!--[endif]-->支持程序化的Bean管理方式的持久化和声明式的容器管理持久化。
 <!--[if !supportLists]-->n  <!--[endif]-->提供集成多个持久化技术的框架,包括JDBC,Hibernate,JDO和iBATIS。
 
声明式安全
 <!--[if !supportLists]-->n  <!--[endif]-->通过用户和角色支持声明式安全。用户和角色的管理和实现是容器专有的。
 <!--[if !supportLists]-->n  <!--[endif]-->自身不具有安全的实现。
<!--[if !supportLists]-->n  <!--[endif]-->一个建立于Spring之上的开源的安全框架Acegi,通过Spring配置文件或类元数据提供声明式安全。
 
分布计算
 <!--[if !supportLists]-->n  <!--[endif]-->提供容器管理的远程方法调用。
 <!--[if !supportLists]-->n  <!--[endif]-->通过RMI,JAX-RPC和Web服务提供远程调用的代理。
 
         对于大部分的J2EE项目,EJB和Spring都能满足其技术需求。但是也有例外—你的应用程序也许需要远程事务调用,如果是这样的话,选择EJB更合适。但Spring提供了对JTA事务的集成,也能满足。但如果你要找的是提供声明式事务管理和灵活的持久化引擎的J2EE框架,Spring是最好的选择。
EJB的复杂性
         EJB以下的复杂性使大家倾向于选择轻量级的容器:
<!--[if !supportLists]-->n  <!--[endif]-->编写一个EJB太过于复杂—写一个EJB必需接触至少四个文件:业务接口,home接口,bean实现和部署描述子。可能还牵扯到其他类,如utility类和Value Object。相反Spring使你通过POJO定义你的实现,和通过注入或AOP来缠绕任何额外的服务。
<!--[if !supportLists]-->n  <!--[endif]-->EJB具有侵入性—为使用EJB容器提供的服务,则必需使用javax.ejb接口。这使组件代码与EJB技术绑定,使组件很难用于EJB容器之外。Spring通常不要求组件实现、扩展或使用任何特定于Spring的类或接口,是组件更具有可复用性,即使没有 Spring的存在也能使用。
<!--[if !supportLists]-->n  <!--[endif]-->实体EJBs功能较弱—实体EJBs不如其他ORM工具特性多,也不够灵活。Spring可以集成很多其他 ORM框架。Value Object会导致重复的代码,使用Spring和其他ORM工具,实体对象不与持久化机制耦合,可以传递于应用程序的不同层。
1.6.2 考虑其他轻量级容器
         下表列出了IoC的类型。
类型
 名称
 描述
 
类型1
 接口依赖
 为使容器管理依赖,Beans必需实现特定的接口。
 
类型2
 Setter注入
 依赖和属性通过Beans的setter方法配置。
 
类型3
 构造函数注入
 依赖和属性通过Beans的构造函数配置。
 
下面看一下其他轻量级容器。
PicoContainer
         PicoContainer是一个小型的轻量级的容器,通过构造函数和setter函数的形式提供IoC。有一个子项目NanoContainer通过XML和各种脚本语言提供对配置PicoContainer的支持。
         PicoContainer的一个局限是,对于任何一个类型,在注册表中只允许存在一个实例。
         PicoContainer只是一个容器,不提供其他Spring提供的强大功能,如AOP和第三方框架集成。
HiveMind
         HiveMind是一个相对较新的IoC容器。它也是通过构造函数和setter函数来缠绕和配置服务。HiveMind允许用XML文件或它的简单的数据语言定义你的配置。
         HiveMind还通过Interceptors提供类似AOP的特性。但是没有Spring的AOP框架强大。
         它提供管理组件的框架,但不提供对其他技术的集成。
Avalon
         Avalon是第一批开发出来的IoC容器之一。但设计中存在很多错误。Avalon主要提供接口依赖的IoC。这使Avalon成为侵入式的框架。
1.6.3 Web框架
         Spring有自己的Web框架。先比较一下其他的Web框架。
Struts
         Struts可以看作是Web MVC框架的事实上的标准。
         最常使用的Struts类是Action类。Action是一个抽象类,因此你的所有处理输入的类都必需继承这个类,相比之下Spring提供了Controller接口。
         另一个重要的区别是二者处理表单输入的方式。通常情况下,当用户提交一个Web表单时,进来的数据对应于你的应用程序中的一个对象。为处理表单提交,Struts要求使用ActionForm类处理传入的参数。这意味这你必需创建一个类来将表单提交映射到你的领域对象。Spring允许你直接将表单提交映射到领域对象。
         Struts提供内置的声明式表单验证支持。这意味着你可以通过XML文件来定义验证传入的表单数据的规则。这使验证与业务逻辑分离,也会导致一些笨拙和混乱。Spring不提供声明式验证,不过你可以自己集成一个验证框架,如Jakarta Commons Validator。
         Spring可以集成Struts。
WebWork
         WebWork提供多视图技术。WebWork与其他框架的最大区别是它为处理Web提交增加了一个抽象层。
         WebWork提供了一个Spring没有提供的功能:Action链。它允许你将一个逻辑请求映射到一系列的Actions。
Tapestry
         Tapestry与之前提到的Web框架有很大区别。Taperstry不提供基于请求-响应的Servlet机制的框架。它是一个从可复用组件创建Web应用的框架。
         Tapestry背后的思想是,减轻开发者思考Sessions属性和URLs,而是以组件和方法的形式考虑Web应用。
         Tapestry不是一个使用JSPs的框架,而是代替JSPs。
1.6.4 持久化框架
         Spring没有内置的持久化框架,Spring提供对Hibernate,JDO,OJB和iBATIS的集成点。Spring的JDBC和ORM框架工作于Spring的事务管理框架。
第二章 缠绕Beans
         任何稍具规模的应用程序都是由多个组件组成,它们一起工作来完成一个业务目标。这些组件必需意识到其他组件的存在,相互会话来完成工作。
         创建对象关联的通常办法会导致难以复用和进行单元测试的代码。
         在Spring中,组件本身不负责管理与其他组件的关联。相反,对写作组件的引用是由容器给他们的。创建应用程序组件之间关联的动作成为缠绕(Wiring)。
2.1 包含Beans
         你在为Spring框架配置beans的时候,你实际上是在向Spring容器发出指令。容器处于Spring框架的核心。Spring容器使用IoC来管理组成应用程序的各个组件。Spring具有两个不同类型的容器。Bean工厂是最简单的容器(由 org.springframework.beans.factory.BeanFactory接口定义),它为依赖注入提供简单的支持。应用程序上下文建立于bean工厂的概念之上,它提供诸如从属性文件解析文本消息之类的应用程序框架服务,以及向感兴趣的应用程序事件监听者发布应用程序事件的能力。
2.1.1 介绍BeanFactory
       不同于其他工厂模式的实现只分派一种类型的对象,这里的BeanFactory是一个通用的工厂,可以分派很多类型的Beans。
         Bean工厂知道应用程序中的很多对象,在实例化对象时,它能够创建协作对象之间的关联关系。这移除了Bean本身和它的客户端的配置负担。因此,当Bean工厂分发对象时,这些对象是已经完全配置好的,已经知道了他们协作的对象,已经可以供使用。
         Spring中有很多BeanFactory的实现,但最有用的是org.springframework.beans.factory.xmlBeanFactory,它基于包含在XML文件中的定义来加载Beans。
         为创建XmlBeanFactory,向构造函数传递一个java.io.InputStream。这个InputStream向工厂提供XML文件。 Beans以懒加载的方式加载到Beans工厂中,Bean工厂会立即加载Bean的定义,但只要当这些Beans被使用的时候才会被实例化。
         为从BeanFactory获取Bean,只要调用getBean()方法,向它传递你要获取的bean的名称。当调用getBean()时,工厂会实例化Bean并开始使用依赖注入来设置Bean的属性。
2.1.2 使用应用程序上下文
         ApplicationContext提供更多的东西:
<!--[if !supportLists]-->n  <!--[endif]-->提供解析文本消息的方法,包括对这些消息提供I18N支持。
<!--[if !supportLists]-->n  <!--[endif]-->提供加载文件资源,如图片,的通用方法。
<!--[if !supportLists]-->n  <!--[endif]-->可以向注册为监听者的Beans发布事件。
因为ApplicationContext的额外功能,几乎在所有的应用程序中都优先使用ApplicationContext,而非BeanFactory。
ApplicationContext的实现有很多,而常用的有三个:
<!--[if !supportLists]-->n  <!--[endif]-->ClassPathXmlApplicationContext—从在classpath中定位的XML文件加载一个上下文定义,将上下文定义文件作为classpath资源处理。
<!--[if !supportLists]-->n  <!--[endif]-->FileSystemXmlApplicationContext—从文件系统中的XML文件加载上下文定义。
<!--[if !supportLists]-->n  <!--[endif]-->XmlWebApplicationContext—从包含于Web应用中的XML文件加载上下文定义。
获取Bean的方式同BeanFactory,可以使用getBean()方法,因为ApplicationContext继承了BeanFactory接口。
应用程序上下文与Bean工厂的另一个比较大的区别是他们加载单例Beans的方式。BeanFactory懒加载所有Beans,而ApplicationContext预先加载所有的单例Beans。
2.1.3 Bean的生命周期
         Bean可以使用之前BeanFactory执行几步设置操作:
<!--[if !supportLists]-->1.       <!--[endif]-->容器找到Bean的定义并实例化Bean。
<!--[if !supportLists]-->2.       <!--[endif]-->Spring使用依赖注入产生Bean定义中指定的所有的属性。
<!--[if !supportLists]-->3.       <!--[endif]-->如果Bean实现了BeanNameAware接口,工厂调用setBeanName()方法,传递Bean的ID。
<!--[if !supportLists]-->4.       <!--[endif]-->如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory方法,并传递工厂本身的实例。
<!--[if !supportLists]-->5.       <!--[endif]-->如果存在与Bean关联的BeanPostProcessors,则调用他们的 postProcessBeforeInitialization方法。
<!--[if !supportLists]-->6.       <!--[endif]-->如果指定了Bean的init方法,则调用这个方法。
<!--[if !supportLists]-->7.       <!--[endif]-->最后,如果存在与Bean关联的BeanPostProcessors,则调用他们的 postProcessAfterInitialization方法。
从这时候起,Bean可以被应用程序使用,并一直存在与Bean工厂中,直到不需要它。有两种方式将Bean从Bean工厂中移除。
<!--[if !supportLists]-->1.       <!--[endif]-->如果Bean实现了DisposableBean接口,调用destroy()方法。
<!--[if !supportLists]-->2.       <!--[endif]-->如果指定了自定义的destroy方法,则调用这个方法。
处在Spring应用程序上下文中的Bean与BeanFactory中的唯一区别是,如果Bean实现了ApplicationContextAware接口,则调用它的setApplicationContext方法。
2.2基本的缠绕(Basic Wiring)
只需要一点XML基础。
2.2.1使用XML缠绕
理论上Bean缠绕可以由任何配置源驱动,包括属性文件,关系数据库,甚至是LDAP目录。XML文件是最常用的方式。有几个支持通过XML缠绕Beans的Spring容器:XmlBeanFactory,ClasspathXmlApplicationContext,FileSystemXmlApplicationContext,XmlWebApplicationContext。
上下文定义文件的根节点是<beans>元素,这个元素具有一个或多个<bean>元素作为子元素。每一个<bean>元素都定义一个配置到Spring容器中的JavaBean。
2.2.2 增加一个Bean
         Spring中Bean的最简单的配置包括Bean的ID和Bean的完整类名。
原型vs单例
         缺省情况下,所有Spring中的Beans都是单例的。如果每一次都要请求一个特定的实例的话,可以定义一个原型Bean。将<bean>元素的singleton属性设置为false就可以将Bean定义为原型Bean。
初始化与销毁
         使用init-method属性指定的方法在Bean实例化之后立即执行。destroy-method属性指定的方法在Bean从容器移除之前调用。 Spring提供了两个接口,可以执行相同的功能:InitializingBean和DisposableBean。前者提供了一个方法 afterPropertiesSet(),在Bean的所有属性设置后执行,后者提供的方法destroy()在Bean从容器中移除时调用。使用接口的好处是不需要配置,但是也会将Bean与Spring的API绑定起来。
2.2.3 通过setter方法注入依赖
         使用<bean>元素的<property>子元素来以setter方法进行注入依赖。
简单的Bean的配置
         使用<property>元素的子元素<value>元素可以来给Bean设置原始类型的属性。原始类型的属性的设定无须指定类型,Spring会自动进行类型转换。
引用其他Beans
         使用<property>元素的子元素<ref>元素来引用其他Beans。
内部的Beans
         可以将<bean>元素直接嵌入到<property>元素。这种缠绕方式的缺点是被嵌入的元素不能在别处使用。而且影响XML文件的可读性。
缠绕集合
         Spring支持很多种集合类型作为Bean属性,如下表所示。
XML
 类型
 
<list>
 java.util.List, arrays
 
<set>
 java.util.Set
 
<map>
 java.util.Map
 
<props>
 java.util.Properties
 
         缠绕集合是使用上表中提到的XML元素,而不是使用<value>或<ref>。
缠绕lists和arrays
         List的元素可以是<value>,<ref>,<bean>或其他<list>。
缠绕集合
         集合可以保证元素的唯一性。
缠绕maps
         Map中的每一个记录都用一个<entry>元素定义。<entry>的key属性限定为只能是String类型。
缠绕Properties
         使用<props>元素缠绕,每一个<props>元素的子元素都是用一个<prop>元素定义。
设定null值
         使用<null/>元素。
Setter注入的一个代替方式
         通过构造函数注入。
2.2.4 通过构造函数注入依赖
         使用<constructor-arg>元素来设定构造函数的参数。<constructor-arg>的子元素可以使用<property>元素可以使用的任何子元素。
处理模糊的构造函数参数
         如果出现模糊的情况,Spring会抛出org.springframework.beans.factory.UnsatisfiedDependencyException。有两种方法来解决这个问题,使用下标或类型。
         使用<constructor-arg>的index属性指定参数的下标。还可以使用type属性指定参数的类型。
如何选择:使用构造函数还是使用setter方法?
         这是一个有争议的问题。
         支持使用构造函数注入的理由:
<!--[if !supportLists]-->n  <!--[endif]-->强制执行一个强依赖协议。简单的说,Bean如果没得到所有的依赖就不会被实例化。
<!--[if !supportLists]-->n  <!--[endif]-->既然所有的依赖都可以通过构造函数设定,使用setter方法比较多余。
<!--[if !supportLists]-->n  <!--[endif]-->只允许通过构造函数设定函数,可以让这些属性不可更改(immutable)。
支持setter方法注入依赖的理由:
<!--[if !supportLists]-->n  <!--[endif]-->如果依赖比较多,构造函数的参数会比较长。
<!--[if !supportLists]-->n  <!--[endif]-->如果构造可用的对象的方法有多个的话,就会有多个构造函数。
<!--[if !supportLists]-->n  <!--[endif]-->如果构造函数的多个参数的类型一样的话,很难确定参数的目的是什么。
<!--[if !supportLists]-->n  <!--[endif]-->构造函数注入使自身不具有好的集成性。
2.3 自动缠绕(autowiring)
         可以通过设定<bean>元素的autowire属性来自动缠绕。
         有四种类型的自动缠绕:
<!--[if !supportLists]-->n  <!--[endif]-->byName—在容器中查找与指定的名称相同的Bean。
<!--[if !supportLists]-->n  <!--[endif]-->byType—在容器中查找一个唯一的与指定类型相同的Bean。如果没有找到则不缠绕,如果找到多个,抛出 org.springframework.beans.factory.UnsatisfiedDependencyException。
<!--[if !supportLists]-->n  <!--[endif]-->constructor—在容器中查找与被缠绕的Bean的构造函数匹配的Bean,使用指定的构造函数的参数。
<!--[if !supportLists]-->n  <!--[endif]-->autodetect—试图先使用constructor,然后使用byType方法。
2.3.1 处理自动缠绕的模糊性
         如果使用byType或constructor自动缠绕,可能会找到多个Bean。Spring在这种情况下会抛出异常。
2.3.2 混合使用自动缠绕和显式缠绕
         对一个Bean的多个属性,可以设定自动缠绕属性,再显示缠绕需要覆盖的属性。
2.3.3 缺省自动缠绕
         设定<beans>的default-autowire=”byName”,会对所有的Beans缺省byName自动缠绕。
2.3.4 自动缠绕还是不自动缠绕
         自动缠绕可能会引起一些问题。例如,属性改名会导致缠绕不成功。
2.4 使用Spring的特殊Beans
         通过实现特定的接口,可以使你的Beans变得特殊,成为Spring框架的一部分,你可以配置Beans:
<!--[if !supportLists]-->n  <!--[endif]-->可以通过后置处理配置文件来参与Bean和Bean的工厂的生命周期。
<!--[if !supportLists]-->n  <!--[endif]-->从外部属性文件加载配置信息。
<!--[if !supportLists]-->n  <!--[endif]-->改变Spring的依赖注入,在配置Bean属性的时候,自动将String类型转换成其他类型。
<!--[if !supportLists]-->n  <!--[endif]-->加载文本消息,包括国际化的消息。
<!--[if !supportLists]-->n  <!--[endif]-->监听或响应由其他Beans或容器发布的应用程序事件。
<!--[if !supportLists]-->n  <!--[endif]-->使其意识到自己在Spring容器中的身份。
2.4.1 后置处理Beans
         BeanPostProcessor给你两个机会来在它被创建和缠绕之后来更改它。postProcessBeforeInitialization方法和postProcessAfterInitialization。
书写Bean后置处理器
         定义一个类实现BeanPostProcessor接口。
注册Bean后置处理器
         如果应用程序运行于Bean工厂,则需要使用程序来进行注册,调用工厂的addBeanPostProcessor方法,将上面定义的类作为参数传入。如果应用程序运行于应用程序上下文,只需要将定义的类注册为一个Bean。容器会将其识别为后置处理器并应用于所有的Beans。
Spring自带的Bean后置处理器
         例如,ApplicationContextAwareProcessor对所有实现ApplicationContextAware接口的Beans设置应用程序上下文。
         DefaultAdvisorAutoProxyCreator基于容器中所有的候选advisors创建AOP代理。
2.4.2 后置处理Bean工厂
         BeanFactoryPostProcessor后置处理Bean工厂,在Bean工厂加载Bean定义之后,所有Beans被实例化之前。 postProcessBeanFactory方法。如在应用程序上下文中使用,则将其注册为正常的Bean就行了,容器自动将其注册为 BeanFactoryPostProcessor。不能在Bean工厂容器中使用。
         有两个比较有用的实现。PropertyPlaceHolderConfigurer从一个或多个外部属性文件中加载属性,并用这些属性填充Bean缠绕 XML文件中的占位符变量。CustomEditorConfigurer让你注册java.beans.PropertyEditor来将属性缠绕值翻译成其他属性值。
2.4.3 使配置外部化
         可以将整个应用程序都配置在一个Bean缠绕文件中,但有时将配置提取成多个文件更有益。
         如果使用ApplicationContext作为Spring容器的话,外部化属性是很容易的。可以使用 PropertyPlaceHolderConfigurer来告诉Spring从外部属性文件来加载特定的配置。如果你的配置分解为多个外部属性文件的话,使用PropertyPlaceHolderConfigurer的locations属性来设置<list>元素。
2.4.4 定制属性编辑器
         将String值缠绕到其他类型的属性。
         Spring自带了几个自定义编辑器:
<!--[if !supportLists]-->n  <!--[endif]-->URLEditor—将String对象缠绕到URL对象。
<!--[if !supportLists]-->n  <!--[endif]-->ClassEditor
<!--[if !supportLists]-->n  <!--[endif]-->CustomDateEditor
<!--[if !supportLists]-->n  <!--[endif]-->FileEditor
<!--[if !supportLists]-->n  <!--[endif]-->LocaleEditor
<!--[if !supportLists]-->n  <!--[endif]-->StringArrayPropertyEditor
<!--[if !supportLists]-->n  <!--[endif]-->StringTrimmerEditor
2.4.5 解析文本消息
         Spring的ApplicationContext支持参数化的消息,它通过MessageSource接口使参数化消息对容器可用。Spring自带了一个MessageSource的实现ResourceBundleMessageSource,它简单地使用Java的 java.util.ResourceBundle来解析消息。配置文件中的bean必须命名为messageSource,否则不能被 ApplicationContext识别。
         一般情况下,通过ApplicationContext的getMessage方法来访问消息,也可以在JSP中使用<spring:message>标签来访问。
2.4.6 监听事件
         在应用程序的生命周期中,ApplciationContext会发布很多事件,来告诉监听者应用程序正在进行什么工作。这些事件都是抽象类 org.springframework.context.ApplicationEvent的子类,其中的三个事件为:
<!--[if !supportLists]-->n  <!--[endif]-->ContextClosedEvent—当应用程序上下文关闭的时候发布。
<!--[if !supportLists]-->n  <!--[endif]-->ContextRefreshedEvent—当应用程序上下文初始化或刷新的时候发布。
<!--[if !supportLists]-->n  <!--[endif]-->RequestHandledEvent—当请求已经被处理的时候,在Web应用上下文环境内发布。
如果你想要Bean对应用程序事件作出响应,只需实现org.springframework.context.ApplicationListener接口。
2.4.7 发布事件
         可以让应用程序发布自己的事件。实现ApplicationEvent接口来定义事件,使用ApplicationContext的publishEvent方法来发布事件。
2.4.8 让Beans有意识
         通过实现BeanNameAware接口,BeanFactoryAware接口和ApplicationContextAware接口,Beans可以意识到他们的名字,他们的BeanFactory以及他们的ApplicationContext的存在。需要警告你的是,实现了这些接口之后Beans 就会与Spring耦合。
知道你是谁
         BeanNameAware的setBeanName()方法。
知道你在哪生存
         Spring的BeanFactoryAware和ApplicationContextAware接口让Beans意识到容器的存在。
第三章 创建方面(Aspects)
3.1 介绍AOP
3.1.1 定义AOP术语
方面(Aspect)
         一个方面(Aspect)是你实现的一个横切的功能。它是你模块化的应用程序的一个方面或领域。最常见的例子是写日志(logging)。
连接点(joinpoint)
         一个连接点是应用程序执行过程中的一个点,在这个点可以插入一个方面(Aspect)。这个点可以是调用方法,可以是抛出异常,也可以是修改一个字段。在这些点,方面的代码可以插入到应用程序的正常流程来增加新的行为。
修订(Advice)
         Advice是方面的实际实现。以logging为例,logging advice包含包含实际实现loggin的代码。Advice在jointpoints插入到应用程序中。
切入点(Pointcut)
         切入点定义应该应用何种连接点Advice。Advice可以应用于AOP框架支持的任何连接点。
引入(Introduction)
         Introduction允许你向既存的类增加方法或属性。
目标(Target)
         Target是被修订的类。可以是你写的类,也可以是第三方的类。
代理(Proxy)
         代理是向目标对象应用修订后创建的对象。就客户端对象而言,目标对象和代理对象是相同的。
编织(Weaving)
         编织是向目标对象应用方面来创建一个新的代理的对象的过程。方面在指定的连接点被织入目标对象。编织可以发生在目标类的生命周期的多个点:
<!--[if !supportLists]-->n  <!--[endif]-->编译期—方面在目标类被编译时织入。这需要一个特殊的编译器。
<!--[if !supportLists]-->n  <!--[endif]-->类加载期—方面在目标类被加载进JVM时织入。这需要一个特殊的ClassLoader来增强目标类的字节码。
<!--[if !supportLists]-->n  <!--[endif]-->运行时—方面在应用程序执行的某个时刻被织入。
Advice包含需要应用的横切行为。
3.1.2 Spring的AOP实现
Spring的修订是使用Java编写
         切入点通常以XML形式写在Spring配置文件中。其他框架,如AspectJ,要求必需使用一种特殊的语法来定义方面和切入点。
Spring在运行时修订对象
         Spring只有在代理Bean在应用程序中被需要时才创建代理对象。Spring有两种方式可以产生代理类。如果你的目标对象实现了暴露了必要方法的接口,则Spring使用JDK的java.lang.reflect.Proxy接口。这个类允许Spring动态地创建新类来实现必要的接口,织入任何修订,和代理任何从这些接口的对目标类的方法调用。如果你的目标类不实现接口,Spring使用CGLIB来产生目标类的一个子类。创建子类时,Spring织入修订,并使用子类代理所有到目标类的调用。
Spring实现AOP Alliance接口
         AOP Alliance是一个都对用Java实现AOP感兴趣的多方的联合项目。他们的目标是标准化Java AOP接口来提供不同Java的AOP实现的互操作性。这意味着实现他们接口的AOP修订会在任何与AOP Alliance兼容的框架中复用。
Spring只提供方法连接点
         这会有碍于创建细粒度的修订,如监听对对象字段的更新。
3.2 创建修订(Advice)
         因为在方法的执行过程中有多个点Spring可以织入修订,所以有多种类型的修订。下表列出了Spring提供的修订类型。
修订类型
 接口
 描述
 
Around
 org.aopalliance.intercept.MethodInterceptor
 拦截对目标函数的调用
 
Before
 org.springframework.aop.BeforeAdvice
 在目标方法被调用之前调用
 
After
 Org.springframework.aop.AfterReturningAdvice
 在目标方法返回之后调用
 
Throws
 Org.springframework.aop.ThrowsAdvice
 当目标方法抛出异常时调用
 
3.2.1 Before修订
         MethodBeforeAdvice提供目标方法,传递给目标方法的参数,以及方法调用的目标对象的访问。将修订应用于目标对象是通过Spring的配置文件。
3.2.2 After修订
         AfterReturningAdvice还可以访问被修订方法的返回值。
3.2.3 Around修订
         MethodInterceptor与前两者有两个不同的地方。首先,MethodInterceptor控制目标方法是否被实际调用。另一方面,它控制返回什么对象。
3.2.4 Throws修订
         ThrowsAdvice的afterThrowing方法具有两个签名。Throws不能捕获和处理异常,只能抛出其他异常。
3.2.5 引入(Introduction)修订
         引入修订为目标对象引入新方法或属性。
3.3 定义切入点
         Spring的切入点允许我们定义我们的修订在何处以一种 灵活的方式织入我们的类。
3.3.1 在Spring中定义切入点
         Spring根据被修订的类和方法来定义切入点。修订(Advice)被织入目标类和它的方法,基于它们的特征,如类名和方法签名。Spring的切入点框架的核心接口是Pointcut接口。ClassFilter接口确定满足修订条件的类。实现此接口的类确定作为参数传入的类是否应该被修订。
         ClassFilter是根据类来过滤方面,而MathodFilter是根据方法来过滤。
3.3.2 理解修订(advisors)
         大部分的方面是由定义方面的行为的修订(Advice)和定义方面应该在何处执行的切入点的组合。Spring认识到这一点并且提供Advisor来组合二者,确切的说,使用PointcutAdvisor来做到这一点。大部分的Spring的内置切入点都有一个对应的PointcutAdvisor。
3.3.3 使用Spring的静态切入点
         我们更倾向于使用静态切入点因为它们执行效率更高,因为他们只计算一次(创建代理的时候),而不是每次运行时调用都计算。Spring提供了一个方便的超类来创建静态切入点:StaticMethodMatcherPointcut,如果你想创建自定义的切入点,只要实现这个接口的isMatch方法。但大部分时候,使用Spring自带的切入点就够用了。
NameMatchMethodPointcut
         根据提供的名称来匹配方法名,提供的名称可以带通配符。
正则表达式切入点
         RegexpMethodPointcut使你可以使用正则表达式来定义切入点。
3.3.4 使用动态切入点
         也存在切入点需要计算运行时属性的情况。Spring提供一个内置的动态的切入点:ControlFlowPointcut。这个切入点根据当前线程的调用堆栈的信息来进行匹配。使用这个切入点会影响性能。
3.3.5 切入点操作
         Spring提供对切入点的操作如合集和交集操作来创建新的切入点。Spring提供两个类来创建这些类型的切入点。第一个类是 ComposablePointcut。可以对既存的ComposablePointcut,对象和Pointcut,MethodMatcher和 ClassFilter对象创建合集或交集来组装ComposablePointcut。可以通过调用一个ComposablePointcut的一个实例的intersection或union方法来组装。必需使用Pointcuts来操作两个Pointcut对象,如使用Pointcuts的 union方法来组合两个Pointcut。
3.4 创建介绍(Introductions)
         Introductions影响整个类。Introductions允许动态地构造组合对象,实现与多继承相同的效果。
3.4.1 实现IntroductionInterceptor
         Spring通过MethodInterceptor的一个特殊的子接口IntroductionMethodInterceptor来实现 Introductions。这个接口增加一个额外的方法implementsInterface。Spring提供了一个方便的类来实现这一切,DelegatingIntroductionMethodInterceptor,它继承自 IntroductionMethodInterceptor。
3.4.2 创建IntroductionAdvisor
         Spring提供了一个缺省的实现:DefaultIntroductionAdvisor。
3.4.3 谨慎使用Introduction Advice
         你不能为自己的代码创建的对象使用Introductions。
3.5 使用ProxyFactoryBean
         BeanFactory对象是负责创建其他JavaBeans的JavaBeans对象,于是ProxyFactoryBean创建代理对象。同其他JavaBeans一样,它具有控制自己行为的属性,下表是它的每一个属性的解释。
属性
 使用
 
target
 代理的target bean。
 
proxyInterfaces
 proxy应该实现的接口列表。
 
interceptorNames
 应用于target的advice的bean名称。这些可以是Interceptors,advisors或其他任何类型的修订的名称。可以设定此属性来在一个BeanFactory中使用这个bean。
 
singleton
 对每一个getBean调用,工厂是否应该返回相同实例的代理。如果你要使用带状态的修订,应该将这个属性设为false。
 
aopProxyFactory
 使用的ProxyFactoryBean的实现。Spring自带了两个实现,你极有可能不需要此属性。
 
exposeProxy
 目标类是否应该访问当前代理。可以通过AppContext.getCurrentProxy。应该尽量避免,因为这引入了Spring特定的AOP代码。
 
frozen
 工厂创建之后是否可以对代理的修订进行更改。
 
optimize
 是否主动优化已经产生的代理。
 
proxyTargetClass
 是否代理目标类,而不是实现接口。
 
         最经常使用的属性是target,proxyInterfaces和interceptorNames。
3.6 自动代理
         如果我们有很多类的话,显示地代理每一个类会显得很笨拙。幸运的是,Spring的自动代理设施使容器可以为我们创建代理。Spring提供了两个类来做这项工作:BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator。
3.6.1 BeanNameAutoProxyCreator
         为所有匹配一组名称的Beans产生代理。
3.6.2 DefaultAdvisorAutoProxyCreator
         为任何Advisors提供上下文环境。
3.6.3 元数据自动代理
         代理配置由代码级别的属性决定。常见应用是声明式事务。
第二部分 Spring的业务层
         介绍如何使用IoC和AOP为应用程序实现业务层功能。
第四章 数据库相关问题
4.1 学习Spring的DAO理念
         Service对象通过接口来访问DAOs。这样有两个好处:第一,因为service不与具体的DAO实现耦合,使service对象更容易测试。第二,数据访问层使用具体的持久化技术,使用接口不会暴露你使用的技术。
Spring提供在它的所有DAO框架中一直的异常层次结构。
4.1.1 理解Spring的DataAccessException
         Spring的DAO不会抛出特定技术的异常,如SQLException或者HibernateException。所有抛出的异常都是与特定技术无关的类org.springframework.dao.DataAccessException的子类。这使你的数据访问接口可以抛出通用的 DataAccessException,而不必与具体的持久化实现耦合。
你不必一定要处理DataAccessException
         DataAccessException是一个RuntimeException,因此它是unchecked的异常。这意味着,当持久化层抛出这些异常时,你的代码不一定要处理。这遵循了Spring的通用理念:checked异常会导致很多不必要的catch或throws语句,使代码比较凌乱。
         DataAccessException是从Spring的NestedRuntimeException继承,根Exception可以从NestedRuntimeException的getClause方法获得。
Spring对异常进行分类
         如下表所示,Spring有丰富的异常层次:
异常
 抛出时机
 
CleanupFailureDataAccessException
 操作执行成功,但是清理数据库资源时发生异常(如,关闭数据库连接)。
 
DataAccessResourceFailureException
 数据访问资源完全失败,例如不能连接到数据库。
 
DataIntegrityViolationException
 插入或更新违反完整性约束,例如,违反一致性约束。
 
DataRetrievalFailureException
 某些数据获取不到,例如,不能根据主键找到一行。
 
DeadLockLoserDataAccessException
 当前线程是死锁的失败者。
 
IncorrectUpdateSemanticDataAccessException
 更新时发生非计划的事情,如更新了比期望要多的行。当抛出此异常时,操作的事务没有回滚。
 
InvalidDataAccessApiUsageException
 不正确的使用了Java数据访问API,例如执行前编译查询语句时,编译失败。
 
InvalidDataAccessResourceUsageException
 不正确地使用了数据访问资源,例如,使用坏的SQL语法访问关系数据库。
 
OptimisticLockingFailureException
 乐观加锁失败,这由ORM工具或自定义的DAO实现抛出。
 
TypeMismatchDataAccessException
 Java类型和数据库类型存在失配,例如试图将一个String插入到numeric类型的数据库列。
 
UncategorizedDataAccessException
 发生错误,但是不能确定具体的错误。
 
4.1.2 使用DataSources
         在Spring框架中,通过DataSource获得Connection对象。
从JNDI取得DataSource
         Spring应用程序通常运行于J2EE应用服务器或Tomcat之类的Web服务器。这些服务器可以通过JNDI提供DataSource。在 Spring中我们处理这件事情也是通过Spring bean。在这种情况下,我们使用JndiObjectFactoryBean。
创建DataSource连接池
         如果我们的Spring容器运行的环境不存在DataSource,而我们又希望使用连接池的好处,我们只需实现一个连接池bean,实现 DataSource接口即可。一个很好的例子是Jakarta Commons DBCP项目的BasicDataSource类。只需在Spring配置文件中配置一个bean即可。
测试时使用DataSource
         Spring自带了一个轻量级的DataSource实现,用于单元测试或单元测试套件。
4.1.3 一致性DAO Support
         一个模板方法定义了一个过程的骨架。Spring将数据访问过程的固定和可变的部分分离成两个不同的类:模板与回调。模板处理数据访问的不变部分—控制事务,管理资源和处理异常。回调接口的实现定义你的应用程序特定的东西—创建语句,绑定参数和处理结果集。
         模板回调设计的顶部有一个DAO Support类,你的DAO类可以从这些类继承。
4.2 在Spring中使用JDBC
4.2.1 JDBC代码的问题
         JDBC在为你提供与数据库紧密结合的API的同时,也使你必需负责处理与访问数据库所有的相关工作。包括管理数据库资源和处理异常。
         代码比较冗赘。
4.2.2 使用JdbcTemplate
         Spring的JDBC框架通过担当资源管理和错误处理的负担来清理你的JDBC代码。Spring的数据访问框架都处理一个模板类。 JdbcTemplate的工作所需要的只是一个DataSource。因为Spring的所有DAO模板类都是线程安全的,在我们的应用程序中对每一个 DataSource只需要一个JdbcTemplate实例。
写数据
         PreparedStatementCreator接口的实现负责创建一个PreparedStatement,这个接口只提供了一个方法createPreparedStatement。
         这个接口的实现通常也实现另一个接口:SqlProvider。通过实现这个类的方法—getSql()—使你的类向JdbcTemplate类提供SQL字符串。
         对PreparedStatementCreator的补充是PreparedStatementSetter。实现这个接口的类接受一个PreparedStatement参数,负责设定所有的参数。
         如果要更新多行,可以使用BatchPrepareStatementSetter。如果你的JDBC驱动支持批量操作,更新会批量进行,创建高效的数据库访问,否则Spring会模拟批处理,但是语句会一个一个执行。
读数据
         我们要告诉Spring如何处理ResultSet中的每一行,通过实现RowCallbackHandler的方法processRow来处理。
         如果通过查询获取多个对象,可以实现一个子接口。例如,如果要获取某个类的所有对象,可以实现ResultReader接口。Spring提供了这个接口的一个实现,RowMapperResultReader。RowMapper接口负责将ResultSet的一行映射到一个对象。
         JdbcTemplate也提供了很多返回结果为int或String等简单类型的方法。
调用存储过程
         Spring提供对存储过程的支持是通过实现接口CallableStatementCallBack。
4.2.3 将操作创建为对象
         通过增加一层与直接JDBC操作的隔离。
创建SqlUpdate对象
         为创建执行插入或更新的可复用对象,继承SqlUpdate类。
使用MappingSqlQuery查询数据库
         通过继承MappingSqlQuery来将一个查询建模为对象。
4.2.4 自动递增键值
         通过DataFieldMaxValueIncrementer接口实现。
4.3 Spring的ORM框架支持简介
         Spring支持Hibernate等很多开源ORM工具。
4.4 在Spring中集成Hibernate
4.4.1 Hibernate概览
         Hibernate通过XML配置文件将对象映射到关系数据库。通常来说,每一个持久类都有一个对应的XML配置文件,扩展名为.hbm.xml。根据规范,文件名与类名相同。
4.4.2 管理Hibernate资源
         在应用程序生命周期中只需要一个SessionFactory实例,因此可以在Spring配置文件中配置此对象。可以使用Spring的 LocalSessionFactoryBean。使用Spring,可以不再使用hibernate.properties,可以缠绕到 LocalSessionFactoryBean的hibernateProperties属性。使用Spring可以配置 mappingDirectoryLocations属性可以指定一个作为应用程序classpath的一个子集的路径,在这个路径下的所有*.hbm.xml文件都会被配置。如同Spring的其他DAO框架一样,有一个HibernateTemplate这样一个模板类。
4.4.3 通过HibernateTemplate访问Hibernate
         HibernateTemplate与一个回调接口:HibernateCallBack。HibernateCallBack只要一个方法doInHibernate。
4.4.4 继承HibernateDaoSupport
4.5 Spring与JDO
         JDO是Sun的标准持久化规范。
4.5.1 配置JDO
         与Hibernate的SessionFactory类似,JDO具有一个生命周期很长的对象来持有持久化配置,PersistenceManagerFactory。如果不使用Spring我们通过JDOHelper得到这样一个对象。
         在Spring中,使用LocalePersistenceManagerFactoryBean配置 PersistenceManagerFactoryBean。有了JDO PersistenceManagerFactory,下一步就是将此bean缠绕到JdoTemplate。
4.5.2 使用JdoTemplate访问数据
         JdoTemplate类只要一个方法execute(JdoCallBack)。JdoCallBack也只有一个方法doInJdo。
4.6 Spring与iBATIS
4.6.1 设定SQL Maps
         也是使用XML配置文件配置SQL Maps。
4.6.2 使用SqlMapClientTemplate
         同其他ORM框架一样,只需实现SqlMapClientTemplate的方法doInSqlMapClient。及SqlMapClientCallBack。
4.7 Spring与OJB
         Spring通过PersistenceBroker与OJB集成。
4.7.1 设置OJB的PersistenceBroker
         还是XML文件。
第五章 管理事务
5.1 理解事务
5.1.1 用四个词解释事务
         ACID用来描述事务:
<!--[if !supportLists]-->n  <!--[endif]-->Atomic—事务由一个或多个活动绑定起来作为一个工作单元。
<!--[if !supportLists]-->n  <!--[endif]-->Consistent—一旦事务结束,系统的状态与业务状态一致。
<!--[if !supportLists]-->n  <!--[endif]-->Isolated—事务应当允许多个用户同是工作而不会相互添乱。
<!--[if !supportLists]-->n  <!--[endif]-->Durable—事务结束后,事务的结果应当持久化。
5.1.2 理解Spring的事务管理支持
         Spring既支持程序式事务,也支持声明式事务。程序式事务可以灵活控制事务的边界,而声明式事务可以使操作从业务规则中解耦。EJB也支持声明式事务,但Spring的声明式事务提供更多的属性,如隔离级别和超时。
5.1.3 Spring事务管理器简介
         Spring不直接管理事务。相反,它带有一些事务管理器,这些管理器将事务管理的职责代理给由JTA或其他持久化机制提供的平台相关的事务实现。Spring的事务管理器如下表所示。
事务管理器实现
 目的
 
org.springframework.jdbc.datasource.DataSourceTransactionManager
 在单个JDBC DataSource上管理事务
 
org.springframework.orm.hibernate.HibernateTransactionManager
 当Hibernate是持久化机制时用来管理事务。
 
org.springframework.orm.jdo.JdoTransactionManager
 当使用JDO作为持久化时使用的事务管理器。
 
org.springframework.transaction.jta.JtaTransactionManager
 使用JTA实现管理事务,当一个事务跨越多个源时,必需使用这个事务管理器。
 
org.springframework.orm.ojb.PersistenceBrokerTransactionManager
 使用OJB时使用的事务管理器。
 
         为使用事务管理器,必需在你的应用程序上下文中声明这个事务管理器。
JDBC事务
         如果使用JDBC做应用程序的持久化,DataSourceTransactionManager可以为你处理事务边界。使用XML缠绕到应用程序上下文中。
Hibernate事务
         如果你的应用程序使用Hibernate进行持久化,可以使用HibernateTransactionManager可以为你处理事务。 HibernateTransactionManager将事务管理的职责代理到net.sf.hibernate.Transaction对象。
JDO(Java Data Object)事务
         JdoTransactionManager。
OJB事务
         PersistenceBrokerTransactionManager。
JTA事务
         JtaTransactionManager。如果使用多个数据源,需要使用JtaTransactionManager。
5.2 Spring中的程序式事务
         向你的代码中添加事务的一种方法是使用Spring的TransactionTemplate类程序式地添加事务边界。TransactionTemplate利用了回调机制。以实现TransactionCallBack接口开始。
如果你想完全控制事务的边界,使用程序式事务比较好。通常你不需要如此精确的边界,这就是为什么通常在应用程序代码之外声明事务。
5.3 声明事务
         Spring对声明式事务的支持是通过AOP框架实现的。为在Spring应用程序中利用声明式事务,使用TransactionProxyFactoryBean。
5.3.1 理解事务属性
         在Spring中,一个事务属性是值对事务策略如何应用于一个方法的一个描述。这个描述可以包括以下参数中的一个或多个:
<!--[if !supportLists]-->n  <!--[endif]-->传播行为
<!--[if !supportLists]-->n  <!--[endif]-->隔离级别
<!--[if !supportLists]-->n  <!--[endif]-->只读提示
<!--[if !supportLists]-->n  <!--[endif]-->事务有效时间
传播行为
         传播行为定义了相对于客户端和被调用方法的事务的边界。Spring定义了七种传播行为,如下表所示:
传播行为
 含义
 
PROPAGATION_MANDATORY
 表示一个方法必需运行于一个事务,如果没有进行中的事务,则抛出一个异常。
 
PROPAGATION_NESTED
 表示如果存在进行中的事务,则方法应该运行于一个嵌套的事务。嵌套的事务可以从闭包事务中分别提交或回滚。如果不存在嵌套事务,则与PROPAGATION_REQUIRED行为相同。厂商一般对这种行为支持不是很好,查询资源管理器的文档来确定是否支持嵌套事务。
 
PROPAGATION_NEVER
 表示当前方法不应当运行于事务上下文。如果存在进行中的事务,则抛出一个异常。
 
PROPAGATION_NOT_SUPPORTED
 表示当前方法不应当运行于事务,如果存在进行中事务,在方法执行期间事务暂停。如果使用JTATransactionManager,则对TransactionManager的访问是必需的。
 
PROPAGATION_REQUIRED
 表示当前方法必需运行于事务内,如果存在一个进行中的事务,则使用这个事务,否则开启新的事务。
 
PROPAGATION_REQUIRED_NEW
 表示当前方法必需运行于自己的事务内。开启一个新事务,如果存在进行中事务,则在方法执行期间暂停。如果使用JTATransactionManager,则对TransactionManager的访问是必需的。
 
PROPAGATION_SUPPORTS
 表示当前方法不必需一个事务上下文,但是如果有进行中的事务,这个方法也可以运行于这个事务。
 
隔离级别
         事务的隔离级别如下表所示:
隔离级别
 含义
 
ISOLATION_DEFAULT
 使用底层数据存储的缺省隔离级别。
 
ISOLATION_READ_UNCOMMITED
 允许读取没有提交的更改。可能会导致脏读,幻影读和不可重复读。
 
ISOLATION_READ_COMMITED
 允许从没有提交的并发事务中读取。阻止脏读,但仍有可能发生幻影读和不可重复读。
 
ISOLATION_REPEATABLE_READ
 对相同字段的多次读取产生相同的结果,除非由自身的事务更改。阻止脏读和不可重复读,但仍有可能发生幻影读。
 
ISOLATION_SERIALIZABLE
 完全符合ACID的隔离级别,阻止脏读,幻影读和不可重复读。这是速度最慢的隔离级别,因为通常通过在事务中完全的表锁定来实现。
 
并不是所有的资源管理器都支持所有的这些隔离级别。
只读
         声明事务是只读的,给予底层数据存储机会对事务进行优化。
事务超时
         设定事务过一定时间后自动回滚。
5.3.2 声明简单的事务策略
         TransactionProxyFactoryBean查询一个方法的事务属性,从而确定如何管理该方法的事务策略。 TransactionProxyFactoryBean具有一个transactionAttributeSource属性,该属性缠绕到 TransactionAttributeSource接口的一个实例。TransactionAttributeSource接口用作查找一个方法的事务属性的引用。通过调用该接口的getTransactionAttribute()方法来一个特定方法的事务属性。
         MatchAlwaysTransactionAttributeSource是最简单的TransactionAttributeSource的实现,当调用其getTransactionAttribute()方法时,不管事务中缠绕的是哪一个方法,每次都返回相同的 TransactionAttribute(PROPAGATION_REQUIRED和ISOLATION_DEFAULT)。
改变缺省的TransactionAttribute
         缺省策略是PROPAGATION_REQUIRED和ISOLATION_DEFAULT,如果要改变的话,只需对transactionAttribute缠绕另一个TransactionAttribute。
5.4 根据方法名声明事务
         EJB规范的一个主要特性是容器管理的事务(CMT)。使用CMT,可以在EJB的部署描述子中声明事务策略。Spring采用了EJB的声明式事务模型,提供多个事务属性源,使你可以对POJOs声明事务策略。
5.4.1 使用NameMatchTransactionAttributeSource
 


TAG:

 

评分:0

我来说两句

日历

« 2024-05-07  
   1234
567891011
12131415161718
19202122232425
262728293031 

数据统计

  • 访问量: 20935
  • 日志数: 26
  • 建立时间: 2011-03-26
  • 更新时间: 2012-12-06

RSS订阅

Open Toolbar