站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2EE 专题spring可以代替EJB吗

spring可以代替EJB吗

摘要: Spring框架虽然很流行但并不是一个标准的开源框架
艾伯特.爱因斯坦曾经说过:“一切都应该尽可能地简单,但是不能更简单。”确实如此,简化一门理论的基本假设,使我们可以专注于真正关键的地方,这正是一直以来对科学真理的追求。企业软件开发同样如此。

提供一个将复杂的事物(例如,事务、安全或持久性)对开发者进行隐藏的应用框架是简化企业软件开发的关键。一个设计良好的框架可以提高代码重用率、开发者的生产力及软件的质量。然而,现有J2EE1.4的EJB2.1框架被普遍认为设计差,且过于复杂。不满于EJB2.1的框架结构,Java开发者尝试了各种各样的中间件服务传递方法。最引人注目的是,以下两个框架引起了开发者极大兴趣并得到了大量正面的反馈。他们以未来企业Java应用所选框架的姿态展现。

Spring框架虽然很流行但并不是一个标准的开源框架。它主要由Interface21 Inc开发和控制。Spring框架结构是基于依赖注入(Dependency Injection (DI))的设计模式。它可以独立或在现有的应用服务器上运行,而且大量地使用了xml配置文件

EJB3.0是由Java Community Process (JCP)制订的标准框架,为所有主要的J2EE厂商支持。JBoss已经提供了试用版EJB3.0标准的开源或商业性质实现。EJB3.0充分利用了Java的注释

这两个框架结构都有一个共同核心设计理念:将中间件服务传递给耦合松散的POJOS (Plain Old Java Objects, 简单洁净Java对象)。这样的框架利用截取执行上下文或在运行时将服务对象注入POJO来把应用服务“缠绕”到POJO。POJO本身并不关心这种“缠绕”,对这种框架结构也没有什么依赖。因此,开发者可专注于业务逻辑和脱离框架的POJO单元测试。除此之外,由于POJO并不须要继承框架的类或实现其接口,开发者能够极其灵活地搭建继承结构和建造应用。

然而,在拥有同一理念的同时,两个框架结构使用不同的方式来传递POJO服务。许多书籍或文章都将Spring 或EJB3.0和EJB2.1做了比较,但是对Spring 和EJB3.0的比较并没有仔细研究过。在本文中,我将对Srping和EJB3.0框架背后的关键不同处进行考察,并讨论其优缺点。本文的观点也适用于其它更少为人知的框架,因为他们都是对“耦合松散的POJO”的设计。希望这篇文章可以帮助你选择适合你需求的最好框架。

厂商无关性
开发者选择Java平台其中最引人注目的理由之一:厂商无关性。EJB3.0正是一套设计为厂商无关的开放性标准。EJB3.0标准为所有企业Java社团里开源或商业性质厂商所开发和支持。它将开发者与应用服务器实现完全隔离。例如,JBoss的 EJB3.0实现基于Hibernate,Oracle的基于TopLink,但是开发者并不须要学习Hibernate- 或TopLink的具体API来使应用可在Jboss或Oracle上运行。厂商无关性使EJB3.0与现今其它POJO中间件框架区别开来。

但是,正如许多EJB3.0评论家迅速所指出的,在本文撰写时EJB3.0标准还没有到达一个最终版本。大概还有一到两年的时间EJB3.0才能广泛地为所有主要J2EE厂商所支持。即使你的应用服务器本身不支持EJB3.0,你仍然可以通过下载安装”内嵌的”EJB3.0产品来运行EJB3.0的应用。例如,JBoss的内嵌EjB3.0是开源产品且可以在任何J2SE5.0兼容的环境运行(例如, 在任何Java服务器上),此产品正处于软件测试阶段。其它厂商不久也将发布自己的内嵌EJB3.0产品,特别是针对标准中关于数据持久性的部分。

另一方面,Spring一直以来都是非标准的技术,在未来可预知的一段时间内这种情况将持续下去。虽然你可以在任何应用服务器上使用Spring框架,Spring应用会被锁入在Spring本身和你选择整合进Spring的具体服务中。

Spring框架是一个开源项目,但同时它有一个XML格式的配置文件和编程接口。当然任何一个非标准的产品都会有这种“锁入”(lock-in)的情况,并不是Spring特有的。但Spring应用的长期生存能力仍然还得托Spring这个项目的福(或者是Interface21公司,它雇佣了大部分Spring核心开发人员)。除此之外,假如你用到任何一个具体的Spring服务,例如,Spring事务管理器或则Spring MVC,你也会被锁入到这些API里。
Spring的应用对终端用户是不可知的。例如,对数据持久服务,Spring框架兼容不同的DAO和JDBC的模版帮助类,如Hibernate, iBatis, 和 JDO。所以假如你需要为spring应用切换在数据持久化服务(例如从JBDC到Hibernate),你需要修改你的代码以适合新的模版帮助类。

服务整合
从一个很高的角度上看,Spring框架处于应用服务器和服务库的上方。服务整合的代码(如,数据访问模板和帮助类)属于框架,并暴露于应用开发者。相反,EJB3.0框架与应用服务器高度整合,服务整合代码也包装在一个标准接口后面。

因此,实现EJB3.0的厂商可以大大地优化整体性能和提升开发者的体验。例如,在JBoss EJB3.0的实现中,当你在用EntityManager持久化一个Entity Bean时,后台的Hibernate会话事务已经自动地帮定到调用方法的JTA 的事务上,在JTA 事务提交的同时Hibernate会话事务也提交了。你甚至可以使用一个简单的 @PersistenceContext 注释(稍候例子演示)将EntityManager和它后台的Hibernate事务绑定到一个stateful session bean的应用事务中。在一个会话中应用事务横跨多个线程,这在事务性网页应用很有用,例如,多页面的购物车。
由于高度整合的EJB3.0的框架,使简单、集成的编程接口成为可能。Oracle EJB3.0框架和其后台的Toplink持久化服务也同样程度地整合。

另一个EJB3.0整合服务的绝好例子就是集群支持。假如你在一个服务器集群上部署了一个EJB3.0的应用,所有容错(fail-over)、负载均衡、分布式缓冲和状态复制都已经自动为应用所获得可用。后台的集群支持被隐藏在EJB3.0的框架后面,对EJB3.0开发者来说这些都是完全透明不可见的。

在Spring 里,很难优化框架和服务之间的通讯。例如,为了使用Spring里的声明事务服务来管理Hibernate事务,你必须显示地在XML文件中配置 Spring TransactionManager和Hibernate SessionFactory对象。Spring必须电显示地管理横跨多个HTTP请求的事务。除此之外,没有别的方法均衡Spring应用里的集群。

服务组合的弹性
由于Spring的服务整合代码作为编程接口的一部份暴露在外,应用开发者有按自己需求装配服务的弹性。这个特点使你能够组合自己的轻量级应用服务器。 Spring的一个普遍用法就是将Tomcat和Hibernate组合在一起支持数据库驱动的web应用。在这种情况,Spring本身提供事务服务, Hibernat提供持久化服务——这种设置创建了一个袖珍型的应用服务器。

EJB3.0应用服务器典型地不提供这种根据需求任你挑捡服务的弹性空间。大多数时间,你得到的只是一系列包装好的特性,其中一些你可能根本就不需要。但是如果应用服务器像JBoss一样提供一个模块性的内部设计,那么你可以只取其中一部分,而把不必要的部分剥去。在任何情况,去自定义一个功能强大的应用服务器是没有什么价值的。

当然,假如应用已经超过单个点,那么你应该加入常用服务器上的服务,例如,资源池(resource pooling),消息队列(message queuing)和集群(clustering)。就总体的资源消耗而言,Spring解决方法和其他EJB3.0解决方法一样是重量级的。

在Spring 框架里,具有弹性的服务装配使得将虚拟对象而不是真正的业务对象绑定到应用中做脱离容器的单元测试更简单。在EJB3.0应用中,大多数组件都是简单 POJO,他们可以很容易地在容器外被测试。但是对于与容器服务相关的对象(例如持久化实实体管理器EntityManager)建议用容器内测试。因为这样会比虚拟对象测试方法更简单,强壮及准确。

XML Vs.注解
从应用开发者的观点上来看,Spring的编程开发接口主要基于XML配置文件而EJB3.0广泛地应用Java注解。XML可以表达复杂的关系,但是它也冗长且不够健壮;注解简单明了,但是很难在注解里表达复杂或继承性的关系。

Spring 选择XML或EJB3.0选择注解都是有他们两者框架后的体系结构决定的。因为注解只能容纳很少的配置信息,只有整合前的框架(重头戏都在框架里)才可以把广泛地使用注解作为配置选择。正如我们所讨论过的,EJB3.0刚好符合这个要求,而Spring作为一个普通的DI框架并不符合。

当然,EJB3.0和Spring都相互取长补短,在某种程度上他们都支持XML和注解。例如,在EJB3.0中,XML配置文件作为一个可选的重载机制来改变注解的默认行为。注解也可以配置一些Spring服务。

通过例子是学习XML和注解方式之间差异的最好方法。在下面几个环节里,让我们来看看Spring和EJB3.0是怎样提供关键服务给应用的。

声明性服务
Spring 和EJB3.0都将运行时服务(例如,事务、安全、日志和配置服务)绑定到应用。因为这些服务于应用的业务逻辑是没有直接联系,他们只是由应用本身管理。换句话说,这些服务在运行时由容器透明地应用到应用中。开发者或是管理者配置容器,准确地告诉它什么时候怎样应用这些服务。

EJB3.0运用Java注解来配置声明性服务,而Sring使用XML配置文件。在大多数情况下,EJB3.0注解方式对于这种服务更简单明了。这里有一个在EJB3.0中将事务服务运用到POJO的例子。

public class Foo {

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public bar () {
// do something ...
}
}


你也可以为一个代码段声明多个属性,应用多个服务。这是一个在EJB3.0里同时应用事务和安全服务到POJO的例子。

@SecurityDomain("other")
public class Foo {

@RolesAllowed({"managers"})
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public bar () {
// do something ...
}
}


使用XML说明代码属性和配置声明性服务会导致冗长和不稳定的配置文件。下面是一个在Spring应用中的XML片段,其应用一个非常简单的Hibernate事务到方法Foo.bar()中。

<!-- Setup the transaction interceptor -->
<bean id="foo"
class="org.springframework.transaction
.interceptor.TransactionProxyFactoryBean">

<property name="target">
<bean class="Foo"/>
</property>

<property name="transactionManager">
<ref bean="transactionManager"/>
</property>

<property name="transactionAttributeSource">
<ref bean="attributeSource"/>
</property>
</bean>

<!-- Setup the transaction manager for Hibernate -->
<bean id="transactionManager"
class="org.springframework.orm
.hibernate.HibernateTransactionManager">

<property name="sessionFactory">
<!-- you need to setup the sessionFactory bean in
yet another XML element -- omitted here -->
<ref bean="sessionFactory"/>
</property>
</bean>

<!-- Specify which methods to apply transaction -->
<bean id="transactionAttributeSource"
class="org.springframework.transaction
.interceptor.NameMatchTransactionAttributeSource">

<property name="properties">
<props>
<prop key="bar">
</props>
</property>
</bean>


XML 的复杂度会以几何级数增长,如果你向同一个POJO添加更多的拦截器(interceptors)(例如安全拦截器)。意识到只有XML配置文件的局限, Spring使用Apache Commons 元数据在Java源码中来说明事务属性。最新版本的Spring1.2也支持JDK-1.5风格注解。要使用事务元数据,你须要将上面的 transactionAttributeSourc bean变成一个AttributesTransactionAttributeSource实例。并为元数据拦截器添加额外邦定。

    class="org.springframework.aop.framework.autoproxy
.DefaultAdvisorAutoProxyCreator"/>
<bean id="transactionAttributeSource"
class="org.springframework.transaction.interceptor
.AttributesTransactionAttributeSource"
autowire="constructor"/>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor
.TransactionInterceptor"
autowire="byType"/>
<bean id="transactionAdvisor"
class="org.springframework.transaction.interceptor
.TransactionAttributeSourceAdvisor"
autowire="constructor"/>
<bean id="attributes"
class="org.springframework.metadata.commons
.CommonsAttributes"/>


当你有很多事务性方法时,Spring元数据可以简化transactionAttributeSource。但是这并没有解决XML配置文件的根本问题。冗长而又繁琐的事务拦截器, transactionManager,和transactionAttributeSource仍然需要。

依赖注入(Dependency Injection, DI)

中间件容器的一个关键好处之一就是它可以让开发者建造一个关系耦合松散的应用。服务端客户只需要知道服务的接口。容器依据具体的实现实例化服务对象,使他们为客户端所用。在不改变接口和客户端代码的情况下,这使得容器可以在多种服务实现之间切换。

依赖注入的模式是实现耦合松散应用的最好方法之一。它更易用,比其他方法也明了多了,比如通过JNDI依赖性查询或容器回调。使用DI,框架就像一个对象工厂,它创建服务对象然后按照运行时配置将这些服务对象注入到应用的POJO里。站在应用开发者的角度,客户端POJO在被使用时可自动获得正确的服务对象。

Spring和EJB3.0都提供广泛的DI模式支持。但是他们之间仍存在很大的不同之处。Spring支持一般意义上且复杂的DI API,其基于XML配置文件。EJB3.0支持大多数普通服务对象(如EJB及context对象)的注入和任何简单注解的JDNI。

EJB63.0 注解非常简单易用。@Resource 标记表示注入大多数普通服务对象和JDNI对象。以下例子展示了怎样把服务的JDNI的默认DataSource 对象注入到POJO的一个属性变量中。DefaultDS是DataSource.的JDNI名字。MyDb变量在第一次被使用时被赋上了正确的值。

public class FooDao {

@Resource (name="DefaultDS")
DataSource myDb;

// Use myDb to get JDBC connection to the database
}


除了直接属性变量注入,Ejb3.0的@Resource注解也可以用来在setter方法里面注入对象。例如,下面的例子就是注入session context对象。应用从不会显示地调用setter方法,其在其他方法被调用前由容器来触发。

@Resource 
public void setSessionContext (SessionContext ctx) {
sessionCtx = ctx;
}


针对更复杂的服务对象,定义了专用的注入注解。例如,@EJB注释用来注入EJB的Stub,@PersistenceContext注解用来注入处理 EJB3.0实体bean访问数据库的EntityManager对象。下面是一个怎样将EntityManager对象注入有状态的 session bean的例子。@PersistenceContext的type属性具体说明了被注入的EntityManager有一个扩展的事务 transaction context。transaction context并不会同JTA transaction manager一起自动提交。因此它可以用在在一个会话横跨多个线程的应用事务中。

@Stateful
public class FooBean implements Foo, Serializable {

@PersistenceContext(
type=PersistenceContextType.EXTENDED
)
protected EntityManager em;

public Foo getFoo (Integer id) {
return (Foo) em.find(Foo.class, id);
}
}


EJB3.0标准通过注解可以被注入的服务器资源。但是它并支持将用户定义的应用POJO之间的相互注入。

在Spring中,首先你必须为POJO中的服务对象定义一个setter方法。下面的例子说明POJO需要一个Hibernate session 的引用

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例子,具体描述了将一个数据源绑定到一个 Hibernate session factory,然后从Hibernate session factory到Hibernate template object,最后从template object到应用的POJO。Spring代码如此复杂的部分原因是因为我们须手手动注入后台Hibernate plumbing objects。而EJB3.0 EntityManager是自动被服务器管理和配置。这又将我们带回到Spring并不像EJB3.0那样高度与服务整合的论点上。

<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,包括你自己在应用定义的那些POJO。假如你想在EJB3.0应用中用Spring的DI功能 ,你可以通过JNDI把一个Spring bean factory注入到EJB。在一些EJB3.0的应用服务器里,厂商可能会额外定义非标准的POJO注入API。一个很好的例子就是JBoss MicroContainer。它比Spring更一般化,因为它处理Aspect-Oriented Programming(AOP)的依赖。

结论

Spring和Ejb3.0虽然都是为了向企业服务提供耦合松散的POJO,但是使用了不同方法来达到这个目的。两者都大量地使用了依赖注入。

对于EJB3.0,基于标准的方案、注解的广泛使用、与应用服务器的高度整合都使得EJB3.0拥有更好的厂商无关性,更高的开发效率。依赖注入和集中的XML配置文件协调一致的使用使开发者能够构建更有弹性的应用,并且可以同时和几个应用服务提供者一起协作。

鸣谢
作者感谢tephen Chambers,、ill Burke、Andy Oliver的珍贵意见。

资源
Spring框架(参见CodeZoo: Spring)
EJB 3.0
JBoss EJB 3.0
Oracle Application Server EJB 3.0 Preview

Michael Juntao Yuan 善长于提供end-to-end的企业解决方案, 也是一个移动方面的专家,是avid这个开源项目的支持者。

赫赫,我来说一下spring vs EJB,首先强调,我不是ejb的拥护者,但我欣赏他的完整、他的学院气,同时也深感他的硬伤,我不是spring的拥护者,虽然去年3、4月间看

到spring的时候曾经让我眼前一亮。对ejb有太多的误解,对spring有太多的吹捧,我希望这是一个相对公平的比较.

我认为spring和ejb的差异在这样三个方面,一个是受众也就是这两个叫framework也好叫platform也好的东西的scope;另一个是component architecture,最后一个是语义



[b]1.Scope比较[/b]

先说scope,ejb的scope是什么?ejb针对什么系统来设计的?这个我想一个人一个答案,我说了不算,同样大家说了也不算,我们来看规范(题外说一句,我想本来我没啥

资格在这里谈论ejb,我应用ejb的项目不多,但是我有个习惯,就是读规范学技术,不管别人怎么说先看这个东西是怎么来的,我想这一点还使我有些开口的自信,不至于太

贻笑大方),ejb规范的头几句:
[b]Enterprise JavaBeans is an architecture for component-based computing.Enterprise beans are components of transaction-oriented enterprise

applications.[/b]

好了,很明确,ejb是针对transaction-oriented enterprise application的组件,那么也就使说ejb是面向[b]以事务为中心[/b]的企业软件,而不是别的。ejb的核心是

transaction,不是domain model或别的。
why transaction?我在电力行业做过一阵信息化软件架构师,在电力这样一个需要处理大量数据的领域里,很少有oo model,而是做完entity分析,交给dba去优化,以数据

性能为主,这样的系统里,数据操作的粒度就是transaction,而不是object或是别的。我想这该算是一个大型企业级系统,我看到的是transaction,因此ejb这个把scope定

在enterprise级的东西而言,这样的设计还使合理。而且ejb对transaction这部分的处理确实比较完整,cmt,bmt,不同的transaction scope控制的都很好。基于这种认识,

我认识transaction script是ejb的标准应用模式,而不是domain model或是别的。
这是对ejb最大的误解的来源,我看过的所有ejb书里,只有o'reilly的一本隐约提到transaction是ejb的中心。其他一律不提,疯狂的鼓吹ejb多好多好ejb多么万能,我想

,如果不知道ejb是transaction-oriented的,那么ejb奇怪的对象模型的确不可接受。

再说spring,spring是什么呢?我没有看到特别明确的定义,不过我想仿照ejb,定义spring为:
Spring is a Javabean-based framework that supporting component architecture development.Spring provides lighter context for heterogeneous entrprise

application.

我e文很差,cet-6 6次都没过,我想说明的是,spring是一个轻量化的组件架构,可以支持多种企业应用程序模式.看到这里有人又该说了,no,no还有ioc,还有aop,没错这

些很cool的特性我没说,但是包含了,component architecture意味着两个方面,component working(编写组件)和container working(编写容器环境),spring的ioc和aop是

用在container working里的.当然肯定还有其他的一些没有概括,但是我想主体我还是说到了的.这样scope就很明确了,spring的基础是javaBean,javaBean支持完整的oo建

模,因此spring可以适用更多的结构,domain model和别的一些。

那么开始比较,spring有一个理论上普适的组件模型,但是鉴于大型应用多为transaction-oriented,那么用spring的理由就是domain model,ejb不能提供完整的oo模型而

spring可以。

结论:由于scope不同,其实比较spring和ejb那个更适合企业开发没什么意义,因为这里面根本就是两个不同的范畴,在scope上指责ejb不如spring,就好像说raimundox,你

就不能替老婆把孩子生了,还让她那么痛苦的怀胎十月。其实我也不想,我也想替,可惜我们这功能.....扯远了,ejb也是,没这功能你怎么强求他呢?你要说ejb设计的不好

,也不对,人家有专门的领域。因此我说,在scope上比较spring和ejb没意义,根本不是一个级别的。

[b]2.component architecture[/b]

Component architecture有一个基本观点,就是component和context的分离,理想情况下,component只负责业务的实现,而container提供context,这个context只技术

context,比如事务比如安全的支持。于是,component architecture的基本观点就是business关注点和technique关注点分离,business由component负责,technique由

context或者叫container实现。那么很明确了,component architecture里有两种编程,针对component的和针对container的。
好,有了这个理解,我们可以继续了,如果有人有疑意,那么抱歉,这片文章并不适合您,后面全部的论点都是基于这个观点的,如果不认可这个,下面的也不会认可,您

也不用浪费时间了。

首先看ejb的component方面,ejb在component architecute做得非常的好,ejb就是一个业务组件,在container-managed的情况下,组件通过声明可以应用容器提供的技术

context,当container-managed不满需要的情况下,可以bean-managed,只要保持对外的事务语义就可以了(记得吗?ejb是transaction-oriented,事务最重要)。在ejb里

,组件和容器的约定非常明确,哪些需要组件写,哪些是容器提供的非常明确,这是component architecture里很好的习惯,明确组件和容器的界限(ejb的一个缺点,矫枉过

正,有一些操作也被禁止)。编写代码非常容易,业务,only业务。其实ejb的规范里,ejb的coder其实最好是domain expert,实现业务逻辑就好了。

现在来看spring的component方面,spring以javaBean为基础,贫容器,也就是对容器没要求,于是,spring第一个缺点,contianer和component的约定不清晰(写到这里我

已经听到一些人在叫了,这是spring的优点,自由,别急,后面我会证明这是spring的软肋),但是spring用了一种比较聪明的办法,那就是加强container working.

看一下spring的container working,通过spring的aop api,我们可以很容易的为特定组件定制容器环境,把一个组件需要的容器技术环境以aspect的形式weave到component

里,非常的灵活,也非常的强大,spring通过这种形式来弥补组件/容器约定不足的情况,一切由component选择,容器除了装配(ioc/dip)不提供任何技术context,想要什

么自己来,这个给了component实现者自己选择的权利,很好(但也隐含了spring的最大的不足,别急我后面会说)。

再来看ejb,非常遗憾,ejb container working的能力几乎为0,当然算上jca的话还不算太差,但是那只是资源,而不是技术context。why?因为ejb认为他已经提供了所有

企业级开发所必需的技术context了,事务(ejb里我总把他放在第一位)、分布、并发、安全等等。在ejb看来container working的能力几乎无用,而且不安全,除了jboss开放

了比较多的container working接口其他的ejb container提供这方面的支持很少很少.当然提供很多技术context并不是坏事,要命的是不能配置,要么全用要么不用(倒是很

原子),这是ejb最大的不足,容器环境不可配,也是spring强于ejb的地方。

上面我们已经看到了spring和ejb都是component architecture,那么component能想到的最直接的用处就是复用。那么比较这一点就是比较ejb和spring component

architecture的关键。看到这里spring的支持者们该常出一口气了,spring复用一定强于ejb复用,赫赫,但我的结论正好相反,ejb复用优于spring复用!!收起你们的愤怒

,收起你们不屑,听我把话说完。

在component architecture里,component是业务实现,而不该有技术代码,就算用也要通过容器环境去和容器交互来完成操作,比如ServletContext等东西。[b]那么其实

组建结构下复用的关键不是组建而是容器!![/b]

以前有一个颇有名气的dx(gigix别看了,说你呢),说"COM和EJB都鼓吹模块化和复用,模块化是真的,复用是骗人的",com我不是很熟,不好下结论,ejb呢?ejb不易复用我

承认,但是骗人吗?不骗,后面我给出一种ejb复用的思路大家参考。反正组件一点技术都不作,只有业务逻辑想用就要有相应的容器环境,那么容器环境的复用性才是组件复

用的关键。ejb不易复用是因为如果脱离容器,我们就必须给它提供相应的技术context,事务、分布、并发等等一个也不能少,[b]因此容器外复用ejb效率很低[/b]。注意,

[b]是容器外[/b],组件本来就是跑在容器里的,谁让你非要拿出去用),而容器内呢?因为ejb规范规定ejb容器应该兼容,别说webSphere到bea的移植有多难,其实不难,或

者说难度比把spring组件移植到pico复杂一点,当然你用vendor-specified的特性就没办法了,这不再规范内,你违规就别怨人家。因此,ejb的复用是可以的,而且是规范保

证的,容器外也有办法,也不是很难,我后面说。

再看spring,的确他很灵活,但这正是致命伤,component完全是业务实现,但是容器呢?spring怎么保证容器的环境?没有,只能你自己保证,当你沾沾自喜的说,spring

里的component都是pojo,可以很好复用的时候,可曾想到,这复用的代价是要复用容器。比如有个componentA,在SystemA里需要事务模型A和安全模型A,在SystemB里需要事

务模型B和安全模型B,当你从SystemA里复用componentA的时候,你要怎样?重写事务模型B和安全模型B,然后你可以堂而皇之的说,你复用了组件。的确,你复用了组件,但

是你重写了容器,值吗?更可怕的是,spring容器和组件没有约定,那么怎么保证你的组建不写技术代码?ejb只要Bean-Managed并提供统一的事务模型就好了,spring呢?你

靠什么保证?你自己?这是spring一大硬伤,完全container-managed下缺少特定的component边界控制.你可以说,特殊要求的事务模型ejb还实现不了呢,的确,这是有可能

,但是ejb transaction model不能适用的情况有多少?如果真的不行,只能说ejb的简单复用在这里失效。

对于组件还有一个问题就是部署,这也是ejb为人诟病的地方.的确,ejb的部署够复杂,但在ejb规范里有一个专门的角色来负责部署的,ejb是component architecture,那

么比如有一个人来粘合技术和业务,这个人不该是programmer(我刚才说了,ejb的实现者最好是业务专家,实现业务逻辑),ejb的部署才是很厉害的人,他需要知道什么业务

需要什么样的技术支持,该怎样得到性能,因此deployer才是ejb architecture里最牛的,我重来不以为写ejb的是高手,但是一直都敬仰ejb的deployer.当然这里有一个调试

困难的问题,这是ejb的硬伤,没办法,这甚至是组件开发的硬伤.
再来看spring,spring宣称他部署简单,我觉得rod johnson在转移视线,想想看,打成一个war和打成一个ear有多大的区别?那么部署的差异在哪?差异在ejb的deploy

description和spring的context.xml的编写上!在用spring开发中要有一个人来写context.xml这个人往往比较了解系统,知道什么组件用什么拦截,那个组件依赖那个,甚至

会是架构师在作这件事情,那么和ejb里对系统有大局观的人来做deploy有多大区别?可能就是Xml的编写吧,我想在工具的支持下就是熟练度的问题,因此我觉得部署上

spring和ejb差不多,spring不用启server,调试放便些。

结论,在component architecture上,spring灵活,ejb统一完整,各胜擅长,spring的灵活以降低复用为代价,但是如果有common的技术实现,的确很好复用,但是

spring+一套common的技术实现也就约等于ejb了吧?

[b]3.语义[/b]

那么spring复用的问题表明了什么呢?其实是缺乏语义的支持,ejb开发可以看作在一个统一的语义环境下来作的,这个语义由ejb规范规定,因此ejb的复用有语义保证,而

spring呢?贫语义,一切都要开发者自己来实现。因此,如果ejb的环境语义可扩展并且可配置(比如去掉分布),那么spring毫无优势,标准的一致的完整的组件架构使ejb

会大有作为,但是现在并没有,才有了spring的火爆.这是一种畸形的胜利,完备语义的输给了贫语义的,问题是什么,强迫消费...谁让ejb非得强迫客户去买用不到的分布式

环境的单?但是统一语义的威力不会因此掩灭,现在有两条路,spring联合os社区,制定lightweight j2ee语义集合,争取成为标准。第二,ejb实现技术语义可配置可扩展。

谁会胜利?不好说,但是似乎ejb的脚步快一些!

[b]附:容器外复用ejb[/b]

其实ejb在容器外完全是可以用的,但是为了最大限度保证能用,bean-managed是推荐(不是cmp,bmp而是cmt,bmt),那么怎么传送一个transaction进去?SessionContext(

好像是这名记不清了,都快2:00了,困呀...就是ejb那个context接口),一个接口嘛,自己mock一下,给一个transaction进去就好了。怎么装配?spring的setter

injection。如果用spring,那么cmt也可以实现,拦截啦,不过就看能不能实现ejb transaction model了。entity bean,如果是bmp,就用它好了,cmp,继承一个上

hibernate。这些都模拟好了,找一个in memory的jndi,把spring context封进去,这样相当于我们用spring实现了一个lightweight ejb container(其实就是给spring一个

ejb api的皮),轻到什么程度?除了注射什么都没有。
然后client就可以构造jndi,然后lookup了
看到这里一定有人说,你吃饱了撑的,这么费劲容器外复用ejb,为什么不直接用spring,这样也不够pojo,每个组件都有ejb的类的继承,好,我告诉你这么做的好处,首先

,虽然不够pojo,但是足够bean,因此spring来管理ejb是可以的,证明我的观点容器外使用ejb可以(赫赫,不要说偶rpwt...).其次的,当业务发展了,你的系统需要分布了

,把spring去掉,拿出ejb,redeploy,ok了,延展,稳定,分布都有了,这才是复用该有的境界,改一个部署整个环境换掉,去掉lightweight的ejb container,换乘

heavyweight的就是重量级。
当然这么实现很难,在ejb3里会容易些,我敢打赌,spring以后一定是lightweight ejb container的供应商,免不免费,os不os要看rod johnson了,不过我觉得希望不大。

致谢:

首先感谢dlee,在和他的讨论中形成了这篇文章的主题,然后是冰云,他帮我审稿直到2:04,udoo,perhaps都提出了中肯的意见,谢谢他们.

↑返回目录
前一篇: 客户端与EJB的通信是如何传输的?
后一篇: 什么是servlet