前言
在上一篇博客,我们介绍了MVC的演变过程,以及简单地实现了自定义MVC,在这篇博客中,我们进一步优化代码
一、 工作流程图
二、简单的实现自定义MVC
- 创建一个能处理所有前端发送过来请求的Servle,即中央控制器,拿到所有方法的反射代码就在这里,并根据请求的类型调用相应的业务逻辑(去子控制器)
- 创建一个子控制器,用于处理特定的用户请求或操作,这是真正处理用户请求的Servlet
- 创建一个定义方法的Servlet,继承子控制器,供子控制器调用方法
Controller层——Servlet
中央控制器
package com.xqx.framework; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** 中央控制器 即工作流程图中的ActionServlet * @author W许潜行 * 2023年6月29日 下午8:10:04 */ @WebServlet("*.action") public class DispatherServlet extends HttpServlet { Map<String,Action> mapAction=new HashMap<>(); /** * 初始化方法 */ public void init() throws ServletException { mapAction.put("/book", new BookAction()); super.init(); } 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();// /J2EE_MVC/book.action //得到请求的类 uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));// /book //拿到对应的action Action action = mapAction.get(uri); //调用方法 action.execute(request, response); } }
当有请求进入时,我们首先获取请求的URI,并从中获取类似"/book"的路径名。然后,我们使用这个路径名作为键在mapAction中查找对应的Action对象。最后,执行该Action的execute方法来处理请求。
这个DispatcherServlet类的目的是根据传入的请求路径来分发请求给不同的Action类处理,通过这种方式实现请求的路由和控制,实现了基本的MVC模式。
子控制器
package com.xqx.framework; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 字控制器,真正处理请求的类 * * * @author W许潜行 2023年6月29日 下午8:17:41 */ public class Action { public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 拿到jsp传来的method 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) { // TODO Auto-generated catch block e.printStackTrace(); } } }
这个Action类的作用是通过反射机制根据传入的method参数值来调用具体的方法进行请求处理。每个实际的Action类都可以继承这个基类,并重写具体的方法来实现自己的业务逻辑。
具体Action类
package com.xqx.framework; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /**定义crud方法 * @author W许潜行 * 2023年6月29日 下午8:55:46 */ public class BookAction extends Action{ public void list(HttpServletRequest request, HttpServletResponse response) { System.out.println("list"); } public void upd(HttpServletRequest request, HttpServletResponse response) { System.out.println("upd"); } public void del(HttpServletRequest request, HttpServletResponse response) { System.out.println("del"); } public void add(HttpServletRequest request, HttpServletResponse response) { System.out.println("add"); } }
view层——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>极易MVC</h1> <a href="bookAdd.action">新增</a> <a href="bookDel.action">删除</a> <a href="bookUpd.action">修改</a> <a href="bookList.action">查看</a> <hr> <h1>简易MVC</h1> <a href="book.action?method=add">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> <hr> <h1>普易MVC</h1> <a href="book.action?method=add">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> </body> <h1>MVC架构初实现</h1> <a href="book.action?method=add">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> </body> </html>
打印结果:
简单的MVC架构就基本完成了,但还有很多优化之处,接下来我们来优化。
三、初步实现自定义MVC
简单MVC架构中的问题
既然要做到通用,那里面就不能出现写死的类呀
怎样可以灵活地拿到所有要初始化的地址/类呢?
答案是:通过反射建模完成,将所需要操作的类,在XML文件中配置即可
3.1 配置XML文件
<?xml version="1.0" encoding="UTF-8"?> <config> <action path="/order" type="com.xqx.framework.OrderAction"> <forward name="list" path="res.jsp" redirect="false" /> <forward name="toList" path="res.jsp" redirect="true" /> </action> <action path="/book" type="com.xqx.framework.BookAction"> <forward name="list" path="res.jsp" redirect="false" /> <forward name="toList" path="res.jsp" redirect="true" /> </action> </config>
3.2 建模
ForwardModel
package com.xqx.framework.model; public class ForwardModel { private String name; private String path; private boolean redirect; public ForwardModel() { // TODO Auto-generated constructor stub } @Override public String toString() { return "Forward [name=" + name + ", path=" + path + ", redirect=" + redirect + "]"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public boolean isRedirect() { return redirect; } public void setRedirect(boolean redirect) { this.redirect = redirect; } public ForwardModel(String name, String path, boolean redirect) { super(); this.name = name; this.path = path; this.redirect = redirect; } }
ActionModel
package com.xqx.framework.model; import java.util.HashMap; import java.util.Map; public class ActionModel { private String path; private String type; private Map<String, ForwardModel> fMap = new HashMap<>(); public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Map<String, ForwardModel> getfMap() { return fMap; } public void setfMap(Map<String, ForwardModel> fMap) { this.fMap = fMap; } public void push(ForwardModel fd) { fMap.put(fd.getName(), fd); } public ForwardModel pop(String name) { return fMap.get(name); } }
ConfigModel
package com.xqx.framework.model; import java.util.HashMap; import java.util.Map; public class ConfigModel { private Map<String, ActionModel> aMap = new HashMap<String, ActionModel>(); public void push(ActionModel ac) { aMap.put(ac.getPath(), ac); } public ActionModel pop(String path) { return aMap.get(path); } }
ConfigModelFactory
package com.xqx.framework.model; import java.io.InputStream; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class ConfigModelFactory { public static ConfigModel build() throws Exception { String xPath = "/config.xml"; return build(xPath); } public static ConfigModel build(String xPath) throws Exception { ConfigModel cm = new ConfigModel(); InputStream is = ConfigModelFactory.class.getResourceAsStream(xPath); SAXReader sr = new SAXReader(); Document doc = sr.read(is); List<Element> action = doc.selectNodes("//action"); for (Element actionEle : action) { ActionModel am = new ActionModel(); am.setPath(actionEle.attributeValue("path")); am.setType(actionEle.attributeValue("type")); List<Element> forward = actionEle.selectNodes("forward"); for (Element forwardEle : forward) { ForwardModel fm = new ForwardModel(); fm.setName(forwardEle.attributeValue("name")); fm.setPath(forwardEle.attributeValue("path")); fm.setRedirect(!"false".equals(forwardEle.attributeValue("redirect"))); am.push(fm); } cm.push(am); } return cm; }
建模的详细介绍——>建模详解
3.2 Servlet
中央控制器
package com.xqx.framework; import java.io.IOException; import java.util.Map; 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.xqx.framework.model.ActionModel; import com.xqx.framework.model.ConfigModel; import com.xqx.framework.model.ConfigModelFactory; import com.xqx.framework.model.ForwardModel; /** * 中央控制器 即工作流程图中的ActionServlet * * @author W许潜行 2023年6月29日 下午8:10:04 */ @WebServlet("*.action") public class DispatherServlet extends HttpServlet { // Map<String,Action> mapAction=new HashMap<>(); // 之前子控制器在Map里,现在xml文件里 private ConfigModel configModel; /** * 初始化方法 */ public void init() throws ServletException { // mapAction.put("/book", new BookAction()); try { // 包含所有子控制器 configModel = ConfigModelFactory.build(); } catch (Exception e) { e.printStackTrace(); } super.init(); } 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();// /J2EE_MVC/book.action // 得到请求的类 uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));// /book // 拿到对应的type ActionModel actionModel = configModel.pop(uri); if (actionModel == null) { throw new RuntimeException("action is null"); } String type = actionModel.getType(); try { // 类实例 Action action = (Action) Class.forName(type).newInstance(); if (action instanceof ModelDriver) { ModelDriver md=(ModelDriver) action; Object model = md.getModel(); Map<String, String[]> parameterMap = request.getParameterMap(); BeanUtils.populate(model, parameterMap); } // 调用方法 list/toList String execute = action.execute(request, response); // 为了动态配置业务代码执行完毕将会转发/重定向到指定页面 ForwardModel forwardModel = actionModel.pop(execute); if (forwardModel == null) { System.out.println("定义跳转一个错误页面..."); return; } if (!forwardModel.isRedirect()) { response.sendRedirect(request.getContextPath() + "/" + forwardModel.getPath()); } else { request.getRequestDispatcher(forwardModel.getPath()).forward(request, response); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
init()方法用于初始化,在该方法中,从配置文件中构建了一个ConfigModel实例,该实例包含了所有的子控制器的配置信息。
在doPost()方法中,首先获取请求的URI,然后从URI中提取出请求的类名,即控制器名称。
根据控制器名称从configModel中获取相应的ActionModel对象,ActionModel对象包含了具体控制器的配置信息,包括控制器类的全名和该控制器的执行结果与转发/重定向的配置。
根据ActionModel对象中的控制器类名实例化一个控制器对象,并判断控制器是否实现了ModelDriver接口,如果实现了,将请求参数封装到模型对象中。调用控制器的execute()方法执行具体的业务逻辑,返回执行结果。
根据执行结果从ActionModel对象中获取相应的ForwardModel对象,ForwardModel对象包含了执行结果对应的转发/重定向路径的配置信息。
根据ForwardModel对象判断是进行转发还是重定向操作,然后将请求转发或重定向到相应的页面。
如果没有找到对应的ActionModel或ForwardModel,则打印错误信息。
子控制器
package com.xqx.framework; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 子控制器,真正处理请求的类 * * * @author W许潜行 2023年6月29日 下午8:17:41 */ public class Action { public String execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 拿到jsp传来的method String method = request.getParameter("method"); String res = null; try { Method m = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class); m.setAccessible(true); //拿到方法返回的值 list/toList res = (String) m.invoke(this, request, response); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return res;// } }
该Action类的作用是通过动态调用不同的方法来执行不同的业务逻辑,并根据方法的返回值作为执行结果返回给调用者。
模型驱动接口
package com.xqx.framework; /**模型驅動接口 * @author W许潜行 * 2023年7月2日 下午7:17:02 * @param <T> */ public interface ModelDriver<T> { T getModel(); }
BookAction
package com.xqx.framework; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xqx.entity.Book; /** * 定义crud方法 * * @author W许潜行 2023年6月29日 下午8:55:46 */ public class BookAction extends Action implements ModelDriver<Book>{ Book book=new Book(); public String list(HttpServletRequest request, HttpServletResponse response) { // String bid = request.getParameter("bid"); // String bname = request.getParameter("bname"); // String price = request.getParameter("price"); // book.setBid(Integer.valueOf(bid)); // book.setBname(bname); // book.setPrice(Float.valueOf(price)); //得到所有参数 // Map<String, String[]> bookMap = request.getParameterMap(); request.setAttribute("content", "hello"); System.out.println("BookActionlist"); return "toList"; } public String upd(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("content", "hello"); System.out.println("BookActionupd"); return "list"; } public String del(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("content", "hello"); System.out.println("BookActiondel"); return "list"; } public String add(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("content", "hello"); System.out.println("BookActionadd"); return "list"; } @Override public Book getModel() { // TODO Auto-generated method stub return book; } }
通过继承Action类和实现ModelDriver接口,BookAction类提供了具体的业务方法,并且通过实现getModel()方法,将创建的Book对象作为模型对象,方便在控制器中使用和操作。
这样,当请求调用BookAction类的方法时,可以进行相应的业务处理,并将结果封装到模型中,方便在JSP中展示或进行后续操作。
3.3 jsp
bookList.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>极易MVC</h1> <a href="bookAdd.action">新增</a> <a href="bookDel.action">删除</a> <a href="bookUpd.action">修改</a> <a href="bookList.action">查看</a> <hr> <h1>简易MVC</h1> <a href="book.action?method=add">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> <hr> <h1>普易MVC</h1> <a href="book.action?method=add">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> </body> <h1>MVC架构简单实现</h1> <a href="book.action?method=add">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> </body> <h1>MVC架构初实现</h1> <a href="book.action?method=add&&bid=1&&bname=aa&&price=9.9">新增</a> <a href="book.action?method=del">删除</a> <a href="book.action?method=upd">修改</a> <a href="book.action?method=list">查看</a> </body> </html>
res.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> res页面,参数为:${content} </body> </html>
打印结果:
点击新增:
点击查看: