下面我们从一个发展角度来对J2EE/Java EE的这些框架诞生进行一番考量,可能对我们的选择有很大帮助。
首先我们需要明白一个高质量的J2EE系统是什么样子?高质量的J2EE/Java EE系统标准实际就是OO设计的标准,松耦合是OO设计的主要追求目标之一,那么无疑解耦性成为衡量J2EE/JEE质量的首要标准。实际选择中,还需要兼顾可伸缩性/性能/开发效率等方面综合考虑。
J2EE/Java EE号称多层结构,为什么多层比两层好?因为多层结构解耦性好,带来维护拓展方便灵活。
典型的J2EE/Java EE至少划分三个层次:表现层/业务逻辑组件层/持久层。
如图,表现层英文是Presentation Layer,是实现显示功能的,这部分一般使用B/S结构来完成,当然你也可以使用专门远程客户端来实现;
业务逻辑层因为是由大量组件(Components)组成的,也可称为组件层,组件从不同角度又可分为各种类型,然后又有不同的流派,目前占主要位置的是Model+Service,模型加服务,所以这一层又称为业务服务层Business Service;也有称为Model业务层;
持久层是负责对象持久化也就是数据库操作的层次,英文Persistence Layer。
SUN伙伴们推出J2EE标准时,分别对这三个层次规定了标准实现,表现层使用Jsp/Servlet技术;业务组件层使用EJB的会话Bean;持久层使用实体Bean。同时,标准将业务层和持久层在物理上组成一个新的容器EJB容器,与表现层技术完全一样的容器,这样,J2EE技术被细化为Web和EJB,物理上有Web容器和Web应用程序;以及EJB容器和EJB应用程序。
当然,J2EE/JEE的发展不止这些,这三个层次技术分别独立发展,高歌猛进,下面分别单独陈述,当你了解某种框架技术为什么诞生时,你可能就知道你该在什么情况下选择它们了,工具总是因目的而生!
表现层框架
J2EE/Java EE虽是多层结构,但它不是一种强制性多层结构,也就是说,你也可能做成传统两层结构,有的初学者直接使用Jsp嵌入Java代码调用数据库这样结构实际不是多层结构,还是以前的两层结构。
在Jsp中嵌入大量代码,一旦报空指针错误就很难找出问题,很多初学者下载JiveJdon 2.5后就经常碰到这个问题,因此大量有关空指针错误问题出现论坛里,为什么他们不能自己解决呢? 那是因为无法定位错误在Jsp中的位置,Tomcat等服务器只告诉我们错误在index_jsp.java(Jsp的java文件)位置,搞得一些人经常会跑到Tomcat服务器内部翻找Jsp的Java文件,这一过程无比痛苦(为了减少初学者这种痛苦,本站暂停了JiveJdon2.5的下载)。
J2EE/Java EE的发展就是降低这种痛苦,首先想到的方式是:在Jsp调试上下苦功,要求Tomcat等服务器提供详细的错误定位;可惜到Tomcat 5.5我们也没看到这种功能;实际上,根本解决之道就是将Jsp的调试变成java组件的调试。
首先通过MVC模式规定Jsp只能等同于Html,不能包含Java代码,将Jsp和Java代码分离,可是这样分离之后,它们结合起来又麻烦了,所以,虽然你使用MVC模式,但是还是直接基于Jsp技术,带来的是开发效率的降低。
Struts框架解决了这个问题,通过ActionForm可以将Jsp和JavaBeans方便快速地结合起来,但是有人又抱怨Struts的ActionForm限制太死,与Jsp虽能对应,只能一个ActionForm一个表单对应,而不能任意组件JavaBeans都可以和Jsp任意字段对应,这时就出来了组件型框架JSF/Tapestry。
业务逻辑层框架
可伸缩性
因为EJB标准的推出,业务组件层以前基本是EJB的天下,但是EJB功能实在太强大,它考虑了世界顶级大型系统需求,因此免不了显得很复杂,当初,基本上所有的大型企业高端都是选用J2EE,选用J2EE实际是选用EJB。EJB强调的高可伸缩性为大型企业日益发展提供最大的发展空间,不再因为企业快速发展导致整个企业系统结构都要发生根本变化,这是使用EJB的现实优势。
这种企业系统的可伸缩性不是因为EJB存在才显得重要,而是我们企业架构选择需要考量的基本因素。换句话说,无论我们使用EJB与否,这个问题都需要考虑:一台服务器不够用怎么办?如果这台服务器死机会对企业运营带来什么影响?如果每个星期这台服务器停机维护升级会不会对企业带来打击?我的企业系统是不是需要可靠的、几乎不当机的7x24运行?当企业系统面对大量外部访问用户时,一台服务器是否够用?多台服务器联动的需求是否涉及软件技术更换?
在这个现实因素考量后,如果觉得不是很重要,或者说将来一段时间内这些因素无所谓,那么可以不选用EJB了。
为什么有不选用EJB的理由?因为它复杂,学习起来比较困难,EJB帮助我们考虑企业中可能碰到的大部分问题,实际上,有的我们不需要,这也就是为什么说EJB是一个重量级解决方案所在。
与重量级相比的是轻量,业务组件层轻量级解决方案有Spring/HiveMidn/Jdon Framework等,轻量一词曾经因为EJB的出现而变得时髦,给人造成的感觉是:EJB花了老鼻子力气完成的那些功能,使用我轻量级解决方案可以轻而易举也能解决,举重若轻啊,其实这是一种误解。
当曾经以轻量自居的Spring将事务机制纳入自己怀抱中时,它也开始低调轻量了,实际是不轻不重了;当然如果它把分布式计算和事务再次加入时,天平砝码也会沉下去的。
初学者总是愿意使用简单的解决方案,学习使用方便,因此比较喜欢轻量级框架技术,这是正常的,但是在使用轻量级别框架之前必须明白:你的系统将来真的只需要一台服务器即可?你的项目完成期真的非常紧急?
如果只需要一台巨强服务器,就不必选择EJB,EJB是因分布式多服务器而生,对于单台服务器,缺乏本地透明性,也就是说:你无法透过EJB直接和本地JVM或文件系统等打交道,透明性也是衡量一个框架的重要指标。
当然,重量和轻量并不完全对立,EJB3就是为了简化J2EE的使用,使得EJB不只是擅长处理大型企业系统,中小型系统也使用很方便,这实际上也是EJB轻量化的一种努力。什么是Java EE 5?
所以,对于架构选择来说,根本前提是需要明白你的系统现在或将来一段时间(注意需要考虑将来一段时间,不能只看眼前)是中小型系统还是大型系统?
灵活性/定制性/透明性
当然这个答案有时我们自己也可能很难给出,那么我们还需要从其他角度来考量EJB和非EJB之选,例如笔者曾经经历一个大型实时娱乐平台社区系统,从规模上说肯定是大型系统,设计目标10万人在线,从这个角度非选用EJB不可!
但是,EJB因为还有事务机制,虽然应用程序可以选择失效EJB事务,但是EJB容器设计因为考虑了事务,所以在其内核上总是会显得臃肿,是一种累赘,这也是一种重量表示,不需要的东西存在肯定会影响效率,那么难道我不能根据我的需求,对EJB整体包括EJB容器进行可配置式的切割?也就是说,我上面这个案例只需要EJB的分布式计算功能,其他的功能我都拆除,只剩余我需要的功能能运行就可以了,轻装上阵。
可惜,这种任意切割,应需而定的目标在EJB3标准还没有被重视,所幸的是,Ioc/AOP技术为这种目标实现提供了实现可能,但是,只有Ioc/AOP还是不够,特别是看Ioc的范围,如果你只把应用系统组件纳入Ioc管理时,自由解耦只属于应用系统,我上面案例的目标还是无法达到,当你把框架本身组件也纳入Ioc管理时,任意切割,应需而定的目标才真正实现。
Spring和EJB3属于“只把应用系统组件纳入Ioc管理”,这从JBoss 4.0版本可以看出;那什么框架会把框架本身组件也纳入Ioc管理呢?Apache的Hivemind和笔者开发的Jdon框架。
什么样的组件可以被纳入Ioc管理呢?POJO组件,POJO这个概念其实当初是针对EJB缺点而推出,EJB要求应用系统的组件必须继承或依赖EJB容器,这样使得调试变的不方便,但是后来,POJO的概念已经不只最初这些概念,POJO代表那种与周围完全脱离关系、自由自在的Object了,如果应用系统的Model或者Service都是POJO,意味着你的应用系统不依赖任何其他系统,解耦性灵活性高。
POJO是因为EJB而提出的,当EJB自己倾向POJO时,大家都在宣称自己是POJO时,POJO概念就没有立足点了,这也是Java领域哭笑不得的现象,但是我个人更倾向把非EJB的JavaBeans组件通称为POJO。
Hivemind的Ioc依赖注射部分功能和Jdon框架非常类似,当上百个POJO组件配置时,完全不必指定它们之间的依赖关系;你可以将自己应用程序的组件注册到Registry中,这样Hivemind将帮助你启动这些组件,正如你在Jdon框架中将你的组件写入mycontainer.xml中,Jdon框架也将自动启动你这些组件,并解决它们之间的互相调用,包括和框架本身组件互动。
HiveMind和Jdon框架定义的POJO Service有如下特点:
不象EJB那样缺乏本地透明化(location transparency)概念,这些POJO Service总是能定位到同样一个JVM;也不象基于XML的Web服务web services那样没有语言透明(language transparency)概念,这些POJO Service总是可以被一组Java接口来概括替代(通过调用Java interface来替代调用这些具体Service);当然,更不会类似JMX或Jini,不能进行service的热装载( hot-loading)。
注意:当Ioc/AOP提供高度灵活的同时,已经有初学者开始抱怨Spring的过分灵活,那么比Spring在组件上更灵活的Jdon框架只能算是一种追求极端,它不一定构成你进行架构选择的主要依据!
上面这些讨论只是表明在灵活性(解耦性/透明性)这条需求道路上深究下去的选择,你自己的应用系统需求会产生各种不同的要求,有些要求甚至是极致的,这种偏向程度就成为你架构选择的目标,如果你的这种极致要求在目前Java世界里还没有可选框架时,那么你就要动手自己做了,这也说明什么时候你开始自己做框架(如Jdon框架),虽然在大多数情况下我们是不必要自己发明轮子的。
快速构建性
前面是从灵活性和定制性这个角度讨论架构选择目标,但是在一般情况下,我们还是从上手难易、开发效率这个角度来进行架构选择,从这个角度来说,就是需要我们将选用的框架尽可能的多帮助我们实现一些功能,但这又是和使用难易是矛盾的,因此有个取舍问题,取舍有个准则:这个框架尽量能提供越多功能;尽量需要我们少写代码,甚至不写代码(使用XML配置),少动脑筋。
关于XML配置这里也涉及难易问题,XML配置语法不能太复杂,有太多小开关Switch也增加学习成本。
从这个角度看,EJB无论是EJB2或EJB3提供的功能是最齐全的,但是XML配置开关太多 ,Spring属于中等,组件XML配置不算简单,但是因为有不少Struts+Spring+Hibernate之类现成开源代码可供参考,因此学习起来难度也不大,Spring越来越象一个J2EE API(注意,JDK是J2SE API) ,Spring除不能提供分布式计算外,也因为过分灵活降低了一些开发效率,例如它的组件依赖关系一般需要逐步指定,auotwiring功能还没有深入骨髓成为核心功能。Ioc容器的革命性优点。
Spring除了提供组件层功能以外,还有表现层支持Spring MVC;也有持久层实现的JDBC模板,这样,整个J2EE/Java EE系统各个层次Spring都提供了缺省实现,在这方面无疑提高了开发效率,但是Spring提供丰富API目的好像不是为了快速开发,而是为了建立一个完整的功能齐全的API功能库。正如它网页上开头文字所述:As the leading full-stack Java/J2EE application framework,注意full-stack(完整齐全)是它突出的名词。
那么,还有另外一个空白,就是以开发效率为主要考虑,这类框架除了必须考虑足够灵活性和丰富功能以外,宗旨是为了在一般缺省情况下快速完成一个J2EE/Java EE系统,这非常类似MDA工具了,但是一个完全丧失灵活性和定制性的MDA工具也不是我们欢迎的。
Jdon框架的发展目标是为了填补这个空白,相信也会越来越多框架向这个目标迈进,当然不可否认,Spring也可能调转枪头走入这个领域,EJB2/EJB3正依靠JBuilder等这样商业化开发工具向这个领域靠拢,这个发展方向实际是4GL RAD Tools。
很多人在快速构建方面也很早进行了探索,涌现出各种工具:如何构建一个快速业务构件平台? 但是如何把快速构建和构件(组件)灵活性有机结合在一起?它是考验一个业务构件(业务组件)平台好坏的准则。有些构件平台虽然开发迅速,但是对于特殊情况,可供程序员定制透明操作部分不多,很死,典型的是两层结构以前的Delphi,开发很快速,但是无法象J2EE这样深入到系统各个层次进行定制/维护/拓展!
业务组件框架对比
|
持久层框架
持久层框架目前有EJB2/EJB3的实体Bean、Hibernate和各种JDO产品,当然还有直接写SQL语句的JDBC,如iBatis等。
持久层框架质量好与坏区分就是是否是O/R Mapping,也就是对象和关系数据库映射,关系数据库需要实现定义好Schema结构;对象因为字段而变的也有一个自己的结构,如何将对象数据自动持久化到数据库中,首先我们得定义两者结构的对应,这实际是数据的元数据定义。
因为Hiberante/Toplink/JDO这样O/R Mapping工具帮助你实现对象和数据库转换,克服了对象和数据库阻抗现象,O/R Mapping总结 ,所以才使得我们更多的可以对象方式(从模型Model对象)来考虑Java EE/J2EE系统,可以完全放弃以前那种以数据库为中心的思维方式:数据库时代的终结。
所以,是否选用好的持久层框架,取决于你整个团队思维是否彻底OO了,是否需要真正OO,当然,对于一些小型项目,有时我们觉得直接使用JDBC模板反而更加轻松快捷一点,这也是Spring的JDBC模板/iBatis/Jdon的Jdbc模板存在的理由了。
例如新增一个数据表,在Jdon框架只需要下面几行代码即可:
String sql = "INSERT INTO testuser (userId , name) VALUES (?, ?)"; List queryParams = new ArrayList(); queryParams.add(userTest.getUserId()); queryParams.add(userTest.getName()); jdbcTemp.operate(queryParams,sql); |
这种方式和O/R Mapping兴师动众的多个XML配置和关系映射思考相比,对于习惯SQL语句的程序员反而更加迅速。
从以上可以看出,灵活性/快速性/简单性/可伸缩性是我们进行架构选择的主要几个依据,架构选择实际就是在这几个策略之间做一个平衡。当然,还有一个非常重要的因素,因为它不属于某个层次的技术,性能/缓存是必须和上面因素综合考虑的因素。
因为性能最初是我们使用计算机的基本原因,别忘记这个根本