MVC模式分析与实现
1、 什么是模式?
模式描述了一个出现在特定设计环境中的特殊的再现设计问题,并为它的解决方案提供了一个经过充分验证的通用图示。
2、 模式类别
(1) 体系结构模式
表示软件系统的基本结构话组织视图。它提供一套预定义的子系统,规定它们的职责,并包含用于组织它们之间关系的规则和指南。
(2) 设计模式
提供一个用于细化软件系统的子系统或组件,或它们之间的关系图式
(3) 惯用法
具体针对一种编程语言的低层模式
3、 模型-视图-控制器(MVC)模式
模型:包含核心功能和数据。封装了内核数据和功能。模型独立于特定输出表示或者输入方式
视图:视图组件向用户显示信息。视图从模型获得数据。一个模型可能有多个视图
控制器:处理用户输入。每个视图都有一个控制器组件。控制器组件接受输入,通常将鼠标Move、Click、键盘Type或者浏览器发送的请求等用户输入翻译成为对视图或者模型的服务器请求。
模型组件包含了应用程序的核心功能。封装了相应的数据并输出执行特定应用程序处理的过程。控制器代表用户调用这些过程。模型也提供个访问它封装数据的函数,这些函数可以由视图组件使用。
变更-传播机制保证了模型和用户接口之间的一致性,维护了一个模型中相依组件的注册表。所有视图还有一些控制器在这个表中登记了对变更通知的需求。模型状态的改变将触发变更-传播机制。每个在表中登记的视图和控制器都会收到变更通知。变更-传播机制是模型与视图控制器之间的唯一连接。
视图和控制器组成了用户接口,用户仅仅通过控制器和系统交互。
视图组件向用户呈现信息。不同的视图用不同的方法呈现信息。每个组件都有一个更新函数。这个函数被模型变更通知**。在初始化阶段,视图向模型登记请求变更通知。每个视图将创建一个合适的控制器。
控制器组件接受作为事件的用户输入。事件如何发送到控制器由用户界面平台决定。事件被翻译成为对模型或者视图的请求。如果控制器的行为依赖于模型的状态,那么控制器也需要向模型登记请求变更通知。
4、 MVC模式的优点:
(1)同一模型的多个视图。 将模型和用户界面分离。多视图可以在单一的模型中实现
(2)同步化视图。 变更-传播机制确保了所有加入的观察者可以在正确的时间被告知应用程序的数据变化
(3)可插入的视图和控制器。 允许动态/静态地交换模型的视图和控制器对象。
(4)式样和感觉的可交换性。 模型不依赖于具体的用户界面平台,因此系统的可移植性好。
(5)框架潜力。 可以开发这个模式的应用程序框架。
5、MVC模式的缺点:
(1)增加了复杂性。 有时获得的灵活性并不大,但是缺增加了复杂性
(2)潜在的过多的更新因素。 有时一个用户动作就导致很多更新。有些更新并不是所有视图都关系
(3)视图和控制器之间的紧密联系。
(4)视图和控制器与模型的紧密耦合。 视图和控制器直接调用模型。模型接口的改变会使得视图和控制器的代码过时
(5)视图中的数据访问的低效率。 数据访问依赖于模型接口。可能过多的访问不必要的数据。
(6)移植时对视图和控制器的修改是不可避免的。 视图和控制器中的软件包含了平台相关的东西。
(7)现代的用户接口工具使用MVC的困难性。 现代的用户接口工具中提供了预制的控制流管理,内部事件的处理等。
6、自定义WEB-MVC框架的实现
M-javaBean(pojo和dao)负责业务数据模型存取
V—JSP+Html负责生成动态网页
C—Servlet负责生成动态网页
1) 该MVC框架主要由五部分组成:
<1>、ControllerServlet.java:请求分发控制器,解析请求中的动作名字,调用动作工厂对象生成的动作对象处理请求,处理后返回的结果为目的URL,控制器再将请求和应答对象转向目标URL
<2>、ActionFactory.java:该工厂类将请求中的动作名转换成servlet可以用来完成其工作的动作类
<3>、Action.java:接口类,该接口定义所有动作的公共接口
<4>、具体Action的实现:指实现了Action接口的类,会被ActionFactory工厂根据请求中的名字创建,从而调用其具体实现的方法处理Reques\Response对象后,返回一个URL,由主控servlet转发给用户。可以理解为一个具体的Action实现类用于处理页面上每一种用户点击(请求)
<5>、系统视图View层实现:*.JSP、*.HTML
2) 实现源码:
<1>控制器Servlet实现
package wxy.MVCDemo.Servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import wxy.MVCDemo.action.Action;
import wxy.MVCDemo.action.ActionFactory;
/**
* 系统控制器实现
* 功能:
* 1、解析请求中的命令名
* 2、根据请求的命令调用工厂对象创建命令处理对象
* 3、调用命令对象处理请求和应答对象,返回目标路径名
* 4、本控制器将请求和应答转发至目标路径名
* Servlet implementation class ControllerServlet
*/
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public ControllerServlet() {
super();
}
/**
* 主控servlet分发用户请求的入口
*/
public void service(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
try{
//得到请求的命令名字:解析出*.do请求中*所指代的字符串,即动作名字
String ActionName=getActionName(request);
System.out.println("request Action is:"+ActionName);
//根据请求的动作名,得到控制器servlet中配置的Action实现类名字
String ActionClassName=this.getInitParameter(ActionName);
System.out.println("ActionClassName is:"+ActionClassName);
//通过工厂类创建命令对象
Action action=ActionFactory.getIns().getAction(ActionClassName);
//使用动作对象处理动作,返回结果为处理后要输出的目标页面
String URL=action.execute(request,response);
if(URL!=null){
//输出目标页面
System.out.println("dest URL is :"+URL);
getServletContext().getRequestDispatcher(URL).forward(request, response);
}
}catch(Exception e){
e.printStackTrace();
getServletContext().getRequestDispatcher("/error.jsp").forward(request, response);
}
}
/**
* 解析到请求中的“命令”字
* @param request
* @return
*/
protected String getActionName(HttpServletRequest request) {
String path=request.getServletPath();
return path.substring(1,path.lastIndexOf("."));
}
}
<2>Action对象工厂类实现
package wxy.MVCDemo.action;
/**
* 命令对象工厂类实现
* 根据命令名字,创建命令对象
* ActionFactory是一个单实例类(整个系统只需要使用其一个对象)
* @author wxy
*
*/
public class ActionFactory {
/**
* 单实例访问方法
* @return 单实例对象
*/
public static ActionFactory getIns(){
if(null==ac){
ac=new ActionFactory();
}
return ac;
}
/**
* 根据具体的action类名字创建Action对象
* @param actionClass 具体的Action类全名
* @return Action类型对象
*/
public Action getAction(String actionClass){
Action actionInstance=null;
try{
Class c=Class.forName(actionClass);
actionInstance=(Action)c.newInstance();
}catch(Exception e){
e.printStackTrace();
}
return actionInstance;
}
//不需要创建对象
private ActionFactory(){}
private static ActionFactory ac=null;
}
<3>Action接口类定义
package wxy.MVCDemo.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 系统中的命令处理器接口
* @author wxy
*
*/
public interface Action {
/**
* 所有的具体Action实现这个接口
* @param request 请求对象
* @param reponse 应答对象
* @return 结果页面
*/
public String execute(HttpServletRequest request,HttpServletResponse reponse);
}
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<servlet>
<description></description>
<display-name>控制器ControllerServlet</display-name>
<servlet-name>ControllerServlet</servlet-name>
<servlet-class>wxy.MVCDemo.Servlet.ControllerServlet</servlet-class>
<init-param>
<!-- 将具体Action名字和全类配置到servlet参数中 -->
<param-name>loginAction</param-name>
<param-value>cn.netjava.action.LoginAction</param-value>
</init-param>
</servlet>
<display-name>MVCWeb</display-name>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<!-- 将所有以.do路径结尾的请求发送到主控servlet处理 -->
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
<4>具体的Action实现类
package wxy.MVCDemo.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 由login.jsp输出账户密码,如果账号正确,则在Session中放入用户对象,转向主页;如果错误,转向登录页面
* @author wxy
*
*/
public class LoginAction implements Action{
public String execute(HttpServletRequest request,
HttpServletResponse reponse) {
String UserName=request.getParameter("UserName");
String pwd=request.getParameter("pwd");
//数据库操作
//..........
if(UserName.equals("wxy")&&pwd.equals("wxy")){
//将用户对象放入session
request.getSession().setAttribute("UserName", UserName);
return "/mainPage.jsp";
}
request.getSession().setAttribute("ERROR_MSG", "账号或者密码输入有误!");
return "/login.jsp";
}
}
RegUserAction.do
DisplayMainPageAction.do
。。。。。。
(记着在web.xml中配置)
<5>系统view层实现
login.jsp
<%@ 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>用户登录</title>
</head>
<body>
<form action="loginAction.do" method="post">
用户名:<input type="text" name="UserName" id="userName"><br>
密码:<input type="password" name="pwd" id="userPwd"><br>
<input type="submit" value="登录"/>
</form>
</body>
</html>
mainPage.jsp
<%@ 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>
<h1 style="color:red"><%=request.getAttribute("UserName") %>登录成功</h1>
</body>
</html>