MVC核心组件

175 阅读8分钟

先请出 Spring MVC 中的 5 大核心组件,并用一句话概括其功能。混个眼熟,具体功能描述及使用方式在后面慢慢展开。

  • 前端控制器: 类似于超市的入口;

  • 用户控制器: 类似于不同类型的货品架,为消费者提供具体的商品;

  • 映射器: 类似于超市里的导购员。请问,我要的这个牌子的辣椒酱在哪里? 哦,在那里;

  • 适配器: 统一控制器,让其有一致对外的接口;

  • 视图解析器: 找到视图,并负责渲染视图。

  • 还有其它的诸多辅助性组件,当使用到时再逐一介绍。

  1. 前端控制器(DispatcherServlet)

一个问题:控制器是什么?

别被控制器这个名字吓住。其实和原生 Servlet 开发中开发者自定义的 Servlet 的功能是一样的。当然,因为有 Spring 的加持,使用起来更加轻量。Spring MVC 中的控制器有 2 类:

  • 中央控制器,或叫前端控制器: 由 Spring MVC 框架提供,对所有请求进行截获并分流;
  • 用户控制器,或叫响应控制器: 由开发者实现,用来响应用户的具体请求。如登录请求、注册请求……

前端控制器(DispatcherServlet)是 Spring MVC 中最核心的组件,相当于整个程序中的行政、调度中心。其它的组件都是它的附庸,为前端控制器提供相关的服务。

Tips: DispatcherServlet 必须在 Spring MVC 项目启动时被创建。

DispatcherServlet 的基本功能:

1.1 请求分流

浏览器向 Spring MVC 程序发起的所有请求都会汇总给 DispatcherServlet ,再由 它分流到具体的用户控制器:

为什么要对所有请求集中分流?

  • 安全性: 如同去拜访某一个公司,所有的来访人员都要经过前台工作人员筛选、登记、确认后才会被引导到具体的会客室。请求分流的性质也是如此,确保只能通过一个入口进入程序;
  • 标准化: 每一个请求都会以相同的方式进行分流处理。如同拜访公司,如果对每一个人的来访没有统一的接待标准,一定会产生额外的工作量。统一协调,标准化项目,可以高度简化处理流程。

1.2 调度中心

一次请求、响应的完成,需要多个组件通力合作。如何协调各个组件的工作,保证请求、响应过程有条不紊的进行,则需要一个指挥者或说一个核心灵魂组件。DispatcherServlet 就是每一次请求、响应过程中的组织者、调度者。

Tips: DispatcherServlet 本质就是一个和 Spring MVC 程序一起启动的 Servlet 。

  1. 用户控制器(Controller)

用户控制器(也称用户处理器、页面处理器)提供具体的、响应用户请求的逻辑。用户控制器虽然由用户自定义编写,但也需要直接或间接遵循 Spring MVC 的控制器编写规范。

Tips: 如果说前端控制器是大门,那么用户控制器就是具体的洽谈部门。

2.1 编写控制器,比如:

public class IndexController extends AbstractController { @Override protected ModelAndView handleRequestInternal ( HttpServletRequest req, HttpServletResponse resp)throws Exception { System.out.println("Hello,SpringMVC!"); ModelAndView index = new ModelAndView("index"); return index; } }

2.2 映射控制器

原生 Servlet 开发中,需要在 web.xml 中注册、映射 Servlet 后(或者使用注解形式@WebServlet的Servlet),浏览器才能请求到。类似的,基于 Spring MVC 的 WEB 项目,也可以在类前面添加 @Controller 注解,此注解的作用是通知 Spring MVC 的上下文对象(WebApplicationContext),控制器的创建工作交给你了:

@Controller public class UserController{ //第2步:在控制器的方法上添加 @RequestMapping 注解。 //使用此注解可提供一个逻辑名向用户映射此方法。 @RequestMapping("/hello") public String welcome() { System.out.pirntln("hello"); return "hello"; } }

并且需要在Spring的配置文件中开启对@controller注解的支持:

<!--1.处理器映射--> <context:component-scan base-package="com.spring.controller"/> <!--通过此标签,可以自动注册处理器映射对象DefaultAnnotationHandlerMapping 和处理器适配器对象AnnotationMethodHandlerAdapter, SpringMVC会通过这两个Bean来实现对@Controllert和@RequestMapping的支持--> <mvc:annotation-driven/> 此时如果运行项目会报错404,因为还没有告诉 Spring MVC:当控制器要响应浏览器的请求时,如何找到视图页面,这是视图解析器组件的工作。

  1. 映射器(HandlerMapping)

映射器的作用就是检查用户的请求路径中是否存在对应的控制器组件。

Tips: 有点类似于导购员。消费者报一个商品名,然后它告诉你商品真正的所在位置。

使用 Spring MVC 时,如果开发者没有显式的配置映射器,Spring MVC 会启动默认的映射器组件:

  • BeanNameUrlHandlerMapping :查找有没有哪一个 Bean 的名字和用户请求的路径相匹配。

  • RequestMappingHandlerMapping :查找由 @RequestMapping 注解映射的控制器。

无论使用这 2 种映射器的哪一种,理论上都无需显式的配置。Spring 会根据你的请求信息选择对应的映射器。显然, @RequestMapping 映射更直观,所以它使用的更多。

  1. 适配器(Adapter)

所谓适配器组件,其本质就是运用适配器设计模式、匹配不兼容的接口规范。 调用者只能识别接口 2 类型,但是 A 提供的是接口 1 类型。适配器可以把接口 1 转换成接口 2。这样使用者就能使用 A 提供的功能了。

简要描述 3 个适配器的应用场景:

  • SimpleControllerHandlerAdapter: 简单的控制器处理器适配器,支持实现了 Controller 接口的控制器;
  • HttpRequestHandlerAdapter: http 请求处理器适配器,要求编写控制器时实现 HttpRequestHandler 接口。此类控制器可以很方便的获取请求包中的相关信息。但,真正使用的并不多;
  • RequestMappingHandlerAdapter: 请求映射处理器适配器,适配使用了注解的用户控制器。本课程就是使用了此适配器,此适配器的实现比前两个都复杂。

因为有适配器的存在,可以让控制器的设计变得灵活。

Tips: 这 3 类适配器都是 Spring MVC 默认提供的,不用显式配置,除非有定制需求。

  1. 视图解析器(ViewResolver)

5.1 功能

要讲解视图解析器,则需要回溯到用户控制器上。

@RequestMapping("/hello") public String welcome() { System.out.pirntln("hello"); return "hello"; }

如果没有视图解析器的解析,用户控制器中的方法返回的字符串就是一个字符串。但如果有了视图解析器,则会把这个字符串当成一个视图的逻辑名,并映射到真正的视图上。

Tips:视图解析器和映射器有相似之处:

映射器是入口时根据请求控制器逻辑名找到物理控制器;

视图解析器是出口时根据视图逻辑名找到物理视图。

  • Spring MVC 默认使用的 InternalResourceViewResolver 作为视图解析器, 提供对 JSP 视图的支持。

  • 无论是 Spring MVC 默认提供的、还是开发者自定义的视图解析器,都必须实现 ViewResolver 接口。

为什么不在用户控制器的方法中直接返回物理视图(完整资源路径描述),而是使用视图解析映射物理位置?答案很简单:

  • 控制器中的响应代码不需要知道视图的具体位置,通过视图解析器来解耦控制器对视图物理位置的依赖;

  • 通过独立的视图解析器组件,可以扩展 Spring MVC 对各种不同视图技术的支持(Spring MVC 支持多达 13 种视图技术);

  • 简化控制器中的响应代码;

5.2 配置视图解析器

视图解析器需要在 Spring MVC 项目中显式的配置,Spring MVC 虽然提供了视图解析器,但它不可能知道开发者会把物理视图放在哪个位置,所以需要通过配置来指定物理视图的真正位置。

5.3 静态资源

如果浏览器中请求的是一个静态资源(浏览器就能解释的资源,如 Html、Css、Js、图片……),有必要经过前端控制器吗?

当然不需要。

但是,你可以试着在 WEB 项目的根目录下创建名为 static.html 的静态资源,然后在浏览器直接请求一下:http://localhost:8888/sm-demo/static.html,会发现请求不到,那是因为你的请求还是经过了前端控制器。

所以,咱们要告诉 Spring MVC :静态资源还是交回给 Servlet 容器处理吧, 就不劳您大驾了。

  1. 几大组件之间的协作关系

以上几大核心组件之间是如何协作一起完成用户的一次请求的?

当用户在浏览器发出请求的那一刻起,这些组件就紧密地团结在一起,为用户的请求保驾护航。

简要描述一下它们是如何协调一致完成工作的:

  • 浏览器的请求到达前端控制器(DispatcherServlet);
  • 前端控制器解析出请求路径后询问映射器,咱们是否提供了用户需要的用户控制器,映射器把查询结果返回给前端控制器;
  • 适配器的作用就是统一不同类型的用户控制器(也体现了 Spring MVC 中用户控制器的多样性和灵活性);
  • 用户控制器开始工作(具体的响应逻辑);
  • 用户控制器返回视图逻辑名和视图中所需要的数据(ModelAndView);
  • 前端控制器询问视图解析器,你能够根据逻辑名找到物理视图吗?视图解析器开始工作并找到物理视图;
  • 前端控制器渲染物理视图和数据,生成浏览器能够识别的数据格式;
  • 响应浏览器,并在浏览器中显示最终请求结果。

Spring MVC 中,用户的每一次请求都是众多组件通力合作完成的,它们是相亲相爱的一家人。