论坛首页 Java版 企业应用

一种一站式的Java Web框架的设计和实现

浏览 16924 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2007-09-14 关键字: Java web framework Map 敏捷过程
一个月前,我发表了一篇文章《一种快速开发的Java Web架构设计和实现》(http://www.javaeye.com/topic/38277),匆匆发表,很欠成熟。经过一段时间的考虑,特别是吸取了Lucas Lee ,together ,lane_cn 等朋友的建议,重写了一份design文档,并且加一份tutorial文档,重构了所有代码,现在发表出来,听听大伙的意见。
    本来很想把上次那三篇文章删掉,但也许会删掉到别人的宝贵评论,所以最后还是觉得保留,但我还是不太希望别人在那上面浪费时间,因为这篇文章就够了。
    由于文章很长,而且javaeye论坛对word文档的paste支持不是很理想,我就只贴出文章的很小一部分,其它的全部,都在附件里,对此主题感兴趣的朋友,可以下载下来评价评价。
    我在论坛潜水时,也不希望自己读到某些浪费时间的帖子(javaeye是我见过的文章质量最高的论坛),所以,我也不希望自己的帖子浪费大家宝贵的时间。想必上个帖子很浪费大家时间,深表歉意!
    本文是我阶段性的总结,我想肯定受限于我的理论知识和项目经验,希望大家能够理解。
    上个帖子最大的问题是:
[list=]1、对Map简单化了,抽象为一个plain结构,现在是对象图、树状结构。
2、原来所有持久化是spring jdbc,现在基本的CRUD是hibernate的dynamic-map方式。[/list]

    现在,使用此框架的开发思路,可以将一个一定复杂度的CRUD操作减少到100行代码左右。

  大家下载附件读吧,文章太长,我只贴出了很小一部分(附件文件夹下的docs目录里)。



MiniFramework有哪些核心思想,或是原理?
1、    元数据编程
这是MiniFramework中减少代码量和快速开发的一个很重要方面。
MiniFramework中有两类元数据,都非常简单:
  Map和数据库映射 同时我们会避开Hibernate那种复杂的关联映射。而且这种映射可以自动生成。
  JSP页面表单元素类型映射 因为http GET和POST提交数据都是文本key-value键值对,它们和数据库字段类型不匹配,我们必须还原其本来类型,但这个过程是自动化的。我打算开发eclipse插件,让这个映射代码也自动生成。
所有容易变更的信息,譬如sql,页面导航,对象实例引用…

2、    领域对象的表示:Map和List
MiniFramework里面没有具体的对象,譬如Account,Department,Book,而是以另外一种形式体现:Map,这是它最核心的思想。它可以实现所有具体对象的功能,包括对象图。并且易扩展、灵活。
为什么Map这么关键呢?因为它是领域模型的载体,也是自动持久化的目标对象。了解JSON、YAML和REST对MiniFramework中的map载体理解很有帮助:
REST:区别于Web Services SOAP协议的另外一种轻量级格式,更是一种Idea。REST和ajax协作开发,简直是绝配。REST是未来Web Services的发展趋势,更倾向于SOA,因为REST是以Resource为导向,而SOAP是以Action为导向,并且简单、易用。
JSON:这是ajax流行起来后,一种轻量集的数据交换格式。参考http://www.json.org/json-zh.html
YAML:在Ruby on Rails和Python这类动态语言最常用的一种文本数据格式,类似于Java最常用的XML,但更适合人读。参考:http: //wiki.nirvanastudio.org/wiki/YAML/5%B7%D6%D6%D3%C8%CF%CA%B6YAML
从技术的角度考虑,所有的MIS系统,无非是CRUD增删改查,而CRUD的对象,一种载体形式,就可以是Map和List(Map的复数形式)。
关于数据流的处理,可以参考《面向模式的软件架构:卷一》中的“管道模式”,异曲同工。
当然,复杂的MIS就不只是数据流,更倾向于业务流,譬如ERP中的物料管理,SCM的进销存,OA中的工作流。但它们底层都需要数据流支持。

3、    Map和List对象的展现技术:JSTL
当然,我并没有提出什么新东西,我只是提出一种新用法。JSTL是map和list的最好展现方式。如user.department.name,通过具体对象,或是Map对象图,都可以实现。但后者很少人用,因为所有的教程上面都会演示对象模型,用map是忌讳,不OO嘛。 Really?
对象展现技术和对象载体是两个非常关键的部分,关于对象展现的idea,可以参考:
OGNL:这是xwork里面对象属性存取的核心, OGNL也是EL(Expression Language)的一种实现方式。
Associative model of data:不同于关系模型和对象模型的另一种idea。

4、    Map 对象持久化技术:Hibernate
几乎我们所见的大多数Hibernate用法,都是持久化POJO,也就是JavaBean,但是Hibernate也支持另外一种持久化技术:Map持久化(dynamic-map)。
需要特别注意的是:对于Hibernate Map持久化,我只使用它的最基本功能,这意味着开发人员几乎都不需要理解hibernate,我已经将对象的CRUD和基本的List操作都封装起来的,调用工具方法就ok了。
这样,数据的四个最重要的方面中的三个被轻易解决了:对象的载体,对象的呈现,对象的持久化。还有一个,就是对象的复杂查询。

5、    对象数据查询技术:Spring JDBC Framework
应该说,这个方面对象的概念是最弱的,因为查询的往往是结果集。Spring的JDBC framework为我们提供了很好的封装的,另外,我在它的基础上再封装一下,开发人员的学习成本就更低了,它返回的也是Map或List。

6、    业务对象BO的CRUD抽象
每个业务对象,譬如UserBO,我们要求它实现其CRUD操作,R(Read or Retrieve)包括Load和ListAll两种。这些方法都是框架自动完成的。
Load:根据ID标识加载对象,以及其相关对象,形成一个Object Graph,但对外是Map(对象标识是OO的基本四特征之一)。
ListAll:List该对象的所有信息,List里面的item是Map。
Insert:可以创建该主对象,如User,但也许需要创建相关对象,如Contact信息。
Update:创建对象。也可以支持不同级别的更新:更新用户信息,更新密码,更改用户角色等不同操作。像审批流程的通过和驳回,都是update操作。当然,从性能角度考虑,可以分解。
Delete:删除指定对象,以及相关对象,如删除User,其Contact也必然要删除。
任何业务对象都可以这么抽象划分并且实现,但是,复杂查询就需要单独的sql,利用MiniFramework提供的查询helper类处理,返回Map或List。

7、    敏捷开发过程
更少的代码,意味着更快的开发速度,这和敏捷过程是呼应的。现在流行的Ruby on Rails,既是一种快速开发框架,更是实践了敏捷开发的idea。MiniFramework做快速原型非常方便,而且这种原型可以直接用做后续开发。快速原型是理解需求的一大利器!
而且,我们严格要求测试驱动,分层架构为容器外测试提高了保证。
Note:个人也觉得,Rails框架比较适合于互联网开发,Java更适合企业开发。因为企业开发很重视事务处理、团队协作、可维护性、语言的简单性,某些方面是Rails所欠缺的。我现在用Rails和MiniFramework开发同一个demo,我觉得它们的开发效率相差不大。
关于语言的设计和比较,请参考书籍《Concepts of Programming Language》


MiniFramework的架构,你能够大致描述一下吗?
为了保证开发的简单、灵活性,MiniFramework在架构上做了极大的简化,方便、实用、快速是它的原则,在MiniFramework架构考虑中,我们会在常用的J2EE架构上做减法,而不是做加法。但我们会恪守几个设计的基本原则:松耦合、高内聚、职责分离。
在参考常用的架构模式和设计模式过程中,我一直在思考这个问题:为什么我需要XX模式,我可不可以不要XX模式?
下面是我认为在MiniFramework设计过程中几个核心概念:
MVC(Model 2):为什么我需要MVC?因为MiniFramework是定位在交互式的GUI应用上。经验和理论告诉我,MVC是GUI应用设计的基本原则,它保证了清晰职责分离,方便开发、测试和维护。在MVC架构模式理论中,一般遵循Change->Publish,也就是Observer模式,类似于message系统,但Web应用的本质(HTTP无状态协议,它怎么可能知道客户端的你?),可以用Request->Push(推模式),区别于Pull(拉模式)。我更倾向将MVC说成Model 2,区别于Model 1,也不同于MVC。在实际开发中,JSP是作为html模板,只被动接收Request中push过来的对象,决不主动去pull。
Note:Push和Pull,同步和异步,对软件设计很多时候有决定性的影响。
分层架构:分层架构有助于将大系统分解成子任务,每个子任务限制在一个特定的层次上,层间从上到下依赖,从下到上是松耦合。TCP/IP分层模型是它的最好证明。正如OSI 7层模型的实用模型是TCP/IP 4层模型。MiniFramework中,用户接触到的是两层:表示层和业务层,是J2EE和.NET架构的一种折中。在用MiniFramework开发时,持久层被封装起来,成为一些简单的Helper类,不是独立的一层。
之所以我去掉了持久层,只保留的dao helper,是因为,我发现业务比较简单的系统,往往开发人员喜欢在持久层里面写所有的业务,service层只是dao的一个delegate,非常 thin。要是这样,我就干脆去掉一层算了,这便是dao helper的由来。
严格的分层,可以让表示层的UI用Dreamweaver开发,业务层容器外测试,实现敏捷开发。
接口:在分层模型中,一般都非常强调接口,因为可以让上层只依赖于下层的接口,而不是实现,这样实现可以任意替换和变更,在网络开发,特别是和协议打交道的时候,我们会体会到这种设计的优雅。另外,在组件式开发中,我们也倾向于提供接口。
但是,应用MiniFramework,我们倾向于纵向开发,也就是说,某个模块从表示层到持久层都是一人开发,而不是横向:表示层的去调用业务层开发人员的业务层。这时候,纵向开发就不太适合用接口了,因为接口规范完全由本人把握。这时接口很可能只会带来臃肿,和难维护,一点改动往往牵一动百。当然,在某些情况,如开发Mock测试,Web Services,接口还是很有必要。另外,有人说,我用接口可以实现任意层替换啊?譬如我现在用Hibernate,以后换成IBatis。这完全是一个谎言,至少我看到的大多数应用,将Hibernate换成IBatis的成本绝不亚于重新开发,因为耦合太大。另外,我有个疑问:需要更换的可能性有1%吗?
个人觉得,接口比较适合于系统软件开发,而不是商业软件开发;而抽象类的继承机制比较适合商业软件开发,而不是系统软件开发。为了复用,系统软件开发倾向于组合,而不是继承。
Business Object、ActiveRecord和ORM:不同于常用J2EE开发的ORM,也不同于RoR的ActiveRecord, MiniFramework用Business Object来调用持久化的Helper类,而且它是stateless的。没有ActiveRecord的高耦合,也没有ORM的复杂性。
主要参考文献:
《Pattern-Oriented Software Architecture: Volume 1》
Model 2:http://www.javaworld.com/javaworld/jw-12-1999/jw-12-ssj-jspmvc.html
《Patterns of Enterprise Application Architecture》
《Core J2EE patterns》


MiniFramework现在很完善了吗?

还是beta阶段,但是我认为对一般的项目应该够用,我已经做了完整的CRUD例子,其中有三表关联,还是有一定复杂性和代表性。
但是,我现在也在思考一个真正的企业级框架,如果说它是1.0版本的话,MiniFramework就是0.1版了,距离还很远。但我大致上思路很明确,除了以上的idea外,我还会加入以下idea和功能:
Event Driven:提高系统的可扩展性和灵活性,将Map数据作为Event的一个部分,而不是现在的全部。
Business Controller(Interceptor):相同职责统一控制,譬如权限;所有关键数据的createdTime,createdBy,updatedTime,updatedBy统一处理;Audit等。
Micro Kernel:为应用开发提供良好的适应性和扩展性,适应需求,譬如Message-based。
Service Engine:为一些常用的服务提供统一的调用方式,譬如报表系统特别强调的订单号
相关服务,如作业调度,消息系统
支持分布式协议:SOAP,RMI,
支持多种客户端:WAP,Browser,Client,PDA
对集群、安全的灵活支持
上面的很多概念并不是来源于MIS系统,但是我想将其思想引入到MiniFramework中,但还要保证MiniFramework的简单、易用。
  • sql.rar (1.3 KB)
  • 描述: mysql数据库脚本
  • 下载次数: 345
   
最后更新时间:2007-01-15
前阵子看过LZ的文章

与LZ思路类似,也发过相关的文章
http://www.javaeye.com/topic/38847
mvc,持久层,连接池,缓存等都是DIY
差不多是一站式的

可分享交流学习下
   
0 请登录后投票
最后更新时间:2007-01-15
直面现实,佩服楼主,研究一下。
   
0 请登录后投票
最后更新时间:2007-01-15
我对你的框架的主要意见在于:你采用代码生成的方式。
  这个方式我认为对于开发速度和可维护性的好处很有限。原因在于,如果你修改了自动生成的代码,那么难以再次使用生成代码同时保持改动不被覆盖。对于代码生成,我认为比较合适的是EJB那样不需要程序员维护的自动生成的代码。
  另一个方面,代码生成相对于动态解释而言,主要的优势在于性能比较高。但高了多少?我认为还是比较有限的。无非就是把很多动态的变量,固定在代码里了,比如字段名,动态解释肯定是从配置文件里读到内存里,存在一个变量里,而代码生成就是一个固定的字符串。
  所以从这个角度,你基本上没有采用或者考虑过我的建议。当然这只不过是我的建议而已。

  如果采用动态解释,(即运行时按元数据执行相应的操作,如CRUD),那么hibernate,spring,JSTL等流行的框架和技术很可能无法适用,这也是你难以取舍的地方,一般人都希望将最流行的框架用起来,否则有被主流抛弃之风险,这也是我之前面临的困境,不过我仍然坚持了动态解释的路子。
 
  一个CRUD 1百行代码固然不错,不过就我的元数据编程观点来看,是远远不够的。就基本的、惯用模式的CRUD,我觉得应当只给出表名、字段名、字段对应的组件即可完成。当然,对于更复杂的情形,依然允许通过定制各种属性,接入Java程序来实现。

  给你提一个方向,试想,先抛弃你提到的所有的框架,如果他们给了你困扰的话,不要为了用它而用;然后想想我上面说的目标是否能实现? 我觉得不是那么难的。也欢迎其他人来思考这个问题。
   
0 请登录后投票
最后更新时间:2007-01-16
Lucas Lee 写道
我对你的框架的主要意见在于:你采用代码生成的方式。
  这个方式我认为对于开发速度和可维护性的好处很有限。原因在于,如果你修改了自动生成的代码,那么难以再次使用生成代码同时保持改动不被覆盖。对于代码生成,我认为比较合适的是EJB那样不需要程序员维护的自动生成的代码。

上面这段话我很认同,所以我也没有采用代码自动生成的方式,我采纳你的建议,是以前你回复的这部分:

引用
我的建议还是一样,"元数据"化。
你看你的代码,都已经整理得比较清楚了,我估计你再写几个模块,代码模式、结构基本都一样。(我也经过这么一段时间,开始觉得很规整,有点软件工程的意思,但后来觉得枯燥,总是在重复。)那么究竟哪些地方不同呢?无非就是表名、字段、字段类型等等这些“元数据”是不同的,那么将这些元数据抽取出来,单独定义,那么剩下的不就是完全一样的东西么?那么不就能做成一个统一的唯一的程序么?
就是说,一个支撑、解释程序,加一些元数据,可以构成一套完整的系统。

我这里还没有提及灵活性、扩展性的部分,但至少就简单的数据维护部分,上述思路是适用的。


非常感谢你的关注和耐心!
   
0 请登录后投票
最后更新时间:2007-01-15
我忽然感觉遗漏了一个问题,就是代码行数的问题,如果以我demo中的那个例子来看,还不足以说明,因为如果精通Hibernate,像级联保存,级联删除,自动级联更新,这些在Hibernate配置文件里面配好,可能更省代码,而且也会快速开发。
但是,国内真实的项目环境并不是这样,往往精通,或者熟悉Hibernate的都是少数,毕竟中国的软件开发环境大多数是中低端。
另外,我去掉了持久层,只保留的dao helper,是因为,我发现业务比较简单的系统,往往开发人员喜欢在持久层里面写所有的业务,service层只是dao的一个delegate,非常thin。要是这样,我就干脆去掉一层算了,这便是dao helper的由来。
另外,对于一个项目,老板最关心的,不会是技术,而是成本和期限。如果你的团队能力是一个客观水平,除了培训,你怎样设计整合一个框架,让大家都能快速上手,并且减少技术不透明性,减少代码调试时间(Map很容易调试),可能是一个必须面对的客观现实。
我之所以要将Hibernate复杂关联隐射都去掉,而采用手工,主要就是想降低开发难度,把hibernate操作给隐藏起来,毕竟开发是一个团队的事情。我想取得技术和成本的一种平衡。
   
0 请登录后投票
最后更新时间:2007-01-16
以前的项目经历告诉我,实际的软件开发、实际的项目过程,实际的软件架构,特别是在这个占中国软件业非常大比重的外包行业,它们和书本上告诉我们的有很大出入。而且,我们往往忽视了商业项目和产品、系统软件开发的区别。
我还是很认同一些经典书籍的理论,譬如对我影响很大的两本书:
《Applying UML and Patterns》
《Patterns of Enterprise Application Architecture》
但是,如果我们完全照搬,是非常危险的,虽然Martin Fowler先生总是提醒我们要结合实际Context。经常上Javaeye的朋友,一定会发现这样的帖子关注点总是最高的:用Struts+Spring+Hibernate开发时,对PO、dao和service的理解(往往是service太thin了)。就说现在的一个帖子吧:http://www.javaeye.com/topic/29867

就像你去人才市场,你觉得企业的宣传广告你很信吗?不过,我们的简历,特别是毕业时候的,又有多少是符合实际的呢?有个60%就很不错了。

Pragmatic 和theoretical,对于我们同等重要。
   
0 请登录后投票
最后更新时间:2007-01-16
我也有过类似的经历,在刚开始的项目中,Service和DAO两层是分开的,并且DAO层用了接口,结果发现Service几乎全部被架空了。所以后来的项目进行了改进,将Service和DAO两层合并,也不用接口了,因为几乎所有的业务逻辑都是数据库操作,而且数据库变更的可能性极小。
在前端表现层我用的是FreeMarker,比起JSP来开发时效率更快,里面的Macro机制使得很多页面显示逻辑得以封装,整个团队的开发效率可以进一步提升。另外,修改时还没有编译过程,节省时间,报错定位也很准。
这种情况下的开发可能不OO,但在某种程度上可以适合一批项目的开发,团队也容易控制。
   
0 请登录后投票
最后更新时间:2007-01-16
受教了:)
但感觉这种Map+hbm.xml的方式并不比POJO+annotation的方式快,而且维护成本更大。 写个POJO,用IDE生成get,set方法很快就搞定了。以前也试过写个程序用map来描述数据,过了几个月后再去改这个程序时,还要回头查某个map里面到底有些什么key,如果是pojo,一目了然。
个人觉得手动维护hbm.xml文件简直就不是一般的麻烦,现在一般都是用annotation或用xodclet生成
   
0 请登录后投票
最后更新时间:2007-01-16
大体看了一下,侧重于技术角度,好象是在SSH的基础上多了一些对Webwork,Hibernate的封装,建议和SSH对比着说一下,更能说明“快速开发”的本意。 :(  :( 
   
0 请登录后投票
论坛首页 Java版 企业应用

跳转论坛:
JavaEye推荐