Spring MVC 面试总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC 面试总结相关的知识,希望对你有一定的参考价值。
参考技术A SpringMVC的核心就是DispatcherServlet,DispatcherServlet实质也是一个HttpServlet,继承 了FrameworkServlet,实现 ApplicationContextAware 接口,继承 HttpServletBean 抽象类 , 负责初始化 Spring Servlet WebApplicationContext 容器,同时该类覆写了 doGet、doPost 等方法,并将所有类型的请求委托给 doService 方法去处理,doService 是一个抽象方法,需要子类实现,DispatcherServlet负责初始化 Spring MVC 的各个组件,以及处理客户端的请求,协调各个组件工作WebApplicationContext 是实现 ApplicationContext 接口的子类,专门为 WEB 应用准备的
它允许从相对于 Web 根目录的路径中加载配置文件,完成初始化 Spring MVC 组件的工作。
从 WebApplicationContext 中,可以获取 ServletContext 引用,整个 Web 应用上下文对象将作为属性放置在 ServletContext 中,以便 Web 应用环境可以访问 Spring 上下文。
ContextLoaderListener 继承 ContextLoader 类,实现 Servlet 容器启动和关闭时,分别初始化和销毁 WebApplicationContext 容器,WebApplicationContext 有两处
Servlet 3.0 新增的一个接口ServletContainerInitializer ,配合@HandlesTypes 注解来指定希望被处理的类,容器在启动时使用 JAR 服务 的时候会通过SPI机制发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,实现的 onStartup 方法中可以向 ServletContext 对象(Servlet 上下文)添加之前在 web.xml 中配置的 Filter和 Servlet,这样一来就可以去除 web.xml 文件了
SpringMVC的实现类 SpringServletContainerInitializer 就是实现了ServletContainerInitializer接口,负责初始化web环境,SpringServletContainerInitializer把所有的初始化任务委托给WebApplicationInitializer这个接口的实现类,其中AbstractDispatcherServletInitializer便是创建DispatcherServlet 的关键类
SpringBoot 是通过 org.springframework.boot.web.embedded.tomcat.TomcatStarter 这个类进行初始化流程的,TomcatStarter同样也是实现了ServletContainerInitializer接口,通过构造方法传入的ServletContextInitializer去初始化Web环境,其中ServletWebServerApplicationContext 负责 加载Filter、Servlet、Listener
ServletWebServerApplicationContext 的 onRefresh() 方法触发配置了一个匿名的 ServletContextInitializer
这个匿名的 ServletContextInitializer 的 onStartup 方法会去容器中搜索到了所有的 RegisterBean 并按照顺序加载到 ServletContext 中
这个匿名的 ServletContextInitializer 最终传递给 TomcatStarter,由 TomcatStarter 的 onStartup 方法去触发 ServletContextInitializer 的 onStartup 方法,最终完成装配
注册方式一: Servlet3.0 注解(@WebServlet,@WebFilter,@WebListener) +@ServletComponentScan , 在启动类上面添加 @ServletComponentScan 注解去扫描到这些注解
注册方式二: RegistrationBean,比如 ServletRegistrationBean 和 FilterRegistrationBean 都继成 RegistrationBean,它是 SpringBoot 中广泛应用的一个注册类,负责把 Servlet,Filter,Listener 给容器化,使它们被 Spring 托管,并且完成自身对 Web 容器的注册
用户的浏览器发送一个请求,这个请求经过互联网到达了我们的服务器。Servlet 容器首先接待了这个请求,并将该请求委托给 DispatcherServlet 进行处理。
1. DispatcherServlet 主要通过 dispatch 这个方法去处理整个流程,这个流程他会先去通过 HandlerMapping 的getHandler方法得到一个HandlerExecutionChain对象
HandlerMapping 就是一个接口,里面就只有一个方法就叫getHandler,他有很多实现类,但是大体主要通过两种方式获得Handler 一种是 AbstracHandlerMethodMapping 和 AbstractUrlHandlerMapping ,一个是基于 Method 进行匹配的,比如@RequestMapping注解,一个是基于 URL 进行匹配,我们平常用的最多的还是RequestMappingHandlerMapping,这个就是 AbstracHandlerMethodMapping 的子类
HandlerExecutionChain 这个对象就是包含了拦截器和处理器对象,这个处理器对象是object类型的,对象比如说,我们平常通过在方法上标记@RequestMapping注解,然后呢他这个对象就是HandlerMethod 类型, 这个 HandlerMethod封装了很多属性,在访问请求方法的时候可以方便的访问到方法、方法参数、方法上的注解、所属类等并且对方法参数封装处理,也可以方便的访问到方法参数的注解等信息。拦截器呢,就是实现了HandlerInterceptor接口的类,就是在执行方法之前和执行方法之后进行一些处理
2. 拿到这个 HandlerExecutionChain 对象之后呢,还要找一个HandlerAdapter 适配器,他通过遍历 HandlerAdapter 组件,判断是否支持处理该 handler 处理器,支持则返回该 HandlerAdapter 组件,这个HandlerAdapter组件负责处理执行整个流程,参数的解析,方法的执行,返回值的处理,都是在他里面完成的,
像我们平时用的最多的还是RequestMappingHandlerAdapter类,他就是执行 HandlerMethod 类型的处理器,也就是通过 @RequestMapping 等注解标注的方法
3. 然后呢通过执行HandlerAdapter的handle方法,会给我们返回一个ModelAndView 对象,这个呢就是数据和视图地址,然后呢就会调用processDispatchResult这个方法去解析视图,这里面他会通过render这个方法进行页面渲染逻辑,这里面就会交给viewResolvers去处理,但是我们平常开发都是标注了ResponseBody注解,所以在HandlerAdapter这里面会将我们返回的结果直接将数据通过HttpServletResponse.write写进去,最后直接返回
Spring MVC的拦截器的实现了HandlerInterceptor接口 , 里面主要有三个方法 preHandle,postHandle,afterCompletion,分别是前置处理,后置处理和已完成处理
这个HandlerInterceptor呢,他会在在HanderMapping的子类 AbstractHandlerMapping 中的 getHandler 方法中去获取,一般是在获取完handler处理器后然后会去和HandlerInterceptor去组装成 HandlerExecutionChain 对象,
HandlerInterceptor拦截器呢会在AbstractHandlerMapping initApplicationContext ()方法中初始化完成的,因为这个方法是重写了父类 ApplicationObjectSupport 的initApplicationContext()ApplicationObjectSupport最上层的接口ApplicationContextAware中的 setApplicationContext ,所以他会在初始化该ben的时候调用setApplicationContext这个方法,然后这个方法将 interceptors 初始化成 HandlerInterceptor 类型,添加到 adaptedInterceptors 中
HandlerMapping 组件,请求的 处理器匹配器 ,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),下面先说 AbstractHandlerMapping
AbstractHandlerMapping 抽象类,一个基类,实现了“为请求找到合适的 HandlerExecutionChain 处理器执行链”对应的的骨架逻辑,而暴露 getHandlerInternal() 抽象方法,交由子类实现。
AbstractUrlHandlerMapping
AbstractHandlerMethodMapping
因为其中处理器的实现有多种,例如通过实现 Controller 接口、HttpRequestHandler 接口,或者使用 @RequestMapping 注解将方法作为一个处理器等。Spring MVC 就无法直接执行这个处理器,所以这里需要一个处理器适配器,由它去执行处理器, HandlerAdapter 接口也就三个方法,一个是判断是否支持该处理器的,一个是执行处理器返回ModelAndView的,还有一个是返回请求的最新更新时间的
HttpRequestHandlerAdapter :执行实现了 HttpRequestHandler 接口的处理器
SimpleControllerHandlerAdapter :执行实现了 Controller 接口的处理器
SimpleServletHandlerAdapter :执行实现了 Servlet 接口的处理器
RequestMappingHandlerAdapter : 执行 HandlerMethod 类型的处理器,也就是通过 @RequestMapping 等注解标注的方法,主要说说RequestMappingHandlerAdapter的执行流程:
1. 会先创建ServletInvocableHandlerMethod并且设置一些属性,这个是对HandlerMethod 处理器的封装,然后通过这个对象的invokeAndHandle方法去执行
2 然后需要通过 HandlerMethodArgumentResolver 对象进行参数解析
3 在通过反射执行对应的 Method 方法对象
4 最后通过 HandlerMethodReturnValueHandler 对象对执行结果进行处理,设置到 response 响应中,生成对应的 ModelAndView 对象
ServletInvocableHandlerMethod 封装 HandlerMethod 处理器对象,它还包含 HandlerMethodArgumentResolver 参数解析器和 HandlerMethodReturnValueHandler 返回值处理器等组件
参数解析器,先说说他是怎么处理的吧, 在ServletInvocableHandlerMethod解析参数会先用到一个HandlerMethodArgumentResolverComposite对象,这也是一个实现HandlerMethodArgumentResolver接口的对象,他呢是一个组合对象,里面就包含了很多的参数解析器,他会遍历所有的参数解析器去调用supportsParameter这个方法去看是否支持解析该参数,如果支持呢他就会返回这个参数解析器,不支持就会给返回一个IllegalStateException的异常,然后呢就会调用resolveArgument去解析这个参数,像解析参数处理器的就非常多了,常见的有:
RequestParamMethodArgumentResolver: 基于 @RequestParam 注解的 也可以不带
PathVariableMethodArgumentResolver : 基于 @PathVariable 注解的
ServletModelAttributeMethodProcessor :基于ModelAttribute注解的,处理实体类,也可以不带
RequestResponseBodyMethodProcessor: 基于@RequestBody注解的
在转换的过程中呢还有个参数转换器,比如啊我们从浏览器发过来的请求都是text类型的,所以必须要把这这些类型转换成我们的目标类型,比如Date,Number啊这些类型,都需要通过转换器去完成,WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中,拿到这些处理好的值,直接通过反射调用我们写好的方法获得拿到需要返回的值就好了
HandlerMethodReturnValueHandler 返回值处理器将返回结果设置到响应中,或者设置相应的 Model 和 View 用于后续的视图渲染。这个接口也就两个方法,一个是判断是否支持该返回类型的,还有一个就是处理返回值的
RequestResponseBodyMethodProcessor : 负责处理@ResponseBody注解的返回值,现在前后端分离,后端基本是提供 Restful API,最常用的就是他,RequestResponseBodyMethodProcessor中还有个最重要的就是MessageConverters内容协商,利用 MessageConverters将返回的数据 写为json,他 =会根据请求头Accept去看浏览器接收什么样的数据类型,在根据MessageConverters实现类的MediaType这个类去和你匹配看自己能支持那些内容类型的数据,那你浏览器需要json就会得到MappingJackson2HttpMessageConverter这个对象,然后把对象转成json写到response中
ViewNameMethodReturnValueHandler : 适用于前后端未分离,Controller 返回视图名的场景,例如 JSP、thymeleaf模板引擎就用这个
处理器异常解析器,将处理器执行请求时发生的异常,解析成对应的 ModelAndView 结果,大致就是通过@ControllerAdvice+@ExceptionHandler注解处理全局异常,底层就是HandlerExceptionResolver 接口支持的,其中一个实现类 ExceptionHandlerExceptionResolver 会在初始化的时候去扫描 扫描 @ControllerAdvice 注解的 Bean 们然后找到有 @ExceptionHandler 注解的方法,则添加到 exceptionHandlerAdviceCache 中,执行的时候他里面也有参数解析器和返回值处理器,处理完了之后也会返回ModelAndView对象
视图解析器,根据视图名和国际化,获得最终的视图 View 对象。Spring MVC 执行完处理器后生成一个 ModelAndView 对象,如果该对象不为 null 并且有对应的 viewName,那么就需要通过 ViewResolver 根据 viewName 解析出对应的 View 对象
MultipartResolver 组件,内容类型( Content-Type )为 multipart/* 的请求的解析器,主要解析文件上传的请求。例如,MultipartResolver 会将 HttpServletRequest 封装成 MultipartHttpServletRequest 对象,
DispatcherServlet
Spring MVC 的核心组件,是请求的入口,负责协调各个组件工作
MultipartResolver
内容类型( Content-Type )为 multipart/* 的请求的解析器,例如解析处理文件上传的请求,便于获取参数信息以及上传的文件
HandlerMapping
请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors)
HandlerAdapter
处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户处理器可以实现 Controller 接口、HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器
HandlerExceptionResolver
处理器异常解析器,将处理器( handler )执行时发生的异常,解析( 转换 )成对应的 ModelAndView 结果
RequestToViewNameTranslator
视图名称转换器,用于解析出请求的默认视图名
LocaleResolver
本地化(国际化)解析器,提供国际化支持
ThemeResolver
主题解析器,提供可设置应用整体样式风格的支持
ViewResolver
视图解析器,根据视图名和国际化,获得最终的视图 View 对象
FlashMapManager
FlashMap 管理器,负责重定向时,保存参数至临时存储(默认 Session)
@Controller 注解有什么用?
@Controller 注解标记一个类为 Spring Web MVC 控制器 Controller。Spring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping 注解的方法,根据注解信息,为这个方法生成一个对应的 处理器 对象,在上面的 HandlerMapping 和 HandlerAdapter组件中讲到过。
当然,除了添加 @Controller 注解这种方式以外,你还可以实现 Spring MVC 提供的 Controller 或者 HttpRequestHandler 接口,对应的实现类也会被作为一个 处理器 对象
@RequestMapping 注解有什么用?
@RequestMapping 注解,在上面已经讲过了,配置 处理器 的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个 控制器 的 URI 前缀
@RestController 和 @Controller 有什么区别?
@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 ACCEPT 请求头来决定。
@RequestMapping 和 @GetMapping 注解的不同之处在哪里?
@RequestMapping:可注解在类和方法上;@GetMapping 仅可注册在方法上
@RequestMapping:可进行 GET、POST、PUT、DELETE 等请求方法;@GetMapping 是 @RequestMapping 的 GET 请求方法的特例,目的是为了提高清晰度。
@RequestParam 和 @PathVariable 两个注解的区别
两个注解都用于方法参数,获取参数值的方式不同,@RequestParam 注解的参数从请求携带的参数中获取,而 @PathVariable 注解从请求的 URI 中获取
返回 JSON 格式使用什么注解?
可以使用 @ResponseBody 注解,或者使用包含 @ResponseBody 注解的 @RestController 注解。
当然,还是需要配合相应的支持 JSON 格式化的 HttpMessageConverter 实现类。例如,Spring MVC 默认使用 MappingJackson2HttpMessageConverter
ServletRegistrationBean 和 FilterRegistrationBean 都继成 RegistrationBean,它是 SpringBoot 中广泛应用的一个注册类,负责把 Servlet,Filter,Listener 给容器化,使它们被 Spring 托管,并且完成自身对 Web 容器的注册
以上是关于Spring MVC 面试总结的主要内容,如果未能解决你的问题,请参考以下文章