SpringMVC框架学习02_MVC思想和前端控制器思想

【作者:任小龙,叩丁狼高级讲师。原创文章,转载请注明出处。】

在上一篇文章中,我们讲了框架的由来以及Web开发中的最佳实践就是三层架构开发,并留了一个坑,什么是MVC思想。在本文中,我们继续把这个坑挖大一点,当然在边挖的过程中也来填一点坑,边挖边填,边填边挖,这并不是去折磨读者,而是一种思想的演变过程,编程在乎的不就是思想吗,挖坑和填坑可以是一种学习的乐趣。
本文需要探讨的有两个点:
一、MVC思想;
二、前端控制器思想;

MVC思想

MVC思想在Java Web中已经学习过,简单回顾下:

MVC是一种架构型的模式,所体现的就是一种责任分离原则。
它本身并没有引入任何新的功能,旨在帮助我们将应用开发的结构划分的更加合理。

它展现的是:

  • 视图展示与业务逻辑相分离
  • 流程控制逻辑与业务逻辑调用相分离
  • 流程控制逻辑与视图展示逻辑分离

    标准的MVC模型图

上图展示了模型(M)、视图(V)、控制器(C)三者的关系和请求流程,下面我们来说说三者分别表示什么。

Model(模型)

Model是数据模型,其实就是JavaBean对象,细分可以表示为业务组件(service)、领域模型组件(domain)等,Model包含要展示的数据和拥有的业务功能方法。

View(视图)

View是用户界面,说人话就是用户能看到的网页界面,当然该界面上还需要展示数据模型中的具体数据。

Controller(控制器)

Controller是控制器,起控制调度作用,主要职责包括接收用户请求、调用业务方法处理请求、共享模型数据、并跳转到响应的视图界面。

上图是一个标准的MVC模型视图,我们发现模型组件居然可以直接把数据推送给视图组件,如果是在CS领域这是可以通过观察者模式(有人也称之为监听模式)完成的,但是在BS领域确是不行的。

C/S即Client/Server(客户机/服务器)结构,C/S结构在技术上很成熟,它的主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。但是该结构的程序是针对性开发,变更不够灵活,维护和管理的难度较大。通常只局限于小型局域网,不利于扩展。并且,由于该结构的每台客户机都需要安装相应的客户端程序,分布功能弱且兼容性差,不能实现快速部署安装和配置,因此缺少通用性,具有较大的局限性。要求具有一定专业水准的技术人员去完成。
B/S即Browser/Server(浏览器/服务器)结构,就是只安装维护一个服务器(Server),而客户端采用浏览器(Browse)运行软件。B/S结构应用程序相对于传统的C/S结构应用程序是一个非常大的进步。 B/S结构的主要特点是分布性强、维护方便、开发简单且共享性强、总体拥有成本低。但数据安全性问题、对服务器要求过高、数据传输速度慢、软件的个性化特点明显降低,这些缺点是有目共睹的,难以实现传统模式下的特殊功能要求。例如通过浏览器进行大量的数据输入或进行报表的应答、专用性打印输出都比较困难和不便。此外,实现复杂的应用构造有较大的困难。(引用于互联网)

因为在BS领域中必须先得有浏览器发出请求,而后服务端针对该请求做出相应,这种模式我们也称之为请求-响应模式。

请求-响应模式

因此在Web中数据模型是无法主动把数据推送给视图的(无法主动更新用户界面数据),浏览器必须重新发出新的请求,控制器跳转到视图界面,并携带模型中新的数据做响应。

Web中的MVC模型图

前端控制器思想

咱们且先不给前端控制器下定义,在讲解前端控制器思想之前,我们先来看看假设没有前端控制器,我们传统的的请求方式是怎么样的,又存在哪些问题。

没有前端控制器时请求模式图

通过Java Web的学习,我们知道每一个请求都需要执行一个Servlet,如上图。这本身是无可争议的,但其中却隐藏了另一个问题:假如每一次请求都需要执行相同的功能代码,比如都需要做权限检查操作,此时多个Servlet中都得写上权限检查的代码。这样的话,代码不就重复了吗。如此也就违背了DRY(Don’t repeat yourself,不要写重复代码)原则,重复就意味着维护成本很高,一旦需要修改代码,就得修改N个地方。

那针对这种情况,我们就得去想:要是进入每一个Servlet之前,都必须先执行权限检查这一段代码,那每一个Servlet中就变得很纯洁,只做和自己相关的功能处理,这肯定是一件挺愉快的事情。

愉快固然是一件好事,而问题在于,怎么才能让自己愉快?说人话就是,如何才能在处理每一个请求之前,执行某一段代码。在Java中程序最小的单元是类,代码是不能独立存在的,那么我们就得把这段优先执行的代码存放在某个类中,同时这个类必须满足比处理请求对象先执行。此时我们可能想到了过滤器(Filter),且先不说,我们先把这种优先于处理请求对象执行的思想做一个归纳和总结,这种思想其实就是前端控制器思想。

前端控制器(Front Controller)模式要求在Web应用系统的前端(Front)设置一个入口控制器(Controller),用来提供一个集中的请求处理机制。让所有的请求都先发往该控制器作统一处理,然后再把各个请求分发给各自相应的处理程序。

  • 前端控制器,一般用来做一个共同的处理,如权限检查,授权,日志记录等。
  • 前端控制器,集中处理请求的能力,能提高了代码可重用性和可拓展性。

那么拥有前端控制器后到底是怎样的请求模式:

拥有前端控制器的请求模式图

通过上图,我们能发现用户所有的请求都优先发送给了前端控制器,在前端控制器中做了多个请求的共同处理操作,处理完后再分发给各自的请求处理对象,并处理各自的特殊逻辑。可能有人会问,前端控制器能不能做成通用的,处理完所有的请求,答案肯定是不行的,比如删除一个员工、查询出所有员工,这两种请求具体的处理逻辑是不一样的,就不能在前端控制器中统一来处理。

根据处理请求的先后顺序,有人把处理通用请求的控制器称之为前端控制器,把处理具体请求的对象称之为后端控制器。一般的,这里的后端控制器就是一个个的POJO类了,并不是Servlet组件了。不过,它们的起名倒挺讲究的,主要分为两个派别。

  • 一种习惯称之为XxxAction,如 EmployeeAction,这种命名方式来源于早期的Apache 的Struts框架,影响了好几代程序员。
  • 另一种习惯称之为XxxController,如 EmployeeController,这种命名方式源于Spring的处理器Controller。

当然这也就是两种命名方式而已,大可不必在意,一般的在使用Struts/Struts2时,处理请求的对象叫Action,使用SpringMVC时,处理请求的对象叫Controller,或者Handler。

那么现在还剩一个问题没有解决了,也是一个非常重要的问题。

如何才能使前端控制器优先于处理请求对象执行?

答案非常简单,使用Filter(过滤器)或Servlet都可以完成。因为二者都可以接受请求,当获取到客户端的请求后就可以编写统一处理的功能代码,处理完,再根据不同的请求规则,分发到不同的请求对象中去。

这里需要注意的是:使用前端控制器,必须先在web.xml文件中配置前端控制器,无论是基于Filter还是Servlet的,如果前端控制器是基于Servlet实现的还需要优先初始化该Servlet。

  • 使用Filter实现前端控制器

     //前端控制器
    public class CoreFilter implements javax.servlet.Filter {    
        public void init(FilterConfig filterConfig) throws ServletException {
            //初始化操作,比如加载配置文件,读取配置信息到内存中
        }
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
              throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) resp;
            //获取请求资源
            String requestUri = request.getRequestURI();
            //根据请求规则,分发请求到具体的请求对象中
            //TODO
        }
    }
    
  • 在web.xml中配置前端控制器

    <filter>
        <filter-name>CoreFilter</filter-name>
        <filter-class>cn.wolfcode.web.filter.CoreFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CoreFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  • 使用Servlet实现前端控制器

    //前端控制器
    public class DispatcherServlet extends javax.servlet.http.HttpServlet {
        public void init() throws ServletException {
            //初始化操作,比如加载配置文件,读取配置信息到内存中
        }
        protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
            //获取请求资源
            String requestUri = request.getRequestURI();
            //根据请求规则,分发请求到具体的请求对象中
            //TODO
        }
    }
    
  • 在web.xml中配置前端控制器

      <servlet>
          <servlet-name>DispatcherServlet</servlet-name>
          <servlet-class>cn.wolfcode.web.servlet.DispatcherServlet</servlet-class>
          <!-- 在启动Web服务器时,优先初始化前端控制器 -->
          <load-on-startup>0</load-on-startup>
      </servlet>
      <servlet-mapping>
          <servlet-name>DispatcherServlet</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
    

这里没有提供具体前端控制器实现,我们可以在后面文章中着手去模拟基于两种方式的MVC框架。
因为使用是Java8版本,所以新的接口方法都提供了默认的实现,并不要求必须实现类覆盖,如Filter中的init和destroy方法,需要的时候我们才手动覆盖。


小结

1、MVC是一种架构思想,提现的是责任分离思想,各自做各自最擅长的事情。
2、前端控制器思想,处理了所有请求共同的操作,体现了DRY原则。

3、使用MVC框架,需先拷贝jar,而后配置前端控制器。


猜你喜欢

转载自blog.csdn.net/wolfcode_cn/article/details/80654657
今日推荐