POJO应用框架:Spring Vs. EJB3.0
作者Michael Juntao Yuan 翻译 杨庆跃
爱因斯坦曾说过:事情应尽可能的简单,但不是为了简单而简单。实际上,追求科学真理就是不断简化理论的基本假设以使我们能够处理实际问题。企业软件开发中也遵循同样的道理。
简化企业软件开发的一个关键点是为开发者提供一个隔离复杂性(如事务、安全、持久化等)的应用框架。一个设计良好的框架能够促进代码重用、提高开发者生产率、最终提高软件品质。然而,J2EE体系中的EJB2.1框架被普遍认为设计不良并且过于复杂。由于不满EJB2.1框架,Java开发者尝试了多种新的框架,下面两种得到了更多关注和正面评价,被认为是未来企业级Java应用的首选框架:
Spring框架:Spring框架是一个大受欢迎但非标准的开源框架。Spring框架的体系基于依赖注入(DI)设计模式。Spring可以独立运行,也可以与已有的应用服务器结合,Spring大量依赖于XML配置文件。
EJB3.0框架:EJB3.0框架是由JCA(Java Community Process)制定的标准框架,被所有的主要J2EE厂商支持。已有开源和商用服务器支持EJB3.0,EJB3.0大量使用标注。
这两种框架遵循同样的设计哲学:他们的目标是为普通Java对象(POJO)提供松耦合的中间件服务。框架通过两种方式将应用服务与运行中的POJO关联起来:拦截POJO的执行上下文或将服务对象注入POJO中。POJO不关心与服务的关联并且很少依赖与框架。因此,开发者可以利用POJO开发业务逻辑和进行单元测试而不必考虑框架。另外,由于POJO不需要继承框架的类或实现框架的接口,开发者具有灵活性。
尽管使用了同样的设计哲学,这两个框架使用两种不同的方法为POJO提供服务。大量书籍和文章讨论了Spring或EJB3.0与EJB2.1的差异,但很少讨论Spring与EJB3.0的差异。本文将给出Spring框架与EJB3.0框架的关键区别,并且讨论他们的优劣。本文讨论的内容也适用于其他遵循“松耦合POJO”设计理念的企业中间件框架。希望本文能帮助你选择自己所需的最好框架。
厂商独立性
开发者选择Java平台的一个最重要原因是该平台是否具有厂商独立性。EJB3.0是为厂商独立性而设计的开放标准。EJB3.0规范由企业Java领域最主要的开源及商业厂商开发并提供支持。
EJB3.0框架使开发者与应用服务器的实现相独立。例如JBoss的EJB3.0实现基于Hibernate、Oracle的EJB3.0实现基于TopLink,开发者不必学习Hibernate和TopLink的API规范仍然可以使他们的应用运行于JBoss或Oracle应用服务器之上。
尽管可以在任何的应用服务器之上使用Spring框架,但Spring应用将被限定于Spring本身或与Spring绑定的特定服务。
服务集成
从较高层次看,Spring框架位于应用服务器和类库之上。服务集成代码(如数据访问模板或助手类)驻留在框架中并暴露于开发者。相反,EJB3.0框架与应用服务器紧密集成,服务集成代码被封装成标准接口。
这样,EJB3.0厂商尽可以优化全局性能和用户体验。比如,JBoss的EJB3.0实现中,当使用EntityManager持久化一个POJO实体时,底层的Hibernate session事务自动调用JTA事务,并且与JTA事务一同提交。使用简单的@PersistenceContext标注,就可以为有状态会话Bean中的应用程序事务关联上EntityManager对象及其底层的Hibernate事务。应用程序事务可以跨越同一会话中的多个线程,这在事务性的Web应用中是很有用的,例如多页面的购物车。
关于EJB3.0服务集成的另外一个例子是对集群的支持。如果将EJB3.0应用部署于服务器集群,所有的故障恢复、负载均衡、分布式缓存以及状态复制等服务都可以使用。底层的集群服务对于EJB3.0开发者来说完全透明。
在Spring中,优化框架与服务之间的交互相对困难。比如,为了使用Spring的声明性事务管理Hibernate事务,不得不在XML文件中显式配置Spring的TransactionManager和Hibernate的SessionFactory对象。Spring应用开发者必须显式管理跨越多个HTTP请求的事务。另外,为Spring应用配置集群服务是相当复杂的。
服务组装的灵活性
既然Spring中的服务集成代码作为编程接口的一部分被暴露出来,应用程序开发者也就具有了组装服务的灵活性。这个特性使你可以组装一个轻量级的应用服务器。一个常用的Spring框架是将Tomcat与Hibernate整合起来支持基于数据库的web应用,在这种情况下,Spring本身提供事务服务Hibernate提供持久化服务,这就相当于创建了一个小型的应用服务器。
EJB3.0应用服务器不具备上述灵活性,使用者得到的是一套打包好的服务及特性(尽管有些特性可以添加或移除)。
XML Vs. 标注
从应用程序开发者的角度出发,Spring编程接口主要基于XML配置文件而EJB3.0大量使用的是Java标注。XML文件可以表述复杂的关系,但是冗长并且不健壮。标注简单精确但很难表述复杂结构或层次结构。
Spring和EJB3.0对于XML或标注的选择基于两个框架的体系结构:标注只能表示较少的配置信息,因此像EJB3.0这样事先集成了大量特性及服务的框架适合使用标注。而Spring作为一个通用的依赖注入框架不适合大量使用标注。
当然,随着Spring与EJB3.0两种框架的互相学习与渗透,他们都支持XML和标注。例如在EJB3.0中XML配置文件可以使用并且将覆盖程序代码中的标注。
声明性服务
Spring和EJB3.0都可以将运行时服务(如事务、安全、日志、消息等)连接到应用程序。这些服务不直接与应用程序的业务逻辑关联,他们不能被应用程序直接操作,而是由容器(Spring和EJB3.0)在运行时应用到应用程序上的。开发者或管理员通过配置容器决定何时以及如何应用这些服务。
依赖注入
中间件容易的的一个关键优势是允许开发者构建松耦合的应用。服务使用者只需知道服务接口,容器负责调用具体实现来实例化服务对象并且提供给调用者。这样,容器在不更改接口以客户端代码的前提下切换不同的服务实现。
依赖注入模式是实现松耦合应用程序的最好方式之一,他比传统方式易于使用且更加优雅。
使用依赖注入时,框架作为对象工厂依据配置信息建造服务对象并且在运行时把这些服务对象注入到应用程序的POJO中。在应用程序开发者看来,POJO对象在需要特定服务时会自动获得这些服务。
Spring和EJB3.0都为依赖注入提供了广泛的支持,但他们之间有一些显著的不同。Spring支持通用的、复杂的、基于XML配置文件的依赖注入API;EJB3.0中可以通过标注注入大部分常用服务对象(如EJB和上下文对象)以及任何JNDI对象。
EJB3.0的依赖注入标注非常简明并且易于使用。@Resource标注用于注入大部分常用服务对象以及JNDI对象,下面的例子演示如何通过JNDI将服务器的默认DataSource对象注入到POJO的变量中。DefaultDS是DataSource的JNDI名称。myDb变量在首次使用前自动分配合适的值。
public class FooDao {
@Resource (name="DefaultDS")
DataSource myDb;
// Use myDb to get JDBC connection to the database
}
在EJB3.0中,@Resource标注除了用于变量的注入,还可以用于setter方法的注入。下面的例子用于注入session context对象。
@Resource
public void setSessionContext (SessionContext ctx) {
sessionCtx = ctx;
}
对于更加复杂的服务对象,还可以使用其他注入标注。@EJB标注用语注入EJB对象,@PersistenceContext标注用语注入EntityManager对象。
EJB 3.0规范定义了如何注入服务器端资源,但它不支持应用程序中的POJO之间互相注入。
在Spring中,需要在POJO中为服务对象定义一个Setter方法(或构造器),下面例子演示了一个使用Hibernate session factory的POJO:
public class FooDao {
HibernateTemplate hibernateTemplate;
public void setHibernateTemplate (HibernateTemplate ht) {
hibernateTemplate = ht;
}
// Use hibernateTemplate to access data via Hibernate
public Foo getFoo (Integer id) {
return (Foo) hibernateTemplate.load (Foo.class, id);
}
}
接下来,需要通过XML配置告诉容器如何在运行时得到服务对象并使之与POJO关联。下面例子是XML设置,data soure与Hibernate session factory相关联,Hibernate session factory与Hibernate template object关联,最后Hibernate template object与POJO关联。
<bean id="dataSource"
class="org.springframework
.jndi.JndiObjectFactoryBean">
<property name="jndiname">
<value>java:comp/env/jdbc/MyDataSource</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm
.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="hibernateTemplate"
class="org.springframework.orm
.hibernate.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="fooDao" class="FooDao">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<!-- The hibernateTemplate can be injected
into more DAO objects -->
尽管Spring中基于XML的依赖注入配置比较复杂,但其功能强大。可以注入任何POJO,包括将应用程序中的POJO互相注入。
结论
Spring和EJB3.0框架都致力于为松耦合的POJO提供企业级服务,但他们使用不同的方式达到目标。两个框架都将依赖注入作为重点。
EJB3.0是基于标准的框架,大量使用标注、应用服务器集成度高、厂商独立、开发效率高。Spring大量使用XML配置文件,允许开发者构建灵活的应用程序,可同时整合多个应用服务提供者。
参考:
POJO Application Frameworks: Spring Vs. EJB 3.0
原文地址:
http://www.onjava.com/pub/a/onjava/2005/06/29/spring-ejb3.html