Spring 与 EJB 比较
在开源领域,Spring开源框架已成为企业应用开发中使用最多的开源框架。Spring框架的优秀不但表现在其优雅的底层设计、使用方便快捷、面向工作实践、超强粘合能力等方面,另外一个不可忽视的方面是Spring拥有一个世界一流且活跃的技术开发团队。随着Spring的流行,于是,大家开始对比Spring与另一种流行的框架组件EJB。目前网上关于Spring和EJB的争论颇多。很多架构师认为Spring会替代EJB,也有架构师认为Spring是开源的东西,是不成熟且无法和商业解决方案媲美的框架,因此,在Spring与EJB的对比过程,有若干对Spring的片面认识。而本文希望通过对这些误区的分析,给Spring一个原本的认识与理解。
一、 前言
EJB 3.0框架是JCP定义的并且被所有主流J2EE提供商支持的标准框架。EJB 3.0规范的发布版本目前已经有开源的和商业的实现,如JBOSS和ORACLE。EJB 3.0大量使用了JAVA注解(Java annotations,是JDK1.5提供的新功能)。
EJB最初的设计思想考虑的是为分布式的应用服务的,分布式是针对大型应用构造的跨平台的协作计算,EJB最初的目的就是为这种计算服务的。但是软件发展到目前为止,大多数应用不需要采用分布式的解决方案,因此用EJB显得太臃肿了。Spring的出现恰恰为了解决这个问题。举个例子来说,EJB就是导弹,专门设计为打高空飞机。但是现在发现飞机不多。于是将它用来对付步兵,这个实在太糟糕了。这个时候有人发明了狙击步枪(Spring),发现对付步兵太好用了。
Spring框架是一个广受欢迎的但是非标准的开源框架。它主要由Interface21公司开发和控制。Spring框架的体系结构是基于注射依赖(DI)模式。Spring框架使用了大量的XML配置文件,它可以独立应用,或者在现有的应用服务器上工作。
这两个框架有着一个共同的核心设计理念:它们的目标是为松耦合的POJO类提供中间件服务。框架通过在运行时截取执行环境,或将服务对象注射给POJO类的方式,将应用服务和POJO类“连接”起来。POJO类本身并不关注如何“连接”,而且也很少依赖于框架。
这样,开发者可以将注意力集中在业务逻辑上,可以对他们的POJO类进行与框架无关的单元测试。并且,由于POJO类不需要继承框架的类或实现框架提供的接口,开发者可以在更加灵活性的基础上构建继承体系,和搭建应用。
尽管有着共同的理念,但这两个框架采取了不同的方式来提供POJO服务。由于已经出了大量的比较Spring和EJB3.0的文章。但发现,随着Spring的发展,其中对Spring的认识难免有失偏颇的地方,因此,本文将考察它们之间几个关键的认识上的误区进行分析。
二、 Spring的XML VS EJB的注释
从应用开发者的角度来看,Spring的编程接口主要基于XML配置文件,而EJB 3.0则大量的使用了JAVA注解。XML文件可以表达复杂的关系,但是它们更加冗长而且不健壮。注解的方式很简单明了,但是很难去表达复杂的或者继承性的结构。
由于EJB 3.0和Spring相互学习了很多特性,所以,它们都在某种层次上支持XML和注释。例如,EJB 3.0中可以应用XML配置文件作为一个选择性的机制,用来改变注释的默认行为。注释也可以用来配置一些Spring服务。
在Spring 2中,采用了@PersistenceContext注释的方式来整合JPA(Java 持久性 API),从而实现了EntityManager对象的注入,同时通过@Transactional实现声明式事务管理。Spring 2利用注释来支持AspectJ(一种面向切面的框架,它扩展了Java语言),如@Aspect、@Before、@After、@Around等等注释。Spring 2中,使用@Repository注释,可以直接操作JPA或Hibernate API,而不需要使用Spring模板。
Spring的元数据模型非常的灵活的,因此在Spring中可以快速的建立起基于注释的元数据模型。而从最近发布的Spring 2.5看来,事实上也是这样。Spring 2.5全面支持JSR-250注释(JSR-250技术规范主要涉及J2SE和J2EE平台上开发普通语义概念的标注,提供一种独立技术),如@Resource、@PostConstruct、@PreDestroy、@WebServiceRef及@EJB。
特别值得一提的是@Resource,最早是为了在EJB 3使用Spring的依赖注入功能。但如今其功能已经扩展了,不但支持像JNDI的查找,还可以注入任何的Spring管理对象。这就把Spring的优势(Spring支持任何对象的依赖注入)和EJB的优势(使用注释代替XML)充分的结合起来了。
Spring 2.5中提供了完整的基于注释的依赖注入模型,如@Autowired及@Qualifier注释。用户通过@Autowired注解来对Bean的属性变量、属性Setter方法以及构造函数进行标注,配合AutowiredAnnotationBeanPostProcessor完成对Bean的自动装配。
@Component注释为用户自定义原形进行了扩展。Spring可以自动的检测到注释的组件。例如下面的代码
<context:component-scan base-package="org.springframework.samples.petclinic.web" />
Web控制器不需要额外的XML配置,因为使用了基于注释的依赖注入以及基于注释的请求映射。Web控制器通过如下的代码进行管理:
@Controller
public class ClinicController {
private final Clinic clinic;
@Autowired
public ClinicController(Clinic clinic) {
this.clinic = clinic;
}
…
三、 EJB使用JPA,Spring使用Hibernate
很多人都认为,在EJB 3中通过使用@PersistenceContext注释提供的entityManager对象来获得JPA的数据访问,而在Spring中,通过对SessionFactory对象的注入获得Hibernate数据访问。从而自然而然地认中,EJB使用JPA来操作数据对象,而Spring使用Hibernate来操作数据对象。
作为EJB3.0的一部分,JPA是一个好东西。其简单的配置方式及强大的默认配置支持,使其可以轻松自由的存在于轻量与重量之间。事实上,Spring同样支持使用JPA来操作数据对象(例如JpaTemplate),此外Spring提供了@PersistenceContext注释来支持JPA。在轻量级 Spring 框架的第二代中添加了一大批特性,即使是新的服务器应用程序开发人员也能够轻松上手。其关键增强之一就是 Spring 2 与JPA的集成。@PersistenceContext注释的使用示例如下面的代码所示:
package quickstart.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.transaction.annotation.Transactional;
import quickstart.model.Person;
@Transactional
public class PersonServiceImpl implements PersonService {
private EntityManager em;
@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
@SuppressWarnings("unchecked")
public List<Person> findAll() {
Query query = getEntityManager().createQuery("select p FROM Person p");
return query.getResultList();
}
public void save(Person person) {
if (person.getId() == null) {
// new
em.persist(person);
} else {
// update
em.merge(person);
}
}
public void remove(int id) {
Person person = find(id);
if (person != null) {
em.remove(person);
}
}
private EntityManager getEntityManager() {
return em;
}
public Person find(int id) {
return em.find(Person.class, id);
}
}
@PersistenceContext会让Spring在实例化的时候给服务注入一个EntityManager。@PersistenceContext注解可以放在实例变量,或者setter方法前面。如果一个类被注解为@Transactional,Spring将会确保类的方法在运行在一个事务中。
当 Spring JPA 应用程序在 Tomcat 上运行时,要让 JPA 支持正常工作,需要在类装入期间进行字节码“连接”。来自 Tomcat 的标准类装入器不支持这个,需要用特定于 Spring 的类装入器实现这个功能。要把这个特定于 Spring 的类装入器安装到 Tomcat 服务器,首先要把 spring-tomcat-weaver.jar 拷贝到 Tomcat 的 server/lib 子目录。这个目录包含的库属于 Tomcat 服务器私有,可以在 Spring 2 下载的 dist/weaver 目录下找到 spring-tomcat-weaver.jar 库。
四、 提供商无关性
开发者选择JAVA平台的一个最重要的原因就是它的提供厂商无关性。EJB 3.0是一个被设计为对提供商没有依赖性的开放的标准。EJB 3.0规范由企业JAVA社区的主流开源组织和厂商共同编写和支持的。EJB 3.0框架使开发者的应用程序实现可以独立于应用服务器。
比如,JBoss的EJB 3.0的实现是基于Hibernate的,Oracle的EJB 3.0实现是基于TopLink的,但是,在JBoss或者Oracle上跑应用程序,开发者既不需要去学习Hibernate,也不需要学习TopLink提供的独特API。厂商无关性使EJB 3.0框架区别于当前其他任何的POJO中间件框架。
很多人认为,尽管在任何应用服务器都上可以使用Spring框架,但基于Spring的应用仍然被限制于Spring本身,以及在应用中使用到的Spring提供的各种特别服务。但事实上是不是如此呢?大家应该知道,Spring的应用程序中,JtaTransactionManager使用了自动检测机制,不管是MBeans应用服务器还是Tomcat应用服务器。同理,当使用JPA时,Spring自动检测persistence.xml文件,并且创建EntityManagerFactory对象。在上面这些机制中,Spring不管是采用注释(如PersistenceContext、@Transactional、@Resource等等)还是采用XML(如”jee:indi-lookup”等等),都可以像EJB应用一样的与应用服务器提供商无关。
五、 小结
笔者认为,EJB和Spring设计的角度根本不同,就目前来看,还不能说哪一个能完全打倒另外一个。首先EJB最初的设计思想考虑的是为分布式的应用服务的。就因为这个原因,使得开发一个EJB不难,但是开发一个好的EJB却非常难。此外对于中小型的应用项目而言,基本不采用分布式的解决方案,那么为什么要采取一个为分布式设计的方案来解决非分布式的问题呢? Spring就是为了解决这个问题而诞生的。
本文中,笔者希望比较客户的评价Spring相对EJB所具有的一些特性,同时,Spring可以与EJB进行协同的工作,Spring可以应用到EJB应用中去,同样,EJB可以在Spring应用中很好的使用。同时,Spring如今有力的支持注释:@Resource、@PersistenceContext、@PostConstruct、@PreDestroy、@EJB及@WebServiceRef。当然,Spring 中不只是能使用Hibernate这样的ORM框架,同样可以使用JPA。更妙的是,Spring越来越与应用服务器提供商无关了,很容易实现在不同的应用服务中进行移植。