1、SpringMVC的十大组件
1.1 DispatcherServlet 前端控制器
统一处理请求与响应,整个工作流程的控制中心,负责调度其他组件来处理用户请求。
1.2 HandlerMapping 处理器映射器
根据请求信息(url、header、method等)匹配对应的Handler,也就是找到开发者自定义的Controller中处理请求的方法。 常见实现类RequestMappingHandlerMapping请求映射处理器映射,用于处理@RequestMapping注解定义的处理器。
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
根据请求获取处理器,返回一个HandlerExecutionChain处理器执行链对象。
1.3 HandlerExecutionChain 处理器执行链
包含三个属性:handler、interceptorList、interceptorIndex
public class HandlerExecutionChain {
//请求处理器,通常是开发者自定义的Controller及其方法
private final Object handler;
//当前请求匹配到的拦截器列表
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
//拦截器索引,记录执行到了第几个拦截器
private int interceptorIndex = -1;
}
1.4 Handler 处理器
一般指我们自定义的controller,在DispatcherServlet的控制下handler对具体的请求进行处理。
1.5 HandlerAdapter 处理器适配器
负责对Handler的方法进行调用。由于Handler有多种类型并且开发者自定义的Handler的方法参数类型与个数是不固定的,需要使用HandlerAdapter使用参数解析器对请求参数进行处理,对外暴露的统一的调用方式(handler方法),内部隐藏handler的调用细节。
public interface HandlerAdapter {`
boolean supports(Object handler);`
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
- support方法:判断当前HandlerAdapter能否处理handler的调用(是否能对请求参数处理)。
- handle方法:负责调用handler对用户的请求进行处理,返回一个ModelAndView对象。
常见实现类RequestMappingHandlerAdapter,其内部用来调用@RequestMapping标注的方法,处理请求参数(对方法参数进行解析),调用处理器执行请求并处理请求响应。
1.6 ModelAndView 模型与视图
来存放视图的名称和共享给客户端的数据。
1.7 ViewResolver 视图解析器
负责视图解析,根据视图的名称得到对应的视图对象(View)。
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
接口常见实现类InternalResourceViewResolver的resolveViewName方法获取视图的过程为:
- 判断视图viewName是否以
redirect:开头,如果是,则返回RedirectView类型的视图对象,RedirectView是用来重定向的,RedirectView内部用到的是response.sendRedirect(url)进行页面重定向;否则继续向下step2。 - 判断viewName是否以
forward:开头,如果是,则返回InternalResourceView类型的视图对象,InternalResourceView是用来做跳转的,InternalResourceView内部用到的是request.getRequestDispatcher(path).forward(request, response)进行页面跳转;否则继续向下step3。 - 判断当前项目是否存在jstl所需的类,如果是,则返回JstlView类型的视图,否则返回InternalResourceView类型的视图,这两个视图的render方法最终会通过
request.getRequestDispatcher(path).forward(request, response)进行页面的跳转,跳转的路径是:InternalResourceViewResolver的前缀prefix + viewName+InternalResourceViewResolver的后缀prefix。
1.8 View 视图
负责将结果展示给用户,View接口源码如下:
public interface View {
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;}
render方法根据指定的模型数据(model)渲染视图,即render方法负责将结果输出给客户端。
View接口常见的2个实现类
- RedirectView:负责重定向的,内部通过
response.sendRedirect(url)进行页面重定向。 - InternalResourceViewResolver:负责页面跳转的,内部通过
request.getRequestDispatcher(path).forward(request, response)进行页面的跳转。
1.9 HandlerExceptionResolver 处理器异常解析器
HandlerExceptionResolver接口有个resolveException方法,用来解析异常,返回异常情况下对应的ModelAndView对象
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
1.10 HttpMessageConverter Http报文转换器
将请求报文转换为Java对象,或将Java对象转换为响应报文。在处理@RequestBody、RequestEntity对象、@ResponseBody、ResponseEntity对象的时候会用到。
2、工作流程
所有的请求都会到达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 = newNestedServletException("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类型的请求
processedRequest = 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对象
mappedHandler = 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;
}
- 根据处理器获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());该方法会遍历所有的HandlerAdapter,找到能够处理当前Handler的HandlerAdapter,若没找到则会报ServletException。此方法通常返回的是RequestMappingHandlerAdapter类型的对象,RequestMappingHandlerAdapter这个类会根据HandlerMethod提供的信息,通过反射调用@RequestMapping标注的方法。 - 调用拦截器的preHandle方法,若返回false,处理结束。当该请求匹配多个拦截器时,若某个拦截器的
preHandle方法返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法。比如有3个拦截器,1、2的preHandler返回了true,而3返回的是false,那么这里将按照2、1的顺序调用他们的afterCompletion方法。
//调用之前得到的HandlerExecutionChain的applyPreHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
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;
}
-
调用handler实际处理请求,获取ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());handle方法内部会调用RequestMappingHandlerAdapter的invokeHandlerMethod方法,在该方法中HandlerAdapter会组装目标方法需要的参数、通过反射调用处理请求的目标方法,获取方法的返回值以及对方法的返回值进行处理。 -
调用拦截器的postHandle方法,配置多个拦截器时逆序调用拦截器的
postHandle方法。 -
处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//⑦-1:如果有异常,进行全局异常处理
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
if (mv != null && !mv.wasCleared()) {
//⑦-2:渲染视图
render(mv, request, response);
if (errorView) {
//调用request.removeAttribute方法清理request中错误信息
WebUtils.clearErrorRequestAttributes(request);
}
}
if (mappedHandler != null) {
//⑦-3:调用拦截器的afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
3、总结
1、用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获;
2、DispatcherServlet根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回;
4、DispatcherServlet 根据获得的 Handler,选择一个合适的HandlerAdapter;
5、如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】;
6、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求,在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
- HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的类型信息。
- 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。
- 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
7、Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
8、此时将开始执行拦截器的postHandle(…)方法【逆向】
9、根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
10、渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】
11、将渲染结果返回给客户端。