自定义MVC的初步实现

简介: 自定义MVC的初步实现

前言

在上一篇博客,我们介绍了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>

打印结果:

点击新增:

点击查看:

目录
相关文章
|
1月前
|
前端开发 Java 数据库
springBoot:template engine&自定义一个mvc&后端给前端传数据&增删改查 (三)
本文介绍了如何自定义一个 MVC 框架,包括后端向前端传递数据、前后端代理配置、实现增删改查功能以及分页查询。详细展示了代码示例,从配置文件到控制器、服务层和数据访问层的实现,帮助开发者快速理解和应用。
|
6月前
|
前端开发 Java
自定义mvc的增删改查
自定义mvc的增删改查
65 0
|
6月前
|
XML 前端开发 数据格式
自定义MVC引用XML配置文件实现
自定义MVC引用XML配置文件实现
65 0
|
6月前
|
设计模式 前端开发 搜索推荐
自定义mvc框架
自定义mvc框架
70 0
|
11月前
|
设计模式 前端开发
自定义mvc
自定义mvc
50 0
|
11月前
|
XML 前端开发 数据格式
自定义MVC超详细易懂----增删改查
自定义MVC超详细易懂----增删改查
102 0
|
11月前
|
存储 前端开发 架构师
自定义MVC实现 很详细(下)---优化版
自定义MVC实现 很详细(下)---优化版
|
11月前
|
存储 设计模式 前端开发
自定义MVC实现
自定义MVC实现
|
11月前
|
安全 Java
自定义mvc----增删改查终极篇
自定义mvc----增删改查终极篇
44 0
|
11月前
|
XML 设计模式 前端开发
自定义MVC---引用XML
自定义MVC---引用XML
50 0