自定义MVC框架

时间:2023-03-08 22:55:24
自定义MVC框架

我们在学习自定义MVC框架的时候常常会听到Model1 ,Model2和MVC。那么什么是Model1 什么是Model2什么又是MVC呢?

什么是Model1?

Model1就是一种纯jsp开发技术,将业务逻辑代码和视图渲染代码杂糅在一起。

什么是Model2?

Model2是在Model1的基础上,将业务逻辑的代码分离开来,单独形成一个Servlet,Model2也是基于MVC开发

什么是MVC框架?

MVC是三个单词的缩写,这三个单词分别为:模型(Model)、视图(View)和控制(Controller)。一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

其实我们之前也使用过MVC的思想,我们在学习Model2也就是Servlet的时候,用的思想就是基于MVC开发思想

既然我们已经知道了MVC的作用,那么我们就可以开发自己的MVC框架了,就以我们之前学习的Struts2框架为例,定义一个自己的MVC框架

如何开发自己的MVC框架?

开发前的准备   jar包

自定义MVC框架

一个就够了,该jar包的作用就是解析xml文件

第一步:准备配置文档

既然是框架,那肯定少不了的东西就是配置文件

我们配置一个xml文件,如下

<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义我们的DOC约束文件 -->
<!-- 定义根节点 (包含元素)-->
<!-- ELEMENT 表示元素 -->
<!-- ATTLIST 表示属性 -->
<!DOCTYPE myframework[ <!ELEMENT myframework (actions)>
<!ELEMENT actions (action*)>
<!ELEMENT action (result*)> <!ATTLIST action
name CDATA #REQUIRED
class CDATA #REQUIRED
>
<!ATTLIST result
name CDATA #IMPLIED
redirect (true|false) "false"
>
]
>
<myframework>
<actions>
<action name="loginAction" class="action.LoginAction">
<result name="success">success.jsp</result>
<result name="input">index.jsp</result>
</action>
</actions>
</myframework>

自定义MVC框架

自定义MVC框架

第二步:我们准备自己的Action接口,用于存放结果集和要执行的方法

package action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public interface Action { //定义字符串常量
public static final String SUCCESS="success";
public static final String NONE="none";
public static final String ERROR="error";
public static final String INPUT="input";
public static final String LOGIN="login"; //准备一个方法,用于获取数据
public String execute(HttpServletRequest request, HttpServletResponse sponse)throws Exception;
}

第三步:定义一个ActionMapping用来存放Action节点

package action;

import java.util.HashMap;
import java.util.Map; /*
* Action 的配置文件信息
* */
public class ActionMapping {
//访问的Action的名称
private String name;
//访问的Action的对应的Action的类全称
private String ClassName;
//result定义的结果集
private Map<String,String> resultMAp=new HashMap<String, String>(); //往集合里面添加配置文件中的数据信息
public void addResult(String resultName,String result){
resultMAp.put(resultName, result);
} //根据resultName获取对应的result页面
public String getResult(String resultName){
return resultMAp.get(resultName);
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return ClassName;
}
public void setClassName(String className) {
ClassName = className;
} }

第四步:定义ActionMappingManager,管理ActionMapping

package action;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; /*
* ActionMapping 的管理类
* */
public class ActionMappingManager {
/**管理ActionMapping 一个ActionMapping表示一个Action
* 但是我们配置文件中可能出现多个Action节点,所以我们需要一个ActionMapping的管理类
* */
private static Map<String,ActionMapping> actionMappings=new HashMap<String,ActionMapping>(); public ActionMappingManager() { }
//带参构造
public ActionMappingManager(String[] configFileNames){
for (String filaName : configFileNames) {
//调用根据文件名读取配置文件的方法
init(filaName);
}
} //根据Action名称获取到某一个具体的Action
public ActionMapping getActionMapping(String actionName){
//根据ActionName获取到Action
ActionMapping actionMapping=actionMappings.get(actionName);
return actionMapping;
} public void init(String configFileName){
try {
//读取配置文件,肯定用到了输入流
InputStream is=this.getClass().getResourceAsStream("/"+configFileName);
//开始读取xml文件
Document doc=new SAXReader().read(is);
//获取根节点
Element root=doc.getRootElement();
//获取Actions节点
Element actions = (Element)root.elementIterator("actions").next();
//开始遍历Actions节点
for(Iterator<Element> action=actions.elementIterator("action");action.hasNext();){
//获取到Action节点,将其属性进行封装
Element actionElement=action.next();
//获取到name
String name=actionElement.attributeValue("name");
//获取到ClassName
String ClassName=actionElement.attributeValue("class");
//一个Action对应着一个ActionMapping,创建ActionMapping进行赋值
ActionMapping actionMapping =new ActionMapping();
actionMapping.setName(name);
actionMapping.setClassName(ClassName);
//遍历Action的子元素result
for(Iterator<Element> result=actionElement.elementIterator("result");result.hasNext();){
Element resultElement = result.next();
//获取result属性值
String resultName=resultElement.attributeValue("name");
String resultValue=resultElement.getText();
//将每个result封装到ActionMapping中去
actionMapping.addResult(resultName, resultValue);
}
//将ActionMapping放入ActionMappingManager中去
actionMappings.put(actionMapping.getName(), actionMapping);
} } catch (Exception e) {
e.printStackTrace();
}
} }

第五步:利用反射机制,找到具体类的实例

package action;
/*
* 利用反射机制,根据类的类类型获取到类的实例
* */
public class ActionManager {
public static Action creatAction(String className){
Class clazz=null; try {
//判断当前线程是否有该Action
clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if(clazz==null){
try {
//根据类的全路径,手动创建一个类的类类型
clazz=Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Action action=null;
try {
//根据类的类类型创建出一个类的实例
action=(Action)clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return action;
}
}

第六步:写业务逻辑Action

package action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class LoginAction implements Action { public String execute(HttpServletRequest request, HttpServletResponse sponse)
throws Exception {
//写具体的业务逻辑 return SUCCESS;
} }

第七步:准备Servlet

package servlet;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import action.Action;
import action.ActionManager;
import action.ActionMapping;
import action.ActionMappingManager; public class MVCServlet extends HttpServlet { /**
出品:巴黎的雨季
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
} /**
出品:巴黎的雨季 */ ActionMappingManager actionMappingManager=null; public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
//根据ActionName获取到ActionMapping
ActionMapping actionMapping = actionMappingManager.getActionMapping(getActionName(request));
//根据ActionMapping中的ClassName获取到具体的类的实例
Action action = ActionManager.creatAction(actionMapping.getClassName());
//执行业务逻辑,获取到resultName
String resultName = action.execute(request, response);
//根据resultName获取具体的result视图
String result = actionMapping.getResult(resultName);
//重定向到页面
response.sendRedirect(result); } catch (Exception e) {
e.printStackTrace();
} } //根据请求的上下文获取到ActionName
public String getActionName(HttpServletRequest request){
//获取到URI
String uri = request.getRequestURI();
//获取上下文路径
String contextPath = request.getContextPath();
//从上下文中截取ActionPath
String actionPath = uri.substring(contextPath.length());
//获取到ActionName
String actionName=actionPath.substring(,actionPath.lastIndexOf('.')).trim();
return actionName;
} //在加载Servlet的时候就读取配置文件信息
@Override
public void init(ServletConfig config) throws ServletException {
//读取配置信息
String configStr = config.getInitParameter("config");
String[] fileNames=null;
if(configStr==null||configStr.isEmpty()){
fileNames=new String[]{"myframework.xml"};
}else{
fileNames=configStr.split(",");
}
//读取配置文件,将文件中的信息保存到ActionMappingManager中
actionMappingManager=new ActionMappingManager(fileNames);
}
}

第八步:修改web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MVCServlet</servlet-name>
<servlet-class>servlet.MVCServlet</servlet-class>
</servlet> <servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>

测试页面(login.jsp)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head> <body>
<form action="loginAction.action" method="post">
用户名:<input type="text" name="uname"/><br/>
密码:<input type="password" name="pwd"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>

这样我们就完成了一个自定义的MVC框架了