基于JavaEE架构模式下的MVC

基于SSH2框架构建JavaEE应用程序

——作者:李赞红 (lifenote@21cn.com),转载请保留版权!

http://www.blogjava.net/lzhidj/archive/2011/02/21/344613.html

http://www.blogjava.net/lzhidj/archive/2011/02/21/344754.html

http://www.blogjava.net/lzhidj/archive/2011/02/26/345219.html

一、前言

虽然Struts2+Spring3+Hibernate3三大框架上市已久,但廉颇未老,目前仍然是众多JavaEE应用程序的首选技术。本文将向您详细阐述SSH2应用程序的构架、思想、技术与技巧,打通JavaEE的任督二脉,行文力求通畅简洁,希望能让读者豁然开朗,快速掌握JavaEE应用程序的开发流程与关键技术。

本文并非面向无Java基础的读者,您需要掌握一定的编程思想、Java技术和Struts2、Spring3与Hibernate3等框架技术,并了解Oracle等DBMS。

二、JavaEE体系架构

JavaEE体系架构采用传统的MVC设计模式,分为Model、View、Controller三层,其中:Model即模型层,定义数据模型和业务逻辑。为了将数据访问与业务逻辑分离,提高业务精度,降低代码之间的耦合,模型层又细分为DAO层与业务层,DAO全称为Data Access Object(数据访问对象),将数据库访问代码封闭起来,Hibernate API也在此封装,不再出现在其他层或向其他层暴露;业务层是整个系统最核心也最具价值的一层,该层封装应用程序的业务逻辑,处理数据,关注客户需求,在业务处理过程中会访问原始数据或产生新数据,或者需要持久化数据,DAO层提供的DAO类能很好地帮助业务层完成数据处理,业务层本身则侧重于对客户需求的理解和业务规则的适应,自然也包括大部分的计算,总体说来,DAO不处理业务逻辑,只为业务层提供辅助,获取原始数据或持久化数据等操作。View即视图层,为最终用户提供一个友好的交互界面,用户可以查看请求结果,也可以通过表单等交互手段实现数据录入。Controller层即控制器,控制器是Model与View的桥梁,将二者很好的衔接,通过View接收用户数据,Controller将数据传输给Model,Model对数据进行处理;或者Model读取数据后,Controller将数据传递给View,View向用户展示数据。一来一往,Controller成了Model与View之间的快乐使者。

关于MVC更多的解释请参考百度百科链接(http://baike.baidu.com/view/31.htm),下图展示了MVC的结构:


  

从调用关系上看,左为高层,右为低层,下为高层,上为低层,高层可以调用低层,但低层不能调用高层,层与层之间的调用是单向的。

从数据传输上看,数据可以从视图层传输到DAO进而保存到数据库,也可以从数据库中读取数据进行处理或者显示,所以,数据的传输是双向的。

从技术实现上看,视图层使用HTML/JSP组件实现,控制器使用Servlet或Action组件实现,模型层使用JavaBean组件或EJB技术实现。

Hibernate封装在DAO层,负责数据访问操作;Struts充当控制器角色,对用户数据进行合法性检验和类型转换,为视图层提供标签简化页面显示,提供国际化支持等等;Spring是应用程序的管家,DAO、Service(业务)、Action等对象由Spring创建并维护各对象之间关系,同时提供声明式事务管理,简化事务编程。

三、如何理解业务?

软件工程中的业务与日常生活中的业务不同,平常的业务偏指销售,指需要处理的事务。这里的业务涉及一个以上组织,按某一共同的目标、通过信息交换实现的一系列过程,其中每个过程都有明确的目的,并延续一段时间。将范围更局限一点,业务是指为完成某个事务而进行的一系列活动,需要对象的参与并按照一定的规则触发一系列行为。

业务包含三大元素:对象、行为和业务规则。

对象是主体,是行为的触发者,这里的对象可能是用户、订单、产品或DAO对象,也可能是外部系统,比如支付系统。一个业务的完成,可能需要一个对象参与,也可能是多个对象参与,这取决于业务本身。行为是对象的动作,多个行为按照一定的顺序执行,构成业务完整的执行能力。业务规则是对业务的一种约束,每一个行为所产生的结果必须对规则负责,规则监视对象、行为以及业务执行的结果,保证业务的正确性和准确性。

我们以取款业务为例,来分析本业务中涉及的对象、行为和规则。

取款业务描述:输入用户账号(银行刷卡完成),提醒用户输入密码,银行系统核对密码是否正确,如果正确,用户输入取款金额,系统更改账户金额并记录取款信息,打印票据,取款完成。

这是一个司空见惯的业务,从这个业务中,我们提取出如下的元素信息:

对象:账号,交易,打印机;

行为:核对卡号和密码,更改账户余额,记录取款交易信息,打印票据;

规则:一次最多只能取2000元,一天最多只能取5次,取款金额大于50000需要预约; 

根据以上的分析,我们很容易转变成源代码。很多时候,我们遇到的业务比这个要复杂得多,项目的规模也很大,这就要求我们具备全局观,控制全场,运筹帷幄,仔细分析,辅助建模,达到最理想的效果。

四、数据传输、数据模型与Dozer

数据传输是程序员实现各种功能时刻需要考虑的问题,从数据模型的建立,到数据模型的转换,从数据的合法性验证,到数据类型的转化,我们要时刻小心,精心设计与组织。数据模型与数据传输可简单可复杂,完全取决于设计者的经验与意图,当然,项目的规模也是我们应该考虑的因素,一个小型项目实在没必要将问题复杂化。

我们首先考虑数据从视图(View)传输到数据库(DB)的数据模型变化过程。用户从界面输入数据,此时,数据毫无结构可言,是零散的、无组织的,数据提交到控制器后,一方面考虑到OOP的严谨性,另一方面考虑到数据的封装,会将零散的无组织的数据封装成Value Object(简称VO)对象,VO就是JavaBean,并传送到业务类中,在数据模型比较复杂的情况下,业务类的方法参数和返回值都应该是VOVO的集合,VO转换成POPersistence Object)后传送到DAODAO调用Hibernate API持久化数据。简单来说,业务类对外暴露的数据模型是VODAO对外暴露的数据模型是PO


数据从数据库传递到视图层的数据模型变化恰恰相反,Hibernate将数据库中的记录转换成POPO传递给业务类后转换成VOVO被传送到视图层进行显示处理。


以上转换过程如下图:



  

为什么同时需要POVO?前面说过,这不是必须的,如果项目比较小,直接使用PO就行了。但PO有如下缺点:

PO反应了数据库的物理模型,向外暴露PO存在一定的风险;

PO过于僵化,无法适应变化莫测的业务需求;

PO在持久化状态下与数据库同步,可能导致数据意外修改;

PO将导致程序缺乏健壮性。

VO没有PO的缺点,相对而言,VO更加灵活,能适应各种需求的变化,下面是POVO的区别:

VO是用new关键字创建,由GC回收的。PO则是向数据库中添加新数据时创建,删除数据库中数据时削除的。并且它只能存活在一个数据库连接中,断开连接即被销毁;

VO是值对象,精确点讲它是业务对象,是存活在业务层的,是业务逻辑使用的,它存活的目的就是为数据提供一个生存的地方。PO则是有状态的,每个属性代表其当前的状态。它是物理数据的对象表示。使用它,可以使我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换;

VO的属性是根据当前业务的不同而不同的,也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。PO的属性是跟数据库表的字段一一对应的;

PO一般只有一个,但对应的VO可能有多个。

我们举一个简单的例子来说明VOPO的区别:比如要实现用户注册与登陆的功能,在物理模型中创建一个用户表,字段分别为用户ID(标识列)、用户名、密码、注册日期等等,定义PO时应该有这四个字段的映射属性。现在,我们来实现用户注册这一功能,为了接收用户输入的注册信息,必须定义VO,注册用户需要输入的信息有:用户名、密码1、密码2,这正好是VO的属性。而实现用户登陆功能时,用户需要输入的信息只有用户名和密码,所以,该VO的属性只有两个:用户名、密码。可以看出,PO侧重于物理模型,而VO则更关注用户的实际需求。如下图所示:

因为数据传输是双向的,所以VOPO之间存在相互转换的问题,这会给编程带来麻烦,我们总是要通过getter方法取出数据,再通过setter方法给对方属性赋值,在属性很多的情况下,让人深感繁琐,而Dozer工具能简化这个问题。

Dozer(http://dozer.sourceforge.net/)是一个JavaBean映射工具,能实现对象属性值之间的相互赋值,Dozer支持简单类型映射、复合类型映射、双向映射以及递归映射,默认情况下,Dozer能实现类型相同、名字相同的属性之间的赋值,如果属性名不同,则必须在xmldozerBeanMapping.xml)配置文件中指定,如果不必为JavaBean的每个属性赋值,也可以在xml中指定。Xml的定义可以参考http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd

五、面向接口编程

面向对象编程(OOP)最有魅力对开发技术改进最有影响力的就是接口,接口定义一套规范或标准,如何实现取决于他的实现类,这给开发人员提供了足够的灵活性,大大增强了系统的可塑性,为开发人员提供了更加先进有效的扩展性能。JDBC是接口应用最典型的示例之一,Java JDK定义了一组接口,如ConnectionStatementResultSet等,但并不作出具体的实现,事实上,sun公司也没有能力去实现,因为不同的DBMS有不同的数据组织格式,如何连接数据库、如何执行SQL语句、如何处理返回的结果集,sun公司无从知晓,所以,通过接口实现数据访问的工作巧妙地转移到了数据库提供商,不得不说这招实在高明。

在我们的系统中,合理利用接口的特征能有效提高系统的扩展性和灵活性,尤其Spring的出现,通过对IOC的强化,更加突出了接口的作用,学习者更容易从代码级别理解接口的意义。Spring无污染非侵入式的设计,降低了组件替换和功能扩展的成本,为开发人员打开了程序开发的另一扇窗。

并不是系统中的所有组件都需要提供接口,定义接口时应该先明确功能扩展点,对于那些比较固定有一定格式的代码则无需定义接口,通常情况下,为业务类定义一个接口是不错的选择,业务变更是家常便饭,为业务类提供接口有利于我们主动适应业务的变化。

Eclipse将“重构”做得比较强大了,如果你的业务类还没有接口,通过“Refactor->Extract Interface”菜单可以自动为业务类生成相应的业务接口。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值