自定义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包
一个就够了,该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>
第二步:我们准备自己的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(1,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="0"> <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框架了