导读:
Spring Framework概述 (2)
原文:Introduction to the Spring Framework
原著:Rod Johnson
译者:Digital Sonic
实体关系映射(O/R mapping, ORM)集成
当然你经常需要使用实体关系映射,而不是使用关系型数据访问。你的整体应用程序框架也必须支持这个。因而Spring继承了 Hibernate(版本2 和3)、JDO(版本1 和2)、TopLink和其他ORM产品。他的数据访问架构允许与任何底层数据访问技术集成。Spring和Hibernate是一个相当流行的组合。
为什么你要用一个ORM产品加上Spring,而不是直接使用它呢?Spring在以下方面增加了重要价值:
* 会话管理。Spring提供了对诸如Hibernate或Toplink 会话的有效、简单而且安全的处理。相关的单独使用ORM工具的代码通常需要使用同一个“Session”对象来达到有效且合的事务处理。Spring能使 用一个声明性的AOP方法拦截器或使用显式的Java 代码级的“模板”封装类来透明创建一个会话并将其绑定到当前线程。因而Spring解决了许多影响ORM 技术用户的使用问题。
* 资源管理。Spring应用程序上下文能处理Hibernate SessionFactory、JDBC数据源和其他相关资源的定位和配置。这使得这些值易于管理和更改。
* 完整的事务管理。Spring允许你将你的ORM代码封装为一个声明性的AOP方法拦截器或者一个显式的Java 代码级的“模板”封装类。不管哪种情况,事务语义都为你处理了,并且在异常发生时也作了恰当的事务处理(回滚等)。正如我们后面讨论的,你也可以使用并切 换不同事务管理器而不影响你的ORM相关代码,并从中获益。一个额外的好处是为了支持大多数ORM工具JDBC相关代码能完全事务性地与ORM代码集成。 这对于处理ORM没有实现的功能时很有用。
* 如上所述的异常封装。Spring能封装来自ORM层的异常,将它们从私有(可能是经过检查的)异常转换为一组抽象的运行时异常。这允许你可以不用烦人的 样板化的catch/throw和异常声明仍能在恰当的层中处理大多数不可恢复的持久化异常。你仍能在任何需要的地方捕获和处理异常。请记住JDBC异常 (包括数据库特有方言)也被转换为相同层次,这意味着你能在一致的编程模型内用 JDBC执行一些操作。
* 避免和厂商绑定。ORM解决方案有不同的性能和特性,没有能满足所有情况的完美解决方案。作为选择,你会发现某一个功能正好不匹配你用的ORM工具的一个 实现。这让你意识到你的数据访问对象接口的特定工具实现对你的架构有影响。一旦你因为功能、性能或其他原因需要切换到另一个实现,现在使用了Spring 让最终的切换变得更容易了。Spring对你ORM工具的事务和异常的抽象,和它的IoC方法一起让你在映射器/DAO 对象实现数据访问功能间轻松切换,简单地实现将ORM特有代码控制在你应用程序的一个范围里而不牺牲你ORM工具的能力。和Spring一起发布的宠物医 院范例程序演示了Spring通过提供使用JDBC、Hibernate、TopLink和Apache OJB实现的不同持久层而带来的可移植性方面的价值。
* 简化测试。Spring的控制反转方法使得切换实现和诸如Hibernate会话工厂、数据源、事务管理器和映射器对象实现(如果需要)之类的资源位置变得简单了。这简化了隔离和每部分持久化相关代码的单独测试。
综上所述,Spring让混用数据存取过程更简单了。尽管ORM在很多案例里赢得了颇有价值的生产力,无论一些ORM厂商怎么宣称,ORM不是所有问题的解决方案。即使你混用各种持久化方法,即使你不用JTA,Spring仍能提供一致的架构和事务策略。
在ORM并不理想的地方,Spring的简化JDBC并不是唯一选择:iBATIS SQL Maps 提供的“映射语句”也值得一看。它在保持自动从查询结果中创建映射对象的同时提供了对SQL的高层控制。Spring集成了SQL Maps。Spring的宠物店范例程序演示了iBATIS集成。
事务管理
仅抽象一个数据访问API是不够的;我们还需要考虑事务管理。JTA是当然的解决方案,但直接使用它的API是很笨重的,因而许多J2EE 开发者曾觉得EJB CMT是事务管理的唯一明智的选择。Spring改变了这一点。
Spring提供了它自己的针对事务管理的抽象。Spring 使用它来提供:
* 通过一个类似JdbcTemplate的回调模板来实现可编程的事务管理,比起直接使用JTA容易许多。
* 类似EJB CMT的声明性事务管理,但不需要EJB容器。实际上,正如我们所见,Spring的声明性事务管理能力是EJB CMT语义上的超集,有些独特的重要的好处。
Spring的事务抽象的独特之处在于它不绑定于JTA或其他任何事务管理技术。Spring使用事务策略概念,这减弱了底层事务基础部分(例如JDBC)对应用程序代码的影响。
为什么你需要关心这个?JTA不是对所有事务管理问题的最好答案吗?如果你正在写一个只与一个数据库打交道的应用程序,你不需要JTA的复杂 性。你不关心XA事务或两阶段提交。你甚至可以不需要一个提供这些东西的高端应用服务器。但另一方面,你不会愿意在和多个数据源打交道时重写你的代码。
假定你决定直接使用JDBC或Hibernate的事务来避免JTA的开销。一旦你需要与多个数据源打交道,你会不得不找出所有事务管理代 码并用 JTA事务来替换它们。这难道不具吸引力?这导致包括我在内的大多数J2EE开发者推荐只使用全局JTA事务,并用像Tomcat这样的简单Web 容器来有效管理事务程序。不管怎样,使用Spring事务抽象,你只要重新配置Spring来使用JTA,而不是JDBC或Hibernate的事务策略 就可以了。这是一个配置的修改,不是代码变更。因此,Spring让你的应用程序能随意缩放。
AOP
从2003年起在企业关注问题上(例如一般使用EJB来做的事务管理)使用AOP解决方案大家有了很大的兴趣。
Spring的AOP支持的首要目标是为POJO 提供J2EE支持。Spring AOP能够在应用服务器之间移植,所以没有厂商绑定的风险。它可以工作在web 容器或者是EJB容器中,而且已被成功应用于WebLogic、Tomcat、JBoss、Resin、Jetty、Orion和其他很多应用服务器及 web 容器上。
Spring AOP支持方法拦截。所支持的关键AOP概念包括:
拦截(Interception):自定义的行为能插入到任何接口和类的方法调用的前面或后面。这和AspectJ 术语中的“around advice”相近。
引入(Introduction):一个执行逻辑(advice)会导致一个对象实现额外的接口。这会引起继承混合。
静态和动态切入点:在程序执行过程中指定发生拦截的地方。静态切入点关注方法签名;动态关注点还能考虑被计算处的方法参数。切入点与拦截器分开定义,这使得一个标准拦截器可以被用在不同应用程序和代码上下文中。
Spring支持有状态的(每个执行逻辑对象一个实例)和无状态的(所有执行逻辑用一个实例)拦截器。
Spring不支持字段拦截。这是一个经过深思熟虑的设计决定。我总觉得字段拦截不符合封装原则。我更愿意认为AOP是OOP的一个补充而不 是与它冲突。在5到10 年后,我们在AOP的学习曲线上走得更远了很自然地会觉得应该在应用程序设计的桌面上给AOP留个位置。(那时像AspectJ 这样的基于语言的解决方案会比今天更具吸引力。)
Spring用动态代理(需要存在一个接口)或运行时CGLIB字节码生成(这使得类的代理成为可能)来实现AOP。这两种方法能在任何应用服务器中运行,也能工作在独立环境中。
Spring是第一个实现了AOP联盟接口(www.sourceforge.net/projects/aopalliance)的AOP框架。这些代表了不同AOP框架的拦截器也许可以协同工作。
Spring集成了AspectJ,提供了将AspectJ 的方面无逢集成到Spring应用程序中的能力。从Spring 1.1 开始已经可以用Spring的IoC容器依赖注入AspectJ 的方面,就像任何Java 类一样。因此AspectJ 的方面可以依赖于任何Spring管理的对象。对即将到来的AspectJ 版本5 的集成更令人激动,基于注释驱动切入点,AspectJ 开始提供用Spring依赖注入任何POJO的能力。
因为Spring拦截的是实例级对象而不是类加载器级的,所以可以在同一个类的不同实例上使用不同的执行逻辑,或把未拦截的对象和已拦截的对象一起使用。
也许Spring AOP的常见用途是用于声明性事务管理。这构建于前面讨论过的事务抽象之上,并且可以用任何POJO 进行声明性事务管理。依靠事务策略,底层机制可以是JTA、JDBC、Hibernate或是其他任何提供事务管理的API。
以下是与EJB CMT的主要不同:
事务管理可以应用于任何POJO。我们建议业务对象实现接口,这只是一个好的编程习惯,不是由框架所强制的。
通过使用Spring的事务API在一个事务性POJO 中实现可编程的回滚。我们为此提供了静态方法,使用ThreadLocal变量,所以你不必传播一个类似EJBContext这样的上下文对象来保证回滚。
你能定义声明式的回滚规则。应用程序开发者常想要事务能够在遇到任何异常时回滚,但EJB在遇到未捕获的应用程序异常时不会自动回滚(仅仅在 未检查的异常和其他Throwable异常还有“系统”异常时才回滚)。Spring的事务管理允许你声明式地指定哪个异常和子类是应该引起自动回滚的。 默认的行为和EJB一样,但你可以指定经检查异常和未经检查异常一样回滚。这在最小化可编程回滚需要上有很大好处,而这可编程回滚也建立了对Spring 事务 API的依赖(就像EJB可编程回滚建立对EJBContext的依赖一样)。
底层Spring事务抽象支持保存点(如果底层事务基本结构支持的话),所以Spring的声明性事务管理能支持嵌套事务,和EJB CMT特有的传播模式(Spring支持和EJB一样的语义)。因而,举例来说,如果你在Oracle上执行JDBC操作,你能通过Spring使用声明 性嵌套事务。
事务管理没有绑定JTA。正如前面解释的,Spring事务管理能和不同事务策略合作。你还可以使用Spring AOP实现应用程序特有方面。是否要这样做取决于你对AOP概念的理解程度,而不是Spring的能力,但这会是很有用的。我们见到的成功例子包括:
在安全检查要求的复杂度超过了标准J2EE 安全基础结构的地方自定义安全拦截器。(当然,在开始你自己的安全结构前,你应该看看Acegi Security for Spring,一个强大、灵活的用AOP和Spring整合的安全框架,这也反映了Spring架构上的方法。)
在开发中使用调式和记录方面。
实现在一个地方使用一致的异常处理策略的方面。
发送邮件给管理员或用户,警告不正常情况的拦截器。
应用程序特有方面能成为消除许多方法对样板代码需要的有效途径。
Spring AOP透明地与Spring BeanFactory概念集成。代码从一个Spring的BeanFactory中获得一个对象的时候不用知道它是不是被拦截。和任何对象一样,契约是在对象实现的接口上定义的。
下面的XML片断描述了如何定义一个AOP代理:
<bean id="myTest" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.springframework.beans.ITestBean</value>
</property>
<property name="interceptorNames">
<list>
<value>txInterceptor</value>
<value>target</value>
</list>
</property>
</bean>
注意,尽管引用的或BeanFactory的getBean()方法返回的bean类型依赖于代理接口,bean定义里的类总是AOP框架的 ProxyFactoryBean。(支持多代理方法。) ProxyFactoryBean的“interceptorNames”属性接受一个String列表。(必须用bean名称而不是bean引用,因为 如果代理是一个原型那么新的有状态拦截器需要被创建。)列表里的名字可以是拦截器或者切入点(拦截器和何时该被应用的信息)。上面列表里的 “target”值自动建立一个“调用拦截器”封装目标对象。它是工厂里的一个实现了代理接口的bean的名称。例子里的myTestbean能像工厂里 的其他bean那样被使用。例如其他对象可以通过<ref>元素引用它,并且这些引用可以用Spring IoC设置。
有很多方法能更简单地建立代理,如果你不需要AOP框架的全部功能(例如不用XML而用Java 5.0的注释来驱动事务性代理,或者用一段简单的XML实现对一个Spring工厂里的许多bean应用一致的代理策略)。
还可以不用BeanFactory而用编程方法来构建AOP代理,虽然这种方法很少用:
TestBean target = new TestBean();
DebugInterceptor di = new DebugInterceptor();
MyInterceptor mi = new MyInterceptor();
ProxyFactory factory = new ProxyFactory(target);
factory.addInterceptor(0, di);
factory.addInterceptor(1, mi);
// 一个用来封装目标的“调用拦截器”被自动添加
ITestBean tb = (ITestBean) factory.getProxy();
我们相信最好把应用程序的装配从Java代码里拿出来,AOP也不例外。
使用AOP作为EJB(版本2或以上版本)的替代物来进行企业服务是的重要性正在加大。Spring很成功地展现了这个主张的价值。
MVC web框架
Spring包括一个强大且高度可配置的MVC web框架。
Spring的MVC模型尽管不是源自Struts,但和Struts的很相似。一个Spring的Controller和Struts 的Action很像,它们都是多线程服务对象,只有一个实例代表所有客户端执行。但是,我们相信Spring MVC比起Struts有一些显著的优点。例如:
* Spring在控制器、JavaBean模型和视图间提供清晰的划分。
* Spring的MVC非常灵活。不像Struts那样强迫你的Action和Form对象有具体的继承(你只能用Java 的继承),Spring的MVC完全基于接口。此外,几乎Spring MVC框架的每个部分都能通过插入你自己的接口来配置。当然,我们也提供了简单的类作为一个可选的实现。
* Spring,像WebWork一样,提供拦截器和控制器,这使得提取处理多个请求的公共行为变得容易了。
* Spring MVC是真正视图无关的。如果你不愿意你不会被强制使用JSP;你能用Velocity、XLST或其他视图技术。如果你想用自定义的视图机制(比如你自己的模板语言),你可以轻松实现Spring的View接口来整合它。
* Spring的Controller 和其他对象一样是通过IoC来配置的。这让它们易于测试,和其他Spring管理的对象漂亮地集成在一起。
* 因为没有强迫使用具体的继承和显式地依赖于调度器Servlet的控制器,Spring MVC的web 层比起Struts 的web 层更易于测试。
* Web 层变成了业务对象层上的薄薄一层。这鼓励了好的习惯。Struts和其他专门的web框架让你自己实现你的业务对象;Spring为你的应用程序提供了一个完整的框架。
和Struts 1.1 及更高版本一样,你能根据你的需要在Spring MVC应用程序中拥有多个调度器Servlet。
下面的范例演示了一个简单的Spring Controller 如何访问同一个应用程序上下文中定一个业务对象。这个控制器在它的handleRequest()方法中执行了一个Google查询:
public class GoogleSearchController implements Controller {
private IGoogleSearchPort google;
private String googleKey;
public void setGoogle(IGoogleSearchPort google) {
this.google = google;
}
public void setGoogleKey(String googleKey) {
this.googleKey = googleKey;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String query = request.getParameter("query");
GoogleSearchResult result =
// Google属性定义忽略
// 使用Google业务对象
google.doGoogleSearch(this.googleKey, query,start, maxResults, filter, restrict,
safeSearch, lr, ie, oe);
return new ModelAndView("googleResults", "result", result);
}
}
这个代码的原型IGoogleSearchPort是一个GLUE web 服务代理,由一个Spring FactoryBean返回。然而,Spring IoC将这个控制器从底层web 服务库中隔离出来。也可以用一个简单的Java 对象、测试桩、模拟对象或者用像下面要讨论的EJB代理来实现这个接口。这个控制器没有包含资源查找;除了支持它的web 交互的必要代码外没有别的东西了。
Spring也提供了对数据绑定、表单、向导和更复杂的工作流的支持。马上要发表的这个系列文章中的一篇文章会详细讨论Spring MVC。
如果你的需求真的很复杂,你应该考虑Spring Web Flow,一个提供比传统web MVC框架更高层次web 工作流抽象的强大的框架,它的架构师Keith Donald会在最近的TSS文章上讨论它的。
一个不错的Spring MVC框架的入门材料是Thomas Risberg的Spring MVC指南(http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC- step-by-step.html)。也可以看《Web MVC with the Spring Framework》 (http://www.springframework.org/docs/web_mvc.html)。
如果对你喜欢的MVC框架情有独钟,Spring的分层结构允许你使用MVC层以外的Spring的其他部分。我们有把Spring作为中间层管理和数据访问但在web 层使用Struts、WebWork、Tapestry或JSF的用户。
实现EJB
如果你选择使用EJB,Spring可以令你从EJB实现和客户端对EJB的访问中获益。将业务逻辑重构进EJB外观后的POJO 中是一个被广泛认同的最佳实践。(和其它实践相比,这使得对业务逻辑的单元测试更加容易,因为EJB极其依赖容器且很难独立测试。)Spring为会话 bean和消息驱动bean提供了好用的超类,通过自动加载包含在EJB Jar 文件中的基于一个XML文档的BeanFactory来方便实现这点。
这意味着一个无状态的会话EJB可以这样来获得并使用一个协作者:
import org.springframework.ejb.support.AbstractStatelessSessionBean;
public class MyEJB extends AbstractStatelessSessionBean implements MyBusinessInterface {
private MyPOJO myPOJO;
protected void onEjbCreate() {
this.myPOJO = getBeanFactory().getBean("myPOJO");
}
public void myBusinessMethod() {
this.myPOJO.invokeMethod();
}
}
假定MyPOJO是一个接口,实现类(和任何它要求的诸如原始属性和进一步的协作者之类的配置)隐藏在XML bean工厂的定义中。
我们通过一个在标准ejb-jar.xml部署描述符中的名为ejb/BeanFactoryPath的环境变量定义告诉Spring到什么地方去加载XML文档,就像这样:
<session>
<ejb-name>myComponent</ejb-name>
<local-home>com.test.ejb.myEjbBeanLocalHome</local-home>
<local>com.mycom.MyComponentLocal</local>
<ejb-class>com.mycom.MyComponentEJB</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<env-entry>
<env-entry-name>ejb/BeanFactoryPath</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>/myComponent-ejb-beans.xml</env-entry-value>
</env-entry>
</session>
myComponent-ejb-beans.xml文件会被从classpath中加载:在本例中,是在EJB Jar文件的根部。每个EJB能指定它自己的XML文件,所以这个机制能在每个EJB Jar文件中多次使用。
Spring超类实现了EJB的setSessionContext()和ejbCreate()一类的生命周期方法,让应用程序开发者只要选择性地实现Spring的onEjbCreate()方法就可以了。
当EJB 3.0蓝图公布后,我们会对使用Spring IoC容器在那个环境中提供更丰富的依赖注入语义提供支持。我们也同样会将JSR-220 实体/关系映射API作为一个被支持的数据访问API整合进来。
使用EJB
Spring除了让实现EJB更容易外,还让它的使用变得简单了。许多EJB应用程序使用服务定位器和业务代理模式。这比在客户代码中遍布JNDI查找好得多,但它们的一般实现存在明显的缺陷。例如:
* 依赖于服务定位器或业务代理单例的典型EJB调用代码很难测试。
* 在没有一个业务代理的服务定位器模式中,应用程序代码仍然要调用一个EJB home的create()方法,并处理可能的异常。因而它仍然绑定了EJB API和EJB编程模型的复杂性。
* 实现业务代理模式通常会导致明显的代码重复,我们不得不写很多方法来简单地调用同一个EJB方法。
为这些和其他一些原因,传统的EJB访问(就像Sun Adventure Builder 和OTN J2EE 虚拟大卖场里演示的)会降低生产力并带来显著的复杂度。
Spring通过引入少量代码业务代理在这方面先行一步。有了Spring你不再需要在手工编写的业务代理中写另外的服务定位器、JNDI查找或重复方法除非你加入了真正的价值。
举例来说,想象我们有一个使用本地EJB的web控制器。我们会遵循最佳实践并使用EJB业务方法接口模式,所以EJB本地接口要扩展一个非 EJB 特有的业务方法接口。(这样做的一个主要原因是确保本地接口和bean实现类的自动同步。)我们称这个业务方法接口为MyComponent。当然我们也 需要实现本地home接口并提供一个实现了SessionBean和MyComponent 业务方法接口的bean实现类。
有了Spring EJB访问,我们为挂接我们的web 层控制器和EJB实现所需的唯一的Java编码就是暴露控制器上的一个MyComponent 的setter 方法。这会像一个实例变量那样保存引用:
private MyComponent myComponent;
public void setMyComponent(MyComponent myComponent) {
this.myComponent = myComponent;
}
随后我们就能在任何业务方法中使用这个实例变量了。
Spring通过XML bean定义项来自动完成剩下的工作。
LocalStatelessSessionProxyFactoryBean是一个能被任何EJB使用的通用工厂bean。它创建的对象能自动被Spring转换为MyComponent 类型。
<bean id="myComponent" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="myComponent" />
<property name="businessInterface" value="com.mycom.MyComponent" />
</bean>
<bean id="myController" class = "com.mycom.myController">
<property name="myComponent" ref="myComponent" />
</bean>
幕后发生了很多魔法般的事情,出于Spring AOP框架的谦虚,你没有被强迫使用AOP概念来享受这结果。“myComponent”bean定义建立了一个实现了业务方法接口的EJB代理。EJB 本地home在启动时被缓存,所以一般只需要一次JNDI查找。(也有对失败时重试的支持,所以一次EJB重部署不会导致客户端失败。)EJB每次被调用 时,代理调用本地EJB的create()方法并调用EJB的相应业务方法。
myController bean定义将控制类的myController 属性设置到这个代理。
这个EJB访问机制对应用程序代码进行了大量简化:
* Web层代码没有了对使用EJB的依赖。如果我们想要用一个POJO、模拟对象或其他测试桩来代替这个EJB引用,我们可以简单地改变myComponent bean定义而不用修改一行Java 代码。
* 我们没有必要写一行JNDI查找代码或其他EJB组装代码来作为我们应用程序的一部分。
我们还能通过相似的 org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean 工厂bean将相同的方法运用于远程EJB。但是,要把一个远程EJB业务方法接口的RemoteExceptions 隐藏起来却是很棘手的。(如果你希望提供一个匹配EJB远程接口但方法签名中没有“throws RemoteException”语句的客户端服务接口,Spring确实能让你办到这点。)
测试
正如你所注意到的,我和其他Spring的开发者都是全面单元测试的忠实拥护者。我们相信框架经过彻底的单元测试是很重要的,而且框架设计的一个主要目的应该是使构建于框架之上的应用程序应该是易于进行单元测试的。
Spring自己有一个出色的单元测试包。我们发现这个项目的测试优先带来的好处是实实在在的。例如,它使得一个国际化分布式开发团队工作极富效率,而且用户们评论CVS快照往往很稳定,用起来很安全。
我们相信构建在Spring上的应用程序测试很方便,有以下原因:
* IoC易于进行单元测试。
* 应用程序不包含那些一般很难测试的直接使用例如JNDI之类的J2EE服务的代码。
* Spring的bean工厂或上下文能在容器外建立。
在容器外建立一个Spring的bean工厂为开发过程提供了有趣的选择权。在几个使用Spring的web 应用程序项目中,工作始于定义业务接口和在一个web 容器外进行它们的实现的集成测试。只有在业务功能彻底完成后,再在上面加薄薄一层提供web接口。从Spring 1.1 开始,Spring提供了对在部署环紧外进行集成测试的强大且独特的支持。这并不是有意要作为单元测试或针对部署环境的测试的代替品。然而,这可以显著提 高生产力。
org.springframework.test包提供了使用一个Spring容器而不用依赖于一个应用服务器或其他部署环境进行集成测 试的很有价值的超类。这样的测试可以在Junit里运行(甚至是在一个IDE 里)而不用特殊的部署步骤。他们运行起来会比单元测试稍慢,但比Cactus 测试或靠部署到一个应用服务器的测试要快得多。通常很可能在几秒钟而不是几分钟或几小时内要运行好几百个针对一个开发数据库的测试(一般不是一个嵌入式数 据库,而是生产中用的数据库产品)。这样的测试能快速检验你Spring上下文和使用JDBC或ORM工具的数据访问的正确装配,比如SQL语句的正确 性。举例来说,你可以测试你的DAO 实现类。
org.springframework.test包中实现的功能包括:
* 通过依赖注入实现JUnit测试用例移植的能力。这使测试时重用Spring XML配置和消除针对测试的自定义设置代码成为可能。
* 在测试用例之间缓存容器配置的能力,这在用到那些比如JDBC连接池或Hibernate的SessionFactory之类的初始化很慢的资源的地方大大提高了的性能。
* 默认情况下为每个测试方法建立事务并在测试结束时回滚的基础结构。这将允许测试进行各种各样的数据存取而不用担心影响其它测试的环境。从我在几个复杂项目中使用这一功能的的经验看来,用这样一个基于回滚的方法带来的生产力和速度的提升是很明显的。
谁在使用Spring?
有很多应用程序产品使用Spring。用户包括投资和零售的银行组织、知名互联网公司、全球性顾问公司、学院机构、政府机关、防卫设备承包商、几家航空公司和科研组织(包括CERN)。
很多用户使用全部的Spring,但有些使用单独的组件。例如,一些用户从使用我们的JDBC或其它数据存取功能开始。
发展历程
自从这篇文章的第一版在2003年10 月发表以来,Spring经历了1.0 最终发布版(2004年3月)到1.1 版(2004年9 月)到1.2 最终版(2005年5月)的过程。我们相信这样一个哲学“早发布,勤发布”,所以维护性发布和小的改进一般4到6 周发布一次。
从那时起的改进包括:
* 引入一个支持包括RMI和多种web 服务协议在内的多协议远程框架。
* 支持方法注入和其他的IoC容器增强,例如管理从静态或实例工厂方法的调用中获得的对象的能力。
* 集成更多数据访问技术,在最近的1.2版中包括TopLink、 Hibernate 版本3。
* 支持用Java 5.0注释配置的声明性事务管理,不需要用XML元数据来识别事务性方法(1.2)。
* 支持Spring所管理对象的JMX 管理(1.2)。
* 集成了Jasper 报告、Quartz计划和AspectJ。
* 将JSF作为一种web 层技术集成进来。
我们打算继续快速的革新和增强。下一个主要版本会是1.3。计划中的增强包括:
* XML配置增强(计划在1.3 版内),这将允许用自定义XML标签通过定义一个或多个单一的、合法的标签来扩展基本的Spring配置格式。这不仅可能显著简化典型配置并减少配置错误,还会成为基于Spring的第三方产品的开发者的理想选择。
* 把Spring Web Flow集成到Spring的核心中(计划在1.3 版内)。
* 支持运行时应用程序动态重配置。
* 支持用Java 外的语言编写的应用程序对象,比如Groovy、Jython或其他运行于Java 平台上的脚本语言。这些对象能得益于Spring IoC容器的完整服务和在脚本改变时动态重新装载而不影响由IoC容器引用到它们的对象。
作为一个敏捷项目,Spring主要由用户需求驱动。所以我们不会开发没有任用的功能,我们也会仔细倾听来自我们用户社群的声音。
Spring Modules 是一个由Interface 21 的Rob Harrop领导的相关项目,它将Spring平台扩展到那些Spring核心没有必要完全支持的,但仍然对很多用户有价值的领域。这个项目也作为一个孵 化器,所以其中的一些功能会最终集成到Spring核心中。Spring Modules 目前包括如下领域:与Lucene搜索引擎和OSWorkflow工作流引擎的集成、一个基于AOP的声明性缓存解决方案、与Commons Validator 框架的集成。
有趣的是,尽管这篇文章的第一版发表在Spring 1.0 最终版发布的六个月前,几乎所有的示范代码和配置仍然能不经改动地在今天的1.2 版上运行。我们为自己在向下兼容方面的骄人记录感到自豪。这也展现了依赖注入和AOP实现非侵入性API的能力,还表现了我们尽心尽力为社区提供一个能够 运行重大应用程序的稳定框架的严谨。
总结
Spring是一个解决了许多常见J2EE 问题的强大框架。许多Spring的功能也可以被运用于很多超越经典J2EE 的Java环境中。
Spring提供了一种以一致方法管理业务对象的方法,并鼓励好的编程习惯,例如针对接口而不是类编程。Spring的架构基础是一个使用 JavaBean属性的控制反转容器。可是,这只是Spring全貌的一部分:Spring在将IoC容器作为所有架构层的完整解决方案的基本构建块方面 是独一无二的。
Spring提供了一个独特的数据访问抽象,包括一个大大改善生产力并降低错误可能的简单而有效的JDBC框架。Spring的数据访问架构还集成了TopLink、Hibernate、JDO 和其他实体/关系映射解决方案。
Spring提供了唯一的事务管理抽象,这能够在类似JTA或JDBC这样的不同底层事务技术上使用一致的编程模型。
Spring提供了一个用标准Java 写的AOP框架,它提供了声明性事务管理和其他用于POJO 的企业服务或者(如果你希望)也能用于实现你自己的自定义方面。这个框架强大到足以使很多应用程序在享受传统的EJB相关的关键服务的同时放弃EJB的复杂性。
Spring还提供了一个可整合到整个IoC容器中的强大且灵活的MVC web框架。
更多信息
需要更多的关于Spring的信息请参阅:
* Interface21 提供的一个核心Spring培训课程——http://www.springframework.com/training。
* 《Expert One-on-One J2EE Design and Development》(Rod Johnson, Wrox, 2002)。尽管Spring在此书出版后有了很明显的改进,但它仍然是理解Spring动机的好地方。
* 《J2EE without EJB》(Rod Johnson与Juergen Hoeller 合著Wrox, 2004)。《Expert One-on-One J2EE Design and Development》的后续作品,讨论Spring和它的轻量级容器架构的基本原理。
* 《Spring参考手册》。Spring 1.2 的打印版本又超过240页。Spring还带了几个展示最佳实践并可用作你自己的应用程序模板的范例。
* 《Pro Spring》:由核心开发者Rob Harrop深入讨论Spring。
* 《Spring: A Developer’s Notebook》:由Bruce Tate 和Justin Gehtland所著的入门。
* Spring框架主页:http://www.springframework.org/,这里包括Javadoc和几个教程。
* Sourceforge上的论坛和下载。
* Spring开发者邮件列表。
我们为对待论坛和邮件列表中问题的认真态度和出色的回复率感到自豪。我们欢迎您早日加入我们的社区。
关于作者
Rod Johnson拥有差不多十年作为Java 开发者和架构师的经验,并从J2EE 平台出现后就在其上进行开发。他是畅销书《Expert One-on-One J2EE Design and Development》(Wrox,2002)和《J2EE without EJB》(Wrox, 2004与Juergen Hoeller 合著)的作者,也参与过其它J2EE著作的编写。Rod参与了两个Java 标准委员会并经常在大会发言。他是Interface21的CEO,这是一家国际化咨询公司,领导Spring框架开发,提供Spring框架和J2EE 方面的专业服务。
本文转自
http://last999.com/1/read.php?fid=4&tid=20&fpage=1
Spring Framework概述 (2)
原文:Introduction to the Spring Framework
原著:Rod Johnson
译者:Digital Sonic
实体关系映射(O/R mapping, ORM)集成
当然你经常需要使用实体关系映射,而不是使用关系型数据访问。你的整体应用程序框架也必须支持这个。因而Spring继承了 Hibernate(版本2 和3)、JDO(版本1 和2)、TopLink和其他ORM产品。他的数据访问架构允许与任何底层数据访问技术集成。Spring和Hibernate是一个相当流行的组合。
为什么你要用一个ORM产品加上Spring,而不是直接使用它呢?Spring在以下方面增加了重要价值:
* 会话管理。Spring提供了对诸如Hibernate或Toplink 会话的有效、简单而且安全的处理。相关的单独使用ORM工具的代码通常需要使用同一个“Session”对象来达到有效且合的事务处理。Spring能使 用一个声明性的AOP方法拦截器或使用显式的Java 代码级的“模板”封装类来透明创建一个会话并将其绑定到当前线程。因而Spring解决了许多影响ORM 技术用户的使用问题。
* 资源管理。Spring应用程序上下文能处理Hibernate SessionFactory、JDBC数据源和其他相关资源的定位和配置。这使得这些值易于管理和更改。
* 完整的事务管理。Spring允许你将你的ORM代码封装为一个声明性的AOP方法拦截器或者一个显式的Java 代码级的“模板”封装类。不管哪种情况,事务语义都为你处理了,并且在异常发生时也作了恰当的事务处理(回滚等)。正如我们后面讨论的,你也可以使用并切 换不同事务管理器而不影响你的ORM相关代码,并从中获益。一个额外的好处是为了支持大多数ORM工具JDBC相关代码能完全事务性地与ORM代码集成。 这对于处理ORM没有实现的功能时很有用。
* 如上所述的异常封装。Spring能封装来自ORM层的异常,将它们从私有(可能是经过检查的)异常转换为一组抽象的运行时异常。这允许你可以不用烦人的 样板化的catch/throw和异常声明仍能在恰当的层中处理大多数不可恢复的持久化异常。你仍能在任何需要的地方捕获和处理异常。请记住JDBC异常 (包括数据库特有方言)也被转换为相同层次,这意味着你能在一致的编程模型内用 JDBC执行一些操作。
* 避免和厂商绑定。ORM解决方案有不同的性能和特性,没有能满足所有情况的完美解决方案。作为选择,你会发现某一个功能正好不匹配你用的ORM工具的一个 实现。这让你意识到你的数据访问对象接口的特定工具实现对你的架构有影响。一旦你因为功能、性能或其他原因需要切换到另一个实现,现在使用了Spring 让最终的切换变得更容易了。Spring对你ORM工具的事务和异常的抽象,和它的IoC方法一起让你在映射器/DAO 对象实现数据访问功能间轻松切换,简单地实现将ORM特有代码控制在你应用程序的一个范围里而不牺牲你ORM工具的能力。和Spring一起发布的宠物医 院范例程序演示了Spring通过提供使用JDBC、Hibernate、TopLink和Apache OJB实现的不同持久层而带来的可移植性方面的价值。
* 简化测试。Spring的控制反转方法使得切换实现和诸如Hibernate会话工厂、数据源、事务管理器和映射器对象实现(如果需要)之类的资源位置变得简单了。这简化了隔离和每部分持久化相关代码的单独测试。
综上所述,Spring让混用数据存取过程更简单了。尽管ORM在很多案例里赢得了颇有价值的生产力,无论一些ORM厂商怎么宣称,ORM不是所有问题的解决方案。即使你混用各种持久化方法,即使你不用JTA,Spring仍能提供一致的架构和事务策略。
在ORM并不理想的地方,Spring的简化JDBC并不是唯一选择:iBATIS SQL Maps 提供的“映射语句”也值得一看。它在保持自动从查询结果中创建映射对象的同时提供了对SQL的高层控制。Spring集成了SQL Maps。Spring的宠物店范例程序演示了iBATIS集成。
事务管理
仅抽象一个数据访问API是不够的;我们还需要考虑事务管理。JTA是当然的解决方案,但直接使用它的API是很笨重的,因而许多J2EE 开发者曾觉得EJB CMT是事务管理的唯一明智的选择。Spring改变了这一点。
Spring提供了它自己的针对事务管理的抽象。Spring 使用它来提供:
* 通过一个类似JdbcTemplate的回调模板来实现可编程的事务管理,比起直接使用JTA容易许多。
* 类似EJB CMT的声明性事务管理,但不需要EJB容器。实际上,正如我们所见,Spring的声明性事务管理能力是EJB CMT语义上的超集,有些独特的重要的好处。
Spring的事务抽象的独特之处在于它不绑定于JTA或其他任何事务管理技术。Spring使用事务策略概念,这减弱了底层事务基础部分(例如JDBC)对应用程序代码的影响。
为什么你需要关心这个?JTA不是对所有事务管理问题的最好答案吗?如果你正在写一个只与一个数据库打交道的应用程序,你不需要JTA的复杂 性。你不关心XA事务或两阶段提交。你甚至可以不需要一个提供这些东西的高端应用服务器。但另一方面,你不会愿意在和多个数据源打交道时重写你的代码。
假定你决定直接使用JDBC或Hibernate的事务来避免JTA的开销。一旦你需要与多个数据源打交道,你会不得不找出所有事务管理代 码并用 JTA事务来替换它们。这难道不具吸引力?这导致包括我在内的大多数J2EE开发者推荐只使用全局JTA事务,并用像Tomcat这样的简单Web 容器来有效管理事务程序。不管怎样,使用Spring事务抽象,你只要重新配置Spring来使用JTA,而不是JDBC或Hibernate的事务策略 就可以了。这是一个配置的修改,不是代码变更。因此,Spring让你的应用程序能随意缩放。
AOP
从2003年起在企业关注问题上(例如一般使用EJB来做的事务管理)使用AOP解决方案大家有了很大的兴趣。
Spring的AOP支持的首要目标是为POJO 提供J2EE支持。Spring AOP能够在应用服务器之间移植,所以没有厂商绑定的风险。它可以工作在web 容器或者是EJB容器中,而且已被成功应用于WebLogic、Tomcat、JBoss、Resin、Jetty、Orion和其他很多应用服务器及 web 容器上。
Spring AOP支持方法拦截。所支持的关键AOP概念包括:
拦截(Interception):自定义的行为能插入到任何接口和类的方法调用的前面或后面。这和AspectJ 术语中的“around advice”相近。
引入(Introduction):一个执行逻辑(advice)会导致一个对象实现额外的接口。这会引起继承混合。
静态和动态切入点:在程序执行过程中指定发生拦截的地方。静态切入点关注方法签名;动态关注点还能考虑被计算处的方法参数。切入点与拦截器分开定义,这使得一个标准拦截器可以被用在不同应用程序和代码上下文中。
Spring支持有状态的(每个执行逻辑对象一个实例)和无状态的(所有执行逻辑用一个实例)拦截器。
Spring不支持字段拦截。这是一个经过深思熟虑的设计决定。我总觉得字段拦截不符合封装原则。我更愿意认为AOP是OOP的一个补充而不 是与它冲突。在5到10 年后,我们在AOP的学习曲线上走得更远了很自然地会觉得应该在应用程序设计的桌面上给AOP留个位置。(那时像AspectJ 这样的基于语言的解决方案会比今天更具吸引力。)
Spring用动态代理(需要存在一个接口)或运行时CGLIB字节码生成(这使得类的代理成为可能)来实现AOP。这两种方法能在任何应用服务器中运行,也能工作在独立环境中。
Spring是第一个实现了AOP联盟接口(www.sourceforge.net/projects/aopalliance)的AOP框架。这些代表了不同AOP框架的拦截器也许可以协同工作。
Spring集成了AspectJ,提供了将AspectJ 的方面无逢集成到Spring应用程序中的能力。从Spring 1.1 开始已经可以用Spring的IoC容器依赖注入AspectJ 的方面,就像任何Java 类一样。因此AspectJ 的方面可以依赖于任何Spring管理的对象。对即将到来的AspectJ 版本5 的集成更令人激动,基于注释驱动切入点,AspectJ 开始提供用Spring依赖注入任何POJO的能力。
因为Spring拦截的是实例级对象而不是类加载器级的,所以可以在同一个类的不同实例上使用不同的执行逻辑,或把未拦截的对象和已拦截的对象一起使用。
也许Spring AOP的常见用途是用于声明性事务管理。这构建于前面讨论过的事务抽象之上,并且可以用任何POJO 进行声明性事务管理。依靠事务策略,底层机制可以是JTA、JDBC、Hibernate或是其他任何提供事务管理的API。
以下是与EJB CMT的主要不同:
事务管理可以应用于任何POJO。我们建议业务对象实现接口,这只是一个好的编程习惯,不是由框架所强制的。
通过使用Spring的事务API在一个事务性POJO 中实现可编程的回滚。我们为此提供了静态方法,使用ThreadLocal变量,所以你不必传播一个类似EJBContext这样的上下文对象来保证回滚。
你能定义声明式的回滚规则。应用程序开发者常想要事务能够在遇到任何异常时回滚,但EJB在遇到未捕获的应用程序异常时不会自动回滚(仅仅在 未检查的异常和其他Throwable异常还有“系统”异常时才回滚)。Spring的事务管理允许你声明式地指定哪个异常和子类是应该引起自动回滚的。 默认的行为和EJB一样,但你可以指定经检查异常和未经检查异常一样回滚。这在最小化可编程回滚需要上有很大好处,而这可编程回滚也建立了对Spring 事务 API的依赖(就像EJB可编程回滚建立对EJBContext的依赖一样)。
底层Spring事务抽象支持保存点(如果底层事务基本结构支持的话),所以Spring的声明性事务管理能支持嵌套事务,和EJB CMT特有的传播模式(Spring支持和EJB一样的语义)。因而,举例来说,如果你在Oracle上执行JDBC操作,你能通过Spring使用声明 性嵌套事务。
事务管理没有绑定JTA。正如前面解释的,Spring事务管理能和不同事务策略合作。你还可以使用Spring AOP实现应用程序特有方面。是否要这样做取决于你对AOP概念的理解程度,而不是Spring的能力,但这会是很有用的。我们见到的成功例子包括:
在安全检查要求的复杂度超过了标准J2EE 安全基础结构的地方自定义安全拦截器。(当然,在开始你自己的安全结构前,你应该看看Acegi Security for Spring,一个强大、灵活的用AOP和Spring整合的安全框架,这也反映了Spring架构上的方法。)
在开发中使用调式和记录方面。
实现在一个地方使用一致的异常处理策略的方面。
发送邮件给管理员或用户,警告不正常情况的拦截器。
应用程序特有方面能成为消除许多方法对样板代码需要的有效途径。
Spring AOP透明地与Spring BeanFactory概念集成。代码从一个Spring的BeanFactory中获得一个对象的时候不用知道它是不是被拦截。和任何对象一样,契约是在对象实现的接口上定义的。
下面的XML片断描述了如何定义一个AOP代理:
<bean id="myTest" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.springframework.beans.ITestBean</value>
</property>
<property name="interceptorNames">
<list>
<value>txInterceptor</value>
<value>target</value>
</list>
</property>
</bean>
注意,尽管引用的或BeanFactory的getBean()方法返回的bean类型依赖于代理接口,bean定义里的类总是AOP框架的 ProxyFactoryBean。(支持多代理方法。) ProxyFactoryBean的“interceptorNames”属性接受一个String列表。(必须用bean名称而不是bean引用,因为 如果代理是一个原型那么新的有状态拦截器需要被创建。)列表里的名字可以是拦截器或者切入点(拦截器和何时该被应用的信息)。上面列表里的 “target”值自动建立一个“调用拦截器”封装目标对象。它是工厂里的一个实现了代理接口的bean的名称。例子里的myTestbean能像工厂里 的其他bean那样被使用。例如其他对象可以通过<ref>元素引用它,并且这些引用可以用Spring IoC设置。
有很多方法能更简单地建立代理,如果你不需要AOP框架的全部功能(例如不用XML而用Java 5.0的注释来驱动事务性代理,或者用一段简单的XML实现对一个Spring工厂里的许多bean应用一致的代理策略)。
还可以不用BeanFactory而用编程方法来构建AOP代理,虽然这种方法很少用:
TestBean target = new TestBean();
DebugInterceptor di = new DebugInterceptor();
MyInterceptor mi = new MyInterceptor();
ProxyFactory factory = new ProxyFactory(target);
factory.addInterceptor(0, di);
factory.addInterceptor(1, mi);
// 一个用来封装目标的“调用拦截器”被自动添加
ITestBean tb = (ITestBean) factory.getProxy();
我们相信最好把应用程序的装配从Java代码里拿出来,AOP也不例外。
使用AOP作为EJB(版本2或以上版本)的替代物来进行企业服务是的重要性正在加大。Spring很成功地展现了这个主张的价值。
MVC web框架
Spring包括一个强大且高度可配置的MVC web框架。
Spring的MVC模型尽管不是源自Struts,但和Struts的很相似。一个Spring的Controller和Struts 的Action很像,它们都是多线程服务对象,只有一个实例代表所有客户端执行。但是,我们相信Spring MVC比起Struts有一些显著的优点。例如:
* Spring在控制器、JavaBean模型和视图间提供清晰的划分。
* Spring的MVC非常灵活。不像Struts那样强迫你的Action和Form对象有具体的继承(你只能用Java 的继承),Spring的MVC完全基于接口。此外,几乎Spring MVC框架的每个部分都能通过插入你自己的接口来配置。当然,我们也提供了简单的类作为一个可选的实现。
* Spring,像WebWork一样,提供拦截器和控制器,这使得提取处理多个请求的公共行为变得容易了。
* Spring MVC是真正视图无关的。如果你不愿意你不会被强制使用JSP;你能用Velocity、XLST或其他视图技术。如果你想用自定义的视图机制(比如你自己的模板语言),你可以轻松实现Spring的View接口来整合它。
* Spring的Controller 和其他对象一样是通过IoC来配置的。这让它们易于测试,和其他Spring管理的对象漂亮地集成在一起。
* 因为没有强迫使用具体的继承和显式地依赖于调度器Servlet的控制器,Spring MVC的web 层比起Struts 的web 层更易于测试。
* Web 层变成了业务对象层上的薄薄一层。这鼓励了好的习惯。Struts和其他专门的web框架让你自己实现你的业务对象;Spring为你的应用程序提供了一个完整的框架。
和Struts 1.1 及更高版本一样,你能根据你的需要在Spring MVC应用程序中拥有多个调度器Servlet。
下面的范例演示了一个简单的Spring Controller 如何访问同一个应用程序上下文中定一个业务对象。这个控制器在它的handleRequest()方法中执行了一个Google查询:
public class GoogleSearchController implements Controller {
private IGoogleSearchPort google;
private String googleKey;
public void setGoogle(IGoogleSearchPort google) {
this.google = google;
}
public void setGoogleKey(String googleKey) {
this.googleKey = googleKey;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String query = request.getParameter("query");
GoogleSearchResult result =
// Google属性定义忽略
// 使用Google业务对象
google.doGoogleSearch(this.googleKey, query,start, maxResults, filter, restrict,
safeSearch, lr, ie, oe);
return new ModelAndView("googleResults", "result", result);
}
}
这个代码的原型IGoogleSearchPort是一个GLUE web 服务代理,由一个Spring FactoryBean返回。然而,Spring IoC将这个控制器从底层web 服务库中隔离出来。也可以用一个简单的Java 对象、测试桩、模拟对象或者用像下面要讨论的EJB代理来实现这个接口。这个控制器没有包含资源查找;除了支持它的web 交互的必要代码外没有别的东西了。
Spring也提供了对数据绑定、表单、向导和更复杂的工作流的支持。马上要发表的这个系列文章中的一篇文章会详细讨论Spring MVC。
如果你的需求真的很复杂,你应该考虑Spring Web Flow,一个提供比传统web MVC框架更高层次web 工作流抽象的强大的框架,它的架构师Keith Donald会在最近的TSS文章上讨论它的。
一个不错的Spring MVC框架的入门材料是Thomas Risberg的Spring MVC指南(http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC- step-by-step.html)。也可以看《Web MVC with the Spring Framework》 (http://www.springframework.org/docs/web_mvc.html)。
如果对你喜欢的MVC框架情有独钟,Spring的分层结构允许你使用MVC层以外的Spring的其他部分。我们有把Spring作为中间层管理和数据访问但在web 层使用Struts、WebWork、Tapestry或JSF的用户。
实现EJB
如果你选择使用EJB,Spring可以令你从EJB实现和客户端对EJB的访问中获益。将业务逻辑重构进EJB外观后的POJO 中是一个被广泛认同的最佳实践。(和其它实践相比,这使得对业务逻辑的单元测试更加容易,因为EJB极其依赖容器且很难独立测试。)Spring为会话 bean和消息驱动bean提供了好用的超类,通过自动加载包含在EJB Jar 文件中的基于一个XML文档的BeanFactory来方便实现这点。
这意味着一个无状态的会话EJB可以这样来获得并使用一个协作者:
import org.springframework.ejb.support.AbstractStatelessSessionBean;
public class MyEJB extends AbstractStatelessSessionBean implements MyBusinessInterface {
private MyPOJO myPOJO;
protected void onEjbCreate() {
this.myPOJO = getBeanFactory().getBean("myPOJO");
}
public void myBusinessMethod() {
this.myPOJO.invokeMethod();
}
}
假定MyPOJO是一个接口,实现类(和任何它要求的诸如原始属性和进一步的协作者之类的配置)隐藏在XML bean工厂的定义中。
我们通过一个在标准ejb-jar.xml部署描述符中的名为ejb/BeanFactoryPath的环境变量定义告诉Spring到什么地方去加载XML文档,就像这样:
<session>
<ejb-name>myComponent</ejb-name>
<local-home>com.test.ejb.myEjbBeanLocalHome</local-home>
<local>com.mycom.MyComponentLocal</local>
<ejb-class>com.mycom.MyComponentEJB</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<env-entry>
<env-entry-name>ejb/BeanFactoryPath</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>/myComponent-ejb-beans.xml</env-entry-value>
</env-entry>
</session>
myComponent-ejb-beans.xml文件会被从classpath中加载:在本例中,是在EJB Jar文件的根部。每个EJB能指定它自己的XML文件,所以这个机制能在每个EJB Jar文件中多次使用。
Spring超类实现了EJB的setSessionContext()和ejbCreate()一类的生命周期方法,让应用程序开发者只要选择性地实现Spring的onEjbCreate()方法就可以了。
当EJB 3.0蓝图公布后,我们会对使用Spring IoC容器在那个环境中提供更丰富的依赖注入语义提供支持。我们也同样会将JSR-220 实体/关系映射API作为一个被支持的数据访问API整合进来。
使用EJB
Spring除了让实现EJB更容易外,还让它的使用变得简单了。许多EJB应用程序使用服务定位器和业务代理模式。这比在客户代码中遍布JNDI查找好得多,但它们的一般实现存在明显的缺陷。例如:
* 依赖于服务定位器或业务代理单例的典型EJB调用代码很难测试。
* 在没有一个业务代理的服务定位器模式中,应用程序代码仍然要调用一个EJB home的create()方法,并处理可能的异常。因而它仍然绑定了EJB API和EJB编程模型的复杂性。
* 实现业务代理模式通常会导致明显的代码重复,我们不得不写很多方法来简单地调用同一个EJB方法。
为这些和其他一些原因,传统的EJB访问(就像Sun Adventure Builder 和OTN J2EE 虚拟大卖场里演示的)会降低生产力并带来显著的复杂度。
Spring通过引入少量代码业务代理在这方面先行一步。有了Spring你不再需要在手工编写的业务代理中写另外的服务定位器、JNDI查找或重复方法除非你加入了真正的价值。
举例来说,想象我们有一个使用本地EJB的web控制器。我们会遵循最佳实践并使用EJB业务方法接口模式,所以EJB本地接口要扩展一个非 EJB 特有的业务方法接口。(这样做的一个主要原因是确保本地接口和bean实现类的自动同步。)我们称这个业务方法接口为MyComponent。当然我们也 需要实现本地home接口并提供一个实现了SessionBean和MyComponent 业务方法接口的bean实现类。
有了Spring EJB访问,我们为挂接我们的web 层控制器和EJB实现所需的唯一的Java编码就是暴露控制器上的一个MyComponent 的setter 方法。这会像一个实例变量那样保存引用:
private MyComponent myComponent;
public void setMyComponent(MyComponent myComponent) {
this.myComponent = myComponent;
}
随后我们就能在任何业务方法中使用这个实例变量了。
Spring通过XML bean定义项来自动完成剩下的工作。
LocalStatelessSessionProxyFactoryBean是一个能被任何EJB使用的通用工厂bean。它创建的对象能自动被Spring转换为MyComponent 类型。
<bean id="myComponent" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="myComponent" />
<property name="businessInterface" value="com.mycom.MyComponent" />
</bean>
<bean id="myController" class = "com.mycom.myController">
<property name="myComponent" ref="myComponent" />
</bean>
幕后发生了很多魔法般的事情,出于Spring AOP框架的谦虚,你没有被强迫使用AOP概念来享受这结果。“myComponent”bean定义建立了一个实现了业务方法接口的EJB代理。EJB 本地home在启动时被缓存,所以一般只需要一次JNDI查找。(也有对失败时重试的支持,所以一次EJB重部署不会导致客户端失败。)EJB每次被调用 时,代理调用本地EJB的create()方法并调用EJB的相应业务方法。
myController bean定义将控制类的myController 属性设置到这个代理。
这个EJB访问机制对应用程序代码进行了大量简化:
* Web层代码没有了对使用EJB的依赖。如果我们想要用一个POJO、模拟对象或其他测试桩来代替这个EJB引用,我们可以简单地改变myComponent bean定义而不用修改一行Java 代码。
* 我们没有必要写一行JNDI查找代码或其他EJB组装代码来作为我们应用程序的一部分。
我们还能通过相似的 org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean 工厂bean将相同的方法运用于远程EJB。但是,要把一个远程EJB业务方法接口的RemoteExceptions 隐藏起来却是很棘手的。(如果你希望提供一个匹配EJB远程接口但方法签名中没有“throws RemoteException”语句的客户端服务接口,Spring确实能让你办到这点。)
测试
正如你所注意到的,我和其他Spring的开发者都是全面单元测试的忠实拥护者。我们相信框架经过彻底的单元测试是很重要的,而且框架设计的一个主要目的应该是使构建于框架之上的应用程序应该是易于进行单元测试的。
Spring自己有一个出色的单元测试包。我们发现这个项目的测试优先带来的好处是实实在在的。例如,它使得一个国际化分布式开发团队工作极富效率,而且用户们评论CVS快照往往很稳定,用起来很安全。
我们相信构建在Spring上的应用程序测试很方便,有以下原因:
* IoC易于进行单元测试。
* 应用程序不包含那些一般很难测试的直接使用例如JNDI之类的J2EE服务的代码。
* Spring的bean工厂或上下文能在容器外建立。
在容器外建立一个Spring的bean工厂为开发过程提供了有趣的选择权。在几个使用Spring的web 应用程序项目中,工作始于定义业务接口和在一个web 容器外进行它们的实现的集成测试。只有在业务功能彻底完成后,再在上面加薄薄一层提供web接口。从Spring 1.1 开始,Spring提供了对在部署环紧外进行集成测试的强大且独特的支持。这并不是有意要作为单元测试或针对部署环境的测试的代替品。然而,这可以显著提 高生产力。
org.springframework.test包提供了使用一个Spring容器而不用依赖于一个应用服务器或其他部署环境进行集成测 试的很有价值的超类。这样的测试可以在Junit里运行(甚至是在一个IDE 里)而不用特殊的部署步骤。他们运行起来会比单元测试稍慢,但比Cactus 测试或靠部署到一个应用服务器的测试要快得多。通常很可能在几秒钟而不是几分钟或几小时内要运行好几百个针对一个开发数据库的测试(一般不是一个嵌入式数 据库,而是生产中用的数据库产品)。这样的测试能快速检验你Spring上下文和使用JDBC或ORM工具的数据访问的正确装配,比如SQL语句的正确 性。举例来说,你可以测试你的DAO 实现类。
org.springframework.test包中实现的功能包括:
* 通过依赖注入实现JUnit测试用例移植的能力。这使测试时重用Spring XML配置和消除针对测试的自定义设置代码成为可能。
* 在测试用例之间缓存容器配置的能力,这在用到那些比如JDBC连接池或Hibernate的SessionFactory之类的初始化很慢的资源的地方大大提高了的性能。
* 默认情况下为每个测试方法建立事务并在测试结束时回滚的基础结构。这将允许测试进行各种各样的数据存取而不用担心影响其它测试的环境。从我在几个复杂项目中使用这一功能的的经验看来,用这样一个基于回滚的方法带来的生产力和速度的提升是很明显的。
谁在使用Spring?
有很多应用程序产品使用Spring。用户包括投资和零售的银行组织、知名互联网公司、全球性顾问公司、学院机构、政府机关、防卫设备承包商、几家航空公司和科研组织(包括CERN)。
很多用户使用全部的Spring,但有些使用单独的组件。例如,一些用户从使用我们的JDBC或其它数据存取功能开始。
发展历程
自从这篇文章的第一版在2003年10 月发表以来,Spring经历了1.0 最终发布版(2004年3月)到1.1 版(2004年9 月)到1.2 最终版(2005年5月)的过程。我们相信这样一个哲学“早发布,勤发布”,所以维护性发布和小的改进一般4到6 周发布一次。
从那时起的改进包括:
* 引入一个支持包括RMI和多种web 服务协议在内的多协议远程框架。
* 支持方法注入和其他的IoC容器增强,例如管理从静态或实例工厂方法的调用中获得的对象的能力。
* 集成更多数据访问技术,在最近的1.2版中包括TopLink、 Hibernate 版本3。
* 支持用Java 5.0注释配置的声明性事务管理,不需要用XML元数据来识别事务性方法(1.2)。
* 支持Spring所管理对象的JMX 管理(1.2)。
* 集成了Jasper 报告、Quartz计划和AspectJ。
* 将JSF作为一种web 层技术集成进来。
我们打算继续快速的革新和增强。下一个主要版本会是1.3。计划中的增强包括:
* XML配置增强(计划在1.3 版内),这将允许用自定义XML标签通过定义一个或多个单一的、合法的标签来扩展基本的Spring配置格式。这不仅可能显著简化典型配置并减少配置错误,还会成为基于Spring的第三方产品的开发者的理想选择。
* 把Spring Web Flow集成到Spring的核心中(计划在1.3 版内)。
* 支持运行时应用程序动态重配置。
* 支持用Java 外的语言编写的应用程序对象,比如Groovy、Jython或其他运行于Java 平台上的脚本语言。这些对象能得益于Spring IoC容器的完整服务和在脚本改变时动态重新装载而不影响由IoC容器引用到它们的对象。
作为一个敏捷项目,Spring主要由用户需求驱动。所以我们不会开发没有任用的功能,我们也会仔细倾听来自我们用户社群的声音。
Spring Modules 是一个由Interface 21 的Rob Harrop领导的相关项目,它将Spring平台扩展到那些Spring核心没有必要完全支持的,但仍然对很多用户有价值的领域。这个项目也作为一个孵 化器,所以其中的一些功能会最终集成到Spring核心中。Spring Modules 目前包括如下领域:与Lucene搜索引擎和OSWorkflow工作流引擎的集成、一个基于AOP的声明性缓存解决方案、与Commons Validator 框架的集成。
有趣的是,尽管这篇文章的第一版发表在Spring 1.0 最终版发布的六个月前,几乎所有的示范代码和配置仍然能不经改动地在今天的1.2 版上运行。我们为自己在向下兼容方面的骄人记录感到自豪。这也展现了依赖注入和AOP实现非侵入性API的能力,还表现了我们尽心尽力为社区提供一个能够 运行重大应用程序的稳定框架的严谨。
总结
Spring是一个解决了许多常见J2EE 问题的强大框架。许多Spring的功能也可以被运用于很多超越经典J2EE 的Java环境中。
Spring提供了一种以一致方法管理业务对象的方法,并鼓励好的编程习惯,例如针对接口而不是类编程。Spring的架构基础是一个使用 JavaBean属性的控制反转容器。可是,这只是Spring全貌的一部分:Spring在将IoC容器作为所有架构层的完整解决方案的基本构建块方面 是独一无二的。
Spring提供了一个独特的数据访问抽象,包括一个大大改善生产力并降低错误可能的简单而有效的JDBC框架。Spring的数据访问架构还集成了TopLink、Hibernate、JDO 和其他实体/关系映射解决方案。
Spring提供了唯一的事务管理抽象,这能够在类似JTA或JDBC这样的不同底层事务技术上使用一致的编程模型。
Spring提供了一个用标准Java 写的AOP框架,它提供了声明性事务管理和其他用于POJO 的企业服务或者(如果你希望)也能用于实现你自己的自定义方面。这个框架强大到足以使很多应用程序在享受传统的EJB相关的关键服务的同时放弃EJB的复杂性。
Spring还提供了一个可整合到整个IoC容器中的强大且灵活的MVC web框架。
更多信息
需要更多的关于Spring的信息请参阅:
* Interface21 提供的一个核心Spring培训课程——http://www.springframework.com/training。
* 《Expert One-on-One J2EE Design and Development》(Rod Johnson, Wrox, 2002)。尽管Spring在此书出版后有了很明显的改进,但它仍然是理解Spring动机的好地方。
* 《J2EE without EJB》(Rod Johnson与Juergen Hoeller 合著Wrox, 2004)。《Expert One-on-One J2EE Design and Development》的后续作品,讨论Spring和它的轻量级容器架构的基本原理。
* 《Spring参考手册》。Spring 1.2 的打印版本又超过240页。Spring还带了几个展示最佳实践并可用作你自己的应用程序模板的范例。
* 《Pro Spring》:由核心开发者Rob Harrop深入讨论Spring。
* 《Spring: A Developer’s Notebook》:由Bruce Tate 和Justin Gehtland所著的入门。
* Spring框架主页:http://www.springframework.org/,这里包括Javadoc和几个教程。
* Sourceforge上的论坛和下载。
* Spring开发者邮件列表。
我们为对待论坛和邮件列表中问题的认真态度和出色的回复率感到自豪。我们欢迎您早日加入我们的社区。
关于作者
Rod Johnson拥有差不多十年作为Java 开发者和架构师的经验,并从J2EE 平台出现后就在其上进行开发。他是畅销书《Expert One-on-One J2EE Design and Development》(Wrox,2002)和《J2EE without EJB》(Wrox, 2004与Juergen Hoeller 合著)的作者,也参与过其它J2EE著作的编写。Rod参与了两个Java 标准委员会并经常在大会发言。他是Interface21的CEO,这是一家国际化咨询公司,领导Spring框架开发,提供Spring框架和J2EE 方面的专业服务。
本文转自
http://last999.com/1/read.php?fid=4&tid=20&fpage=1