前言
在这篇 自定义MVC框架思想 中我已详细描述了工作原理及流程,本篇主要在此基础上继续做出优化,实现步骤如下:
自定义MVC实现
1. 导入XML配置文件
<?xml version="1.0" encoding="UTF-8"?> <config> <action path="/order" type="com.ycxw.servlet.OrderAction"> <forward name="success" path="/index.jsp" redirect="true" /> <forward name="failed" path="/register.jsp" redirect="false" /> </action> <action path="/book" type="com.ycxw.servlet.BookAction"> <forward name="List" path="/book.jsp" redirect="false" /> <forward name="toList" path="/index.jsp" redirect="true" /> </action> </config>
将其部署到Source Folder文件中
2. 导入XML解析建模
这里就不一一详细解说了,可以去 XML建模 中了解详细建模实例。
3. 优化中央控制器
3.1 修改DisPathServlet中init初始化方法
在DisPathServlet的init方法中将原有Map集合方式替换成XML建模方式
// 通过xml建模方法进行储存
private ConfigModel configModel;
@Override
public void init() throws ServletException {
/**
* 初始化存值就是给每个施工员根据施工证进行存档:
*/
try {
// configModel包含了所有的子控制器
configModel = ConfigModelFactory.build();
} catch (Exception e) {
e.printStackTrace();
}
}
3.2 修改ActionServlet逻辑处理流程
根据请求路径名获取ActionModel
这里如果查不到指定对象,请认真检查xml文件的配置,以及截取的内容是否与xml配置相同
/** * 获取请求路径 */ String uri = request.getRequestURI(); // 截取book uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf(".")); // 要通过uri->> /book,在configModel对象中找 ActionModel actionModel = configModel.pop(uri); // 判断没找对象等于空就抛出异常 if (actionModel == null) throw new RuntimeException("action not config"); /** * 获取config文件中action标签的type属性值 * type指java类 * com.ycxw.servlet.BookAction */ String type = actionModel.getType();
3.3 通过反射机制实例化子控制器类
package com.ycxw.framework; import java.lang.reflect.Method; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 子控制器(action) 处理浏览器请求的类 * * @author 云村小威 * * 2023年6月29日 下午8:30:32 */ public class Action { public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception { // 获取methodName值,这里指前端点击功能传来的方法名 String methodName = request.getParameter("methodName"); //定义一个变量来保存返回值 String res = ""; /** * this--->BookAction/BlogAction/PermissionAction...可能是很多对象 * 所以需要通过反射找到对象带request,response参数的methidName方法 */ Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); m.setAccessible(true); // 动态调用其方法 res = (String) m.invoke(this, request, response); return res ; } }
3.4 中央控制器将请求委托给子控制器处理
Action instance = (Action) Class.forName(type).newInstance();
// 业务代码执行后返回值
String execute = instance.execute(request, response);
3.5 根据请求结果码跳转页面
// 通过返回值拿到,该方法结果是重定向还是转发,还是跳转哪个页面 ForwardModel forwardModel = actionModel.pop(execute); if (forwardModel != null) { // 获取forwardModel是否从定向值 boolean redirect = forwardModel.isRedirect(); /** * 获取xml元素path值 */ String path = forwardModel.getPath(); // 判断是否为重定向 if (redirect) { response.sendRedirect(request.getContextPath() + "/" + path); } else { request.getRequestDispatcher(path).forward(request, response); } }
4. 反射赋值
4.1 创建接口DriverModel<T>
package com.ycxw.framework; /** * 模型驱动接口 * Book book = new Book(); * @author 云村小威 * * @param <T> */ public interface ModelDriver<T> { T getModel(); }
4.2 实现接口类
针对需要进行反射赋值的具体子控制器类,实现该接口DriverModel。
package com.ycxw.servlet; import java.io.IOException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ycxw.entity.Book; import com.ycxw.framework.Action; import com.ycxw.framework.ModelDriver; /** * 施工类 继承子控制器 * * @author 云村小威 * * @2023年6月29日 下午8:32:59 */ public class BookAction extends Action implements ModelDriver<Book>{ //创建表对应的属性对象 Book book = new Book(); @Override public Book getModel() { // TODO Auto-generated method stub return book; } public String load(HttpServletRequest req, HttpServletResponse resp) { //获取所有的参数 Map<String, String[]> map = req.getParameterMap(); System.out.println("Book查询的业务逻辑"); try { resp.sendRedirect("index.jsp"); } catch (IOException e) { e.printStackTrace(); } return "List"; } public String query(HttpServletRequest req, HttpServletResponse resp) { System.out.println("Book查询的业务逻辑"); try { resp.sendRedirect("index.jsp"); } catch (IOException e) { e.printStackTrace(); } return "List"; } public String edit(HttpServletRequest req, HttpServletResponse resp) { System.out.println("Book修改的业务逻辑"); try { resp.sendRedirect("index.jsp"); } catch (IOException e) { e.printStackTrace(); } return "toList"; } public String delete(HttpServletRequest req, HttpServletResponse resp) { System.out.println("Book删除的业务逻辑"); try { resp.sendRedirect("index.jsp"); } catch (IOException e) { e.printStackTrace(); } return "toList"; } public String add(HttpServletRequest req, HttpServletResponse resp) throws Exception { System.out.println("Book新增的业务逻辑"); req.getRequestDispatcher("index.jsp").forward(req, resp); return "toList"; } }
只要实现了DriverModel接口,则必须要对实体类进行初始化,并在getModel()方法中返回实例化后的对象。
4.3 反射对象赋值
再次修改ActionServlet中的业务逻辑处理流程,在反射调用方法之前,请先进行反射参数赋值操作。
/** * 获取config文件中action标签的type属性值 * type指java类 * com.ycxw.servlet.BookAction */ String type = actionModel.getType(); // 通过反射创建对象 Action instance; try { instance = (Action) Class.forName(type).newInstance(); // 判断bookAction有没有实现ModelDriver接口 if (instance instanceof ModelDriver) { // 向下转型获取接口方法 ModelDriver md = (ModelDriver) instance; Object bean = md.getModel(); // 把获取的参数保存到该对象中 BeanUtils.populate(bean, request.getParameterMap()); }
5. 完整优化中央控制器实例
5.1 完整 DisPathServlet类代码
package com.ycxw.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.ycxw.framework.model.ActionModel; import com.ycxw.framework.model.ConfigModel; import com.ycxw.framework.model.ConfigModelFactory; import com.ycxw.framework.model.ForwardModel; /** * 中央控制器(ActionServlet) * * @author 云村小威 * * @2023年6月29日 下午8:14:38 */ @WebServlet("*.action") public class DisPathServlet extends HttpServlet { private static final long serialVersionUID = 1L; // 通过xml建模方法进行储存 private ConfigModel configModel; @Override public void init() throws ServletException { /** * 初始化存值就是给每个施工员根据施工证进行存档: */ try { // configModel包含了所有的子控制器 configModel = ConfigModelFactory.build(); } catch (Exception e) { e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // http://localhost:8080/MVC_project/book.action?methodName=delete... /** * 获取请求路径 */ String uri = request.getRequestURI(); // 截取book uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf(".")); // 要通过uri->> /book,在configModel对象中找 ActionModel actionModel = configModel.pop(uri); // 判断没找对象等于空就抛出异常 if (actionModel == null) throw new RuntimeException("action not config"); /** * 获取config文件中action标签的type属性值 * type指java类 * com.ycxw.servlet.BookAction */ String type = actionModel.getType(); // 通过反射创建对象 Action instance; try { instance = (Action) Class.forName(type).newInstance(); // 判断bookAction有没有实现ModelDriver接口 if (instance instanceof ModelDriver) { // 向下转型获取接口方法 ModelDriver md = (ModelDriver) instance; Object bean = md.getModel(); // 把获取的参数保存到该对象中 BeanUtils.populate(bean, request.getParameterMap()); } // 业务代码执行后返回值 String execute = instance.execute(request, response); // 通过返回值拿到,该方法结果是重定向还是转发,还是跳转哪个页面 ForwardModel forwardModel = actionModel.pop(execute); if (forwardModel != null) { // 获取forwardModel是否从定向值 boolean redirect = forwardModel.isRedirect(); /** * 获取xml元素path值 */ String path = forwardModel.getPath(); // 判断是否为重定向 if (redirect) { response.sendRedirect(request.getContextPath() + "/" + path); } else { request.getRequestDispatcher(path).forward(request, response); } } } catch (Exception e) { e.printStackTrace(); } } }
5.2 测试JSP页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>测试</title> </head> <body> <a href="${pageContext.request.contextPath }/book.action?methodName=add&bid=1&bname=aa&price=13">增加</a> <a href="${pageContext.request.contextPath }/book.action?methodName=delete">删除</a> <a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a> <a href="${pageContext.request.contextPath }/book.action?methodName=query">查询</a> <a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a> </body> </html>
5.3 论证反射对象赋值
1. 通过debug调试,运行到当前通过反射拿到指定对象时为空
2. 执行完populate方法后将自定赋值等效于遍历对象一个个赋值
BeanUtils.populate(bean, request.getParameterMap());
最后在讲解一下populate方法的解释:
6. populate方法详解
BeanUtils.populate(Object bean, Map properties)
BeanUtils
位于org.apache.commons.beanutils.BeanUtils
下,populate是BeanUtils工具类的一个方法。
作用:
这个方法会遍历map<key, value>
中的key
,如果bean
中有这个属性,就把这个key
对应的value
值赋给bean
的属性