Spring Framework概述(1)

 导读:
  
   Spring Framework概述 (1)
  原文:Introduction to the Spring Framework
  原著:Rod Johnson
  译者:Digital Sonic
  自从这篇文章的第一版在2003 年10 月发表以来,Spring 框架正在逐步普及。经历了1.0最终版到现在的1.2版,而且被运用于很多行业和项目中。在这篇文章中,我会解释Spring为什么会获得成功,并告诉你我十分肯定它能帮助你开发J2EE应用程序。
  又是一个框架?
  你可能正在想“不过是另一个的框架”。如今有这么多J2EE 框架,并且你可以建立你自己的框架,为什么你应该读这篇文章或者下载Spring框架(或者你还没有下载)?社区中持续的高关注度暗示了Spring一定有它的价值;这也有很多技术原因。
  以下的几个原因让我相信Spring是独一无二的:
  * 它关注于很多其它框架没有关注的地方。Spring 着重于提供一种管理你业务对象的方法。
  * Spring是全面的、模块化的。Spring采用分层架构,这意味着你可以仅选择其中任何一个独立的部分,而它的架构是内部一致的。因此你能从学习中获得最大的价值。例如,
  你可以仅用Spring 来简化你的JDBC 使用,或者你可以选择使用Spring 来管理你的业务对象。把Spring增量地引入现有的项目中是十分容易的。
  * Spring从设计之初就是要帮助你写出易于测试的代码。Spring是测试驱动项目的一个理想框架。
  * Spring是一个日益重要的集成技术,它的角色已得到一些大厂商的认可。
  * Spring不需要你的项目再依赖于另一个框架。Spring也许能称得上是一个“一站式”商店,提供了大多数传统应用所需要的基础结构。它还提供了别的框架没有涉及到的东西。
  作为一个从2003 年2 月开始的开源项目,Spring 有深厚的历史背景。这个开源项目源自我在2002年底出版的《Expert One-on-One J2EE Design and Development》中的基础代码。书中展现了Spring背后的基础性架构思考。然而,这个架构概念可以追溯到2000 年早期,并反映了我在一系列成功的商业项目的基础结构的开发中所获得的经验。
  从2003 年1 月起,Spring 落户于SourceForge。现在有20 位开发者,一些主要人员把所有的时间都花在了Spring 的开发和支持上。繁荣的开源社区帮助它茁壮成长,这远非任何个人所及。
  Spring架构上的好处
  在继续深入前,让我们来看看Spring带给一个项目的好处:
  * Spring可以有效组织你的中间层对象,无论你是否选择使用EJB。Spring关心那些当你只选择Struts 或其他为J2EE API 量身定做的框架时被留给你解决的问题。Spring 的配
  置管理服务可以被运用于任何运行环境的各种架构性分层中,这也许是中间层中最有价值的。
  * Spring可以消除在很多项目中所常见的单例的过度使用。在我看来,它的主要问题是降低了可测试性和面向对象的程序。
  * Spring 通过一种在应用程序和项目之间一致的方法来处理配置,这消除了需要自定义配置文件格式的烦恼。还记为了知道某个类要找哪个神奇的属性项或系统属性而不得不 去读Javadoc,甚至读源代码吗?有了Spring你只要简单地看看类的JavaBean属性或构造参数。控制反转和依赖注入(将在下文讨论)的使用 帮助实现了这一简化。
  * Spring通过把针对接口而非类编码的代价降低到几乎未零来帮助养成好的编码习惯。
  * Spring被设计为让构建在它之上的应用程序尽可能少地依赖于它的API。大多数Spring应用程序中的业务对象不依赖于Spring。
  * 构建于Spring之上的应用程序很容易进行单元测试。
  * Spring 使得是否使用EJB 成为实现时的选择,而非架构上的决定。你能在不改变调用代码的情况下选择用POJO 或EJB来实现业务接口。
  * Spring帮助你在不用EJB的情况下解决很多问题。Spring能提供一种适用于很多应用程 序的EJB的替代品。例如,Spring可以无需EJB容器,用AOP来提供声明性事务管理;如果你仅与一个数据库打交道,甚至可以没有JTA 实现。
  * Spring 为数据访问提供了一个一致的框架,无论使用JDBC还是像TopLink、Hibernate 或者JDO实现这样的实体关系映射产品。
  * Spring为很多方面提供了一种一致的简单的编程模型,这使得它成为了一种理想的架构“胶”。你可以从Spring访问JDBC、JMS、JavaMail、JNDI和很多其他重要API的途径中发现这种一致性。
  Spring是一种帮助你使用POJO 来构建应用程序的基础技术。要达到这个目标需要一个能将复杂性隐藏起来的成熟的框架。
  因此Spring 真的可以帮助你实现针对你的问题的最简单可行的解决方案。这是十分有价值的。
  Spring做了些什么?
  Spring提供许多功能,所以我将依次地快速浏览每个主要方面。
  任务描述
  首先,让我们明确一下Spring 的范围。尽管Spring 囊括了很多东西,但我们应该清楚的知道它该涉及什么,不该涉及什么。
  Spring的主要目的是使J2EE 更易于使用,培养好的编程习惯。它通过使用一种能适用于很多环境下的基于POJO 的编程模型来实现这一目的。
  Spring 不重新发明轮子。因此你会发现Spring 中没有日志,没有连接池,没有分布式事务调度。所有这些东西都由开源项目(例如提供我们所有的日志输出的Commons Logging,或者是Commons DBCP)或你的应用服务器提供。同样的道理,我们不提供实体/关系映射层。因为有像TopLink、Hibernate和JDO 这样的优秀的解决方案。
  Spring致力于使现有技术更加易用。例如,尽管我们没有底层业务的事务调度,但我们提供了一个凌驾于JTA 或其他事物策略的抽象层。
  Spring不直接与其他开源项目竞争,除非我们觉得我们能提供些新的东西。比如说,像其他开发者一样,我们从未就Struts感到满意,我 们觉得 MVC Web框架还有改进的余地。(随着Spring应用地快速推广,很多人也同意了我们的观点。)在很多领域,例如它的轻量级IoC容器和AOP框架, Spring有直接的竞争,但Spring确实是这些领域的先锋。
  Spring得益于内部一致性。所有开发者正唱着同一首赞歌,基础思想依然忠于《Expert One-on-One J2EE Design and Development》中提出的思想。我们已经能够在多个领域中使用些核心概念,例如控制反转。
  Spring可用于各种应用服务期。当然保证可移植性一直是一个挑战,但我们避免了开发者眼中的各种平台特有的或非标准的东西,支持 WebLogic、Tomcat、Resin、Jboss、Jetty、Geronimo、WebSphere 和其他应用服务器。Spring 的非侵入性、POJO 方法是我们可以利用环境特有特性而不用放弃可移植性,就像Spring 1.2 中在掩护下使用BEA 特有API 从而开启增强WebLogic事务管理功能。
  控制反转(Inversion of Control, IoC)容器
  Spring 的核心是为与JavaBeans 一起工作而设计的org.springframework.beans 包。这个包一般不直接被用户调用,而是作为Spring功能的基础。
  下一个更高的抽象层是bean工厂。一个Spring的bean工厂是一个普通的工厂,它能通过名称获得对象,并管理对象的关系。
  Bean工厂支持两种模式的对象:
  * 单例:这种情况下,存在一个有特定名称,在查找时能被获取的共享对象实例。这是默认的,也是最常用的模式。是无状态服务对象的理想选择。
  * 原型或非单例:在这种情况下,每次获取操作都会创建一个独立的对象作为结果。例如,这能被用来使每个调用者都有自己的独立的对象引用。
  因为Spring 容器管理对象间的关系,它能在以下情况添加值,在诸如受管理的POJO的透明池、支持热交换之类的服务需要的地方,为在运行时交换目标引用但不影响调用者 和线程安全性而由容器引入的一个间接层中。依赖注入的众多优点之一(简单讨论一下)就是这所有的一切几乎是透明的,没有API介入。
  org.springframework.beans.factory.BeanFactory是一个简单的接口,它能通过多种途径被实 现。 BeanDefinitionReader 接口将元数据格式从BeanFactory各自的实现中分离出来,所以Spring提供的普通BeanFactory实现能和不同类别的元数据一起使用。 尽管很少有人发现有这个必要,你还是可以简单地实现你自己的BeanFactory或者BeanDefinitionReader。最常用的 BeanFactory定义是:
  * XmlBeanFactory:它可解析简单直观的定义类和命名对象属性的XML结构。我们提供了一个DTD 帮助简化编写。
  * DefaultListableBeanFactory:它提供了解析属性文件中的bean定义的能力,可通过编程创建BeanFactory。
  每个bean 定义能被当作一个POJO(用类名和JavaBean 初始属性或构造方法参数来定义),或被当作一个FactoryBean。FactoryBean接口添加了一个间接层。一般,这用来创建用AOP 或其他方法的代理对象:例如,添加声明性事物管理的代理。这在概念上和EJB 的拦截机制相近,但实践起来更方便,更有效。
  BeanFactory 能选择性地参与于一个层次结构中,“继承”先辈的定义。这使得像控制器servlet 这样的个体资源能拥有自己的独立对象集的同时,在整个应用程序中能共享公共配置。
  如此使用JavaBeans 的动机在《Expert One-on-One J2EE Design and Development》的第四章中已经描述过了,你同样也可以在theServerSide 站点上以免费PDF 的形式获得(http://www.theserverside.com/tt/articles/article.tss?l= RodJohnsonInterview)。
  通过bean工厂的概念,Spring成为了一个控制反转容器。(我不太喜欢容器这个词,因为它令人想起了类似EJB容器的重量级容器。一 个 Spring的BeanFactory是一个能用一行代码创建,无需特别部署的容器。)Spring用了名为依赖注入(Dependency Injection, DI)的控制反转,依赖注入是由Martin Fowler、Rod Johnson和PicoContainer 团队在2003 年底命名的。
  控制反转背后的原则常被称为好莱坞原则:“不要打电话找我,我会打给你的。”IoC将创建的职责从应用程序代码中搬到了框架中。但在你的代 码调用一个传统类库时,IoC框架调用你的代码。在很多API 中的生命周期回调证明了这点,比如会话EJB 的setSessionContext()方法。
  依赖注入是IoC 的一种形式,它消除了对容器API 的显式依赖;普通的Java 方法被用来将诸如协作对象或配置值之类的依赖注入应用程序对象实例。涉及到配置的地方,这就意味着在像EJB这样的传统容器架构中,一个组件可以调用容器 并说“我需要的对象X 在什么地方”,有了依赖注入容器指出组件需要对象X,并在运行时将它提供给组件。容器通过方法签名(一般是JavaBean 的属性或构造方法)和可能的诸如XML 的配置数据来实现这一功能。
  两种主要的依赖注入是Setter 注入(通过JavaBean的setter 注入);和构造方法注入(通过构造方法参数注入)。Spring 对两者都提供了很好的支持,你甚至可以在配置一个对象时将两者结合起来。
  在支持各种形式的依赖注入的同时,Spring也提供一系列回调事件,和一个针对某些需要传统查找的API。但是,我们基本上是建议使用纯依赖注入途径的。
  依赖注入有几个重要的好处。例如:
  * 因为组件不需要在运行时查找协作者,所以它们更易开发和维护。在Spring版本的IoC中,组件通过暴露JavaBean的setter方法或构造方法 参数来表示它们对其他组件的依赖。这相当于EJB的JNDI查找,EJB的JNDI查找需要编写代码、设置环境参数。
  * 由于同样的原因,应用程序代码更易测试。例如,JavaBean 属性是简单的,纯Java 的并且容易测试:简单地编写一个创建对象并设置相关属性的独立Junit测试方法。
  * 一个好的IoC实现保留了强类型。如果你需要用一个通用工厂来查找协作者,你可以把结果转换为需要的类型。这并不是主要问题,但这不优雅。使用IoC,你 在代码中表达了强类型依赖后,框架会负责类型转换。这意味着类型不匹配会在框架配置应用程序时被当作错误抛出;你不必在你的代码中担心类转换异常。
  * 依赖是显式的。例如,如果一个应用程序类尝试加载一个属性文件或通过实例连接一个数据库,不读代码可能弄不清楚环境参数(有复杂的测试并降低了部署的灵活性)。使用了依赖注入的手段,依赖是显式的,能通过构造方法或JavaBean属性得知。
  * 大多数业务对象不依赖IoC容器API。这使得使用现有代码变得十分容易,并且能方便地使用IoC 容器内和容器外的对象。比如说,Spring 的用户经常把Jakarta CommonsDBCP数据源配置为一个Spring bean:没有必要写代码来实现这一步。我们说一个IoC容器不是侵入性的:使用它并不用让你的代码依赖于它的API。几乎任何POJO能作为一个 Spring bean工厂中的组件。现有的JavaBean或有多参数构造方法的对象都能很好地工作,但Spring 也需要对从静态工厂方法实例化的对象或者IoC 容器管理的其他对象的方法提供特别支持。
  这最后一点需要强调一下。依赖注入不像传统容器架构(比如EJB)那样应用程序代码存在最小限度的对容器的依赖。这意味着你的业务对象可以潜在地被运行于其他依赖注入框架,或不使用任何框架,而不需要改变代码。
  以我和Spring用户的经验来说,就算再怎么强调IoC(特别是依赖注入)带给应用程序代码的好处都不为过。
  尽管依赖注入刚在J2EE 社区中达到它的黄金时间,但它并不是一个新概念。还有其他可选的DI容器:notably、PicoContainer 和HiveMind。PicoContainer 是轻量级的且强调通过构造方法而不是JavaBean属性来表现依赖。它不在Java 代码外使用元数据,与Spring相比这限制了它的功能。HiveMind在概念上更接近Spring(它也关注IoC以外的东西),但它缺乏 Spring项目这样广泛的领域且没有相同规模的用户社群。EJB 3.0也会提供基本的DI能力。Spring的BeanFactory是非常轻量级的。用户们曾成功地在applet和独立Swing应用程序中使用过它 们。(它们在EJB容器中也有很好表现。)它们没有特殊的部署步骤,也没有与容器本身相关的可察觉的启动时间(尽管容器配置这类对象需要花时间去初始 化)。这种在应用程序的任何一层中都能立即实例化一个容器的能力是十分有价值的。
  Spring的BeanFactory的概念贯穿于整个Spring中,这也是Spring如此内部一致的一个关键原因。在IoC容器中,Spring也是唯一一个将IoC作为基本概念贯穿在一个功能丰富的框架中的。
  对于一个应用程序开发者来说,一个或多个BeanFactory提供了一个定义明确的业务对象层。这类似于本地会话bean 层,但更简单且更强大。不像EJB,这层中的对象可能是相关的,它们的关系被拥有它们的工厂管理着。有一个定义明确的业务对象层对于一个成功的架构来说是 十分重要的。
  一个Spring的ApplicationContext是BeanFactory的一个子接口,它提供了如下支持:
  * 消息查找,支持国际化
  * 一种事件机制,允许发布应用程序对象或者可选的注册以接收事件
  * 自动识别特殊的应用程序细节或通用bean定义来自定义容器行为
  * 可移植文件和访问资源
  XmlBeanFactory 例子
  Spring用户一般用XML“bean定义”文件来配置他们的应用程序。一个Spring XML bean定义文件的根是<beans>元素。<beans>元素包含一个或多个<bean>定义。我们一般会指定每 个bean定义的类和属性。我们还必须指定id,这是我们在自己的代码里使用这个bean时的名字。
  让我们来看一个简单的例子,这个例子配置三个有J2EE 应用程序常见关系的对象:
  * 一个J2EE数据源
  * 一个使用数据源的DAO
  * 一个在工作中使用DAO 的业务对象
  下面的例子中,我们使用Jakarta Commons DBCP项目中的一个BasicDataSource。(C3PO项目中的ComboPooledDataSource也是一个不错的选择。) BasicDataSource,和许多别的现有类一样,能轻松地使用于Spring bean工厂中,因为它提供了JavaBean样式的配置。在关闭时需要调用的close方法能通过Spring的“destroy-method”属性 来注册,这样BasicDataSource就不需要实现Spring的接口了。
  <beans>
  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/mydb" />
  <property name="username" value="someone" />
  </bean>
  所有我们感兴趣的BasicDataSource属性都是字符类型的,所以我们用“value”属性来指定它们的值。(这个快捷方式是 Spring 1.2 引进的。这个对<value>子元素的一个方便的替代,它甚至在有问题的XML属性值中也能使用。)Spring使用标准JavaBean PropertyEditor机制在需要时将字符表达式转换为其他类型。
  现在我们定义DAO,其中有一个对数据源的bean引用。Bean之间的关系用“ref”属性或<ref>元素来指定。
  <bean id="exampleDataAccessObject" class="example.ExampleDataAccessObject">
  <property name="dataSource" ref="myDataSource" />
  </bean>
  业务对象有一个DAO 的引用和一个int属性(exampleParam)。在这个例子中,我用了与1.2 版之前相似的子元素语法。
  <bean id="exampleBusinessObject" class="example.ExampleBusinessObject">
  <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
  <property name="exampleParam"><value>10</value></property>
  </bean>
  </beans>
  对象间的关系一般在配置中显式设置,就像例子中那样。我们认为在大多数情况下这是一件好事。但是,Spring也提供了对我们称为“自动装配 (autowire)”的支持,它指出了bean之间的依赖。这种做法的限制(和PicoContainer 一样)是如果存在一个特定类型的多个bean,那么没办法判断该解析那个类型的哪个实例依赖。从积极的一面看,无法满足的依赖能在工厂初始化时被捕捉。 (Spring还提供了对显式配置的一个可选依赖检查来实现这个目标。)如果你不想显式编写这些关系,我们可以像下面这样在上面的例子中使用自动装配这个 特性。
  <bean id="exampleBusinessObject" class="example.ExampleBusinessObject" autowire="byType">
  <property name="exampleParam" value="10" />
  </bean>
  使用这种用法,Spring会判断出exampleBusinessObject的dataSource属性应该设置到当前 BeanFactory 中的DataSource实现。如果在当前BeanFactory中没有或有多个满足要求的类型,会产生一个错误。我们仍然需要设置 exampleParam属性,因为它不是一个引用。自动装配有降低配置量的好处。它也意味着容器能使用反射来学习应用程序结构,所以如果你添加一个附加 的JavaBean属性构造方法参数,它可以成功地移植而不用改变配置。这是自动装配需要精心计算的一个折衷。
  将关系从Java 代码中提取出来比起硬编码来说有很大好处,因为它可以改变XML文件而不用改变一行Java 代码。例如,我们可以简单地通过改变myDataSource bean定义来引用一个不同的bean类来使用另一个连接池或者测试数据源。我们能像下面这样用Spring的JNDI位置FactoryBean来从一 个XML片断里的应用服务器中获得数据源。这对Java 代码和任何其他bean定义没有影响。
  <bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/myDataSource" />
  </bean>
  现在让我们看看范例业务对象的Java 代码。注意下面的代码中没有对Spring的依赖。与EJB容器不同,一个Spring的BeanFactory不是侵入式的:你通常不需要有意将它编入你的对象中。
  public class ExampleBusinessObject implements MyBusinessObject {
  private ExampleDataAccessObject dao;
  private int exampleParam;
  public void setDataAccessObject(ExampleDataAccessObject dao) {
  this.dao = dao;
  }
  public void setExampleParam(int exampleParam) {
  this.exampleParam = exampleParam;
  }
  public void myBusinessMethod() {
  // 使用dao
  }
  }
  注意属性的setter,它们符合bean定义中的XML引用。这些在对象被使用前由Spring调用。
  这类应用程序bean不需要依赖Spring:他们不需要实现任何Spring接口或扩展Spring类,它们仅需要遵循JavaBeans 命名规范。在Spring 应用程序上下文外重用是很方便的,比如在一个测试环境中。用它的默认构造方法初始化它,并通过setDataSource()和 setExampleParam()手动设置其属性。只要你有一个无参数的构造方法,你可以自由定义其他获取多个属性的构造方法如果你想支持一行代码中的 可编程构造。
  注意业务接口调用者中没有声明将要使用的JavaBean属性。它们是实现细节。我们能轻松插入拥有不同bean属性的实现类而不用影响关联的对象或调用代码。
  当然Spring的XML bean工厂还有许多别的功能,但让你对基本的方法一个了解。和简单属性、JavaBeans PropertyEditor 的属性一样,Spring 能处理列表、映射和java.util.Properties。其他高级容器功能包括:
  * 内部bean,一个属性元素包含一个上层看不到的匿名bean定义。
  * Post处理器,特别的自定义容器行为的bean定义。
  * 方法注入,IoC的一种形式,容器实现一个抽象方法或覆盖一个具体方法来诸如一个依赖。这比依赖注入的Setter 或构造方法注入更少用。但是,在为每次调用查找新的对象实例或允许配置改变额外时间时避免显式容器依赖方面这很有用,例如,在一个有SQL查询支持的方法 实现和一个file系统读入另一个。
  Bean工厂和应用程序上下文经常在J2EE服务器或web 容器定义的范围内存在关联, 比如:
  * Servlet上下文:在Spring MVC框架中,一个为每个web程序定义的应用程序上下文包含公共对象。Spring提供了通过一个监听器或不依赖于Spring MVC框架的servlet来初始化这类上下文的能力,这也可以被用在Struts、WebWork或其他web框架中。
  * 一个Servlet:Spring MVC框架中的每个控制器servlet有它自己的源自根(应用程序级)应用程序上下文的上下文。同样也能在Struts或其他web框架中实现这个。
  * EJB:Spring提供了简化EJB认证的EJB超类,还提供了一个从EJB Jar 文件中加载的BeanFactory。
  这些J2EE规范提供的钩避免了使用单例来自展一个bean工厂。
  然而,如果我们愿意的话,可以通过编码初始化一个BeanFactory,尽管这是没有什么价值的。例如,我们能像下面两行代码那样建立一个bean工厂并获得一个定义好的业务对象的引用:
  XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("myFile.xml", getClass()));
  MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject");
  这个代码能工作在应用服务器外:它不依赖于J2EE,因为Spring的IoC容器是纯Java 的。Spring Rich项目(一个用Spring简化Swing应用程序开发的框架)演示了如何在一个J2EE 环境外使用Spring,还有文章下面要讨论的Spring的集成测试特性。依赖注入和相关功能太通用、太有价值以至无法被局限在一个J2EE 或服务端环境中。
  JDBC抽象和数据存取异常层次
  数据存取是Spring的另一个闪光点。
  JDBC提供了底层数据库的很好的抽象,但使用它的API十分痛苦。这些问题包括:
  * 需要详细的异常处理来保证ResultSet、Statement 和(最重要的) Connection 在使用后被关闭。这意味着要正确使用JDBC需要很多代码。这也是一个常见的错误的源头。连接泄漏可以让应用程序很快崩溃。
  * SQLException相对来说不能说明任何问题。JDBC不提供一个异常的层次,而是用抛出SQLException来响应所有的错误。要找到是什么 东西错了(例如,是死锁还是非法的SQL)需要检查SQLState的值和错误码。这些值随着数据库的不同而不同。
  Spring通过两种方法解决这些问题:
  * 提供API 将冗长的易出错的异常处理从应用程序代码中移到框架中。框架会负责所有的异常处理;应用程序代码可以集中精力在写恰当的SQL和提取结果上。
  * 为你的应用程序提供一个有意义的异常层次来代替SQLException。当Spring 第一次从数据源获得一个连接时,它会检查元数据来决定是什么数据库产品。再用这信息将SQLExceptions 映射到它自己的从org.springframework.dao.DataAccessException 继承下来的层次中的正确异常。这样一来你的代码可以和有意义的异常打交道,不用担心私有的SQLState和错误码。Spring的数据存取异常不是 JDBC特有的,所以你的DAO 不用因为它们可能会抛出的异常而绑死在JDBC上。
  下面的UML类图阐明了一部分数据存取异常的层次,展现了它的完善度。注意这里异常没有一个是JDBC特有的。有些JDBC特有的异常是这 些异常的子类,但调用的代码一般是完全抽象于对JDBC的依赖的:如果你想用完全独立于API的DAO 接口来隐藏你的持久策略,这是最基本的。
  
  
  
  Spring提供了两层JDBC抽象API。第一层,在org.springframework.jdbc.core包中,用回调将控制权(并将相关的错误处理和连接获取与释放)从应用程序代码移到框架中。这是一种不同类型的控制反转,但与配置管理用的有一样的价值。
  Spring用相似的回调方法来处理其他几个包含获得和清除资源的特殊步骤的API,例如JDO(获得和释放一个PersistenceManager),事务管理(使用JTA)和JNDI。执行这些回调的Spring类叫模板(template)。
  例如,Spring 的JdbcTemplate 对象可以通过如下方法用来执行SQL 查询和保存列表中的结果:
  JdbcTemplate template = new JdbcTemplate(dataSource);
  List names = template.query("SELECT USER.NAME FROM USER",
  new RowMapper() {
  public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
  return rs.getString(1);
  }
  });
  mapRow回调方法会被ResultSet中的每一行调用。
  注意回调方法内的应用程序代码可以自由抛出SQLException:Spring会捕获任何异常再在它自己的层次内重新抛出。应用程序开发 者能选择捕获和处理哪个异常,如果有的话。JdbcTemplate提供了很多方法来支持不同的场景,包括已准备的语句和批处理更新。像运行SQL函数这 样的简单任务可以像下面这样不用回调来完成。这个例子也示范了绑定变量的使用:
  int youngUserCount = template.queryForInt("SELECT COUNT(0) FROM USER WHERE
  USER.AGE   Spring的JDBC抽象相对标准JDBC而言性能损失非常小,甚至在处理庞大结果集的时候也是如此。(在2004 年的一个项目中,我们记录到一个金融项目每个事务执行1200000 条插入操作。Spring JDBC的开销是最小的,Spring的使用方便了调整批处理大小和其他参数。)更高层的JDBC抽象包含在 org.springframework.jdbc.object包中。这是建立在核心JDBC回调功能上的,但是它提供了一个API,其中的对 RDBMS的操作(无论是查询、更新或者是存储过程)被做成了一个Java 对象。这个API的灵感部分来自于JDO的查询API,我发现它很直观很好用。
  一个返回User 对象的查询对象大概是这样的:
  class UserQuery extends MappingSqlQuery {
  public UserQuery(DataSource datasource) {
  super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?");
  declareParameter(new SqlParameter(Types.NUMERIC));
  compile();
  }
  // 将一个记录集映射到一个Java 对象
  protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
  User user = new User();
  user.setId(rs.getLong("USER_ID"));
  user.setForename(rs.getString("FORENAME"));
  return user;
  }
  public User findUser(long id) {
  // 用超类的方法并进行强制类型转换
  return (User) findObject(id);
  }
  }
  这个类可以这样被使用:
  User user = userQuery.findUser(25);
  这样的对象经常是DAO 的内部类。他们是线程安全的,除非子类做了些不寻常的事。另一个org.springframework.jdbc.object包中的重要类是 StoredProcedure类。Spring通过一个带业务方法的Java 类来代理一个存储过程。如果你喜欢,你能定义一个存储过程实现的接口,这意味你可以彻底把你的应用程序带从一个存储过程的依赖中解放出来。Spring的 数据存储异常层次是基于未经检查(运行时)异常。在几个项目中使用了Spring之后我越来越确信这是正确的决定。
  数据存取异常一般是不可恢复的。例如,如果我们不能连接到数据库,一个特定的业务对象就不能解决要处理的问题。一个潜在的异常是乐观锁冲 突,但不是所有的应用程序使用乐观锁。强制编写代码去捕获不能很好处理的致命异常一般是不太好的。把它们抛给类似Servlet或EJB容器这样的高层去 处理更合适。所有的Spring数据存取异常是DataAccessException的子类,所以如果我们想要选择去捕获所有的Spring数据存取异 常,我们能轻松办到。
  注意,如果我们想要从一个未经检查的数据存取异常中恢复过来,我们仍然可以做到。我们能编写只处理可恢复情况的代码。例如,如果我们认为只有一个乐观锁冲突是可恢复的,我们可以在一个Spring的DAO 中编写如下代码:
  try {
  // 工作
  }
  catch (OptimisticLockingFailureException ex) {
  // 我对这很感兴趣
  }
  如果Spring数据存取异常是经过检查的,我们需要如下代码。注意,随便什么情况我们都能这么写。
  try {
  // 工作
  }
  catch (OptimisticLockingFailureException ex) {
  // 我对这很感兴趣
  }
  catch (DataAccessException ex) {
  // 致命;仅再次抛出它
  }
  第一个例子的一个潜在缺陷(编译器不会强制处理潜在可恢复异常)在第二个例子中仍然存在。因为我们被强制去捕获基本异常 (DataAccessException),编译器不会强制对一个子类(OptimisticLockingFailureException)的检 查。因此编译器可能会强制我们去写代码处理一个不可恢复的问题,但不会对强制我们处理的可恢复问题提供帮助。
  Spring对未经检查的数据存取异常的使用和许多(也许是大多数)成功的持久化框架是一致的。(确实,这部分受到了JDO 的影响。)JDBC是少数几个使用经检查的异常的数据存取API。例如,TopLink和JDO只使用未经检查的异常。Hibernate在版本3 中可以在经检查的和未经检查的异常间切换。
  Spring JDBC可以通过以下途径来帮助你:
  * 你不再需要写finally块来使用JDBC。
  * 连接泄漏会成为过去。
  * 总的来说你写的代码少了,并且那些代码清楚地集中在需要的SQL上。
  * 你不再需要翻你的RDBMS文档来找那因错误的字段名而返回的晦涩的错误码。你的应用程序不依赖于特定的RDBMS错误处理代码。
  * 无论是用何种持久化技术,你会发现可以很方便地实现DAO 模式而不用让业务逻辑依赖于任何特定的数据存取API。
  * 你能在例如BLOB处理和调用存储过程这样的高级应用中获得更好的可移植性(与纯JDBC相比),从而获益。
  在实践中我们发现,所有这些都有助提高生产力和减少错误。我曾经讨厌写JDBC代码;现在我发现我可以关注我想要执行的SQL,而不是随之而来的JDBC资源管理。如果需要,Spring的JDBC抽象能被单独使用——你可以不使用Spring的其他部分。

本文转自
http://last999.com/1/read.php?fid=4&tid=19&fpage=1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值