(1) Spring DispatcherServlet
SpringMVC的核心就是DispatcherServlet,DispatcherServlet实质也是一个HttpServlet,继承了FrameworkServlet,实现 ApplicationContextAware 接口,继承 HttpServletBean 抽象类,负责初始化 Spring Servlet WebApplicationContext 容器,同时该类覆写了 doGet、doPost 等方法,并将所有类型的请求委托给 doService 方法去处理,doService 是一个抽象方法,需要子类实现,DispatcherServlet负责初始化 Spring MVC 的各个组件,以及处理客户端的请求,协调各个组件工作
(2) Spring MVC 中的 WebApplicationContext ?
WebApplicationContext 是实现 ApplicationContext 接口的子类,专门为 WEB 应用准备的
它允许从相对于 Web 根目录的路径中加载配置文件,完成初始化 Spring MVC 组件的工作。
从 WebApplicationContext 中,可以获取 ServletContext 引用,整个 Web 应用上下文对象将作为属性放置在 ServletContext 中,以便 Web 应用环境可以访问 Spring 上下文。
(3) WebApplicationContext 加载过程
ContextLoaderListener 继承 ContextLoader 类,实现 Servlet 容器启动和关闭时,分别初始化和销毁 WebApplicationContext 容器,WebApplicationContext 有两处
(4) 说一下Spring MVC为何没有web.xml文件了
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
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 的关键类
(5) SpringBoot中 加载 Servlet 的流程
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 方法,最终完成装配
扩展一下 关于SpringBoot 如何配置 Servlet
注册方式一:Servlet3.0 注解(@WebServlet,@WebFilter,@WebListener) +@ServletComponentScan , 在启动类上面添加 @ServletComponentScan 注解去扫描到这些注解
注册方式二:RegistrationBean,比如 ServletRegistrationBean 和 FilterRegistrationBean 都继成 RegistrationBean,它是 SpringBoot 中广泛应用的一个注册类,负责把 Servlet,Filter,Listener 给容器化,使它们被 Spring 托管,并且完成自身对 Web 容器的注册
(6) Spring MVC 工作流程
用户的浏览器发送一个请求,这个请求经过互联网到达了我们的服务器。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写进去,最后直接返回
(7) 介绍下 Spring MVC 拦截器?
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中
preHandle
在会在HandlerAdapter执行handle之前调用HandlerExecutionChain的applyPreHandle方法,会执行请求匹配的拦截器的前置处理 ,在执行的过程中会记录通过interceptorIndex 记录执行的当前执行的下标
postHandle
会在HandlerAdapter执行handle之后调用HandlerExecutionChain.applyPostHandle 执行请求匹配的拦截器的后置处理 会根据interceptorIndex 下标倒序执行
afterCompletion
会在所有请求处理完成之后调用, 不管有没有异常,都会执行,调用HandlerExecutionChain的triggerAfterCompletion方法 也是根据interceptorIndex 下标倒序执行
(8) 说说什么是HandlerMapping ?
HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),下面先说AbstractHandlerMapping
AbstractHandlerMapping
AbstractHandlerMapping 抽象类,一个基类,实现了“为请求找到合适的 HandlerExecutionChain 处理器执行链”对应的的骨架逻辑,而暴露 getHandlerInternal() 抽象方法,交由子类实现。
HandlerMapping 处理器组件的实现类分为两种:
AbstractUrlHandlerMapping
基于 URL 进行匹配 ,当然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,还是使用这种方式
AbstractHandlerMethodMapping
基于 Method 进行匹配。例如,我们所熟知的 @RequestMapping 等注解的方式
AbstractHandlerMethodMapping初始化时会扫描@Controller或者@RequestMapping注解标注的 Bean 对象,会将带有@RequestMapping注解(包括其子注解)解析成RequestMappingInfo对象。接下来,会将RequestMappingInfo、该方法对象、该方法所在类对象往MappingRegistry注册表进行注册,其中会生成HandlerMethod处理器(方法的所有信息)对象保存起来。当处理某个请求时,HandlerMapping 找到该请求对应的HandlerMethod处理器对象后,就可以通过反射调用相应的方法了
(9) 说一说HandlerAdapter 适配器?
因为其中处理器的实现有多种,例如通过实现 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
ServletInvocableHandlerMethod 封装 HandlerMethod 处理器对象,它还包含 HandlerMethodArgumentResolver 参数解析器和 HandlerMethodReturnValueHandler 返回值处理器等组件
说一说HandlerMethodArgumentResolver
参数解析器,先说说他是怎么处理的吧, 在ServletInvocableHandlerMethod解析参数会先用到一个HandlerMethodArgumentResolverComposite对象,这也是一个实现HandlerMethodArgumentResolver接口的对象,他呢是一个组合对象,里面就包含了很多的参数解析器,他会遍历所有的参数解析器去调用supportsParameter这个方法去看是否支持解析该参数,如果支持呢他就会返回这个参数解析器,不支持就会给返回一个IllegalStateException的异常,然后呢就会调用resolveArgument去解析这个参数,像解析参数处理器的就非常多了,常见的有:
RequestParamMethodArgumentResolver:基于 @RequestParam 注解的 也可以不带
PathVariableMethodArgumentResolver :基于 @PathVariable 注解的
ServletModelAttributeMethodProcessor:基于ModelAttribute注解的,处理实体类,也可以不带
RequestResponseBodyMethodProcessor:基于@RequestBody注解的
在转换的过程中呢还有个参数转换器,比如啊我们从浏览器发过来的请求都是text类型的,所以必须要把这这些类型转换成我们的目标类型,比如Date,Number啊这些类型,都需要通过转换器去完成,WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中,拿到这些处理好的值,直接通过反射调用我们写好的方法获得拿到需要返回的值就好了
说一说HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler 返回值处理器将返回结果设置到响应中,或者设置相应的 Model 和 View 用于后续的视图渲染。这个接口也就两个方法,一个是判断是否支持该返回类型的,还有一个就是处理返回值的
RequestResponseBodyMethodProcessor: 负责处理@ResponseBody注解的返回值,现在前后端分离,后端基本是提供 Restful API,最常用的就是他,RequestResponseBodyMethodProcessor中还有个最重要的就是MessageConverters内容协商,利用 MessageConverters将返回的数据 写为json,他 =会根据请求头Accept去看浏览器接收什么样的数据类型,在根据MessageConverters实现类的MediaType这个类去和你匹配看自己能支持那些内容类型的数据,那你浏览器需要json就会得到MappingJackson2HttpMessageConverter这个对象,然后把对象转成json写到response中
ViewNameMethodReturnValueHandler: 适用于前后端未分离,Controller 返回视图名的场景,例如 JSP、thymeleaf模板引擎就用这个
(10)说一说HandlerExceptionResolver ?
处理器异常解析器,将处理器执行请求时发生的异常,解析成对应的 ModelAndView 结果,大致就是通过@ControllerAdvice+@ExceptionHandler注解处理全局异常,底层就是HandlerExceptionResolver 接口支持的,其中一个实现类ExceptionHandlerExceptionResolver会在初始化的时候去扫描 扫描 @ControllerAdvice 注解的 Bean 们然后找到有 @ExceptionHandler 注解的方法,则添加到 exceptionHandlerAdviceCache 中,执行的时候他里面也有参数解析器和返回值处理器,处理完了之后也会返回ModelAndView对象
(11)说一说ViewResolver ?
视图解析器,根据视图名和国际化,获得最终的视图 View 对象。Spring MVC 执行完处理器后生成一个 ModelAndView 对象,如果该对象不为 null 并且有对应的 viewName,那么就需要通过 ViewResolver 根据 viewName 解析出对应的 View 对象
(12)说一说MultipartResolver文件上传
MultipartResolver 组件,内容类型( Content-Type )为 multipart/* 的请求的解析器,主要解析文件上传的请求。例如,MultipartResolver 会将 HttpServletRequest 封装成 MultipartHttpServletRequest 对象,
(13)Spring MVC 的核心组件大概描述一下
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 容器的注册