论坛首页 入门技术论坛

一种快速开发的Java Web架构设计和实现(续)

浏览 23720 次
该帖已经被评为新手帖
作者 正文
   发表时间:2006-12-10  

这篇文章写得太粗糙,不想浪费大家时间(原帖删),还是看我后来的改进版吧:
http://www.iteye.com/topic/47085
虽然它有很大的局限性,但还是值得参考,批判去看它吧。
本文的评论还是值得一读。

-----------------------------------------------------------------------
本来彻底是将本文删除的,    但还是考虑到最初想法的雏形,就还是贴出来,仅做备份。


1、 架构的抽象
     我先总整体上概括出一些抽象,然后从三层架构逐一说明:
* 计算机所做的任何工作,都是遵循这样一个过程:输入-》处理-》输出,大至应用架构,中至分层框架,小到对象方法、函数。
* 任何对象,对于其数据(属性),都可以用Map结构的key-》value表示。如简单的JavaBean,复杂的对象,其value是另外一个Map或List。
* 任何对象,其集合都可以表示为List。

从上面的抽象来看,我所说的架构,也完全可以看成Map输入,Map或List输出,最终的输出就是html页面。
下面,我从分层的角度再逐一抽象:

1.1 持久层
我之所以先要说持久层,是因为它不依赖于其它层,这样更容易理解。
持久层是和数据库打交道的,我所指的持久层,其实不是完整的DAO模式实现,更不是ORM,只是一些Helper类, 确切的说,只要两个Helper类,一个是QueryManager,负责Retrieve,包括read和list两个只读操作,另外一个就是PersistenceManager,负责Create、Update和Delete,也就是完整的CRUD。

* 所有的数据库操作,完全可以抽象为上面的CRUD操作。譬如审批流程的通过和驳回,都是update操作。
* 对于CRUD操作,其输入是sql语句和sql查询参数,而且,sql查询参数,其实就是一个Object数组,譬如插入Database的User表,其参数是username,password,email,age,createdTime等,age是一个Integer对象,createdTime是一个Timestamp类型。
* 对于Retrieve操作,其输出是单个对象或单个对象的集合,如loadUserById和listAllUsers,而这两者可以表示为Map和List。
* 对于其它操作,可以返回void,或int,也就是update的条数,但int也可以用Map包装。

1.2 业务层(服务层)
  业务层,也就是Service层,处理单个或复合的QueryManager+PersistenceManager,或者处理像email通知等。譬如创建一个用户,其业务流程可能是:自动为其创建账号信息,注册其blog,发送一封激活邮件,这些全部在一个事务中。
* 对于业务层,其输入就是一个Map对象,而这Map对象主要来自于Web输入。而且,可以在这个Map里面添加其它对象,如createdTime。
* sql语句,再加上sql语句的参数,就完全可以持久化一个对象,或执行查询,而该sql参数,就来自于Map输入,这个赋值过程可能需要类型转换,但很简单。
* 对于service方法执行的最终结果,如果是数据查询,就是Map或List,对于数据操作,一般是void。而该返回值,就是Action所需要的。

1.3 表示层
     表示层,我这里主要指Web层,也就是和Web服务器打交道的那部分。
     它负责调用service层的service方法,如loadUserById,将其结果返回到前端表示层页面,service可以看成MVC的M。
* Web层的输入,如创建用户的表单form,其实,就是一个键值对,而这完全可以表示为一个Map。如果对于checkbox这类输入,譬如用户的业余爱好hobby,可以看成key=hobby,value为一个字符串数组。如果查询参数只是一个id,当然就是Map的size为1了。
* Web层调用service的输出,如果是单个对象,譬如用户的个人信息,可以看成一个Map,如果是用户列表,就是用一个个Map填充的List。
* Web层Action的处理方法,如execute(),输入路径是一个url地址,输出也是一个url地址,也就是说该execute方法,是处理Map输入,而其return是一个String类型的url, 它对应一个page页面,如userDetail.jsp。但是一定要将service调用的返回值Map或List填充上。Action就是对应MVC的C。
* 结果页面负责显示Map或List数据。这也就是MVC的V的概念。Note:WebWork和模板引擎就是这么做的。

以上就是所有的抽象,总结起来,大概就是:
* 在表示层,页面表单输入Map,在action里面处理,action里面调用service层的方法,输出Map、List或void。
* Service层的service方法输入基本为页面表单的Map,转化为sql参数,然后执行sql。输出Map或List或void。
* Persistence层的Helper方法,输入sql和sql参数,输出Map、List或void。

  而上面的输入Map和输出Map或List都不用处理,不用Bean to Map,也不用Map to Bean,直来直往就可以了,而且添加、修改map里面信息也很容易。
  Map to Bean这些冗余和复杂,极易出错的行为,就是Web框架如Struts的ActionForm,Hibernate的Model几乎必备的。

2、 架构的实现
     如果上面的抽象原则大家都能够认同,那么实现就不难了,而且我们也完全可以运用成熟的第三方框架,来形成我们的整体架构。不过,单独实现,形成自己的产品,也不是很难。
     贯穿在整个架构中,有两个核心概念:弱类型和容器。弱类型我是从Javascript和Ruby语言中受启发,通过Map引入到Java中来的。容器我是借鉴Lisp语言的,因为Lisp语言的原意就是:LISt Processor。
    
     持久层的实现
     持久层,主要就是将数据库的查询出来的ResultSet,转化为Map或List。由于项目时间关系,我暂时在Spring的JDBC框架上,加工出两个Helper类,处理所有sql操作,另外,事务处理直接用Spring的声明式事务。
     对于项目开发人员,都无需了解Spring,只需要了解两个Helper类的使用就ok了,事务最后由一个人加上就ok了。也就是说,省去了大部分持久层开发,这个占项目40%左右的工作量。
    
     业务层的实现
     业务层也非常简单,就是取出Web层传入的Map某些值,组成sql参数,执行sql语句就ok了,不过sql语句可能有一定难度,但去理解由Hibernate配置文件和Model生成的sql,难度小很多,而且效率也很高,即使Hibernate有Cache机制,但用好的恐怕不多。iBatis这种和Hibernate并肩的ORM框架就是手写sql的。
    
     表示层的实现
     表示层也非常简单,因为用户输入表单提交的结果Map,我只需要一个RequestParamUtils.getInput(request) 工具方法,就可以得到,而且直接将它传给service层,省去了像Struts里面的ActionForm,那冗余的字段,get、set方法。
     表示层验证:特殊的可以用validate的Java代码,一般用Javascript就ok了,而且Javascript我会形成一个类库,很easy。我们不打算用Validation框架。如果数据验证设涉及完整性,一般考虑在Database上加约束。如果用户禁用Javascript如何?请问,我们会没事把自己的电脑设置在安全模式下吗?而且IE禁用Javascript也不容易,你试试看。别告诉我我们网站的潜在用户有用IE4.0的。
     我现在是用Webwork,不过在前一个项目中,我自己实现了一个MVC的Web框架,很好用,而且不像Struts存在线程安全的问题。之所以要用Webwork,那是我担心我的framework还需要扩展。但使用Webwork,只使用其最基本的一部分,差不多就只用到其20%功能,而且是最简单的,新手一学就会,只需要理解了打印helloworld就可以胜任了。另外,我还进行了封装,提取了一个BaseAction。
     对于action处理的结果,如Map、List对象,将之填充于Request或Session对象(BaseAction会做),通过模板技术JSTL,加上JSP展现就可以了,JSTL中的tag可以处理迭代器,日期格式显示等,而JSTL的表达式语言,加上JSP,就是模板了,可以在Dreamweave下,像html那样所见即所得开发,效率超级高。
     分页处理有本架构专门提供的分页tag,项目组成员拿来用就ok了,就如同使用html的input tag一样。
    
     本架构在本人开发的上一个项目中用到部分思想,那个项目是做150多个报表查询,约500条sql语句,这些sql语句专门由一个DBA负责(报表展现的难点在sql),我负责报表展现。实际上,我花的所有的时间不到一个月(10.8—11.6),虽然有两个月期限(另外一个月基本上就是自学其它的,如Lisp、Ruby语言),总共Java代码只有2200多行,包括一个类似于WebWork的Web Framework。也就是说平均一个页面不到15行代码。而且,后来需求引起的变更基本上可以保证半小时内搞定,因为后一个月我就是每天下午5点左右花半个小时修复修复bug,然后发布新版本。
     个人觉得,一个框架或架构的判断标准,主要是“快速开发+适应变更”,另外的附加标准是学习培训成本低。这些对于做外包项目尤为突出。asp开发部署快,学习成本低,但太难维护,特别是运行平台单一,所以逐渐被淘汰。
    
     对于Web开发,个人认为,对于产品和公司自己的项目,Java将逐渐退出其霸主地位,而会被弱类型语言,Python,特别是Ruby的Rails框架所取代,不过对于外包,这种强调团队协作,以及大型应用,Java这类强类型语言还是有其地位,因为它很容易被理解,类型就是注释,编译器检查。对于Web Service,强类型语言有其先天不足,而Web Services是未来的网络计算平台。你看Java最大的赢家IBM在干什么就知道了,它重心已经不在Java,而自觉让位于开源的JBoss。对于.net的Web开发,我觉得市场不是决定于语言有多强,而是Windows这种平台,因为我还没有发现C#比Java有多少创新,VS studio比起eclipse就不是差一个档次(不只是我这么说)。不过,在开发领域,J2EE 大概只有Servlet规范值得骄傲了,不过J2SE和J2ME还是不错。另外一种弱类型语言Javascript,随着Web2.0的兴起,Rich Client的流行,将会是一种必备语言。
     ORM,现在想来,这本身就是一种很蹩脚的东西,数据库都是关系的二维表,为什么就要转来转去的,table-》Object,Object-》table,这样很优雅?很易维护?开发更快?性能更高?我看一项都不具备。因为我们的产品,无非就是输入-》处理-》存储-》处理-》输出,为什么一定要加一个ORM层。
     领域建模,这是学习面向对象的一门必修课。但往往我们就是为了建模而建模,最后模型冗肿,实现困难,还很难维护,牵一动百,怎么去agile?领域建模,我认为主要用它来分析业务,实现未必就要ORM那套,而且,我认为最重要的是数据库E-R图,因为它是一个实实在在的东西,直接决定你的所有上层。
    
     就写到这儿了,忘了说一下,我在该架构下开发一个模块的CRUD操作5个页面,在业务清晰,数据库设计好后,开发不超过一天,工具:eclipse+Dreamweaver。
  当然,一种架构可能只能解决一类或几类问题,但架构下的思想可能解决更多的问题,因为它是超越语言的。当然,我所陈述的思想,也只是对前人的归纳总结,灵活运用罢了。
    
    
    
    
    
    
    
    
    
    
    

   发表时间:2006-12-10  
Ruby是强类型好不好?你不但完全放弃了静态类型,还放弃了强类型。倒真是把Java长项都阉割掉了啊。不男不女的Java能闹腾到哪里去呢?我倒想看看。
0 请登录后投票
   发表时间:2006-12-10  
zwchen 写道
就写到这儿了,忘了说一下,我在该架构下开发一个模块的CRUD操作5个页面,在业务清晰,数据库设计好后,开发不超过一天,工具:eclipse+Dreamweaver。


这叫快速开发???瀑布汗啊
就用最常见的SSH开发这几个页面能超过半天吗?

btw:花时间又看了一下楼主的帖子,简直就是$@$%^%$^
0 请登录后投票
   发表时间:2006-12-10  
to taowen:Ruby我刚刚学习不久,如果理解粗浅,请原谅,但Javascript这中弱类型语言应该没错吧。对静态类型和强类型的区别,我一直比较模糊,希望得到你的指点。另外,用Map处理对象这种思想,我应该是受Sun的Petstore启发的,不知道你对它的event有何看法?

to netfishx:
引用
最常见的SSH开发这几个页面能超过半天吗?
,是的,你说得没错,那时因为你对这些东西都很熟练。
不过我们必须面对开发完后的变更,特别是项目组新手较多的情况,譬如50%人没有接触Java或是应届生,这才是我现在做的项目最突出的问题。我们项目组有人做了一个自动生成代码工具,但里面的细节,要了解就很困难了,譬如Hibernate的cascade和inverse怎么配置。你不能以你的能力去要求别人,项目中学习成本的问题我必须去面对。
0 请登录后投票
   发表时间:2006-12-10  
一天,呵呵。把时间都浪费到数据库了
0 请登录后投票
   发表时间:2006-12-10  
spartak 写道
一天,呵呵。把时间都浪费到数据库了
我们现在开发的项目,sql一般都比较简单,去让团队新手了解Hibernate生成的sql,那种学习成本太高,时间也不容许,特别是处理关联表的时候。以我若干项目的经历,团队成员花在hibernate上的时间太多,但没有什么实质性的改进,譬如一个lazy loading问题。
我们面对的外包领域,这个和一般项目的不同。
0 请登录后投票
   发表时间:2006-12-10  
这样做,不用配置orm映射,前端不用web框架,直接讲request中的参数组成一个map。却是减少了一些工作量,刚开始用这种做法估计也会觉得很快。

楼主的流程应该是这样的:

页面数据 - 收集倒map - dao负责写入数据库 OR 从数据库取出数据映射倒map上。

优点:
  1,节省代码和配置文件,比如orm的映射文件和po。(这个确实能减轻不少工作量)
  2,开发人员不需要掌握其他的框架技术,比如hibernate,ibatis,ww之类。

缺点:
  难维护,因为我在你的action和service里面看到的,所有处理的数据都是map,很难确认这个map具体包含了什么东西,而且在dao层也非产容易出错。比如map包含了用户信息userName->aaa,userPass->bbb,regDate-2003等几个字段,万一我不小心get的时候大小写写错?或者记不清到底map里面包含有那些字段? 如果是明确的po对象,根本不用担心这个问题。

另外,楼主的这个报表项目并不能算一个很好的例子,我也做过类似的项目,报表非常多,但是一般这种报表都是通过存储过程或者sql已经处理好,前端只需要取出数据展示就OK了,基本上没有什么业务逻辑在里面,最多就是权限验证和输入参数的组合。如果稍微复杂一点的逻辑,比如某个业务方法里面,涉及倒多个po,如果这几个全是map对象,god,我想起来就觉得冒冷汗。

0 请登录后投票
   发表时间:2006-12-10  
引用
所有处理的数据都是map,很难确认这个map具体包含了什么东西,而且在dao层也非产容易出错
这个我也有同感,我希望开发人员自己去约束,保证service层测试完全通过。
引用
楼主的这个报表项目并不能算一个很好的例子
这确实是一个有点偏颇的例子,不过,报表确实有一些比较复杂的地方,譬如各频道首页的一个内嵌的table报表,报表的每个字段都有链接list页面,如日、周、月、年、走势图,而且list链接并没有什么严格的规律,list链接里面还有detail链接。
0 请登录后投票
   发表时间:2006-12-10  
引用
比如某个业务方法里面,涉及倒多个po,如果这几个全是map对象
,这个我想把这几个子map字段拿出来,和原来的map同级,表现上不形成Object Graph。我不想一Object为中心,而是plain的table。
0 请登录后投票
   发表时间:2006-12-10  
  我所表达的架构,姑且就这么称呼吧,就是foxty所理解的。
  我之所以要采取这么很不优雅的方式,是因为我们项目团队新人太多,而且不稳定,项目技术不难,但工期紧。让项目组成员了解所谓的领域模型,并用对象的方式实现,太难。
  我只希望项目能构快速实现,并且很容易测试(Service层必须单元测试),而且是项目经理和客户方所最关注的。
   我以前也是追求优雅的架构,但我发现,真正面对现实,至少是外包项目的现实,那种架构实现起来耗时、难维护,出错多。
  Hibernate这种东西,我用了两年,用好确实还不错,但不能只你会啊?因为我是在一个团队里做,别人不会用的时候,就会从session里面得到connection,然后sql操作,绕开做,你咋办?因为你没法要求他们很快就熟练啊。也许,这和很多人的项目经历不一样。
 
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics