前言
上一篇博客介绍了自定义MVC框架的原理,也实现了一个简单的增删改查的框架,接下来就对上一篇框架的实现进行再优化。
一. 自定义MVC框架的实现与优化
1.1 利用xml建模优化
在上一篇博客中,在中央控制器中直接创建action子控制器,如果新增一个子控制器需要在中央控制器中添加,这样并不实用。 为了增加灵活性,可以将action转移到配置文件中配置,中央控制器通过配置来初始化action子控制器。
xml建模如有需要请查看
1.1.1 导入xml建模工具类
1.1.2 导入config.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- config标签:可以包含0~N个action标签 --> <config> <!-- action标签:可以饱含0~N个forward标签 path:以/开头的字符串,并且值必须唯一 非空 type:字符串,非空 --> <!-- forward标签:没有子标签; name:字符串,同一action标签下的forward标签name值不能相同 ; path:以/开头的字符串 redirect:只能是false|true,允许空,默认值为false --> <action path="/book" type="com.xissl.web.BookAction"> <forward name="list" path="/res.jsp" redirect="false" /> <forward name="toList" path="/res.jsp" redirect="true" /> </action> </config>
1.1.3 中央控制器DispatherServlet的优化
修改DispatherServlet中的init初始化方法
将原来的map集合存放子控制器变成xml建模方式
package com.xissl.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.xissl.framework.model.ActionModel; import com.xissl.framework.model.ConfigModel; import com.xissl.framework.model.ConfigModelFactory; import com.xissl.framework.model.ForwardModel; /** * 中央控制器 * @author xissl * */ @WebServlet("*.action") public class DispatherServlet extends HttpServlet{ // 将子控制器存放到xml配置文件中,实则就是模型对象中 private ConfigModel ConfigModel; @Override public void init() throws ServletException { try { // ConfigModel包含了所有的子控制器 ConfigModel = ConfigModelFactory.build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String url = req.getRequestURI(); // 拿到/book,就是最后一个“/”到最后一个“.”为止 url = url.substring(url.lastIndexOf("/"), url.lastIndexOf(".")); // 在ConfigModel对象中找url ActionModel actionModel = ConfigModel.pop(url); // 如果没找到actionModel,抛出异常 if(actionModel == null) // action没有配置 throw new RuntimeException("action not config"); // 获取全路径com.xissl.web.BookAction String type = actionModel.getType(); // 反射实例化 try { Action action = (Action) Class.forName(type).newInstance(); action.execute(req, resp); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
优化思路:
1.通过url来找到config文件中对应的action对象
2.通过该对象获取全路径名com.xissl.web.BookAction
3.然后找到对应的方法执行
1.2 利用反射,进行页面跳转的优化
在我们跳转到另一个界面时,需要写很多关于跳转方式的代码,有重定向,有转发,例如:
重定向:resp.sendRedirect(path);
转发: req.getRequestDispatcher(path).forward(req, resp);
这些代码往往要写很多次,因此通过配置config文件来解决此类问题;
1.2.1 中央控制器DispatherServlet
package com.xissl.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.xissl.framework.model.ActionModel; import com.xissl.framework.model.ConfigModel; import com.xissl.framework.model.ConfigModelFactory; import com.xissl.framework.model.ForwardModel; /** * 中央控制器 * @author xissl * */ @WebServlet("*.action") public class DispatherServlet extends HttpServlet{ // 将子控制器存放到xml配置文件中,实则就是模型对象中 private ConfigModel ConfigModel; @Override public void init() throws ServletException { try { // ConfigModel包含了所有的子控制器 ConfigModel = ConfigModelFactory.build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String url = req.getRequestURI(); // 拿到/book,就是最后一个“/”到最后一个“.”为止 url = url.substring(url.lastIndexOf("/"), url.lastIndexOf(".")); // 在ConfigModel对象中找url ActionModel actionModel = ConfigModel.pop(url); // 如果没找到actionModel,抛出异常 if(actionModel == null) // action没有配置 throw new RuntimeException("action not config"); // 获取全路径com.xissl.web.BookAction String type = actionModel.getType(); // 反射实例化 try { Action action = (Action) Class.forName(type).newInstance(); // 具体业务代码执行后的返回值 String execute = action.execute(req, resp); // 通过返回值拿到该方法结果是转发还是重定向 ForwardModel forwardModel = actionModel.pop(execute); if(forwardModel!=null) { boolean redirect = forwardModel.isRedirect(); String path = forwardModel.getPath(); if(redirect) { // 重定向会导致项目名字丢失 resp.sendRedirect(req.getContextPath()+"/"+ path); }else { req.getRequestDispatcher(path).forward(req, resp); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
1.2.2 子控制器action
package com.xissl.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 xissl * */ public class Action { protected String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String methodName = req.getParameter("methodName"); String res = ""; try { Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class); method.setAccessible(true); res = (String) method.invoke(this, req,resp); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return res; } }
1.2.3 模拟Servlet (BookServlet)
package com.xissl.web; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xissl.framework.Action; public class BookAction extends Action { public String add(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("新增方法bookaddservlet.add..."); req.setAttribute("content", "携带参数"); // resp.sendRedirect("res.jsp"); return "toList"; } public String del(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("删除方法bookaddservlet.del..."); req.setAttribute("content", "携带参数"); // resp.sendRedirect("res.jsp"); return "toList"; } public String upd(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("修改方法bookaddservlet.upd..."); req.setAttribute("content", "携带参数"); // resp.sendRedirect("res.jsp"); return "toList"; } public String query(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("查询方法bookaddservlet.query..."); req.setAttribute("content", "携带参数"); // req.getRequestDispatcher("res.jsp").forward(req, resp); return "list"; } }
1.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> 查询使用转发,增删改使用重定向 <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=query">查询</a> </body> </html>
运行结果如下:
增删改:
查询:
1.3 jsp界面参数传递给后台代码优化
1.3.1 创建一个模型驱动接口
建一个模型驱动接口,使BookAction实现该接口,在中央控制器中将所有要接收的参数封装到模型接口中,从而达到简便的效果。
package com.xissl.framework; /** * 模型驱动接口 * @author xissl * */ public interface ModelDriver<T> { T getModel(); }
1.3.2 中央控制器
package com.xissl.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 org.apache.commons.beanutils.BeanUtils; import com.xissl.framework.model.ActionModel; import com.xissl.framework.model.ConfigModel; import com.xissl.framework.model.ConfigModelFactory; import com.xissl.framework.model.ForwardModel; /** * 中央控制器 * @author xissl * */ @WebServlet("*.action") public class DispatherServlet extends HttpServlet{ // 将子控制器存放到xml配置文件中,实则就是模型对象中 private ConfigModel ConfigModel; @Override public void init() throws ServletException { try { // ConfigModel包含了所有的子控制器 ConfigModel = ConfigModelFactory.build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String url = req.getRequestURI(); // 拿到/book,就是最后一个“/”到最后一个“.”为止 url = url.substring(url.lastIndexOf("/"), url.lastIndexOf(".")); // Action action = actionMap.get(url); // 在ConfigModel对象中找url ActionModel actionModel = ConfigModel.pop(url); // 如果没找到actionModel,抛出异常 if(actionModel == null) // action没有配置 throw new RuntimeException("action not config"); // 获取全路径com.xissl.web.BookAction 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[]> map = req.getParameterMap(); // 将该对象要接收的参数值封装到对应的对象中 BeanUtils.populate(model, map); } // 具体业务代码执行后的返回值 String execute = action.execute(req, resp); // 通过返回值拿到该方法结果是转发还是重定向 ForwardModel forwardModel = actionModel.pop(execute); if(forwardModel!=null) { boolean redirect = forwardModel.isRedirect(); String path = forwardModel.getPath(); if(redirect) { // 重定向会导致项目名字丢失 resp.sendRedirect(req.getContextPath()+"/"+ path); }else { req.getRequestDispatcher(path).forward(req, resp); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
1.3.3 模拟Servlet(BookServlet)
package com.xissl.web; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xissl.entity.Book; import com.xissl.framework.Action; import com.xissl.framework.ModelDriver; public class BookAction extends Action implements ModelDriver<Book>{ Book book = new Book(); public String add(HttpServletRequest req,HttpServletResponse resp) throws Exception { Map<String, String[]> parameterMap = req.getParameterMap(); /** * 1.获取表对应的类属性对象Book book * 2.获取到所有的参数及参数值 * 3.将参数值封装到对应的对象中 * 4.做到所有子控制器通用 */ System.out.println("新增方法bookaddservlet.add..."); req.setAttribute("content", "携带参数"); // resp.sendRedirect("res.jsp"); return "toList"; } public String del(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("删除方法bookaddservlet.del..."); req.setAttribute("content", "携带参数"); // resp.sendRedirect("res.jsp"); return "toList"; } public String upd(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("修改方法bookaddservlet.upd..."); req.setAttribute("content", "携带参数"); // resp.sendRedirect("res.jsp"); return "toList"; } public String query(HttpServletRequest req,HttpServletResponse resp) throws Exception { System.out.println("查询方法bookaddservlet.query..."); req.setAttribute("content", "携带参数"); // req.getRequestDispatcher("res.jsp").forward(req, resp); return "list"; } @Override public Book getModel() { // TODO Auto-generated method stub return book; } }
1.3.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> <a href="book.action?methodName=add&bid=1&bname=zs&price=9.9">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=query">查询</a> </body> </html>
DeBug调试结果如下: