1.什么是MVC?
MVC是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制)。
1.1视图
视图(View)代表用户交互界面,对于Web应用来说,可以概括为HTML界面,但有可能为XHTML、XML和Applet。随着应用的复杂性和规模性,界面的处理也变得具有挑战性。一个应用可能有很多不同的视图,MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的请求,而不包括在视图上的业务流程的处理。业务流程的处理交予模型(Model)处理。比如一个订单的视图只接受来自模型的数据并显示给用户,以及将用户界面的输入数据和请求传递给控制和模型。
1.2模型
模型(Model):就是业务流程/状态的处理以及业务规则的制定。业务流程的处理过程对其它层来说是黑箱操作,模型接受视图请求的数据,并返回最终的处理结果。业务模型的设计可以说是MVC最主要的核心。目前流行的EJB模型就是一个典型的应用例子,它从应用技术实现的角度对模型做了进一步的划分,以便充分利用现有的组件,但它不能作为应用设计模型的框架。它仅仅告诉你按这种模型设计就可以利用某些技术组件,从而减少了技术上的困难。对一个开发者来说,就可以专注于业务模型的设计。MVC设计模式告诉我们,把应用的模型按一定的规则抽取出来,抽取的层次很重要,这也是判断开发人员是否优秀的设计依据。抽象与具体不能隔得太远,也不能太近。MVC并没有提供模型的设计方法,而只告诉你应该组织管理这些模型,以便于模型的重构和提高重用性。我们可以用对象编程来做比喻,MVC定义了一个顶级类,告诉它的子类你只能做这些,但没法限制你能做这些。这点对编程的开发人员非常重要。
业务模型还有一个很重要的模型那就是数据模型。数据模型主要指实体对象的数据 保存(持续化)。比如将一张订单保存到数据库,从数据库获取订单。我们可以将这个模型单独列出,所有有关数据库的操作只限制在该模型中。
1.3控制
控制(Controller)可以理解为从用户接收请求, 将模型与视图匹配在一起,共同完成用户的请求。划分控制层的作用也很明显,它清楚地告诉你,它就是一个分发器,选择什么样的模型,选择什么样的视图,可以完成什么样的用户请求。控制层并不做任何的数据处理。例如,用户点击一个连接,控制层接受请求后, 并不处理业务信息,它只把用户的信息传递给模型,告诉模型做什么,选择符合要求的视图返回给用户。因此,一个模型可能对应多个视图,一个视图可能对应多个模型。
模型、视图与控制器的分离,使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器改变了模型的数据,所有其它依赖于这些数据的视图都应反映到这些变化。因此,无论何时发生了何种数据变化,控制器都会将变化通知所有的视图,导致显示的更新。这实际上是一种模型的变化-传播机制。模型、视图、控制器三者之间的关系和各自的主要功能
1)最上面的一层,是直接面向最终用户的"视图层"(View)。它是提供给用户的操作界面,是程序的外壳。
2)最底下的一层,是核心的"数据层"(Model),也就是程序需要操作的数据或信息。
3)中间的一层,就是"控制层"(Controller),它负责根据用户从"视图层"输入的指令,选取"数据层"中的数据,然后对其进行相应的操作,产生最终结果。
这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口(Interface),供上面一层调用。这样一来,软件就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。
2.实列演化
2.1实体类
Book(书籍)类
package com.xuyahui.entity; public class Book { private int bid; private String bname; public Book() { // TODO Auto-generated constructor stub } public int getBid() { return bid; } public void setBid(int bid) { this.bid = bid; } public String getBname() { return bname; } public void setBname(String bname) { this.bname = bname; } @Override public String toString() { return "Book [bid=" + bid + ", bname=" + bname + "]"; } }
2.2模型驱动
ModelDriven 模型驱动接口
public interface ModelDriven<T> { T getmodel(); }
2.3 配置文件
mvc.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <config> <action path="/regAction" type="test.RegAction"> <forward name="failed" path="/reg.jsp" redirect="false" /> <forward name="success" path="/login.jsp" redirect="true" /> </action> <action path="/loginAction" type="test.LoginAction"> <forward name="failed" path="/login.jsp" redirect="false" /> <forward name="success" path="/main.jsp" redirect="true" /> </action> </config>
2.4实体的方法类
BookAction
public String See(HttpServletRequest request, HttpServletResponse response) { //参数传递 request.setAttribute("String", "ikun的小黑子"); System.out.println("book(书籍)Action的查看(See)方法"); //返回值(是重定向还是转发)forward:转发,redirect:重定向 return "forward"; } public String Upda(HttpServletRequest request, HttpServletResponse response) { //参数传递 request.setAttribute("String", "ikun的小黑子"); System.out.println("book(书籍)Action的修改(Upda)方法"); //返回值(是重定向还是转发)forward:转发,redirect:重定向 return "redirect"; } public String Del(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("String", "ikun的小黑子"); System.out.println("book(书籍)Action的删除(Del)方法"); //返回值(是重定向还是转发)forward:转发,redirect:重定向 return "redirect"; } public String Add(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("String", "ikun的小黑子"); System.out.println("book(书籍)Action的增加(Add)方法"); //返回值(是重定向还是转发)forward:转发,redirect:重定向 return "redirect"; } @Override public Book getModel() { return book; } }
获取参数反射后调用方法
package com.Xuyahui.Servlet; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/BookServlet.do") public class BookServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //根据JSP页面传递的值 String method = request.getParameter("method"); //通过反射类类方法的方法进行增删改查 try { Method m = this.getClass().getDeclaredMethod(method, HttpServletRequest.class,HttpServletResponse.class); m.setAccessible(true); m.invoke(this,request,response); } catch (Exception e) { e.printStackTrace(); } } private void See(HttpServletRequest request, HttpServletResponse response) { System.out.println("book(书籍)Servlet的查看(See)方法"); } private void Upda(HttpServletRequest request, HttpServletResponse response) { System.out.println("book(书籍)Servlet的修改(Upda)方法"); } private void Del(HttpServletRequest request, HttpServletResponse response) { System.out.println("book(书籍)Servlet的删除(Del)方法"); } private void Add(HttpServletRequest request, HttpServletResponse response) { System.out.println("book(书籍)Servlet的增加(Add)方法"); } }
2.5控制器
package com.Xuyahui.framework; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import com.Xuyahui.framework.model.ActionModel; import com.Xuyahui.framework.model.ConfigModel; import com.Xuyahui.framework.model.ConfigModelFactory; import com.Xuyahui.framework.model.ForwardModel; @WebServlet("*.do") public class ControllerServlet extends HttpServlet { private static final long serialVersionUID = 1L; // Map<String, Action> actionMap = new HashMap<String, Action>(); // 以前所有的子控制器是放到map集合中,现在所有的子控制器在mvc.xml中,其实就是放到configModel对象中 private ConfigModel configModel; @Override public void init() throws ServletException { // actionMap.put("/Book", new BookAction()); try { // configModel包含了所有的子控制器 configModel = ConfigModelFactory.build(); } catch (Exception e) { e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uri = request.getRequestURI(); uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf(".")); // Action action = actionMap.get(uri); // 要通过uri找到/book,在configModel对象中找 ActionModel actionModel = configModel.pop(uri); // 判断如果为空在页面给与异常提示 if (actionModel == null) throw new RuntimeException("action not config"); // 获取类路径 String type = actionModel.getType(); Action action = null; try { action = (Action) Class.forName(type).newInstance(); // bookAction有没有实现ModelDriver接口 if (action instanceof ModelDriver) { ModelDriver md = (ModelDriver) action; Object bean = md.getModel(); BeanUtils.populate(bean, request.getParameterMap()); } //具体业务代码执行后的返回值 forward:转发,redirect:重定向 String execut = action.execut(request, response); //要通过返回值拿到,该方法结果是重定向还是转发,还是跳转哪个页面 ForwardModel forwardModel = actionModel.pop(execut); if(forwardModel != null) { //获取返回值true后false boolean redirect = forwardModel.isRedirect(); String path = forwardModel.getPath(); if(redirect) { //getContextPath()加载时防止页面丢失 response.sendRedirect(request.getContextPath() + "/" + path); }else { request.getRequestDispatcher(path).forward(request, response); } } } catch (Exception e) { e.printStackTrace(); } } }
2.6子控制器
package com.Xuyahui.framework; import java.lang.reflect.Method; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Action { public String execut(HttpServletRequest request, HttpServletResponse response) throws Exception { //根据JSP页面传递的值 String method = request.getParameter("method"); //定义返回值确定是重定向还是转发 String res = ""; //通过反射类类方法的方法进行增删改查 try { Method m = this.getClass().getDeclaredMethod(method, HttpServletRequest.class,HttpServletResponse.class); m.setAccessible(true); res = (String) m.invoke(this,request,response); } catch (Exception e) { e.printStackTrace(); } return res; } }
2.7测试及效果
测试代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h3>书籍的方法功能--优化为配置文件及获取参数和跳转方法</h3><hr> <a href="Book.do?method=Add&bid=1&bname=ikun&price=999">增加</a> <a href="Book.do?method=Del&bid=1">删除</a> <a href="Book.do?method=Upda&bid=1&bname=小黑子&price=666">修改</a> <a href="Book.do?method=See&bid=1">查询</a> </body> </html>
结果显示:
增删改的的测试是重定向,不会带参数
查询是转发,所以带参数