推荐学习肝了十天半月,献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图 消息中间件合集:MQ(ActiveMQ/RabbitMQ/RocketMQ)+Kafka+笔记 前言
SpringMVC请求处理相信大家都很熟悉了,本篇主要是基于SpringMVC处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题。
本篇使用的Spring版本为5.2.2.RELEASE
九大组件
SpringMVC几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习SpringMVC来说非常重要。
/** 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 区域解析器,用于国际化 */
private LocaleResolver localeResolver;
/** 主题解析器 */
private ThemeResolver themeResolver;
/** Handler映射信息 */
private List handlerMappings;
/** Handler适配器*/
private List handlerAdapters;
/** Handler执行异常解析器 */
private List handlerExceptionResolvers;
/** 请求到视图的转换器 */
private RequestToViewNameTranslator viewNameTranslator;
/** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */
private FlashMapManager flashMapManager;
/** 视图解析器 */
private List viewResolvers;
1234567891011121314151617181920212223242526HandlerMapping:Handler映射信息,根据请求携带的url信息查找处理器(Handler)。每个请求都需要对应的Handler处理。HandlerAdapter:Handler适配器,SpringMVC没有直接调用处理器(Handler),而是通过HandlerAdapter来调用,主要是为了统一Handler的调用方式ViewResolver:视图解析器,用来将字符串类型的视图名称解析为View类型的视图。ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。MultipartResolver:文件上传解析器,主要用来处理文件上传请求HandlerExceptionResolver:Handler执行异常解析器,用来对异常进行统一处理RequestToViewNameTranslator:请求到视图的转换器LocaleResolver:区域解析器,用于支持国际化FlashMapManager:SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMapThemeResolver:主题解析器,用于支持不同的主题
九大组件中最重的的前三个,HandlerMapping、HandlerAdapter和ViewResolver,因为这是阅读源码时,避不开的三个组件。
调试准备
搭建一个基本的Spring web项目即可
Controller部分
@Controller
public class IndexController {
@RequestMapping("/index/home")
public String home(String id, Student student, @RequestParam("code") String code) {
System.out.println(student.getName());
return "index";
}
@ResponseBody
@RequestMapping("/index/list")
public String list() {
return "success";
}
}
Entity部分
public class Student {
private String name;
private Integer gender;
// getter、setter
}
还是那句话,Spring源码非常庞大,不能只见树木不见森林,需要有针对性的阅读,所以本篇只需要关注主体流程即可。
核心方法
我们都知道,SpringMVC有一个用来分发请求的前端控制器DispatcherServlet,其中用来处理请求的方法就是doService,该方法定义如下
doService
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot=null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot=new HashMap<>();
Enumeration<?> attrNames=request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName=(String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager !=null) {
FlashMap inputFlashMap=this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap !=null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 真正执行的方法
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot !=null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}doDispatch
doDispatch是doService中真正用来处理请求的方法
/**
* 实际处理请求的方法
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest=request;
HandlerExecutionChain mappedHandler=null;
boolean multipartRequestParsed=false;
WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv=null;
Exception dispatchException=null;
try {
// 校验是否是文件上传请求
processedRequest=checkMultipart(request);
multipartRequestParsed=(processedRequest !=request);
// Determine handler for the current request.
// 为当前请求找到一个合适的处理器(Handler)
// 返回值是一个HandlerExecutionChain,也就是处理器执行链
mappedHandler=getHandler(processedRequest);
if (mappedHandler==null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapter
HandlerAdapter ha=getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理GET请求的缓存
String method=request.getMethod();
boolean isGet="GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified=ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 执行拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 利用HandlerAdapter来执行Handler里对应的处理方法
mv=ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果没有设置视图,则应用默认的视图名
applyDefaultViewName(processedRequest, mv);
// 执行拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException=ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException=new NestedServletException("Handler dispatch failed", err);
}
// 根据ModelAndView对象解析视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler !=null) {
mappedHandler.applyAfterConcurrentHandlingStarted(proc