Spring WebMvc总结&面试题整理

从 0 开始深入学习 Spring 专栏目录总览

千辛万苦走到这里,小册要恭喜各位了,到此为止,SpringFramework 的核心知识我们已经学完啦!那么回顾整个 WebMvc 的部分,我们学习了很多知识,大多数都是以实际开发中的实战为主,理论性的知识,以及面试中问到的相对较少,所以本章的总结会围绕实际的开发使用来回顾。

1. Web基础回顾

1.1 MVC与三层架构

对于 MVC 与三层架构而言,这是 Web 开发的基础,小伙伴们一定要熟记于心。

Spring WebMvc总结&面试题整理

Spring WebMvc总结&面试题整理

1.2 Servlet3.0规范的运行时可插拔性

Servlet 3.0 规范的这个运行时可插拔性的特性,它里面定义了一个 ServletContainerInitializer 的东西,这个 ServletContainerInitializer 会借助 Java 的 SPI 技术,可以从项目或者项目依赖的 jar 包中,找到一个 /META-INF/services/javax.servlet.ServletContainerInitializer 文件,并加载项目中所有它的实现类,并配合 @HandlesTypes 注解一起使用,传入一些我们所需要的接口 / 抽象类,以此来实现动态 web 项目组件的装载。

这个规范设计的用法,核心的目的之一是组件的可插拔,有了组件的可插拔,就可以在导入一个带 ServletContainerInitializer 的实现类的 jar 包时,自动加载对应的实现类。

2. WebMvc基础

2.1 DispatcherServlet相关

WebMvc 的基础部分,我们最先接触到的就是这个 DispatcherServlet ,这里面有一些细节,我们也来回顾一下。

2.1.1 servlet-mapping

有关 DispatcherServlet 在配置时的 servlet-mapping 配置,一般有三种情况:

  • / ,它代表的含义是:拦截所有请求、静态资源,但不拦截 .jsp
  • /* ,它的含义是:拦截所有请求、静态资源、.jsp 页面
  • *.do 或者 *.action :只会拦截所有结尾为 .do 或者 .action 的请求

2.1.2 WebApplicationInitializer

SpringWebMvc 支持 Servlet 3.0 的方式,是提供了一个 WebApplicationInitializer 的接口,以及一组抽象子类供我们扩展,我们最常使用的 AbstractAnnotationConfigDispatcherServletInitializer ,只需要继承它,并且重写三个方法,提供父容器和子容器的注解配置类,以及配置 servlet-mapping 即可。

2.2 页面数据传递方案

从后端 Controller ,到前端的页面,进行数据传递,无外乎是利用 HttpServletRequest 域,以此来承载的数据传递方案介质有三种:

  • HttpServletRequest
  • ModelAndView
  • ModelMap / Model

2.3 参数绑定相关

请求的参数绑定,一般我们会使用原生类型,或者模型来收集,通常都是直接对应字段名即可,SpringWebMvc 会帮我们自动封装,不过这里面有几个注解和 API ,是比较常用的:

  • @RequestParam :可指定绑定的参数名、是否必传、参数默认值
  • @PathVariable :可从 url 上取参数值
  • Converter :自定义参数类型转换器使用的核心 API 接口,配合 FormattingConversionServiceFactoryBean 可以完成自定义参数类型转换的配置

2.4 json支持

ajax 异步请求的请求与响应,通常都需要 json 支持,SpringWebMvc 可以整合 json 转换工具框架,来实现 json 的数据转换。

开启 json 转换的支持,可以直接声明 <mvc:annotation-driven/> 标签,或者使用 @EnableWebMvc 注解实现,也可以通过配置 RequestMappingHandlerAdapter ,并向其中传入 messageConverters 。不过在实际开发中,一般都是使用标签或者注解开启的方式,简单而且够用。

如果 ajax 请求的数据是 json ,则可以在请求参数上标注 @RequestBody 注解;如果需要响应 json 数据,可以在方法上标注 @ResponseBody 注解。

2.5 静态资源配置

当 DispatcherServlet 配置的 servlet-mapping 为 / 或者 /* 时,会导致静态资源无法加载的异常,这种情况就需要配置静态资源的放行规则,同时也可以对这些静态资源的浏览器缓存设置过期时间。

3. WebMvc的设计

3.1 WebMvc的父子容器

这也是一个容易被讨论起来的问题,我们还是借助官网的那张图来解释:

Spring WebMvc总结&面试题整理

上面这张图可以很明显的体现出 ApplicationContext 的层次性特征!对于一个基于 SpringWebMvc 的应用,它希望把 Service 、Dao 等类都放到根容器,把表现层的 Controller 及相关的组件都放到 Servlet 的子容器中,以此形成一个层级关系。

这种父子容器的设计主要有两个好处:第一,形成层级关系后,Controller 可以拿到 Service ,而 Service 拿不到 Controller ,可以以此形成一道隔离;第二,如果真的出现特殊情况,需要注册多个 DispatcherServlet 的时候,不必注册多套 Service 和 Dao ,每个 WeBMvc 的子容器都从这一个根容器中取 Service 和 Dao 即可。

3.2 WebMvc的核心组件

SpringWebMvc 中的组件职责划分是相当清晰的:

  • DispatcherServlet :核心的中央处理器,负责接收请求、分发,并给予客户端响应
  • HandlerMapping :处理器映射器,根据 uri ,去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器,和 Handler 一起封装
  • HandlerAdapter :处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler
  • ViewResolver :视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
  • Handler :处理实际请求的处理器

4. Web开发的其他知识点

4.1 RESTful编码风格

RESTful 只是一种编码的风格,不是标准、不是协议、不是规范,仅仅是风格而已。RESTful 的编码风格将 HTTP 协议中定义的 8 种请求方式中的四种方式,赋予了一个真正的意义:

  • GET :获取资源 / 数据
  • POST :新建资源 / 数据
  • PUT :更新资源 / 数据
  • DELETE :删除资源 / 数据

如果用这种编码风格的话,设计的 API 请求路径看上去更简洁、有层次性。就其本身,与传统的参数拼接而言,无好坏之分。

4.2 数据校验

SpringWebMvc 支持的后端数据校验,是基于 JSR – 303 规范的数据校验。通常情况下 SpringWebMvc 会配合 Hibernate-Validator 来实现数据校验,通过在请求的参数或者实体上标注相应的校验注解,并配置校验器,就可以实现注解式的数据校验了。

此外,也可以通过声明不同的分组接口,实现分组校验,以及校验错误信息的外部化。

4.3 文件上传与下载

SpringWebMvc 支持的文件上传,需要一个 CommonsMultipartResolver 的文件上传处理器,配合 Controller 方法中传入的 MultipartFile 类型参数,就可以实现文件上传的接收。

SpringWebMvc 支持的文件下载,只需要在 Controller 方法的返回时,返回 ResponseEntity ,并配置好对应的文件名等请求头信息,即可实现文件下载。

4.4 统一异常处理

SpringWebMvc 推荐使用的注解式统一异常处理,采用 @ControllerAdvice + @ExceptionHandler ,给 Controller 织入异常处理的逻辑,即可在 Controller 方法执行出现异常时予以处理。在实际的异常处理中,SpringWebMvc 遵循一个原则:更细粒度的异常处理,优先级高于粗粒度的异常处理。

4.5 拦截器

有关 SpringWebMvc 拦截器的使用也很简单,通过实现 HandlerInterceptor 接口,并实现对应的方法逻辑即可。这里面的执行机制是有先后顺序的,之前在 75 章中小册也给过一张图:

Spring WebMvc总结&面试题整理

除了这个机制之外,还有一个面试题可能会问到的,就是拦截器与过滤器的对比。小册把之前 75 章中给小伙伴们的分析搬到这里来,这样小伙伴也不用来回切换章节了。

首先我们要明白一点,拦截器是 SpringWebMvc 的概念,而过滤器是 Servlet 的概念。Servlet 、Filter 、Listener 共同称为 Servlet 三大组件,它们都需要依赖 Servlet 的 API 。而拦截器不同,拦截器是 SpringWebMvc 提出,它只是 SpringWebMvc 框架中的一个 API 设计罢了。所以我们可以总结出第一个区别:拦截器是框架的概念,而过滤器是 Servlet 的概念。

那既然两方的概念来源不同,那它们的拦截范围也就不一样了。过滤器 Filter 是在 web.xml 或者借助 Servlet 3.0 规范来注册,任何来自于 Servlet 容器(Tomcat)的请求都会走这些过滤器;拦截器既然是框架的概念,而 SpringWebMvc 的核心是一个 DispatcherServlet ,所以拦截器实际上是在 DispatcherServlet 接收到请求后才有机会工作的,对于 DispatcherServlet 没有接收到的请求,拦截器只能干瞪眼。所以接下来总结出的第二个区别:过滤器可以拦截几乎所有请求,而拦截器只能拦截到被 DispatcherServlet 接收处理的请求。

继续,来源不同还造成一个不同的现象:过滤器由 Servlet 容器创建,与 SpringFramework 的 IOC 没有任何关系,所以无法借助依赖注入,给过滤器中注入属性;而拦截器是被 SpringFramework 的 IOC 统一管理的,它就是一个一个的普通的 bean ,所以可以任意注入需要的 bean 。所以第三条区别:拦截器可以借助依赖注入获取所需要的 bean ,而过滤器无法使用正常手段获取。

如果深入到底层调用,可以发现,过滤器的调用是一层一层的函数回调,而拦截器是 SpringWebMvc 在底层借助反射调用的( invokeForRequest 方法最终是反射的 Controller 方法)。

4.6 跨域问题

跨域是一种现象,跨域是由浏览器的同源策略引起的,而同源策略,是在浏览器上对资源的一种保护策略,最初同源策略只是用于保护网页的 cookie ,后演变的越来越严格,下面会提到。同源策略规定了三个相同:协议相同、域名 / 主机相同、端口相同。当这三个因素都相同时,则浏览器会认为访问的资源是同一个来源。

而跨域现象中,只要触发以下三种情况之一,都会引起跨域问题:

  • http 访问 https ,或者 https 访问 http
  • 不同域名 / 服务器主机之间的访问
  • 不同端口之间的访问

SpringWebMvc 给出的解决方案,是在需要允许跨域的方法上标注 @CrossOrigin 注解即可,并且可以在注解上指定跨域范围。@CrossOrigin 注解的核心底层,是给响应头中添加了一个 Access-Control-Allow-Origin: * 。

4.7 异步请求

Servlet 3.0 规范中支持了异步请求,借助 HttpServletRequest ,可以从中拿到一个 AsyncContext ,使用这个 AsyncContext ,就可以实现异步请求处理。

SpringWebMvc 支持两种方式的异步请求,分别是基于 Callable 的异步请求支持,以及基于 DeferredResult 的异步请求支持。前者操作简单,底层的异步线程池由我们提供,并由 SpringWebMvc 加以使用,后者设计相对复杂,但一切都由我们来控制。

免责声明:
1.本站所有内容由本站原创、网络转载、消息撰写、网友投稿等几部分组成。
2.本站原创文字内容若未经特别声明,则遵循协议CC3.0共享协议,转载请务必注明原文链接。
3.本站部分来源于网络转载的文章信息是出于传递更多信息之目的,不意味着赞同其观点。
4.本站所有源码与软件均为原作者提供,仅供学习和研究使用。
5.如您对本网站的相关版权有任何异议,或者认为侵犯了您的合法权益,请及时通知我们处理。
火焰兔 » Spring WebMvc总结&面试题整理