第四章 LPMT中的MVC多层架构实现
在LPMT中,为了使得系统可扩展、易维护、易修改、结构清晰,我们采用MVC设计模式,构建多层的体系结构,通过各层的协调配合,实现系统需求。
在LPMT中,我们设置了View、Controller、Logic、Model、Data Persistence五个逻辑层,通过各逻辑层之间的配合,应用MVC设计模式,实现系统需求。
4.1.1 Data Persistence
也称数据持久层。数据持久层负责数据库映射,隔离数据库操作,将数据库操作设计成Java APIs。此逻辑层可由Hibernate、DAO、Connection Pool等多种技术实现。在LPMT中,我们采用Hibernate技术,将数据库的表映射成为对象(issuecontrol.objects 包),比如Issue表对应Issue.java、Flag表对应Flag.java、IssueData表对应IssueData.java等,将对数据库的各种操作封装在IssueDAO和UserDAO两个DAO类中。DAO类代码见附录。
4.1.2 Logic
也称事务逻辑层。在一个规范的J2EE架构中,不同层的数据表示应该被限制在层内,而不应该扩散到其它层,这样可以降低层间的耦合性,提高J2EE架构整体的可维护性和可扩展性。比如说View层的逻辑进行了修改,那么只需要修改ActionForm的结构,而不需要触动Data Persistence层和Logic层的代码修改。同样,当数据库表进行了小的调整,那么也只需要修改Data Persistence层数据表示,而不需要触动Model层代码和View层代码。因此,我们在Model层和Data Persistence层中间插入Logic层,以降低Data Persistence和Model层之间的耦合关系。Logic层类以Bean结尾,如IssueBean.java、UserBean.java。Model通过Logic Bean调用Data Persistence层的DAO类,实现对数据库操作以及其他业务逻辑操作。Bean类代码见附录。
4.1.3 Model
也称对象层。包括所有Action类、ActionForm类、和其他显示类(issuecontrol.actionform包、issuecontrol.action包、issuecontrol.view包)。
issuecontrol.actionform包中的所有类都派生自ActionForm,与View层的表单页面一一对应,用于定义客户端显示表单,封装业务数据,在Logic、Model、Controller、View各逻辑层之间作为数据传输媒介。在J2EE架构里面,ActionForm可以由Entity Bean和Session Bean来表示,以期实现业务逻辑重用。
Issuecontrol.view包为显示包,其中的类为显示类,负责特殊对象表单的显示,比如分页显示的Issue等。
Issuecontrol.action包中的所有类都派生自Action,用于封装具体的处理逻辑,调用Logic层的业务逻辑类,实现业务操作,读写ActionForm类,并将结果返回View层显示。部分Action和ActionForm见附录。
4.1.4 Controller
也称控制层。包括ActionServlet、ActionMapping、RequestProcessor等Struts类。ActionServlet负责接受用户请求,并将用户请求引导到正确的页面。ActionMapping包含ActionServlet的目标映射,RequestProcessor负责与Action交互。ActionMapping的目标映射在struts-config.xml配置文件中完成。Struts-config.xml代码见附录。
4.1.5 View
也称显示层。由JSP页面组成。每个JSP页面由HTML和Struts 标签库实现控制和显示逻辑。部分View代码见附录。
上述各逻辑层各司其职,互相配合,尽量降低逻辑层之间的耦合性,提高内聚性。图11为上述逻辑层之间的关系。

(1) 客户端向服务器提交Http请求
(2) ActionServlet接受客户端提交的Http请求,载入属性文件(Properties files),选择目标Action,将控制权交给RequestProcessor。
(3) RequestProcessor根据URL和struts-config.xml中的actionmapping寻找相应的Action;新建或者复用对应的ActionForm,封装包含在请求信息中的表单属性,检查数据的合法性,并将ActionFor传递给目标Action。
(4) 目标Action接受传递过来的ActionForm,读出ActionForm里面的属性,调用Logic层的业务逻辑Bean,而Bean则调用相应DAO类的方法,进行持久对象的持久化操作,最终完成相应的业务逻辑操作。操作过程一旦出现异常,Action也将一并处理。在完成业务逻辑操作后,Action返回一个ActionMapping对象,告诉RequestProcessor指向目标View页面。
(5) View页面利用既定接口,将结果显示给客户端。
下面以分页显示所有Issue为例,说明上述各逻辑层之间是如何配合以完成既定操作。
(1) 客户端向服务器提交URL请求:http://localhost:8000/issuecontrol/issueAction.do
(2) ActionServlet通过RequestProcessor将请求提交到IssueAction
(3) Action判断此次的请求类型为显示,参数action = “view”;通过HttpServletRequest.getParameter方法取得当前页码viewPage;假如viewPage为空,则当前页码为1;设定每页显示的行数pageSize = 8;调用IssueBean.getIssueByPage(new Page(pageNum,pageSize))方法,将结果封装到一个PageView对象selectPageView中;调用httpServletRequest.setAttribute方法将结果放到HttpServletRequest中;返回一个ActionMapping对象,内包含目标映射viewIssue指向IssueList.jsp,将控制权交还给RequestProcessor。IssueBean.getIssueByPage方法是通过调用IssueDAO.getIssueByPage方法完成其业务逻辑的。
(4) RequestProcessor将页面导向IssueList.jsp。
(5) IssueList.jsp将结果显示在客户端。
Action中的代码块示例如下:
String viewPage=httpServletRequest.getParameter("viewPage");
int pageNum=1;
String address = "viewIssue";
//查看所有的Issue
if(("view".equals(action)) || (action == null)) {
address = "viewIssue";
if((viewPage!= null)&&(viewPage.length()!= 0)){
pageNum = Integer.parseInt(viewPage);
}
int pageSize=8;
//1.先进行参数分析
//2.下面调用逻辑层方法得到显示的对象
PageView selectPageView=IssueBean.getIssueByPage(new Page(pageNum,pageSize));
//3.放到request中然后转发
httpServletRequest.setAttribute("items",selectPageView.getItems());
httpServletRequest.setAttribute("selectPageView",selectPageView);
httpServletRequest.setAttribute("action",action);
}