自定义MVC

简介: 自定义MVC

一.什么是MVC

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

MVC结构

  • Model:是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。
  • View:是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。
  • Controller:是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式

  • Model:常用javabean去实现,通过各种类来对数据库的数据进行获取,并封装在对象当中。
  • View:常用JSP来实现,通过可直接观察的JSP页面来展示我们从数据库中获取的数据。
  • Controller:常用servlet来实现,通过servlet来获取经过javabean包装过的对象(已存入数据库中的数据),然后再发送数据传输到JSP界面。

1)不能跨层调用; 2)只能由上往下进行调用:View -> Controller -> Model

1.1.三层架构和MVC的区别

  1. 三层架构是基于业务逻辑来分的,而MVC是基于页面来分的;
  2. 三层是种软件架构,通过接口实现编程,MVC模式是一种复合设计模式,一种解决方案;
  3. 三层架构模式是体系结构模式,MVC是设计模式;
  4. 三层架构模式又可归于部署模式,MVC可归于表示模式。

二.自定义MVC工作原理图

核心组件说明:

  • 中央控制器(ActionServlet): 复杂接收所有的请求,并分别给控制器具体处理。
  • 自控制器(Action):负责处理中央处理器分配的请求
  • 视图(view): jsp页面,负责显示
  • 模型(Model): 负责业务处理逻辑

三.自定义mvc实现

3.1 创建web工程

创建一个web工程,需要加入必要的依赖。

3.2 中央处理器

通过servlet来实现一个中央控制器,负责所有请求的接收。(后续中央控制器在再将请求转发给各个子控制器,此处可以先把请求接进来,转发功能后面再加)

/**
 * 中央控制器,负责接收所有的请求并分别给控制器具体处理
 * @author Administrator
 */
@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {
  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response) {
    doPost(request, response);
  }
  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) {
    System.out.println("dopost ..... ");
  }
}

3.3 Action接口定义

Action接口定义了每个子控制器需要遵循的行为,使得所有的子控制器都有一个同一的抽象类型,所以我们可以在中央控制器中使用Action接口类型来引用所有的子控制器。这样就为用户扩展自定义的子控制器提供了条件

/**
 * 每个子控制器必须实现该接口,负责处理中央处理器分配的请求
 * @author Administrator
 */
public interface Action {
  /**
   * 处理请求
   * @param request  请求
   * @param response 响应
   * @return String 返回转发或重定向的jsp页面名称
   */
  String exeute(HttpServletRequest request, HttpServletResponse response);
}

3.4 实现子控制器

为方便调试,实现两个子控制器

public class BookAction implements Action {
  @Override
  public String exeute(HttpServletRequest request, HttpServletResponse response) {
    return "bookList";
  }
}
public class StudentAction implements Action {
  @Override
  public String exeute(HttpServletRequest request, HttpServletResponse response) {
    // TODO Auto-generated method stub
    return "students";
  }
}

3.5 完善中央控制器

为了便于理解,我们可以分步骤的,循序渐进的完善中央控制器:

  • 编写简单的请求分发实现功能
  • 实现通过配置文件来配置子控制器的功能
  • 完善请求参数处理功能

3.5.1 请求分发功能

为了在中央控制器中完成请求的分发,需要在中央控制器中维护所有子控制器的实例,并且能够依据请求路径,将请求转发给与其关联的子控制器。

/**
 * 中央控制器,负责接收所有的请求并分别给控制器具体处理
 * @author Administrator
 */
@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {
  //用于保存path与action子控制器的映射
  public static Map<String, Action> actionMap = new HashMap<>();
  static {
    actionMap.put("/studentAction", new StudentAction());
    actionMap.put("/bookAction", new BookAction());
  }
  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response) {
    doPost(request, response);
  }
  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) {
    String servletPath = request.getServletPath();
    String path = servletPath.split("\\.")[0];
    Action action = actionMap.get(path);
    String rpath = action.exeute(request, response);
    System.out.println(rpath);
  }
}

3.5.2 使用配置文件配置action

在上面的示例中,在中央控制器中直接创建action子控制器,如果新增一个子控制器需要在中央控制器中添加,这样并不实用。 为了增加灵活性,可以将action转移到配置文件中配置,中央控制器通过配置来初始化action子控制器。

1 此时需要将config.xml文件的解析和建模项目的功能集成进来。

(ConfigModel,ActionModel,ForwardModel,ConfigModelFactory)

2 在项目的src目录下加入如下配置文件(config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!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) "false"
  >
]>
<config>
  <action path="/studentAction" type="org.lisen.mvc.action.StudentAction">
    <forward name="students" path="/students/studentList.jsp" redirect="false"/>
  </action>
</config>

完善中央处理器,通过配置文件来获取子控制器配置

@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {
  //用于保存path与action子控制器的映射
  //public static Map<String, Action> actionMap = new HashMap<>();
  private static ConfigModel configModel;
  static {
    //actionMap.put("/students", new StudentAction());
    //actionMap.put("/books", new BookAction());
    configModel  = ConfigModelFactory.getConfigModel();
  }
  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws IOException, ServletException {
    doPost(request, response);
  }
  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws IOException, ServletException {
    String servletPath = request.getServletPath();
    String path = servletPath.split("\\.")[0];
    Action action = getActionByPath(path);
    String name = action.exeute(request, response);
    ForwardModel forwardModel = getForwardModel(path, name);
    if (forwardModel.isRedirect()) {
      response.sendRedirect(request.getContextPath() + "/" + forwardModel.getPath());
    } else {
      request.getRequestDispatcher(forwardModel.getPath()).forward(request, response);
    }
  }
  //通过请求路径获取对应的action实例
  private Action getActionByPath(final String path) {
    ActionModel action = configModel.find(path);
    try {
      Class<?> clazz = Class.forName(action.getType());
      return (Action)clazz.newInstance();
    } catch (Exception e) {
      throw new RuntimeException("创建Action实例异常"+e.getMessage(), e);
    }
  }
  public ForwardModel getForwardModel(String path, String name) {
    return configModel.find(path).find(name);
  }
}

注: 本例的实现中Action子控制器是多例模式的,及每个请求对应一个Action实例

3.5.3 请求参数处理

1. 定义接口

/**
 * 对于需要处理请求参数的Action可以通过实现该接口获取请求参数的
 * 处理能力,中央控制器将会使用该接口来获取Model对象,并统一处理
 * 参数
 * @author Administrator
 */
public interface ModelDrive {
  Object getModel();
}

在中央处理器中加入请求参数的处理能力

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) 
    throws IOException, ServletException {
  ......
  Action action = getActionByPath(path);
  //处理请求参数
  if(action instanceof ModelDrive) {
    Object model = ((ModelDrive) action).getModel();
    if(model != null) {
      try {
        BeanUtils.populate(model, request.getParameterMap());
      } catch (Exception e) {
        throw new RuntimeException("在中央处理器中处理请求参数时发生异常", e);
      }
    }
  }
  String name = action.exeute(request, response);
  ......
}

2.处理请求参数中的null及空字符串转换问题

为了更方面的处理请求参数中的null及空字符串,转换为数值型数据的问题,加入一个监听器,在应用启动时注册转换器。

/**
 * ServletContextListener接口为Servlet API中的接口,用于监听ServletContext对象的生命周期。
 * 当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent事件,该事件由
 * ServletContextListener来处理。
 * @author Administrator
 */
@WebListener
public class BeanUtilsListener implements ServletContextListener {
  /**
   * 当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,
   * 容器会先销毁所有的Servlet和Filter 过滤器。
   */
  @Override
  public void contextDestroyed(ServletContextEvent arg0) {
    // TODO Auto-generated method stub
  }
  /**
   * 当Servlet 容器启动Web应用时调用该方法。
   * 在调用完该方法之后,容器再对Filter 初始化,
   * 并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
   */
  @Override
  public void contextInitialized(ServletContextEvent arg0) {
    ConvertUtils.register(new IntegerConverter(null), Integer.class);
    ConvertUtils.register(new FloatConverter(null), Float.class);
    ConvertUtils.register(new DoubleConverter(null), Double.class);
    ConvertUtils.register(new LongConverter(null), Long.class);
    ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
  }
}

3.6 完善Action

每个Action只能有一个execute方法,如果处理一个模块的增删改查则需要单独编写多个Action,这样会比较麻烦。如果在一个Action实例中可以处理多个请求方法,则框架会更加灵活。

  • 规定请求参数中必须包含一个“methodName”参数,用于指定处理请求的Action中的方法
  • 构建一个抽象类,该类实现Action子控制器接口,通过反射机制调用其子类中的方法,方法名有请求参数“methodName”指定。
  • 需要开发的Action子控制器,集成上一步构建的抽象类,编写的用于处理请求的方法名要与请求参数“methodName”指定的方法名匹配,同时需要HttpServletRequest和HttpServletResponse两个参数(保留该参数主要为了方面对请求的处理)

1.构建抽象类

public abstract class AbstractDispatchAction implements Action {
  @Override
  public String exeute(HttpServletRequest request, HttpServletResponse response) {
    String methodName = request.getParameter("methodName");
    Class<? extends AbstractDispatchAction> clazz = this.getClass();
    try {
      Method method = clazz.getDeclaredMethod(
          methodName, 
          HttpServletRequest.class,
          HttpServletResponse.class);
      return (String)method.invoke(this, request,response);
    } catch (Exception e) {
      throw new RuntimeException("在调用Action中的["+methodName+"]方法是异常", e);
    } 
  }
}

2.自定义的Action子控制器示例:

public class StudentAction extends AbstractDispatchAction implements ModelDrive {
  private Student student = new Student();
  @Override
  public Object getModel() {
    return student;
  }
  /*@Override
  public String exeute(HttpServletRequest request, HttpServletResponse response) {    
    System.out.println("StudentAction = " + student); 
    return "students";
  }*/
  public String getStudents(HttpServletRequest request, HttpServletResponse response) { 
    System.out.println("getStudents");
    System.out.println("StudentAction = " + student);
    return "students";
  }
  public String addStudent(HttpServletRequest request, HttpServletResponse response) {
    System.out.println("addStudent");
    System.out.println("add student = " + student);
    return "students";
  }
}
发送请求,示例
http://localhost:8080/mvc/studentAction.actionmethodName=addStudent&sid=100&age=23&addr=aabbcc

注意:在请求中需要添加一个methodName的固定参数,该参数指定了需要调用的Action中的方法的名称。

四.其他公用组件的集成

可以将通用分页,字符编码过滤器等组件一集成到mvc框架中便于复用。

五.打jar包

将自定义mvc框架打成jar包,以便于在其他项目中使用。

项目 --(右击)-->Export


相关文章
|
XML 开发框架 前端开发
J2EE之自定义MVC框架知识(中篇)
J2EE之自定义MVC框架知识(中篇)
90 0
|
存储 前端开发 数据可视化
自定义MVC(上)
自定义MVC(上)
73 1
|
3月前
|
前端开发 Java 数据库
springBoot:template engine&自定义一个mvc&后端给前端传数据&增删改查 (三)
本文介绍了如何自定义一个 MVC 框架,包括后端向前端传递数据、前后端代理配置、实现增删改查功能以及分页查询。详细展示了代码示例,从配置文件到控制器、服务层和数据访问层的实现,帮助开发者快速理解和应用。
|
XML 前端开发 Java
自定义MVC的初步实现
自定义MVC的初步实现
59 0
自定义MVC的初步实现
|
8月前
|
前端开发 Java
自定义mvc的增删改查
自定义mvc的增删改查
74 0
|
8月前
|
XML 前端开发 数据格式
自定义MVC引用XML配置文件实现
自定义MVC引用XML配置文件实现
74 0
|
8月前
|
设计模式 前端开发 搜索推荐
自定义mvc框架
自定义mvc框架
80 0
|
设计模式 前端开发
自定义mvc
自定义mvc
58 0
|
XML 前端开发 数据格式
自定义MVC超详细易懂----增删改查
自定义MVC超详细易懂----增删改查
123 0
|
存储 前端开发 架构师
自定义MVC实现 很详细(下)---优化版
自定义MVC实现 很详细(下)---优化版