第7章 Servlet与MVC开发模式

第7章 Servlet与MVC开发模式 前几章已经学习了servlet和JSP的基础知识。本章将重点阐述MVC的基本思想以及servlet在MVC开发模式中的作用。最后,通过几个案例的介绍,使读者更加深刻的理解MVC开发模式。 7.1 MVC开发模式 7.1.1什么MVC MVC的全称为:Model-View-Controller,即把一个应用的输入、处理、输出流程按照模型(Model)、视图(View)和控制(Controller)的方式进行分离,这样一个应用被分成三个层——模型层、视图层、控制层,简称MVC,它是一种软件体系结构。 MVC思想,其实并不是软件设计、更不是Web应用系统的专利。在工程设计、生产管理甚至日常生活中,到处都有体现这种思想的例子。 例1:工程设计模式—汽车架构。 V:各类仪表(包括速度表、油表等)。M:发动机,动力传递系统等。C:油门,刹车等。当事件(踩油门)发生,导致M层工作(转速增大),最后在仪表中得以体现。这种工作模式实际上是:C—M—V模式。 例2:生产管理模式—服装厂。 V:各类生产报表。M:生产线;C:生产指令。显然,它也是一种C—M—V模式。 气象现象实际上是一种M—V模式,M指的是地球(包括大气、海洋等)本身运动规律。V指的是当天的温度、湿度、风速等。当然,如果加上C层,就成了人为控制气象了,如人工增雨作业。 当然,MVC模式也广泛应用于各类软件设计。MVC应用程序总是由这三个部分组成。而且往往是Event(事件)导致Controller改变Model或View,或者同时改变两者。只要Controller改变了Models的数据或者属性,所有依赖的View都会自动更新。类似的,只要Controller改变了View,View会从潜在的Model中获取数据来刷新自己。MVC模式最早是smalltalk语言研究团提出的,应用于用户交互应用程序中。并不是仅仅针对Web项目的。具有窗口风格的桌面应用程序,大都是MVC模式软件, 就以常用的Microsoft Word为例。如果从设计者的角度来看这个软件的结构,显然,V层是用户唯一可见的,也就是用户打开Word时,所看到的那个图形界面,它包括用于输入的各类界面,即可以操作的按钮及菜单等。M层,用户不可见,对于某一个文档,它都有相应的模型,保存了诸如文本内容、字体的大小、背景色、纸张大小等信息,用来确保用户每次打开文档时,能正确显示。此外,Model还要定义一些规则,例如,当用户添加、删除某些文本、改变了背景色等,此文档的模型要则发生相应的变化。对于Controller,用户不可见,它的作用就是管理Model及View 之间的交互。用户在键盘上输入了某个字符,用鼠标选取了某个菜单项,这一动作并不是直接就传送给了屏幕上的界面,而是作为一个请求发送给了Model。Model针对这一请求发生相应的变化,变化的信息再由Controller 返回给View,这时才看到操作的结果。它是由事件来触发M层方法,实现视图的改变。 综上所述,MVC开发模式,实际上,是软件体系结构的一种模式,其基本思想是基于软件分层和面向对象的设计理念,其最终目的是提高软件系统的共享和可维护性。 7.1.2 Web项目中的MVC 早在70年代,IBM就推出了Sanfronscisico项目计划,其实就是MVC设计模式的研究。近来,随着J2EE的成熟,它正在成为在JavaEE平台上推荐的一种设计模型,也是广大Java开发者非常感兴趣的设计模型。MVC模式也逐渐在PHP和ColdFusion开发者中运用,并有增长趋势。随着网络应用的快速增加,MVC模式对于Web应用的开发,无疑是一种非常先进的设计思想,无论你选择哪种语言,无论应用多复杂,它都能为理解、分析应用模型时,提供最基本的分析方法和工具,为构造产品提供清晰的设计框架,为软件工程提供规范的依据。 在Web应用项目中,MVC的定义是区别于其它软件系统的(如桌面窗口程序),以下说明是针对基于JavaEE的Web项目作出说明。 视图(View)代表用户交互界面,可以概括为浏览器界面,一般特指JSP页面,但有可能为XHTML、XML等。随着应用的复杂性和规模性,界面的处理也变得具有挑战性。一个应用可能有很多不同的视图,MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的请求,而不包括在视图上的业务流程的处理。业务流程的处理交予模型(Model)处理。比如一个订单的视图只接受来自模型的数据并显示给用户,以及将用户界面的输入数据和请求传递给控制和模型。 模型(Model):就是业务规则的制定、业务流程的实现等与业务需求有关的系统设计,也即是说它是与系统所应对的领域(Domain)逻辑有关,很多时候,也将业务逻辑层称为领域层。它可以是JavaBean,还可以是普通Java类,也可以是EJB。一个设计良好的M层往往具有标准的应用接口和可重用性。实际上,在具体项目开发中,往往会采用成熟的组件,使得开发效率得以大大提高。模型层在Web项目中,独立性最高,一般情况下,模型返回的数据不带任何显示格式,因此JavaEE项目中,不建议V层直接调用M层。也就是说前几章说明的开发模式(JSP+JavaBean)在MVC模式中是不推荐的。 业务模型还有一个很重要的模型那就是数据模型。数据模型主要指实体对象的数据保存(持久化)。比如将一张订单保存到数据库,从数据库获取订单。我们可以将这个模型单独列出,所有有关数据库的操作只限制在该模型中。该模型为DAO层(数据库访问层),在以下章节中会介绍。 控制(Controller)可以理解为从用户接收请求, 将模型与视图匹配在一起,共同完成用户的请求。划分控制层的作用也很明显,它清楚地告诉你,它就是一个分发器,选择什么样的模型,选择什么样的视图,可以完成什么样的用户请求。控制层一般不做任何的数据处理。例如,用户点击一个连接,控制层接受请求后, 并不处理业务信息,它只把用户的信息传递给模型,告诉模型做什么,并根据模型的计算结果,选择符合要求的视图返回给用户。因此,一个模型可能对应多个视图,一个视图可能对应多个模型,当中,完全由控制层来联系。 在JavaEE中,servlet担当了控制层的作用,显然,它是最合适的人选。因为servlet利用内置对象可以方便地与视图层(JSP页面)进行数据通信(获取用户提交的信息、转发JSP页面相关数据及进行页面重定向等),而且,由于servlet本身就是一个Java类,所以与M层(JavaBean、Java类)的联系是无缝的。所以,servlet担当了一个M层与C层的桥梁作用。 综上所述,MVC的处理过程,首先C层接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回的数据,并通过表示层呈现给用户。以下示意图说明这个过程。 图7-1MVC架构图 7.1.3 关于MVC的总结 以前大部分Web应用程序(用JSP、ASP)都是不分层的。它们将类似数据库查询语句的数据层代码和HTML表示层代码混在一起。经验比较丰富的开发者会将数据从表示层分离开来,但这通常不是很容易做到的,它需要精心的计划和不断的尝试。MVC从根本上强制性的将它们分开。尽管构造MVC应用程序需要一些额外的工作,但是它带来的好处是无庸质疑的。 首先,最重要的一点是多个视图能共享一个模型。现在的项目,需要用越来越多的方式来访问应用程序。对此,其中一个解决之道是使用MVC,无论用户想要Flash界面或是WAP 界面,用一个模型就能处理它们。由于将数据和业务规则从表示层分开,所以可以最大化的重用代码了。 由于模型返回的数据没有进行格式化,所以同样的构件能被不同界面使用。例如,很多数据可能用HTML来表示,但是它们也有可能要用Macromedia Flash和WAP来表示。模型也有状态管理和数据持久性处理的功能,例如,基于会话的购物车和电子商务过程也能被Flash网站或者无线联网的应用程序所重用。 因为模型是自包含的,并且与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。如果想把数据库从MySQL移植到Oracle,或者改变你的基于RDBMS数据源到LDAP,只需改变模型即可。一旦正确的实现了模型,不管数据来自数据库或是LDAP服务器,视图将会正确的显示它们。由于运用MVC的应用程序的三个部件是相互独立,改变其中一个不会影响其它两个,所以依据这种设计思想,能构造良好的松偶合的构件。 另外,控制器的也提供了一个好处,就是可以使用控制器来联接不同的模型和视图,去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。 最后,它还有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化产生管理程序代码。 当然,分层模式也存在着一些问题,具体分析如下。 1、增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。 2、视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。 3、视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。当然,目前有一种新技术可以解决该类问题,如异步通信技术(如ajax技术)等。 4、目前,一般高级的界面工具或构造器不支持MVC架构。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。 7.2 应用案例 本节通过二个案例来说明MVC的基本思想,MVC的工作模式。 7.2.1 一个简单的例子 第五章介绍了把triangle.jsp改造成为JSP+JavaBean模式,本节,在此基础上,改造成为MVC即JSP+JavaBean+servlet模式。在这个案例中,M层(JavaBean)保持原有设计不变,即为类Triangle(用于三角形的面积计算等功能)和类Stringtonum(用于把输入字符串转成三角形的三条边值)。按照MVC模式,设计servlet为TriangleMVC,参考代码如下。 package servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import model.Stringtonum; import model.Triangle; public class TriangleMVC extends HttpServlet { public TriangleMVC() {super();} public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String str=request.getParameter("boy"); Stringtonum angle=new Stringtonum(); boolean flag=false; RequestDispatcher dispatcher = null; Triangle tri=null; if(!angle.strtonum(str)) request.setAttribute("result", "请输入数字"); else{ tri=new Triangle(angle.getNum1(),angle.getNum2(),angle.getNum3()); if(!tri.isTriangle()) request.setAttribute("result", "三边不能组成三角形"); else{ request.setAttribute("result", tri); flag=true; } } if (flag ==false) dispatcher= getServletContext().getRequestDispatcher( "/trierror.jsp"); else dispatcher=getServletContext().getRequestDispatcher( "/trisucess.jsp"); dispatcher.forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } } 上面servlet设计中,其基本处理流程为: 1、获取页面提交的数据(规格化的字符串)。 2、调用业务类Stringtonum的功能,若字符串能转成三条边,则继续以下步骤;否则,进行页面跳转至trierror.jsp,并传值"请输入数字"。 3、调用业务类Triangle的功能,若三条边能组成一个三角形,则继续以下步骤;否则,进行页面跳转至trierror.jsp,并传值"三边不能组成三角形"。 4、进行页面跳转至trisucess.jsp,并把三角形实例(tri)传至该页面。 JSP文件的参考代码如下。 trierror.jsp <%@page contentType="text/html;charset=GB2312" %> <%=(String)request.getAttribute("result") %> trisucess.jsp <%@page contentType="text/html;charset=GB2312" %> <%@page import="modle.*"%> <% Triangle tri=new Triangle(); tri=(Triangle)request.getAttribute("result"); %>

您输入的三边是:
<%=tri.getEdge1()%>
<%=tri.getEdge2()%>
<%=tri.getEdge3()%>

三角形面积是:<%=tri.calArea()%> 主页面trimvc.jsp <%@page contentType="text/html;charset=GB2312" %>

  请输入三角形的三个边的长度,输入的数字用逗号分割:

7.2.2 购物车MVC代码重构 第五章设计了购物车,本节进行MVC模式代码重构。显然M层(JavaBean)保持原有设计不变。另外,由于采用MVC模式,不建议在V层调用M层,所以把购物车(car)放在session中,这是主要的变动。另外,把V层(JSP)拆分成V层和C层,主要任务是设计servlet(即:OderServlet和ModifyMVC)。MVC模式下购物车的流程图如下所示。 图7-2:MVC模式下购物车流程图。 先设计主页面goodsmvc.jsp,参考代码如下。 <%@ page contentType="text/html;charset=GB2312" %>

网上书斋

序号书名价格 简介订购
1computer 12简介
2Cprogram20 简介
3database 30简介

购物详单 主页面基本保持原有设计风格。但提交目的地由原来的JSP页面改为servlet (OrderServlet),另外,应用了一个JS技术,其目的让用户购买商品时多一次提醒和确认。 lookmvc.jsp页面基本保持原有设计风格。同样,提交目的地由原来的JSP页面改为servlet(ModifyServlet)。代码如下。 <%@ page contentType="text/html;charset=GB2312" %> <%@ page import="shop.*"%> <%@page import="java.util.*" %> <% car car1=(car)session.getAttribute("shop"); if(car1==null){car1=new car();}//第一次; Vector v=car1.getV();%>
<% for(int i=0;i"); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); } %>
序号 书名 价格 数量 删除
"+book.getBooknum()+""+book.getBookname()+""+book.getPrice()+""); out.println(""); out.println(""); String str=book.getBooknum(); out.println(""); out.println("

返回购物 对于控制层(C层),主要设计二个servlet。分别是OrderServlet和ModifyMVC。其代码如下所示。 OrderServlet.java package servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.* import shop.car; public class OrderServlet extends HttpServlet { public OrderServlet() {super();} public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String goods=request.getParameter("goods"); HttpSession session=request.getSession(); car car1=(car)session.getAttribute("shop"); if(car1==null){ car1=new car();}//第一次; car1.add(car1.newgoods(goods)); session.setAttribute("shop",car1); RequestDispatcher dispatcher = null; dispatcher=getServletContext().getRequestDispatcher("/goodsmvc.jsp"); dispatcher.forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } } 显然,OrderServlet完成了第五章解决方案中的success.jsp功能。 ModifyMVC,java package servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.*; import shop.car; public class ModifyMVC extends HttpServlet { public ModifyMVC() {super();} public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session=request.getSession(); car car1=(car)session.getAttribute("shop"); String str[]= request.getParameterValues("book"); if(str!=null){ for(int i=0;i 发布文章

文章标题:

文章内容: