SpringMVC的三大组件
DispatcherServlet:前端控制器
这是大家是最熟悉的,是一个 servlet,是 springmvc 处理请求的入口,不需要咱们开发,由框架提供。
作用:统一处理请求和响应,整个流程控制的中心,由它来调用其他组件处理用户的请求。
HandlerMapping:处理器映射器
作用:根据请求的信息(如 url、method、header 等)查找请求处理器,即找到自定义的 controller 中处理请求的方法。
HandlerMapping 接口源码如下,getHandler:根据请求查找请求处理器,会返回一个 HandlerExecutionChain 对象。
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
常见的实现类:
RequestMappingHandlerMapping:请求映射处理器映射,用来处理@RequestMapping 定义的处理器的
HandlerExecutionChain:处理器执行链
HandlerMapping#getHandler 方法会根据请求得到一个 HandlerExecutionChain 对象。
HandlerExecutionChain 源码如下,主要包含了 3 个信息
handler:请求处理器,通常就是我们自定义的 controller 对象及方法
interceptorList:拦截器,当前请求匹配到的拦截器列表
interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
private int interceptorIndex = -1;
}
Handler:处理器
通常需要我们自己开发,一般指我们自定义的 controller,在 DispatcherServlet 的控制下 handler 对具体的请求进行处理。
HandlerAdapter:处理器适配器
他负责对 handler 的方法进行调用,由于 handler 的类型可能有很多种,每种 handler 的调用过程可能不一样,此时就需要用到适配器 HandlerAdapte,适配器对外暴露了统一的调用方式(见其 handle 方法),内部将 handler 的调用过程屏蔽了,HandlerAdapter 接口源码如下,主要有 2 个方法需要注意:
supports:当前 HandlerAdapter 是否支持 handler,其内部主要就是判 HandlerAdapter 是否能够处理 handler 的调用
handle:其内部负责调用 handler 的来处理用户的请求,返回返回一个 ModelAndView 对象
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
常见的实现类:
RequestMappingHandlerAdapter:其内部用来调用@RequestMapping 标注的方法
ModelAndView:模型和视图
这个对象中主要用来存放视图的名称和共享给客户端的数据。
public class ModelAndView {
/*视图*/
@Nullable
private Object view;
/*模型,用来存放共享给客户端的数据*/
@Nullable
private ModelMap model;
}
ViewResolver:视图解析器
这个是框架提供的,不需要咱们自己开发,它负责视图解析,根据视图的名称得到对应的视图对象(View)。
ViewResolver 接口源码
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
这个接口有很多实现类,比如 jsp 的、freemarker、thymeleaf 的等,他们都有各自对应的 ViewResolver。
而比较常的实现类是InternalResourceViewResolver
,这个大家应该比较熟悉吧,目前为止我们前面的文章用到的都是这个视图解析器,用来处理 jsp 格式的视图页面,带大家再回顾一下这个类的配置,如下
<!-- 添加视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
InternalResourceViewResolver 比较重要,这里说下这个类的 resolveViewName 方法获取视图的过程,大家也可以去阅读InternalResourceViewResolver#resolveViewName方法获得
,大致的过程如下:
step1:判断视图 viewName 是否以redirect:
开头,如果是,则返回RedirectView
类型的视图对象,RedirectView 是用来重定向的,RedirectView 内部用到的是response.sendRedirect(url)
进行页面重定向;否则继续向下 step2
step2:判断 viewName 是否以forward:
开头,如果是,则返回InternalResourceView
类型的视图对象,InternalResourceView 是用来做跳转的,InternalResourceView 内部用到的是request.getRequestDispatcher(path).forward(request, response)
进行页面跳转;否则继续向下 step3
step3:判断当前项目是否存在 jstl 所需的类,如果是,则返回 JstlView 类型的视图,否则返回 InternalResourceView 类型的视图,这两个视图的 render 方法最终会通过request.getRequestDispatcher(path).forward(request, response)
进行页面的跳转,跳转的路径是:InternalResourceViewResolver 的前缀 prefix + viewName+InternalResourceViewResolver 的后缀 prefix
View:视图
负责将结果展示给用户,View 接口源码如下,render 方法根据指定的模型数据(model)渲染视图,即 render 方法负责将结果输出给客户端。
public interface View {
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
View 接口常见的 2 个实现类
RedirectView:负责重定向的,内部通过
response.sendRedirect(url)
进行页面重定向InternalResourceViewResolver:负责页面跳转的,内部通过
request.getRequestDispatcher(path).forward(request, response)
进行页面的跳转
HandlerExceptionResolver:处理器异常解析器
负责处理异常的,HandlerExceptionResolver 接口有个resolveException
方法,用来解析异常,返回异常情况下对应的 ModelAndView 对象
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
HttpMessageConverter:http报文转换器
将请求报文转换为 Java 对象,或将 Java 对象转换为响应报文,在处理@RequestBody、RequestEntity、@ResponseBody、ResponseEntity 的时候会用到
public interface HttpMessageConverter<T> {
/**
* 是否可以将请求报文读取给方法参数指定的类型
*/
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
/**
* 是否可以将响应的报文转换为方法参数指定的类型输出
*/
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
/**
* 当前转换器支持的类型
*/
List<MediaType> getSupportedMediaTypes();
/**
* 当前转换器支持的类型
*/
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
/**
* 将http报文转换为给定的类型,然后返回
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/**
* 将给定的对象t,转换为http报文输出到客户端
*/
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
处理流程:源码解析
请求到达入口:doDispatch
springmvc 的所有请求,最终都会到达org.springframework.web.servlet.DispatcherServlet#doDispatch
这个方法,整个请求的大致处理过程都在这个方法中,咱们从这个方法开始分析,源码如下,大家注意代码中的注释,带有标号,比如 ①、②、③ 这样需要的注释,大家需要注意了,这些是关键的步骤,稍后会对这些步骤做详细的说明
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//请求对象
HttpServletRequest processedRequest = request;
//处理器执行链对象
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//模型和视图
ModelAndView mv = null;
//异常对象
Exception dispatchException = null;
try {
//①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
processedRequest = checkMultipart(request);
//用来标记是否是multipart类型的请求
multipartRequestParsed = (processedRequest != request);
//②:根据请求获取HandlerExecutionChain对象
mappedHandler = getHandler(processedRequest);
//如果没有找到处理器,就404了
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//③:根据处理器获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//④:调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//判断异步请求不是已经开始了,开始了就返回了
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
applyDefaultViewName(processedRequest, mv);
//⑥:调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//⑧:调用拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//⑧:调用拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
//对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
//对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
①:解析 multipart 类型的请求
//①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
processedRequest = checkMultipart(request);
checkMultipart(request)
源码
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//判断multipartResolver解析器是否存在 && 请求是否是multipart类型
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
//将请求转换为multipart类型的请求对象,通常为MultipartHttpServletRequest类型
return this.multipartResolver.resolveMultipart(request);
}
return request;
}
②:根据请求获取 HandlerExecutionChain 对象
//②:根据请求获取HandlerExecutionChain对象
mappedHandler = getHandler(processedRequest);
getHandler(processedRequest)
源码如下,遍历所有的处理器映射器HandlerMapping
,调用他们的getHandler
方法得到能够处理当前请求的HandlerExecutionChain
对象,这个对象中包含了 3 个信息
handler:请求处理器,通常就是我们自定义的 controller 对象及方法
interceptorList:拦截器,当前请求匹配到的拦截器列表
interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
有兴趣的可以去看一下RequestMappingHandlerMapping
这个类的源码,也是最常用的一个 HandlerMapping,它会根据@RequestMapping
来找到能够处当前请求的处理器,RequestMappingHandlerMapping#getHandler 方法查找得到的 HandlerExecutionChain 对象中的 handler 类型为HandlerMethod
,代码在下面这个位置
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
HandlerMethod 对象中包含了能够处理请求的 bean 及方法信息
③:根据处理器获取 HandlerAdapter
//③:根据处理器获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
getHandlerAdapter方法
源码,遍历HandlerAdapter
列表,找到能够处理当前 handler 的HandlerAdapter
,如果没找到会报错
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
此方法通常返回的是RequestMappingHandlerAdapter
类型的对象,RequestMappingHandlerAdapter
这个类会根据HandlerMethod
提供的信息,通过反射调用@RequestMapping 标注的方法。
④:调用拦截器的 preHandle 方法
//④:调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mappedHandler.applyPreHandle
源码如下,主要干了 3 个事情
循环调用拦截器的
preHandle
方法如果某个拦截器的
preHandle
方法返回 false,则反向依次调用那些 preHandle 方法返回 ture 的拦截器的 afterCompletion 方法;这句话有点绕,比如有 3 个拦截器,1、2 的 preHandler 返回了 true,而 3 返回的是 false,那么这里将按照 2、1 的顺序调用他们的 afterCompletion 方法记录拦截器的执行位置
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
//调用拦截器的preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
//如果拦截器返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法
triggerAfterCompletion(request, response, null);
return false;
}
//记录当前拦截器执行的位置
this.interceptorIndex = i;
}
return true;
}
triggerAfterCompletion
方法源码如下,通过拦截器当前执行的位置interceptorIndex
逆向调用拦截器的afterCompletion
方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
⑤:调用 handler 实际处理请求,获取 ModelAndView 对象
过程
//⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
ha.handler 方法内部通通过程会走到RequestMappingHandlerAdapter#invokeHandlerMethod
方法,这个方法内部会通过反射调用@RequestMapping 标注的方法,这个方法内部代码比较复杂,咱们就不进去了,这里说一下这个方法主要做了 3 个非常重要的事情:
step1:组装目标方法需要的参数
step2:通过反射调用处理请求的目标方法,获取方法的返回值
step3:对方法的返回值进行处理
下面来细说一下这 3 个步骤,这些地方有好东西,大家集中注意力了。
step1:组装目标方法需要的参数:HandlerMethodArgumentResolver
处理器的方法需要的参数有各种类型的,所以组装这些参数是比较关键的地方,组装参数的源码位于下面这个位置
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
获取方法需要的参数值,会用到HandlerMethodArgumentResolver
这个对象,叫做:处理器方法参数解析器,用来解析请求,得到方法需要的参数,大家看一下这个接口,源码如下,主要有 2 个方法
supportsParameter
:是否能够解析 parameter 指定的参数resolveArgument
:通过请求和 parameter 参数解析得到参数的值
public interface HandlerMethodArgumentResolver {
//判断当前解析器是否能处理这个parameter这个参数,也就是说是否能够将请求中的数据转换为parameter指定的参数的值
boolean supportsParameter(MethodParameter parameter);
//解析参数:从http请求中解析出控制器需要的参数的值
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
这个接口有很多实现类,列几个比较熟悉的,当大家想知道 springmvc 可以接收哪些类型的参数,以及这些参数有什么特点的时候,可以去看看这些类的源码,你会秒懂的
step2:通过反射调用目标方法
也就是调用 controller 中的@RequestMapping 标注的方法,代码位置
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
对应的源码如下,这个方法 springmvc 框架中主要有 2 个地方会调用
第 1 个地方是:调用处理请求的实际方法的时候
第 2 个地方是:方法有异常的时候,异常解析器里面也会用到这个方法,稍后后面会讲
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//1.通过反射调用目标方法,内部会组装目标方法需要的参数
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//如果返回值为空,表示目标方法中已经完成了请求的所有处理,表示请求处理结束了,将执行mavContainer.setRequestHandled(true)标记请求处理完毕
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
//若getResponseStatusReason()不为空,表示请求已经处理过了
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
//走到这里,说明有返回值,标记请求未处理完毕
mavContainer.setRequestHandled(false);
//对返回值进行处理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
step3:处理方法返回值:HandlerMethodReturnValueHandler
大家注意,上面代码中这部分代码,如下,会对反射调用的结果 returnValue 进行处理
//对返回值进行处理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
进入handleReturnValue
方法内部去看一下,最终代码在下面这个位置
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
这个方法的源码如下
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//根据返回值找到HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
//调用HandlerMethodReturnValueHandler#handleReturnValue处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
//根据返回值判断是否是异步请求
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}