Spring In Action读书笔记 - 蒋趁心 - CSDN博客
来源:百度文库 编辑:神马文学网 时间:2024/04/30 15:11:11
Spring In Action读书笔记
序言
软件开发者具有高超的手艺必须具备几个特征。首先,他们必需具有良好的分析思考能力和问题解决能力。开发者的主要角色是创造软件来解决业务问题。这要求分析客户需要和提出成功的具有创新性的解决方案。
软件开发者还必须具有好奇心。软件业中的开发总是不断更换目标,不断进化。新框架,新技术,新语言和新方法学总是不断地出现。每一样都是新工具,需要我们掌握和加入到我们的工具箱中,使我们能够更好更快地完成我们的工作。
最后一个特征,也是最受珍爱的特征就是“惰性”。这种惰性刺激开发者努力工作来寻找需要最少工作量的解决方案。正是有了这种好奇心,惰性和我们拥有的所有的分析能力,四年前我们俩开始行动起来,寻找开发软件的新方法。
当今时代,Java社区的开源软件已经达到了一个关键的数量。无数的开源框架在Java世界遍地开花。为决定选用一种框架,它必需能极大满足我们的需要—它必需能满足我们80%的需求。对于任何不能满足我们的功能,框架必需容易地扩展来包含这些功能。扩展并不意味着随便组装一下,这样以后会感到恶心,而是意味着以优雅的方式来扩展。这样的要求有点过分,哈?
我们立即采用的这样的框架之一就是ant。从一开始我们就可以看出,ant是由一个能理解我们构建Java应用的痛楚的开发者创建的。从从选用ant的那一刻起,不再有javac,不再有CLASSPATH。所需的一切只是直观的(即使有时候比较繁琐)XML配置文件。生活(和构建)变得简单。
随着我们的进展,我们开始采用越来越多的工具。Eclipse成为我们的IDE选择。Log4J成为我们的(也是所有其他人的)缺省的日志工具。Lucene替代了我们的商业搜索解决方案。所有这些工具都满足我们的准则:满足需求的同时,易用,易理解,易扩展。
但仍然缺少一些东西,这些伟大的工具如ant和Eclipse,被设计用来辅助开发软件,或者满足某一特定应用需要,如使用Lucene进行搜索和使用log4J来进行记录日志。他们都没有解决企业应用的核心:持久化,事务和与其他企业资源集成。
直到去年我们发现了令人惊奇的两大企业武器Spring和Hibernate。这两个框架满足了几乎所有的中间层及数据层的需求。
我们首先采用了Hibernate。它是最直观的特性最多的ORM工具。但直到采用了Spring我们才使代码看上去更加优雅。使用Spring的控制反转(Inversion of Control)我们可以抛弃所有的自定义工厂及配置器。事实上这也是我们将Spring集成到我们应用中的最初原因。它的wiring使我们的应用程序更加合理化,移除了我们自制的解决方案。(每一个开发者都会编写自己的框架,但是有时候必需得放弃)。
我们很快发现了一个额外的好处,Spring提供了对Hibernate的很容易地继承。这使我们不必自制对Hibernate的继承类,可以直接使用Spring的支持。这引领我们走进Spring对透明持久化的支持。
如果你稍加注意的话你应该发现了一个模式。越多地使用Spring,你就能发现越多的Spring的特性。而且我们发现的每一个特性都让我们感受到乐趣。它的Web MVC框架在好几个应用中很好地工作。它的AOP支持在好几个地方,特别是安全方面对我们很有帮助。它的JDBC支持对于小型程序很有帮助。对了,我们还用它来做日程。还有JNDI访问。还有email集成。谈及能不能满足开发的需要,Spring可谓是雪中送炭。
我们非常喜欢Spring,并且决定应该写一本关于它的书。幸运地是,我们之中已经有人为manning写过一本书,知道写书是怎么回事。很快“应该写这样一本书的人”成为了我们。我们写这本书的目的是传递Spring的精神。Spring框架是我们工作的乐趣—我们预测也会成为您的乐趣。最后,我们希望这本书能成为你到达终点的快乐的交通工具。
第一部分 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的设计遵循以下信念:
n 好的设计比起底层的实现技术更重要。
n 通过接口松散耦合的JavaBean是很好的模型。
n 代码应该易于测试。
好的设计比起底层的实现技术更重要。
不必为了使用EJB技术而使用它,真正需要它提供的服务才去使用它。
通过接口松散耦合的JavaBean是很好的模型
使用Spring,JavaBean通过接口依赖于协作。你所需要做的就是创建彼此通过接口通信的类,其他的都交给Spring来做。
代码应该易于测试
测试不需启动J2EE容器,因为你测试的是POJO。
1.2 Spring是什么?
Spring是一个开源的框架,由Rod Johnson创建。Spring的创建用来解决企业级应用开发的复杂性。Spring使我们能够使用普通的JavaBean来取得之前使用EJB达到的效果。然而,Spring的作用并不限于服务器端开发。任何应用程序都可得益于Spring的简单性,易测试性和松散耦合性。
简单地讲,Spring是一个轻量级的控制反转和面向方面编程的框架。以下是这个描述的详细解释:
n 轻量级。从两个方面:大小和预处理。Spring发布版本的就是一个JAR包,大小就是1M多一点。经Spring预处理的时间几乎可以忽略。
n 控制反转。Spring通过控制反转来达到松散耦合。通过IOC,对象被动地接受他们的依赖,而不是主动地查找和创建他们的依赖。
n 面向方面。Spring对AOP的支持使业务逻辑从系统服务中分离出来。
n 容器。从包容和管理应用程序对象生命周期的意义上来讲,Spring是一个容器。你可以配置你的每一个JavaBean如何被创建,以及他们之间的关联。不应当把Spring同重量级的EJB容器混淆,因为EJB容器是重量级的而且工作起来很笨重。
n 框架。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配置文件的根元素是
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关注方面,因为它们趋向于存在系统的各个地方。
将这些关注方面分散于系统的多个组件,会对代码引入两个级别的复杂性:
n 实现系统范围关注方面的代码,在多个组件间存在重复。这意味着如果你需要修改这些关注方面的话,你需要修改多个组件。即使你将这些关注方面抽象到单独的模块,对组件的影响只是一个方法调用,这个方法调用也会在多个组件中重复。
n 组件中会散布一些与核心功能无关的代码。
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定义的规范,作为标准具有一些重要的启示:
n 广泛的业界支持—有很多重要的厂商都支持这项技术,如Sun,IBM,Oracle和BEA。这意味着很多年之内EJB都会被支持和积极开发,这使很多公司感觉到选择EJB作为J2EE框架比较安全。
n 广泛的采用—EJB技术已经被成千上万的公司部署。因此EJB是大部分J2EE开发者的工具,拥有EJB技术更容易找到工作,公司使用EJB技术也更容易招聘到技术人员。
n 有工具支持—EJB规范是一个固定的东西,这更容易地使得厂商更快地生产工具来帮助开发者创建EJB应用程序。EJB工具具有更广泛的选择。
Spring和EJB的共同点
作为J2EE容器,EJB和Spring都为开发者开发应用程序提供了强大的特性。下表列出了两个框架的共同特性和它们的实现比较。
特性
EJB
Spring
事务管理
n 必需使用JTA事务管理。
n 支持跨越远程方法调用的事务。
n 通过PlatformTransactionManager接口支持多事务环境,包括JTA,Hibernate,JDO和JDBC。
n 自身不支持分布式事务—必需与JTA事务管理器一起使用。
声明式事务支持
n 可以通过部署描述子声明式地定义事务。
n 可以使用通配符*来针对每一个方法或每一个类定义事务行为。
n 不能声明式地定义回滚行为,必需程序实现。
n 可以通过Spring配置文件或类的元数据声明式地定义事务。
n 可以显示地或通过正则表达式定义对哪些方法应用事务行为。
n 可以针对每一个方法或每一个异常类型声明式地定义回滚行为。
持久化
n 支持程序化的Bean管理方式的持久化和声明式的容器管理持久化。
n 提供集成多个持久化技术的框架,包括JDBC,Hibernate,JDO和iBATIS。
声明式安全
n 通过用户和角色支持声明式安全。用户和角色的管理和实现是容器专有的。
n 自身不具有安全的实现。
n 一个建立于Spring之上的开源的安全框架Acegi,通过Spring配置文件或类元数据提供声明式安全。
分布计算
n 提供容器管理的远程方法调用。
n 通过RMI,JAX-RPC和Web服务提供远程调用的代理。
对于大部分的J2EE项目,EJB和Spring都能满足其技术需求。但是也有例外—你的应用程序也许需要远程事务调用,如果是这样的话,选择EJB更合适。但Spring提供了对JTA事务的集成,也能满足。但如果你要找的是提供声明式事务管理和灵活的持久化引擎的J2EE框架,Spring是最好的选择。
EJB的复杂性
EJB以下的复杂性使大家倾向于选择轻量级的容器:
n 编写一个EJB太过于复杂—写一个EJB必需接触至少四个文件:业务接口,home接口,bean实现和部署描述子。可能还牵扯到其他类,如utility类和Value Object。相反Spring使你通过POJO定义你的实现,和通过注入或AOP来缠绕任何额外的服务。
n EJB具有侵入性—为使用EJB容器提供的服务,则必需使用javax.ejb接口。这使组件代码与EJB技术绑定,使组件很难用于EJB容器之外。Spring通常不要求组件实现、扩展或使用任何特定于Spring的类或接口,是组件更具有可复用性,即使没有Spring的存在也能使用。
n 实体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提供更多的东西:
n 提供解析文本消息的方法,包括对这些消息提供I18N支持。
n 提供加载文件资源,如图片,的通用方法。
n 可以向注册为监听者的Beans发布事件。
因为ApplicationContext的额外功能,几乎在所有的应用程序中都优先使用ApplicationContext,而非BeanFactory。
ApplicationContext的实现有很多,而常用的有三个:
n ClassPathXmlApplicationContext—从在classpath中定位的XML文件加载一个上下文定义,将上下文定义文件作为classpath资源处理。
n FileSystemXmlApplicationContext—从文件系统中的XML文件加载上下文定义。
n XmlWebApplicationContext—从包含于Web应用中的XML文件加载上下文定义。
获取Bean的方式同BeanFactory,可以使用getBean()方法,因为ApplicationContext继承了BeanFactory接口。
应用程序上下文与Bean工厂的另一个比较大的区别是他们加载单例Beans的方式。BeanFactory懒加载所有Beans,而ApplicationContext预先加载所有的单例Beans。
2.1.3 Bean的生命周期
Bean可以使用之前BeanFactory执行几步设置操作:
1. 容器找到Bean的定义并实例化Bean。
2. Spring使用依赖注入产生Bean定义中指定的所有的属性。
3. 如果Bean实现了BeanNameAware接口,工厂调用setBeanName()方法,传递Bean的ID。
4. 如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory方法,并传递工厂本身的实例。
5. 如果存在与Bean关联的BeanPostProcessors,则调用他们的postProcessBeforeInitialization方法。
6. 如果指定了Bean的init方法,则调用这个方法。
7. 最后,如果存在与Bean关联的BeanPostProcessors,则调用他们的postProcessAfterInitialization方法。
从这时候起,Bean可以被应用程序使用,并一直存在与Bean工厂中,直到不需要它。有两种方式将Bean从Bean工厂中移除。
1. 如果Bean实现了DisposableBean接口,调用destroy()方法。
2. 如果指定了自定义的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。
上下文定义文件的根节点是
2.2.2 增加一个Bean
Spring中Bean的最简单的配置包括Bean的ID和Bean的完整类名。
原型vs单例
缺省情况下,所有Spring中的Beans都是单例的。如果每一次都要请求一个特定的实例的话,可以定义一个原型Bean。将
初始化与销毁
使用init-method属性指定的方法在Bean实例化之后立即执行。destroy-method属性指定的方法在Bean从容器移除之前调用。Spring提供了两个接口,可以执行相同的功能:InitializingBean和DisposableBean。前者提供了一个方法afterPropertiesSet(),在Bean的所有属性设置后执行,后者提供的方法destroy()在Bean从容器中移除时调用。使用接口的好处是不需要配置,但是也会将Bean与Spring的API绑定起来。
2.2.3 通过setter方法注入依赖
使用
简单的Bean的配置
使用
引用其他Beans
使用
内部的Beans
可以将
缠绕集合
Spring支持很多种集合类型作为Bean属性,如下表所示。
XML
类型
java.util.List, arrays
java.util.Set
缠绕集合是使用上表中提到的XML元素,而不是使用
缠绕lists和arrays
List的元素可以是。
缠绕集合
集合可以保证元素的唯一性。
缠绕maps
Map中的每一个记录都用一个
缠绕Properties
使用
设定null值
使用
Setter注入的一个代替方式
通过构造函数注入。
2.2.4 通过构造函数注入依赖
使用
处理模糊的构造函数参数
如果出现模糊的情况,Spring会抛出org.springframework.beans.factory.UnsatisfiedDependencyException。有两种方法来解决这个问题,使用下标或类型。
使用
如何选择:使用构造函数还是使用setter方法?
这是一个有争议的问题。
支持使用构造函数注入的理由:
n 强制执行一个强依赖协议。简单的说,Bean如果没得到所有的依赖就不会被实例化。
n 既然所有的依赖都可以通过构造函数设定,使用setter方法比较多余。
n 只允许通过构造函数设定函数,可以让这些属性不可更改(immutable)。
支持setter方法注入依赖的理由:
n 如果依赖比较多,构造函数的参数会比较长。
n 如果构造可用的对象的方法有多个的话,就会有多个构造函数。
n 如果构造函数的多个参数的类型一样的话,很难确定参数的目的是什么。
n 构造函数注入使自身不具有好的集成性。
2.3 自动缠绕(autowiring)
可以通过设定
有四种类型的自动缠绕:
n byName—在容器中查找与指定的名称相同的Bean。
n byType—在容器中查找一个唯一的与指定类型相同的Bean。如果没有找到则不缠绕,如果找到多个,抛出org.springframework.beans.factory.UnsatisfiedDependencyException。
n constructor—在容器中查找与被缠绕的Bean的构造函数匹配的Bean,使用指定的构造函数的参数。
n autodetect—试图先使用constructor,然后使用byType方法。
2.3.1 处理自动缠绕的模糊性
如果使用byType或constructor自动缠绕,可能会找到多个Bean。Spring在这种情况下会抛出异常。
2.3.2 混合使用自动缠绕和显式缠绕
对一个Bean的多个属性,可以设定自动缠绕属性,再显示缠绕需要覆盖的属性。
2.3.3 缺省自动缠绕
设定
2.3.4 自动缠绕还是不自动缠绕
自动缠绕可能会引起一些问题。例如,属性改名会导致缠绕不成功。
2.4 使用Spring的特殊Beans
通过实现特定的接口,可以使你的Beans变得特殊,成为Spring框架的一部分,你可以配置Beans:
n 可以通过后置处理配置文件来参与Bean和Bean的工厂的生命周期。
n 从外部属性文件加载配置信息。
n 改变Spring的依赖注入,在配置Bean属性的时候,自动将String类型转换成其他类型。
n 加载文本消息,包括国际化的消息。
n 监听或响应由其他Beans或容器发布的应用程序事件。
n 使其意识到自己在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属性来设置元素。
2.4.4 定制属性编辑器
将String值缠绕到其他类型的属性。
Spring自带了几个自定义编辑器:
n URLEditor—将String对象缠绕到URL对象。
n ClassEditor
n CustomDateEditor
n FileEditor
n LocaleEditor
n StringArrayPropertyEditor
n StringTrimmerEditor
2.4.5 解析文本消息
Spring的ApplicationContext支持参数化的消息,它通过MessageSource接口使参数化消息对容器可用。Spring自带了一个MessageSource的实现ResourceBundleMessageSource,它简单地使用Java的java.util.ResourceBundle来解析消息。配置文件中的bean必须命名为messageSource,否则不能被ApplicationContext识别。
一般情况下,通过ApplicationContext的getMessage方法来访问消息,也可以在JSP中使用
2.4.6 监听事件
在应用程序的生命周期中,ApplciationContext会发布很多事件,来告诉监听者应用程序正在进行什么工作。这些事件都是抽象类org.springframework.context.ApplicationEvent的子类,其中的三个事件为:
n ContextClosedEvent—当应用程序上下文关闭的时候发布。
n ContextRefreshedEvent—当应用程序上下文初始化或刷新的时候发布。
n 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)
编织是向目标对象应用方面来创建一个新的代理的对象的过程。方面在指定的连接点被织入目标对象。编织可以发生在目标类的生命周期的多个点:
n 编译期—方面在目标类被编译时织入。这需要一个特殊的编译器。
n 类加载期—方面在目标类被加载进JVM时织入。这需要一个特殊的ClassLoader来增强目标类的字节码。
n 运行时—方面在应用程序执行的某个时刻被织入。
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用来描述事务:
n Atomic—事务由一个或多个活动绑定起来作为一个工作单元。
n Consistent—一旦事务结束,系统的状态与业务状态一致。
n Isolated—事务应当允许多个用户同是工作而不会相互添乱。
n 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中,一个事务属性是值对事务策略如何应用于一个方法的一个描述。这个描述可以包括以下参数中的一个或多个:
n 传播行为
n 隔离级别
n 只读提示
n 事务有效时间
传播行为
传播行为定义了相对于客户端和被调用方法的事务的边界。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
NameMatchTransactionAttributeSource的properties属性将方法名映射到事务属性描述子。
事务属性描述子的格式:
PROPAGATION,ISOLATION,readOnly,-Exceptions,+Exceptions
5.4.2 简化名称匹配的事务
TransactionProxyFactoryBean具有一个transactionAttributes属性,可以直接将事务属性缠绕到此属性,而不必缠绕NameMatchTransactionAttributeSource。
5.5 使用元数据声明事务
5.5.1 从元数据获取事务属性
需要将transactionAttributeSource设置为AttributesTransactionAttributeSource。如下所示:
class="org.springframework.transaction.interceptor. AttributesTransactionAttributeSource">
注意这里的构造函数参数attributesImpl,事务属性源使用它与底层的元数据实现进行交互。
5.5.2 使用Commons Attribute声明事务
声明一个属性实现
...
标记事务性方法
Jakarta Commons Attributes通过替换方法/类前面的注释中doclet标签来实现。
设置Commons Attributes的构建
Jakarta Commons Attributes的机制是预编译你的代码中的doclet标签,重写你的类,在代码中嵌入元数据。需要在构建文件中加入与编译器。
5.6 裁剪事务声明
5.6.1 从父TransactionProxyFactoryBean继承
不必每次都声明TransactionProxyFactoryBean。
5.6.2 自动代理事务
使用AOP特性。
第六章 远程访问
6.1 Spring远程访问概览
远程访问是客户端应用程序和服务之间的对话。客户端应用程序向其他系统请求功能,这些功能称作远程服务。Spring的远程访问支持六种RPC模型:RMI,Caucho’s Hessian and Burlap,Spring的HTTP Invoker,EJB和基于JAX-RPC的Web服务。
客户端调用代理,好像是代理在提供服务功能,代理代表客户端同远程服务通信。在服务器端,可以使用以上六种模型将任何Spring管理的Bean的功能暴露为远程服务。
6.2 使用RMI
6.2.1 缠绕RMI服务
Spring的RmiProxyFactoryBean可以创建RMI服务的代理。
6.2.2 导出RMI服务
在Spring中配置RMI服务。
首先书写服务接口,无须从java.rmi.Remote继承。然后定义这个接口的实现类。下面就可以将这个实现类配置成Spring配置文件中的
RMI有很多局限性。首先,跨防火墙工作比较困难。因为RMI的通信使用任意端口,典型的防火墙都不允许这样做。另外,RMI是基于Java的。这意味着服务器端和客户端都必须使用Java编写。
6.3 使用Hessian和Burlap进行远程访问
Hessian和Burlap是由Caucho Technology提供的两个解决方案,它允许通过HTTP提供轻量级的远程服务。Hessian同RMI一样使用二进制消息在客户端和服务端之间进行通信,但同其他二进制远程访问机制不同,Hessian的二进制消息是独立于编程语言的。Burlap是一种基于XML的远程访问技术。
Hessian和Burlap的唯一区别就是基于二进制消息还是基于XML。
6.3.1 访问Hessian和Burlap服务
在Spring中使用Hessian服务与使用基于RMI服务的唯一区别是使用HessianProxyFactoryBean。对于Burlap服务,是使用BurlapProxyFactoryBean。
6.3.2 使用Hessian/Burlap暴露Bean功能
导出Hessian服务
使用HessianServiceExporter。
配置Hessian控制器
RmiServiceExporter与HessianServiceExporter之间的另一个主要区别是,因为Hessian是基于HTTP的,HessianServiceExporter实现为一个Spring MVC控制器。这意味着如果要导出Hessian服务,需要执行两个额外的配置步骤:
1. 在Spring配置文件中配置一个URL处理器,来对合适的Hessian服务bean分派Hessian服务URLs。
2. 在web.xml中配置一个DispatcherServlet,将应用程序部署为一个Web应用程序。
将URL模式映射到Hessian服务。
导出Burlap服务
使用BurlapServiceExporter。
6.4 使用HTTP Invoker
HTTP Invoker是一种新的远程访问模型,它是Spring框架的一部分。它跨越HTTP执行远程访问,而且使用Java的序列化机制。
6.4.1 通过HTTP访问服务
使用HttpInvokerProxyFactoryBean。
6.4.2 将beans暴露为HTTP服务
HttpInvokerServiceExporter。
HTTP Invoker的局限性在于,它的服务端和客户端都必须是基于Spring的。
6.5 使用EJB
6.5.1 访问EJBs
代理EJBs
Spring提供了两个代理工厂beans,来对EJBs进行代理:LocalStatelessSessionProxyFactoryBean和SimpleRemoteStatelessSessionProxyFactoryBean。
lazy-init="true">
缠绕EJBs
与缠绕其他Spring beans相同。
工作机制
首先,在启动的时候,LocalStatelessSessionProxyFactoryBean使用由jndiName属性指定的JNDI名称来通过JNDI查找EJB的本地home接口。然后,每次调用接口方法的时候,代理都调用本地home接口的create()方法获取EJB的引用。最后,代理调用EJB相应的方法。
访问远程EJB
使用SimpleRemoteStatelessSessionProxyFactoryBean。
6.5.2 开发支持Spring的EJBs
Spring提供了四个抽象的支持类来更容易地开发EJBs:
n AbstractMessageDrivenBean—对于开发消息驱动的beans比较有用。不接受来自JMS的消息。
n AbstractJmsMessageDrivenBean—接受来自JMS的消息。
n AbstractStatefulSessionBean
n AbstractStatelessSessionBean
6.6 使用基于JAX-RPC的Web services
Java APIs for XML-based remote procedure call。
6.6.1 引用JAX Web service
实现简单的功能都需要很多代码。
6.6.2 在Spring中缠绕web服务
使用JaxRpcPortProxyFactoryBean。
第七章 访问企业服务
7.1 从JNDI获取对象
JNDI为Java应用程序提供了一个存放应用程序对象的中心库。例如,典型的J2EE应用程序使用JNDI来存取诸如JDBC数据源和JTA事务管理器之类的东西。
Spring的JNDI抽象,使你能够在你的应用程序配置文件中声明JNDI查询,从而可以将这些对象缠绕到其他beans的属性中,好像JNDI对象也是POJO。
7.1.1 使用通常的JNDI
通常的JNDI使用方法违反依赖注入的原则。
7.1.2 代理JNDI对象
JndiObjectFactoryBean可以缠绕到一个属性,它实际上创建JNDI对象并且缠绕到这个属性。
7.2 发送E-mail
为发送e-mail需要一个邮件发送器,由Spring的MailSender接口定义,Spring自带了两个实现:
n CosMailSenderImpl—SMTP邮件发送器的简单实现,基于Jason Hunter的COS实现。
n JavaMailSenderImpl—基于JavaMail API的实现。允许发送MIME消息。
class="org.springframework.mail.javamail.JavaMailSenderImpl">
还可以定义邮件模板。
7.3 定时任务
两个流行的定时API是Java的Timer类和OpenSymphony的Quartz Scheduler。Spring为二者都提供了抽象。
7.3.1 使用Java的Timer进行定时
创建定时任务
通过继承java.util.TimerTask类。
对定时任务作出计划
ScheduledTimerTask定义定时任务执行的频率。
启动计时器
TimerFactoryBean。
缺陷是,只能设定运行的频率,而不能精确设定每天何时运行定时任务。
7.3.2 使用quartz定时器
可以设定每天什么时候启动任务。
创建一个Job
继承Spring的QuartzJobBean。
计划job
Quartz的Trigger类用来决定何时以何种方式运行Quartz Job。Spring自带了两个触发器:SimpleTriggerBean和CronTriggerBean。
启动job
SchedulerFactoryBean。
7.3.3 按计划调用方法
可以定时执行某个方法,而无需分别编写Task或JobBean。Spring提供了MethodInvokingTimerTaskFactoryBean和MethodInvokingJobDetailFactoryBean。
7.4 使用JMS发送消息
Java Messaging Service是用作异步处理的Java API。JMS支持两种消息机制:点对点和发布订阅。
7.4.1 使用JMS模板发送消息
Spring利用回调机制来与JMS消息协调。
使用模板
使用JmsTemplate来发送消息。
缠绕模板
使用JMS1.0.2
区别是JmsTemplate102需要知道是在使用点对点还是发布订阅模式。缺省情况下,JmsTemplate102假定你使用点对点模式。
处理JMS异常
不必处理JMSException。
7.4.2 处理消息
JmsTemplate也可以用来接收消息。
7.4.3 转化消息
MessageConverter接口定义了将JMS消息转化为对象的通用机制。
第三部分 Spring的web层
第八章 构建Web层
8.1 开始使用Spring MVC
8.1.1 request的生命周期
从Spring接收到一个请求,到返回一个响应到客户端,牵扯到了Spring MVC框架的诸多方面。
过程开始于客户端发送一个请求,接受这个请求的第一个组件是Spring的DispatcherServlet。同大部分基于Java的MVC框架一样,Spring MVC使用一个唯一的前端控制器来处理request。在Spring MVC中DispatcherServlet就是前端控制器。负责处理请求的Spring MVC组件是controller,为了知道哪一个controller应该处理某个请求,DispatcherServlet查询一个或多个HandlerMapping。HandlerMapping的工作就是将URL模式映射到Controller对象。一旦DispatcherServlet得到Controller对象,它将request分派给这个控制器来执行业务逻辑。执行完业务逻辑后,Controller返回一个ModelAndView对象给DispatcherServlet。ModelAndView对象可以包含一个View对象或者View对象的名称。如果包含的是View对象的名称,DispatcherServlet通过ViewResolver来查找对应的View对象。最后,DispatcherServlet将request分派给View对象,View对象负责产生一个响应到客户端。
8.1.2 配置DispatcherServlet
DispatcherServlet同任何servlet一样,必需在web应用的web.xml文件中配置。Servlet名称很重要,因为DispatcherServlet根据基于这个名称的xml文件加载应用程序上下文。
分解应用程序上下文
可以将各层的xml文件分开。便于维护。为保证所有这些配置文件都会被加载,必需在web.xml文件中配置上下文加载器。有两个可供选择的加载器:ContextLoaderListener和ContextLoaderServlet。
8.1.3 Spring MVC核心
构造控制器
实现Controller接口,handleRequest方法。
配置Controller Bean
DispatcherServlet使用的缺省处理器映射是BeanNameUrlHandlerMapping。
声明view resolver
View resolver的工作是将返回的ModelAndView中的view名称映射为view对象。对于产生JSP的views,最简单的莫过于使用InternalResourceViewResolver。
将view名称映射为/WEB-INF/jsp/[view名称].jsp
创建JSP
所有的组件放在一起工作
8.2 将requests映射到controllers
Handler Mappings将特定的Controller映射到一个URL模式。Spring MVC的所有handler mappings都实现org.springframework.web.servlet.HandlerMapping接口。Spring提供了三个有用的HandlerMapping的实现:
n BeanNameUrlHandlerMapping—基于控制器的名称将控制器映射到URLs。
n SimpleUrlHandlerMapping—使用在上下文配置文件中定义的属性集合将控制器映射到URLs。
n CommonsPathMapHandlerMapping—使用controller代码中的元数据将控制器映射到URLs。
8.2.1 将URLs映射到bean名称
为设置bean名称映射,首先在上下文配置文件中声明BeanNameUrlHandlerMapping bean。然后将Controller的名称的值设置为URL模式。
8.2.2 使用SimpleUrlHandlerMapping
可以直接将URL模式映射到controllers而无须为controllers命名。
8.2.3 使用元数据映射controllers
在配置文件中使用CommonsPathMapHandlerMapping声明
8.2.4 使用多个HandlerMapping
每一个HandlerMapping类都实现了Ordered接口,这意味着可以声明多个HandlerMapping,并设置order属性来指定其优先级。
8.3 使用Controllers处理请求
Spring MVC的controller类的可选择的集合如下表所示:
类型
类
用于何时
Simple
Controller(接口)
AbstractController
当你的Controller很简单时使用,比起基本的servlet,无须更多的功能。
Throwaway
ThrowawayController
需要一个简单的方式来将请求处理为命令。
Multi-Action
MultiActionController
应用程序有执行相似或相关逻辑的多个动作。
Command
BaseCommandController
AbstractCommandController
控制器从请求接受一个或多个参数,并将他们绑定到一个对象。还可以执行参数验证。
Form
AbstractFormController
SimpleFormController
需要向用户显示一个记录表单,并且处理输入表单的数据。
Wizard
AbstractWizardFormController
需要使用户经过一个复杂的多页的表单,并最终作为一个表单来处理。
8.3.1 编写简单的控制器
将你的Controller实现为AbstractController的子类。HandleRequestInternal方法是主要执行的方法。
ModelAndView简介
ModelAndView对象完全封装view和model数据。
缠绕Controller
8.3.2 处理命令
扩展AbstractCommandController。这个控制器自动地将参数绑定到命令对象,而且提供挂钩,使你能够插入验证器来确保参数是可用的。
Handler方法还接受一个Command参数。
8.3.3 处理form提交
Form控制器接受GET请求时显示表单,接受POST请求时处理表单,控制器知道何时重新显示表单,因此用户可以纠正错误重新提交。
验证输入
Org.springframework.validation.Validator接口为Spring MVC提供验证。
8.3.4 使用wizard处理复杂表单
构建简单的wizard controller
扩展AbstractWizardFormController类。
页面跳转
getTargetPage()
结束wizard
使用特殊的请求参数“_finish”。
取消wizard
增加取消按钮,使用“_cancel”参数。
每次验证一个表单
控制器的validatePage()方法,可以针对每个页面进行验证。
8.3.5 使用一个控制器处理多个动作
MultiActionController。
根据URL模式确定方法名。
解决方法名称
MultiActionController基于方法名称resolver来解决方法名称。缺省的方法名称解决器是InternalPathMethodNameResolver。Spring提供了另外两种:ParameterMethodNameResolver和PropertiesMethodNameResolver。
8.3.6 使用Throwaway控制器
Throwaway控制器充当自己的Command对象。
8.4 解决views
在Spring MVC中,一个view是一个为用户产生结果的bean。View resolver是任何实现ViewResolver接口的bean。
Spring提供了ViewResolver的四个实现:
n InternalResourceViewResolver—将逻辑view名称解决为View对象,这些对象是使用模板文件资源生成。(如JSPs和Velocity模板)
n BeanNameViewResolver—将逻辑view名称解决为DispatcherServlet的应用程序上下文中的View Beans。
n ResourceBundlerViewResolver—将逻辑View名称解决为包含在ResourceBundle中的View对象。
n XmlViewResolver—从XML文件解决View beans,这个XML文件是与DispatcherServlet的应用程序上下文分开的。
8.4.1 使用模板views
InternalResourceViewResolver。如果使用jstl,可以将viewClass属性设置成JstlView。
8.4.2 解决view beans
BeanNameViewResolver。在上下文配置文件中声明为
在分离的XML文件中声明view beans。
XmlFileViewResolver。
从Resouce bundle解决view
ResourceBundleViewResolver。
8.4.3 选择View Resolver
推荐使用InternalResourceViewResolver。如果你的视图将通过自定义的View生成,则建议使用BeanNameViewResolver。
使用多个View Resolver
可以使用多个,使用order属性设定优先级。
8.5 使用Spring的绑定标签
Spring标签库。
8.6 处理异常
使用SimpleMappingExceptionResolver来处理异常。在
第九章 View层的其他选择
9.1 使用Velocity模板
9.1.1 声明Velocity view
Velocity不是基于标签,而是基于自己的语言VTL。
9.1.2 配置Velocity引擎
在Spring配置文件中声明VelocityConfigurer bean。通过resourceLoaderPath告诉Velocity到何处找到模板。
9.1.3 解决Velocity view
在上下文配置文件中声明VelocityViewResolver。
9.1.4 格式化日期和数字
通过指定VelocityViewResolver的dateToolAttribute和numberToolAttribute属性。
9.1.5 暴露request和session属性
使用VelocityViewResolver的exposeRequestAttributes和exposeSessionAttributes属性。
9.1.6 在Velocity中绑定表单字段
Spring自带了几个Velocity宏。
9.2 使用FreeMaker
FreeMaker比Velocity复杂一些,但是功能也更强大一些。具有内置的日期数字格式化,移除空格等功能。
9.2.1 构造FreeMaker view
具有内置的格式化支持。
9.2.2 配置FreeMaker引擎
声明FreeMakerConfigurer。
9.2.3 解决FreeMaker view
FreeMakerViewResolver。
9.2.4 在FreeMaker中绑定表单字段
使用相应的FreeMaker宏。
9.3 使用Tiles设计页面布局
9.3.1 Tiles view
写好模板后,下一步是创建Tiles定义文件。
配置Tiles
TilesConfigurer。
解决Tiles view
使用InternalResourceViewResolver即可,只要将viewClass设定为TilesView。它将逻辑view名称解决为Tiles定义。
9.3.2 Tiles控制器
Tiles的每一个组件都可以有自己单独的控制器。只要在definition文件中设定每一个definition的controllerClass。
9.4 产生非HTML输出
9.4.1 产生Excel表格
Spring提供了AbstractExcelView,只要继承此类,实现它的buildExcelDocument()方法即可。它是基于POI的。
9.4.2 产生PDF文档
AbstractPdfView。基于iText。
9.4.3 产生其他非HTML文件
只要实现View接口的render方法。
第十章 使用其他Web框架
10.1 使用Struts
10.1.1 注册Spring插件
为使Struts可以访问Spring管理的beans,必需注册struts插件,使其知道Spring上下文环境的存在。在struts-config.xml中注册ContextLoaderPlugin。
10.1.2 实现可访问Spring的Struts Action
使Action继承ActionSupport类。
10.1.3 代理Actions
DelegatingActionProxy。
将Actions缠绕为Spring beans
将Actions注册为Spring beans。让代理来找Actions。
使用request代理
DelegatingRequestProcessor。
10.2 使用Tapestry
略。
10.3 与JSF集成
略。
10.4 与WebWork集成
略。
第十一章 Spring应用程序的安全性
本章介绍使用Spring AOP和servlet过滤器来保证应用程序安全性。
11.1 Acegi安全系统简介
Acegi安全框架为基于Spring的应用程序提供声明式安全。它提供一组beans集合,可以配置于Spring上下文环境,充分利用Spring对AOP和依赖注入的支持。
Acegi使用监听request的servlet过滤器来执行认证和实施安全性。
Acegi可以在较低级别上通过保证方法调用的安全实施安全性。通过使用Spring AOP,Acegi代理对象,应用方面来保证用户具有适当的授权来调用安全方法。
11.1.1 安全监听器
安全监听器好比是门栓。
11.1.2 认证管理器
安全监听器的第一个“锁眼”是认证管理器。认证管理器负责决定你是谁。通常通过用户名和密码来认证。
11.1.3 访问决策管理器
一旦Acegi决定你是谁,它就必需决定你是否被授权访问安全资源。访问决策管理器是Acegi的第二个锁眼。
11.1.4 运行管理器
运行管理器可以用来替换你的认证,使你可以访问更深层的安全资源。
11.2 管理认证
在Acegi中,认证管理器的工作是确定用户的身份。认证管理器通过net.sf.acegisecurity.AuthenticationManager接口定义:
public interface AuthenticationManager {
public Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
Authenticate方法接受一个Authentication对象,并试图认证用户。如果认证成功,则返回一个完整的Authentication对象,包含用户的授权信息。如果认证失败,则抛出AuthenticationException异常。Acegi提供了AuthenticationManager的一个实现:ProviderManager。
11.2.1 配置ProviderManager
ProviderManager将认证的职责代理到一个或多个认证提供者。ProviderManager的思想是使你能够对多个身份管理资源认证用户。ProviderManager自身并不进行认证,而是遍历所有的认证提供者,直到其中一个成功认证用户,或者遍历完所有的认证提供者。可以在Spring配置文件中以如下形式配置ProviderManager:
class="net.sf.acegisecurity.providers.ProviderManager">
通过providers属性给予ProviderManager一组认证提供者。认证提供者由net.sf.acegisecurity.provider.AuthenticationProvider接口定义。Spring提供了这个接口的多个实现,如下表所示:
认证提供者
目的
net.sf.acegisecurity.adapters.
AuthByAdapterProvider
使用容器适配器认证。
net.sf.acegisecurity.providers.cas.
CasAuthenticationProvider
使用CAS(Yale Central Authentication Service)认证。
net.sf.acegisecurity.providers.dao.
DaoAuthenticationProvider
从数据库获取用户信息,包括用户名和密码。
net.sf.acegisecurity.providers.jaas.
JaasAuthenticationProvider
从JAAS登录配置获取用户信息。
net.sf.acegisecurity.providers.dao.
PasswordDaoAuthenticationProvider
从数据库获取用户信息,但是让底层的数据存储执行实际的认证。
net.sf.acegisecurity.providers.rcp.
RemoteAuthenticationProvider
使用远程服务认证。
net.sf.acegisecurity.runas.
RunAsImplAuthenticationProvider
认证其身份由运行管理器支配的用户。
net.sf.acegisecurity.providers.
TestingAuthenticationProvider
单元测试。自动认为TestingAuthenticationProvider是可用的,不应在产品中使用。
11.2.2 使用数据库认证
DaoAuthenticationProvider使用它的DAO获取用户名和密码,并使用获取的用户名和密码认证用户。PasswordAuthentcationProvider将认证的责任推给了DAO。
声明DAO认证提供者
authenticationDao属性标识用来从数据库获取用户信息的bean。这个属性应当是net.sf.acegisecurity.providers.dao.AuthenticationDao。Acegi提供了AuthenticationDao的两个实现:InMemoryDaoImpl和JdbcDaoImpl。
使用内存Dao
palmerd=4moreyears,ROLE_PRESIDENT
bauerj=ineedsleep,ROLE_FIELD_OPS,ROLE_DIRECTOR
myersn=traitor,disabled,ROLE_FIELD_OPS
局限性在于必需编辑Spring配置文件并重新部署应用来管理安全。
声明JDBC DAO
class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
JdbcDaoImpl假定你在数据库中使用特定的表来存储用户信息,特别地,使用“Users”表和“Authorities”表。但可以使用usersByUserNameQuery来设定如何获取用户信息。JdbcDaoImpl还提供了usersByUserNameMapping属性来引用MappingSqlQuery接口。这个接口将ResultSet转换成UserDetails对象。
同样可以通过设定authoritiesByUserNameQuery和authoritiesByUserNameMapping属性来获取授权信息。
使用加密密码
Acegi提供了三个密码解码器:
n PlainTextPasswordEncoder(缺省)--不执行任何解码。
n Md5PasswordEncoder—执行MD5解码。
n ShaPasswordEncoder—执行安全哈希算法解码。
可以通过更改DaoAuthenticationProvider的passwordEncoder属性来设定解码器。还可以对解码器使用salt source,即密钥(encrytion key)。Acegi提供了两个salt sources:ReflectionSaltSource和SystemWideSaltSource。
缓存用户信息
DaoAuthenticationProvider通过UserCache接口的实现来支持用户信息的缓存。
public interface UserCache {
public UserDetails getUserFromCache(String username);
public void putUserInCache(UserDetails user);
public void removeUserFromCache(String username);
}
Acegi提供了两种实现:NullUserCache和EhCacheBasedUserCache。前者不执行任何缓存。后者基于开源的ehcache项目。
minutesToIdle设定在没有读取缓存的情况下,用户信息应当驻留多长时间。
11.2.3 使用LDAP中心库进行认证
PasswordDaoAuthenticationProvider将实际的认证代理给它的Dao。
这个Dao属性应当实现PasswordAuthenticationDao接口。Acegi不提供这个接口的实现,但是在它的sandbox中有LdapPasswordAuthenticationDao实现。
11.2.4 使用Acegi和Yale CAS支持单点登录
当在Acegi中使用CAS时,Acegi代表应用程序验证CAS ticket。它通过使用CasAuthenticationProvider来完成。
11.3 控制访问
如同认证管理器负责建立用户的身份一样,访问决策管理器负责决定用户是否有适当的权限来访问安全资源。访问决策管理器由net.sf.acegisecurity.AccessDecisionManager接口定义。
public interface AccessDecisionManager {
public void decide(Authentication authentication, Object object,
ConfigAttributeDefinition config)
throws AccessDeniedException;
public boolean supports(ConfigAttribute attribute);
public boolean supports(Class clazz);
}
Supports方法考量资源的类型和配置属性(安全资源的访问必需条件),据此决定访问决策管理器是否可以对资源做出访问决策。Decide方法是作出最终决策的地方,如果它正常返回不抛出异常的话,则授权对安全资源的访问。否则,拒绝访问。
11.3.1 投票访问决策
Acegi提供了AcessDecisionManager的三个实现:
n net.sf.acegisecurity.vote.AffirmativeBased
n net.sf.acegisecurity.vote.ConsensusBased
n net.sf.acegisecurity.vote.UnanimousBased
这三者的区别之处在于访问决策管理器如何作出最后的决策,如下表所示:
访问决策管理器
如何决策
AffirmativeBased
如果至少有一个投票者投票授权访问,则允许访问。
ConsensusBased
如果大多数投票者投票授权访问,则允许访问。
UnanimousBased
只有当没有任何投票者拒绝访问的时候,才允许访问。
例如,以下对UnanimousBased的配置:
class="net.sf.acegisecurity.vote.UnanimousBased">
11.3.2 决定如何投票
访问决策投票者是任何实现接口net.sf.acegisecurity.vote.AccessDecisionVoter接口的对象:
public interface AccessDecisionVoter {
public static final int ACCESS_GRANTED = 1;
public static final int ACCESS_ABSTAIN = 0;
public static final int ACCESS_DENIED = -1;
public boolean supports(ConfigAttribute attribute);
public boolean supports(Class clazz);
public int vote(Authentication authentication, Object object,
ConfigAttributeDefinition config);
}
Acegi提供了RoleVoter实现。处理基于角色的安全资源配置。
11.3.3 处理角色禁止
如果所有的投票都是拒绝访问,则访问决策管理器拒绝对安全资源的访问。但是可以覆盖这种缺省设置。
11.4 保护Web应用
Acegi对Web安全性的支持主要基于servlet过滤器。这些过滤器截获传入的请求,应用一些安全性处理,然后请求才有应用程序处理。Acegi提供了六种过滤器。如下表所示:
过滤器
目的
通道处理过滤器
确保请求是由安全的通道传输(如https)。
认证处理过滤器
接受认证请求,将请求传输给认证管理器来执行认证。
CAS处理过滤器
接受CAS票据作为一个Yale CAS已经认证一个用户的证明。
HTTP基本认证过滤器
处理由HTTP基本认证执行的认证。
集成过滤器
处理请求间的认证存储(例如,在HTTP Session中的)。
安全执行过滤器
确保用户已经被认证,具有访问受保护Web资源的属性授权必要条件。
当请求提交到Acegi保护的Web应用时,请求被传递给所有Acegi的过滤器。
11.4.1 代理Acegi过滤器
Acegi提供了FilterToBeanProxy,它将工作代理给Spring应用上下文中的bean。代理bean同其他servlet过滤器一样,实现javax.servlet.Filter接口,只是在Spring配置文件中配置。
在web.xml中只包含对FilterToBeanProxy的声明。最后,还要将过滤器与URL模式关联。
11.4.2 执行Web安全性
安全性执行过滤器处理请求截获,决定请求是否是安全的,并给认证和访问决策管理器一个机会来验证用户的身份和特权。
使用过滤器安全截获器
11.4.3 处理login
11.4.6 使用Acegi标签库
只有一个标签
11.5 方法调用安全性
11.5.1 创建安全性方面
发表于 @ 2008年06月01日 11:37:00 | 评论( 2 ) | 编辑| 举报| 收藏
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Jiangcx/archive/2008/06/01/2500632.aspx