个人对 MVC 结构的理解
MVC 架构是 web 应用一个非常经典的架构,它将整个应用分为了 Model、View、Controller三层,三层各司其职,可以更加充分的解耦。
View 即视图层,主要就是展示给用户的部分,比如经典的 JSP 技术,主要就是用来搭建视图的。View层面向用户,可以接受用户的各种请求。View 层并不对请求进行处理,实际处理请求的是 Model 层,该层又可以细分为 Service 层与 DAO 层,前者负责处理业务逻辑,后者则直接与数据库打交道,它们的调用关系就是 Service 层调用 DAO 层。但是一个应用中,有很多业务,一个用户请求到底由谁来负责处理,谁来负责去找具体的服务程序,谁负责用户请求的总体调度与转发,这就是 Controller 层做得是,它实际上就是 View 与 Model 之间的一座桥梁,好的设计一般都会Rich Model,thin Controller。Controller 层很像之前学过的 Servlet,回忆下之前 Servlet 干的事,一般会有一个 doGet(req, resp) 和 doPost(req, resp),它们能够获得用户的 req,之后调用下游的 某个 service 来进行业务处理,然后下游处理完将数据返回给 Controller,它再把处理完的数据返回 View 层让用户感知到。下面附一篇个人觉得对 MVC 架构讲的比较好的博客:MVC的理解和各自分工。
个人觉得这种方式最大的好处就是解耦吧,什么解耦呢,就是让每层之间的依赖关系变少,改动任何一层都不会影响到其他层,如果把各层揉在一起,业务一旦膨胀,代码将变得难以维护。
之前看到过一个对 MVC 架构的一个挺好的比喻,就是饭店里接待员、厨师与原材料,不过我更喜欢想到银行这个场景来比喻,比如前台接待(Controller),具体业务办理员(Service)以及办理员取数据所需要的电脑(DAO),仔细一想,其实这三者分开确实是彼此解耦的,如果少一层,那办事效率就挺低的了。
SpringMVC 自然就是把这么一种设计模式集成在了 Spring 这个框架中,实际本质还是 MVC 的思想,接下来分析下 SpringMVC 的一些角色和运行原理,来看看它是如何将 MVC 的思想集成进入框架的。
SpringMVC 工作原理
SpringMVC 是围绕着一个叫 DispatcherServlet 的东西来设计的,顾名思义,这玩意就是一个用来分发的 servlet。先看一张原理图:
从图中可以看出,整个 SpringMVC 结构依赖于下面几个对象:
- DispatcherServlet
- HandlerMapper
- HandlerAdapter
- ViewResolver
DispatcherServlet 是一个 Front Controller(前置控制器),我对它的理解就是,它就像一个总指挥一样的角色,主要接受用户请求,调度 Handler,返回 View。以下是一个请求的处理过程:
- 用户的请求到达了 DispatcherServlet,它去请求 HandlerMapper 并且后者会将具体的 Handler 返回给 DispatcherServlet (这里的 Handler 暂时可以理解为 Controller)。说白了 HandlerMapper 的作用就是告诉DispatcherServlet 用户的请求需要交给哪个 Handler 去处理
- DispatcherServlet 获得了 Handler 对象后,要去调用一个叫做 HandlerAdapter 的对象去执行 Handler,后者执行完后会返回一个 ModelAndView 给 DispatcherServlet
- DispatcherServlet 拿到这个 ModelAndView 后,把它交给 ViewResolver 进行解析,后者将解析完的 View 返回给 DispatcherServlet
- DispatcherServlet 将拿到的 View 返回给用户
整个大体流程就是这样,但是目前为止我对 HandlerAdapter 的形式和作用还不是很理解,为什么要适配?适配什么?这个等理解了再来补充,但是其他步骤相对还是比较好理解的。再补一张 kuang 神的原理图,其中实线部分是框架做好的,虚线部分是需要开发实现的。
一个 SpringMVC Demo
文字总是比较抽象,接下来通过代码来直观的感受一个 SpringMVC 代码是什么样的。
web.xml文件
之前学 servlet 时,我们需要把自己写的所有 servlet 都注册在该文件下,但是现在由于有了 DispatcherServlet 这个“管家”,就不需要注册其他的 servlet 了。可以看到在该文件中,只注册了一个 DispatcherServlet,并且所有请求都会被它拦截。此外,在该 servlet 初始化时,还会去读取 springmvc-servlet.xml 这个配置文件,主要是去实例化其他几个必须的对象。
springmvc-servlet.xml文件
该文件属于 Springmvc 的配置文件,它里面注册了几个 Bean,正是上面提到的需要依赖的几个对象。没有它们,整个结构就无法正常运转。
在视图解析器中,配置了 prefix 与 suffix 属性,它们分别代表着解析视图路径的前缀和后缀,这样只需要在代码中传视图的名字即可。
此外还配置了一个 id 为 /hello 的 Bean,这意味着当用户访问 localhost:8080/hello 时,就会将请求打到 HelloController,如果不配置这个路由,那么 DispatcherServlet 就不知道谁来处理这个请求。
还记得之前说到的 HandlerMapping 吧,它的作用就是去找哪个 Handler(Controller) 来处理用户请求,它寻找 Handler 的方式也就两种,一种是通过配置文件,另外一种就是注解。那么我大胆猜测,它就是通过这个配置文件来寻找 Handler 的,此外,还可以顺利推测出,/hello 这个 Bean后续一定是通过注解来实现。
Controller
这里写了一个很简单的 Controller,由于实现了 Controller 接口,所以必须要实现其方法 handleRequest(),之前说过,通常 Controller 是轻量的,不处理业务的,由于实例代码没有业务处理,所以在 Controller 中也就不调用 service 了,这里理解一下就行。最终它会返回一个 ModelAndView,并且在代码中设置了视图名,那么数据就会被传递到路径为 /WEB-INF/jsp/hello.jsp 这个视图中。
- jsp 代码
上面 ModelAndView 中添加的名为 msg 的对象的值,会传递给该视图进行展示。浏览器再对 jsp 进行渲染,用户就能看到啦!
