前言
在上期J2EE之自定义MVC框架知识(上篇)中我们了解了一些自定义MVC框架在我们开发编程中带来的一些方便,但基于上篇博客中自定义MVC框架优化的代码还有可优化之处,这期J2EE之自定义MVC框架知识(中篇)将针对上期的博客代码进行进一步优化。让我们跟着思维导图一起来学习了解一下吧。
1.优化中央控制器中的action容器,使其加载变成可配置的
1.1 编写.xml文件(config.xml)
创建一个源文件夹(Source Folder)文件路径如下
<?xml version="1.0" encoding="UTF-8"?> <!-- config标签:可以包含0~N个action标签 --> <!DOCTYPE config [ <!ELEMENT config (action*)> <!ELEMENT action (forward*)> <!ELEMENT forward EMPTY> <!ATTLIST action path CDATA #REQUIRED type CDATA #REQUIRED > <!ATTLIST forward name CDATA #REQUIRED path CDATA #REQUIRED redirect (true|false) 'true' > ]> <config> <!-- action标签:可以饱含0~N个forward标签 path:以/开头的字符串,并且值必须唯一 非空 ,子控制器对应的路径 type:字符串,非空,子控制器的完整类名 --> <action path="/book" type="com.YX.Web.BookAction"> <forward name="success" path="/index.jsp" redirect="true" /> <forward name="failed" path="/register.jsp" redirect="false" /> </action> <action path="/order" type="com.YX.Web.OrderAction"> <forward name="a" path="/index.jsp" redirect="false" /> <forward name="b" path="/welcome.jsp" redirect="true" /> </action> </config>
1.2 导入XML建模相关的类
文件路径展示图
ActionModel类
package com.YX.framework.model; import java.util.HashMap; import java.util.Map; /** * @author 君易--鑨 * 2023年7月2日下午8:47:58 * */ public class ActionModel { private String path; private String type; private Map<String, ForwardModel> fMap = new HashMap<String, ForwardModel>(); 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; } // 将forward往action中放 压栈 public void push(ForwardModel forwardModel) { fMap.put(forwardModel.getName(), forwardModel); } // 从action中通过name值取出forward public ForwardModel pop(String name) { return fMap.get(name); } }
ConfigModel类
package com.YX.framework.model; import java.util.HashMap; import java.util.Map; /** * @author 君易--鑨 * 2023年7月2日下午8:47:58 * */ public class ConfigModel { private Map<String, ActionModel> aMap = new HashMap<String, ActionModel>(); public void push(ActionModel actionModel) { aMap.put(actionModel.getPath(), actionModel); } public ActionModel pop(String path) { return aMap.get(path); } public static void main(String[] args) throws Exception { ConfigModel configModel = ConfigModelFactory.build(); ActionModel actionModel = configModel.pop("/loginAction"); ForwardModel forwardModel = actionModel.pop("success"); System.out.println(forwardModel.getPath()); } }
ConfigModelFactory类
package com.YX.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; /**23种设计模式--工厂模式--创建型模式 * build/newinstance * @author 君易--鑨 * 2023年7月2日下午8:50:10 * */ public class ConfigModelFactory { public static ConfigModel build(String xmlPath) throws Exception { ConfigModel configModel = new ConfigModel(); InputStream in = ConfigModelFactory.class.getResourceAsStream(xmlPath); SAXReader sr = new SAXReader(); Document doc = sr.read(in); // configModel要有类容,就意味着actionmodel要有类容,然后放入到configmodel中去 List<Element> actionEles = doc.selectNodes("/config/action"); for (Element actionEle : actionEles) { // System.out.println(actionEle.asXML()); ActionModel actionModel = new ActionModel(); actionModel.setPath(actionEle.attributeValue("path")); actionModel.setType(actionEle.attributeValue("type")); // actionModel要有forwardModel,就意味着forwardmodel要有类容,然后放入到actionmodel中去 List<Element> forwardEles = actionEle.selectNodes("forward"); for (Element forwardEle : forwardEles) { System.out.println(forwardEle.asXML()); ForwardModel forwardModel = new ForwardModel(); forwardModel.setName(forwardEle.attributeValue("name")); forwardModel.setPath(forwardEle.attributeValue("path")); // 只有填写false才是转发,其它都为重定向 // forwardModel.setRedirect(false); forwardModel.setRedirect(!"false".equals(forwardEle.attributeValue("redirect"))); actionModel.push(forwardModel); } configModel.push(actionModel); } return configModel; } public static ConfigModel build() throws Exception { return build("/config.xml"); } public static void main(String[] args) throws Exception { ConfigModelFactory.build(); } }
ForwardModel类
package com.YX.framework.model; /** * @author 君易--鑨 * 2023年7月2日下午8:47:58 * */ public class ForwardModel { // name、path、redirect private String name; private String path; private boolean 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; } }
1.3 创建一个OrderAction类(用于测试效果)
package com.YX.Web; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.YX.framework.Action; /**订单操作方法类 * @author 君易--鑨 * 2023年7月2日下午8:53:06 * */ public class OrderAction extends Action{ /** * 新增的方法 * * @param request * @param response */ public void add(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了OrderAction新增的方法........"); } /** * 删除的方法 * * @param request * @param response */ public void del(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了OrderAction删除的方法........"); } /** * 修改的方法 * * @param request * @param response */ public void upd(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了OrderAction修改的方法........"); } /** * 查看的方法 * * @param request * @param response */ public void list(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了OrderAction查看的方法........"); } }
1.4 DispatherServlet类的代码优化
package com.YX.framework; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.management.RuntimeErrorException; 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 com.YX.Web.BookAction; import com.YX.Web.OrderAction; import com.YX.framework.model.ActionModel; import com.YX.framework.model.ConfigModel; import com.YX.framework.model.ConfigModelFactory; /** * 中央控制器 * * @author 君易--鑨 2023年6月29日下午10:20:15 * */ @WebServlet("*.do") public class DispatherServlet extends HttpServlet { // public Map<String, Action> actionMap = new HashMap<String, Action>(); // 以前所有的子控制器释放到map集合中,现在所有的子控制器在mvc.xml中,其实就是放到模型对象中 private ConfigModel configmodle; @Override public void init() throws ServletException { // actionMap.put("/book", new BookAction()); // actionMap.put("/order", new OrderAction()); try { // configmodle包含了所有的子控制器 configmodle=ConfigModelFactory.build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.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,在configmodle对象中找 ActionModel actionModel = configmodle.pop(uri); if (actionModel==null) { throw new RuntimeException("action not config"); } // com.zking.Web.BookAction String type = actionModel.getType(); // 反射实例化对象==BookAction bookaction=new BookAction(); Action action; try { action = (Action) Class.forName(type).newInstance(); // 调用识别方法类的方法 action.execute(request, response); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
1.5 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> <p>自定义MVCk框架02 </p> <a href="order.do?methodname=add">订单新增</a> <a href="order.do?methodname=del">订单删除</a> <a href="order.do?methodname=upd">订单修改</a> <a href="order.do?methodname=list">订单查看</a> <a href="book.do?methodname=add">书籍新增</a> <a href="book.do?methodname=del">书籍删除</a> <a href="book.do?methodname=upd">书籍修改</a> <a href="book.do?methodname=list">书籍查看</a> </body> </html>
页面显示
点击功能控制台模拟输出结果(以订单查看、书籍删除为例)
1.6 注意事项:
- 实现对应操作功能之前确保.xml文件配置好了。
如下图中的功能
.xml文件配置图讲解(config.xml)
2.如果实现对应操作功能时.xml文件未配置。(案例如下)
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> <p>自定义MVCk框架02 </p> <a href="order.do?methodname=add">订单新增</a> <a href="order.do?methodname=del">订单删除</a> <a href="order.do?methodname=upd">订单修改</a> <a href="order.do?methodname=list">订单查看</a> <a href="book.do?methodname=add">书籍新增</a> <a href="book.do?methodname=del">书籍删除</a> <a href="book.do?methodname=upd">书籍修改</a> <a href="book.do?methodname=list">书籍查看</a> <a href="good.do?methodname=list">商品查看</a> </body> </html>
页面显示
如果点击商品查看功能页面显示
1.7 弊端
中央控制器中的action容器配置不能灵活配置
2.针对反射调用的业务代码,优化页面跳转代码(以BookAction为例)
2.1 优化Action类代码
package com.YX.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 君易--鑨 2023年6月29日下午9:48:19 * */ public class Action { protected String execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取页面传来的方法参数,用于辨别执行对应操作的方法 String methodName = request.getParameter("methodname"); // 定义一个空变量接受方法结果 String res = ""; // 反射从获取类类开始 this:代表BookServlet类 try { // 获取当前类的方法对象 Method sclass = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); // 打开访问权限 sclass.setAccessible(true); // 执行对应方法名对象的方法 // 变量接受方法结果 res = (String) sclass.invoke(this, request, response); } catch (Exception e) { e.printStackTrace(); } return res; } }
2.2 优化BookAction类代码
package com.YX.Web; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.YX.framework.Action; /**对应表的操作方法类 * @author 君易--鑨 * 2023年6月29日下午10:40:48 * */ public class BookAction extends Action{ //tolist:代表重定向;list:代表转发。 /** * 新增的方法 * * @param request * @param response */ public String add(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction新增的方法........"); request.setAttribute("content", "君易--鑨"); return "tolist"; } /** * 删除的方法 * * @param request * @param response */ public String del(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction删除的方法........"); request.setAttribute("content", "君易--鑨"); return "tolist"; } /** * 修改的方法 * * @param request * @param response */ public String upd(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction修改的方法........"); request.setAttribute("content", "君易--鑨"); return "tolist"; } /** * 查看的方法 * * @param request * @param response */ public String list(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction查看的方法........"); request.setAttribute("content", "君易--鑨"); return "list"; } }
2.3 修改.xml文件(config.xml)
<?xml version="1.0" encoding="UTF-8"?> <!-- config标签:可以包含0~N个action标签 --> <!DOCTYPE config [ <!ELEMENT config (action*)> <!ELEMENT action (forward*)> <!ELEMENT forward EMPTY> <!ATTLIST action path CDATA #REQUIRED type CDATA #REQUIRED > <!ATTLIST forward name CDATA #REQUIRED path CDATA #REQUIRED redirect (true|false) 'true' > ]> <config> <!-- action标签:可以饱含0~N个forward标签 path:以/开头的字符串,并且值必须唯一 非空 ,子控制器对应的路径 type:字符串,非空,子控制器的完整类名 --> <action path="/book" type="com.YX.Web.BookAction"> <forward name="list" path="/res.jsp" redirect="false" /> <forward name="tolist" path="/res.jsp" redirect="true" /> </action> <action path="/order" type="com.YX.Web.OrderAction"> </action> </config>
2.4 创建重定向和转发的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> 跳转成功,携带数据:${content } </body> </html>
2.5 优化DispatherServlet代码
package com.YX.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 com.YX.framework.model.ActionModel; import com.YX.framework.model.ConfigModel; import com.YX.framework.model.ConfigModelFactory; import com.YX.framework.model.ForwardModel; /** * 中央控制器 * * @author 君易--鑨 2023年6月29日下午10:20:15 * */ @WebServlet("*.do") public class DispatherServlet extends HttpServlet { // public Map<String, Action> actionMap = new HashMap<String, Action>(); // 以前所有的子控制器释放到map集合中,现在所有的子控制器在mvc.xml中,其实就是放到模型对象中 private ConfigModel configmodle; @Override public void init() throws ServletException { // actionMap.put("/book", new BookAction()); // actionMap.put("/order", new OrderAction()); try { // configmodle包含了所有的子控制器 configmodle=ConfigModelFactory.build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.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,在configmodle对象中找 ActionModel actionModel = configmodle.pop(uri); if (actionModel==null) { throw new RuntimeException("action not config"); } // com.zking.Web.BookAction String type = actionModel.getType(); // 反射实例化对象==BookAction bookaction=new BookAction(); Action action; try { action = (Action) Class.forName(type).newInstance(); // 调用识别方法类的方法 // 具体业务代码执行后的返回值add/del/upd/list的返回值==》list/tolist String res = action.execute(request, response); // 通过返回值拿到,该方法结果是重定向还是转发那个页面 ForwardModel forwardModel = actionModel.pop(res); if (forwardModel!=null) { boolean redirect = forwardModel.isRedirect(); System.out.println(redirect); String path=forwardModel.getPath(); if (redirect) { response.sendRedirect(request.getContextPath()+"/"+path); }else { request.getRequestDispatcher(path).forward(request, response); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2.6 测试结果
增删改是重定向,查看是转发。
重定向页面输出结果和控制台输出结果
转发页面输出结果和控制台输出结果
2.7 弊端:
jsp传递到后台,封装到实体类的代码过多
2.8 注意事项:
如果画横线的代码是下面这样拼接的话
response.sendRedirect(path);
再jsp页面上实现增删改是跳转页面会出现下面这种情况
3.优化jsp页面参数传递后台的代码
3.1创建一个对应表的实体类(以Book类为例)
Book类代码
package com.YX.entity; /** * 实体类:书籍类 * * @author 君易--鑨 2023年7月3日上午12:30:00 * */ public class Book { // 定义属性 private int bid; private int bname; private float price; /** * 无参构造 */ public Book() { // TODO Auto-generated constructor stub } // 获取属性对应的get、set方法 public int getBid() { return bid; } public void setBid(int bid) { this.bid = bid; } public int getBname() { return bname; } public void setBname(int bname) { this.bname = bname; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } /** * 有参构造 * * @param bid * @param bname * @param price */ public Book(int bid, int bname, float price) { super(); this.bid = bid; this.bname = bname; this.price = price; } // 实体对象信息输出语句体 @Override public String toString() { return "Book [bid=" + bid + ", bname=" + bname + ", price=" + price + "]"; } }
3.2 创建一个接口类(所有的子控制器通用)
ModelDriver类
package com.YX.framework; /**模型驱动接口 * @author 君易--鑨 * 2023年7月3日上午12:54:36 * */ public interface ModelDriver<T> { T getModel(); }
3.3 优化子控制器代码(BookAction为例)
BookAction类
package com.YX.Web; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.YX.entity.Book; import com.YX.framework.Action; import com.YX.framework.ModelDriver; /**对应表的操作方法类 * @author 君易--鑨 * 2023年6月29日下午10:40:48 * */ public class BookAction extends Action implements ModelDriver<Book>{ //tolist:代表重定向;list:代表转发。 /** * 新增的方法 * * @param request * @param response */ // 实例化实体类 private Book book=new Book(); public String add(HttpServletRequest request, HttpServletResponse response) { /* * 1.要有表的内属性对象 * 2.获取到所有的参数及参数getParameterMap() * 3.将参数值封装到表对应的对象中 * 4.实现通用 */ // 获取到所有的请求参数值 Map<String, String[]> parameterMap = request.getParameterMap(); System.out.println("调用了BookAction新增的方法........"); request.setAttribute("content", "君易--鑨"); return "tolist"; } /** * 删除的方法 * * @param request * @param response */ public String del(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction删除的方法........"); request.setAttribute("content", "君易--鑨"); return "tolist"; } /** * 修改的方法 * * @param request * @param response */ public String upd(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction修改的方法........"); request.setAttribute("content", "君易--鑨"); return "tolist"; } /** * 查看的方法 * * @param request * @param response */ public String list(HttpServletRequest request, HttpServletResponse response) { System.out.println("调用了BookAction查看的方法........"); request.setAttribute("content", "君易--鑨"); return "list"; } @Override public Book getModel() { return book; } }
3.4 优化中央控制器的代码
DispatherServlet类
package com.YX.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.YX.framework.model.ActionModel; import com.YX.framework.model.ConfigModel; import com.YX.framework.model.ConfigModelFactory; import com.YX.framework.model.ForwardModel; /** * 中央控制器 * * @author 君易--鑨 2023年6月29日下午10:20:15 * */ @WebServlet("*.do") public class DispatherServlet extends HttpServlet { // public Map<String, Action> actionMap = new HashMap<String, Action>(); // 以前所有的子控制器释放到map集合中,现在所有的子控制器在mvc.xml中,其实就是放到模型对象中 private ConfigModel configmodle; @Override public void init() throws ServletException { try { // configmodle包含了所有的子控制器 configmodle=ConfigModelFactory.build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.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,在configmodle对象中找 ActionModel actionModel = configmodle.pop(uri); if (actionModel==null) { throw new RuntimeException("action not config"); } // com.zking.Web.BookAction String type = actionModel.getType(); // 反射实例化对象==BookAction bookaction=new BookAction(); Action action; try { action = (Action) Class.forName(type).newInstance(); if (action instanceof ModelDriver) {//判断子控制器是否实现模型驱动接口 ModelDriver md=(ModelDriver) action; // 获取对应实体对象 Object model = md.getModel(); Map<String, String[]> map = request.getParameterMap(); // 对应的对象的属性赋值 BeanUtils.populate(model, map); } // 调用识别方法类的方法 // 具体业务代码执行后的返回值add/del/upd/list的返回值==》list/tolist String res = action.execute(request, response); // 通过返回值拿到,该方法结果是重定向还是转发那个页面 ForwardModel forwardModel = actionModel.pop(res); if (forwardModel!=null) { boolean redirect = forwardModel.isRedirect(); System.out.println(redirect); String path=forwardModel.getPath(); if (redirect) { //重定向 response.sendRedirect(request.getContextPath()+"/"+path); // response.sendRedirect(path); }else { //转发 request.getRequestDispatcher(path).forward(request, response); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
3.5 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> <p>自定义MVCk框架02 最终</p> <a href="book.do?methodname=add&bid=1&bname=junyi&price=9.9">书籍新增</a> <a href="book.do?methodname=del">书籍删除</a> <a href="book.do?methodname=upd">书籍修改</a> <a href="book.do?methodname=list">书籍查看</a> </body> </html>
3.6 项目文件架构
3.7 测试优化jsp页面参数传递后台的代码
当我们点击书籍新增时将参数传入后台
结束语
这期博客分享J2EE之自定义MVC框架知识如何去优化上期博客代码就到这里了,如果有老铁想继续学习了解关于J2EE之自定义MVC框架知识请关注我,注意后期博客发表。