1.前言
虽然前面文章深入解析Java自定义MVC框架的原理与实现讲述了Mvc框架,但是那只能算是比较低级的,还能够再优化,接下来我会再优化3次代码,让代码更为简洁,通用。
2.上次代码弊端
虽然简化了代码,但是代码并不 能够通用,MVC这种东西,最终是要打包成jar架包的,可是上一篇文章的代码优化并不能够打包成架包,现在开始第一次优化。
1.利用xml建模反射优化
1.XMl文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE config [<!ELEMENT config (action*)> <!ELEMENT action (forward*)> <!ATTLIST action path CDATA #REQUIRED type CDATA #IMPLIED > ] > <!-- config标签:可以包含0~N个action标签 --> <config> <!-- action标签:可以饱含0~N个forward标签 path:以/开头的字符串,并且值必须唯一 非空 ,子控制器对应的路径 type:字符串,非空,子控制器的完整类名 --> <action path="/book" type="com.niyin.com.BookAction"> <forward name="list" path="/res.jsp" redirect="true" /> <forward name="tolist" path="/res.jsp" redirect="false" /> </action> <action path="/order" type="com.niyin.com.Orderaction"> <forward name="success" path="/index.jsp" redirect="true" /> <forward name="failed" path="/register.jsp" redirect="false" /> </action> <action path="/bookAction" type="test.BookAction"> <forward name="add" path="/bookAdd.jsp" redirect="true" /> <forward name="del" path="/reg.jsp" redirect="false" /> <forward name="list" path="/list.jsp" redirect="false" /> <forward name="upd" path="/login.jsp" redirect="false" /> </action> <action path="/loginAction" type="test.action.LoginAction"> <forward name="a" path="/index.jsp" redirect="false" /> <forward name="b" path="/welcome.jsp" redirect="true" /> </action> </config>
2.对xml建模
package model; import java.util.HashMap; import java.util.Map; public class ActionModel { private String path; private String type; private Map<String ,ForwardModel>fmp=new HashMap<String ,ForwardModel>(); public String getType() { return type; } public void setType(String type) { this.type = type; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public void push(ForwardModel forwardModel) { fmp.put(forwardModel.getName(), forwardModel); } public ForwardModel pop(String name) { return fmp.get(name); } }
package 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 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 =new ConfigMOdelFactory().build(); ActionModel actionModel=configModel.pop("/loginAction"); ForwardModel forwardModel =actionModel.pop("b"); System.out.println(forwardModel.getPath()); } }
package 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 { ConfigModel configModel = new ConfigModel(); InputStream in = ConfigMOdelFactory.class.getResourceAsStream("/config.xml"); SAXReader sr = new SAXReader(); Document doc = sr.read(in); List<Element> actionsEles = doc.selectNodes("/config/action"); for (Element element : actionsEles) { // System.out.println(element.asXML()); ActionModel actionmodel = new ActionModel(); actionmodel.setPath(element.attributeValue("path")); actionmodel.setType(element.attributeValue("type")); List<Element> forwardEles = element.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")); forwardModel.setRedirect(!"false".equals(forwardEle.attributeValue("redirect"))); actionmodel.push(forwardModel); } configModel.push(actionmodel); } return configModel; } public static void main(String[] args) throws Exception { ConfigMOdelFactory.build(); } }
package model; import java.util.HashMap; import java.util.Map; public class ForwardModel { 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; } }
3.修改中央控制器
以前把所有的子控制器放到map集合中,现在所有的子控制器再config。xml中。
package com.niyin.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; import org.apache.commons.beanutils.BeanUtils; import com.niyin.com.BookAction; import com.niyin.com.Orderaction; import model.ActionModel; import model.ConfigMOdelFactory; import model.ConfigModel; import model.ForwardModel; @WebServlet("*.action") public class DispatherServlet extends HttpServlet { private ConfigModel configMOdel; // public Map<String, Action> actionMap=new HashMap<String, Action>(); @Override public void init() throws ServletException { // actionMap.put("/book", new BookAction()); // actionMap.put("/order", new Orderaction()); // actionMap.put("/cat", new CatAction()); try { configMOdel=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/再configMOdel中找 ActionModel actionModel = configMOdel.pop(uri); if (actionModel==null) throw new RuntimeException("action 没有配置"); String type = actionModel.getType(); Action action; try { action = (Action) Class.forName(type).newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
把map集合取消,改为用模型的方式来代替map集合,如果要新增类的话只需要新增模型对象即可,不用再加入map集合中,让代码更易于维护和修改,也可以封装架包提供了可行性。
代码测试
<%@ 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>版本1</p> <a href="Bookservletadd">增加</a> <a href="Bookservletupd">修改</a> <a href="Bookservletdel">减少</a> <a href="Bookservletlist">查询</a> <p>版本2</p> <a href="Bookservletadd?methodName=add">增加</a> <a href="Bookservletupd?methodName=upd">修改</a> <a href="Bookservletdel?methodName=del">减少</a> <a href="Bookservletlist?methodName=list">查询</a> <p>版本3</p> <a href="Bookservletadd?methodName=add">增加</a> <a href="Bookservletupd?methodName=upd">修改</a> <a href="Bookservletdel?methodName=del">减少</a> <a href="Bookservletlist?methodName=list">查询</a> <p>版本4</p> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=del">减少</a> <a href="book.action?methodName=list">查询</a> 版本4的弊端:中央控制器的action容器加载,不可以灵活配置 <p>版本五</p> <a href="order.action?methodName=add">增加</a> <a href="order.action?methodName=upd">修改</a> <a href="order.action?methodName=del">减少</a> <a href="order.action?methodName=list">查询</a> </body> </html>
控制台输出
3.再优化
虽然上面代码优化后可以灵活配置了,但是使用重定向和转发有问题,查询必然转发,增删改使用重定向,如果频繁刷新页面,可能会出现重复提交数据。
1.先优化Action子控制器
package com.niyin.framework; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Action { protected String excute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String methoed = request.getParameter("methodName"); // private String res=""; try { Method m = this.getClass().getDeclaredMethod(methoed, HttpServletRequest.class,HttpServletResponse.class); m.setAccessible(true); res=(String) m.invoke(this, request,response); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return res; } }
把book里面的类型也改为String,并返回tolist,和list,通过返回值来判断它是需要重定向还是转发,并且还节约了代码。
package com.niyin.com; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.niyin.entity.Book; import com.niyin.framework.Action; import com.niyin.framework.ModelDrivern; public class BookAction extends Action implements ModelDrivern<Book> { private Book book=new Book(); private String list(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("list"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); // request.getRequestDispatcher("res.jsp").forward(request, response); return "list"; } private String del(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("del"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); return "tolist"; } private String upd(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("upd"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); return "tolist"; } private String add(HttpServletRequest request, HttpServletResponse response) throws IOException { // TODO Auto-generated method stub System.out.println("add"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); return "tolist"; } @Override public Book getModel() { System.out.println(book); return book; } }
优化中央控制器,通过模型对象获取·forward对象来判断该重定向还是转发。
package com.niyin.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; import org.apache.commons.beanutils.BeanUtils; import com.niyin.com.BookAction; import com.niyin.com.Orderaction; import model.ActionModel; import model.ConfigMOdelFactory; import model.ConfigModel; import model.ForwardModel; @WebServlet("*.action") public class DispatherServlet extends HttpServlet { private ConfigModel configMOdel; // public Map<String, Action> actionMap=new HashMap<String, Action>(); @Override public void init() throws ServletException { // actionMap.put("/book", new BookAction()); // actionMap.put("/order", new Orderaction()); // actionMap.put("/cat", new CatAction()); try { configMOdel=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/再configMOdel中找 ActionModel actionModel = configMOdel.pop(uri); String res= action.excute(request, response); ForwardModel forwardModel = actionModel.pop(res); if (forwardModel!=null) { boolean redirect = forwardModel.isRedirect(); 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(); } } }
运行结果
结果是一样的说明优化成功。
4.优化传值问题
一般来说通过servlet拿到前端的值再把它传入数据库,这个过程要写很多重复代码,现在来优化一下,利用反射等等
1.先定义一个模型驱动接口
package com.niyin.framework; /** * * @author 匿瘾 * * @param <T> * 模型驱动接口 */ public interface ModelDrivern<T> { T getModel(); }
2.让bookaction实现这个接口
package com.niyin.com; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.niyin.entity.Book; import com.niyin.framework.Action; import com.niyin.framework.ModelDrivern; public class BookAction extends Action implements ModelDrivern<Book> { private Book book=new Book(); private String list(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("list"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); // request.getRequestDispatcher("res.jsp").forward(request, response); return "list"; } private String del(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("del"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); return "tolist"; } private String upd(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("upd"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); return "tolist"; } private String add(HttpServletRequest request, HttpServletResponse response) throws IOException { // TODO Auto-generated method stub System.out.println("add"); // response.sendRedirect("res.jsp"); request.setAttribute("content", "你就会"); return "tolist"; } @Override public Book getModel() { System.out.println(book); return book; } }
3.优化中央控制器
在控制器内判断有没有实现接口,如果实现了就把值传进去。
package com.niyin.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; import org.apache.commons.beanutils.BeanUtils; import com.niyin.com.BookAction; import com.niyin.com.Orderaction; import model.ActionModel; import model.ConfigMOdelFactory; import model.ConfigModel; import model.ForwardModel; @WebServlet("*.action") public class DispatherServlet extends HttpServlet { private ConfigModel configMOdel; // public Map<String, Action> actionMap=new HashMap<String, Action>(); @Override public void init() throws ServletException { // actionMap.put("/book", new BookAction()); // actionMap.put("/order", new Orderaction()); // actionMap.put("/cat", new CatAction()); try { configMOdel=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/再configMOdel中找 ActionModel actionModel = configMOdel.pop(uri); if (actionModel==null) throw new RuntimeException("action 没有配置"); String type = actionModel.getType(); Action action; try { action = (Action) Class.forName(type).newInstance(); // bookaction 有没有实现这个接口 if (action instanceof ModelDrivern) { ModelDrivern md=(ModelDrivern) action; Object bean = md.getModel(); Map<String, String[]> map = request.getParameterMap(); BeanUtils.populate(bean, map); // 把值封进去 } String res= action.excute(request, response); ForwardModel forwardModel = actionModel.pop(res); if (forwardModel!=null) { boolean redirect = forwardModel.isRedirect(); 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(); } } }
最后通过degbug查看book里面是否有值判断是否成功的,传入了值。
结果:
可以看到book里面是有值的,所以最后一个优化也成功了。
4.总结
通过建模xml和反射的方式优化代码,让代码以Mvc框架的形式出现,
MVC框架构思巧妙。让代码更为简洁。