重要的前置知识:
Spring MVC中有几大组件,帮助Spring MVC实现请求的处理工作:
前端控制器:DispatcherServlet
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
配置:前端控制器DispatcherServlet由Spring提供,需要我们在web.xml配置文件中手动配置(可以采用注解开发,但也需要手动配置)
处理器映射器:HandlerMapping
作用:根据请求的url、param等信息查找Handler,即控制器方法
处理器适配器:HandlerAdapter
作用:通过HandlerAdapter对处理器(控制器方法)进行执行
配置:通过在SpringMVC.xml文件中配置
<mvc:annotation-driven/>
标签Spring可以自动的帮我们创建RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter( 处理适配器)。
视图解析器:ViewResolver
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、 RedirectView
配置:视图解析器的种类有很多,需要用户根据自己的需求,创建相应的试图解析器,将其交由IOC容器进行管理
处理器:Handler
作用:对具体的用户请求进行处理,就是控制器方法
视图:View
作用:将模型数据通过页面展示给用户
1.tomcat接收到请求,会将请求封装成HttpServletRequest
2.然后选择路径匹配的Servlet调用它的doGet()或者doPost()方法,并将HttpServletRequest作为参数传递过去,因为我们配置了前端控制器,所以tomcat会调用DispatcherServlet的doGet()或者doPost()方法,由于DispatcherServlet没有重写上述方法,所以会调用父类FrameworkServlet的方法
FrameworkServlet
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 调用自己的processRequest()方法
this.processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = this.buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
this.initContextHolders(request, localeContext, requestAttributes);
try {
// 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
this.doService(request, response);
} catch (IOException | ServletException var16) {
failureCause = var16;
throw var16;
} catch (Throwable var17) {
failureCause = var17;
throw new NestedServletException("Request processing failed", var17);
} finally {
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
this.logResult(request, response, (Throwable)failureCause, asyncManager);
this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
}
}
DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
label120:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label120;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
// 将servlet的一些属性信息放入请求域
// 将当前servlet的子webapplicationcontext放入请求域中
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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);
}
RequestPath requestPath = null;
if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
requestPath = ServletRequestPathUtils.parseAndCache(request);
}
try {
// 进行请求的分派(处理请求)
this.doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
if (requestPath != null) {
ServletRequestPathUtils.clearParsedRequestPath(request);
}
}
}
DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
/**
获取处理器的调用链:
mappedHandler:调用链
包含handler、interceptorList、interceptorIndex
handler:浏览器发送的请求所匹配的控制器方法
interceptorList:处理控制器方法的所有拦截器集合
interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
**/
// 会循换遍历全部的处理器映射器,利用处理器映射器找到能与当前请求相匹配的handler,找到了直接返回,找不到遍历下一个处理器映射器,继续getHandler,都找不到返回null,
/**
如果我们配置了 <mvc:annotation-driven/>标签,Spring会注册RequestMappingHandlerMapping处理器映射器,那默认情况下就存在三个处理器映射器
1.RequestMappingHandlerMapping-->帮我们匹配请求uri和检测到的所有uri Map(通过@RequestMapping注解设置的uri),以及遍历获取匹配的拦截器,封装成 HandlerExecutionChain返回
2.BeanNameUrlHandlerMapping
3.SimpleUrlHandlerMapping它继承了AbstractUrlHandlerMapping,其中有一个属性handlerMap,里面存放了我们通过<mvc:default-servlet-handler>标签,注册的DefaultServletHttpRequestHandler,它会作为handler,然后去遍历获取匹配的拦截器,封装成 HandlerExecutionChain返回
**/
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
// 获取处理器适配器:
// 会遍历全部的处理器适配器,找到支持当前处理器的处理器适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
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;
}
}
// 调用前置处理器:遍历处理器调用链中的拦截器列表,调用每个拦截器的preHandler()方法
/**
interceptorIndex初始为-1,每调用一次preHandle()方法,并且preHandler()返回值为true,就会将interceptorIndex加1,实际上它记录的就是刚刚遍历过的返回值为true的拦截器索引,
1. 如果preHandler()返回false,interceptorIndex=当前返回false的拦截器的前一个拦截器的索引, 并会从interceptorIndex对应的索引开始,向前遍历拦截器,调用他们的afterCompletion方法,
2.如果没有拦截器的preHandler()返回false,interceptorIndex=最后一个拦截器的索引,直接返回true
总结:顺序调用拦截器的preHandler()方法,逆序调用之前遍历过的拦截器的afterCompletion()方法
**/
// 如果返回false直接结束该方法,不再执行,返回true继续执行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 处理请求,调用控制器方法获取模型和视图对象
/**
1.获取控制器方法中的参数,将解析到的相对应的参数值,传入方法,进行调用
2.创建modelandview对象,将model封装进mav中
3.获取返回的视图名称,放入mav
4.返回mav
**/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
// 倒序调用后置处理器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// 后续处理,处理模型数据和渲染视图
/**
1.如果前面抛出了异常,异常被捕获,并且,将异常对象对象赋值给dispatchException,所以在此后续处理方法中会判断是否出现了异常,如果出现异常,就遍历异常解析器,调用它们的resolveException()方法,并重新创建异常的mav,返回
2.调用render()方法,进行视图的渲染
①遍历视图解析器,解析视图名称(拼接前缀和后缀),创建视图对象
②模板引擎解析视图对象
③设置响应数据到response中(reaponse的输出流的buffer中)
3.执行拦截器链的afterCompletion(…)方法,倒序执行
**/
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
看下默认情况下的处理器映射器:
拦截器链中的内容:
modelAndView中的内容:
响应数据的内容:
总结:
1) 用户向服务器发送请求,tomcat调用Servlet(DispatcherServlet)的doXXx(..)方法,传入请求和响应对象。最终会调用到doDispatch方法
2) 在doDispatch方法会调用getHandler()方法,获取处理器执行链,处理器执行链包含三部分内容(handler、interceptorList、interceptorIndex)
a)首先,遍历处理器映射器,调用处理器映射器的getHandler()方法,获取处理器执行链:
i)在处理器映射器的getHandler()方法中会调用getHandlerInternal()方法,去解析请求的URL获取 资源标识符URI(URI-web项目的标识符),对于不同的处理器映射器会去不同的Map中匹配路径,获取相对应的处理器(构造器方法对象)
1.RequestMappingHandlerMapping会去
AbstractHandlerMethodMapping.MappingRegistry#pathLookup
(内部存放着全部请求映射路径)中获取2.SimpleUrlHandlerMapping会去
AbstractUrlHandlerMapping#handlerMap
(内部存放着我们我们通过<mvc:default-servlet-handler>
标签注册的默认servletHandler)中获取
ii)如果获取到了,返回获取到的处理器对象。接着获取路径匹配的拦截器列表,封装成HandlerExecutionChain返回
ii)如果getHandlerInternal()方法没有获取到,就去获取默认的处理器(默认为null),也获取不到,直接返回null;获取到了接着获取路径匹配的拦截器列表,封装成HandlerExecutionChain返回
b)如果,处理器映射器的getHandler()方法返回null,继续遍历下一个处理器映射器,调用它的getHandler()方法;如果不为null,就直接返回。(也就是说只要handler为null,处理器执行链就为null,就会直接报404)
3)如果返回的处理器执行链为null,控制台报映射查找不到,客户端展示404错误
4) 如果返回的处理器执行链不为null,DispatcherServlet 根据获得的Handler,调用getHandlerAdapter()方法,选择一个合适的HandlerAdapter。
5) 如果成功获得HandlerAdapter,此时将开始遍历执行拦截器的preHandler(…)方法【正向】,如果有 preHandler(…)方法返回false,就逆向调用已经遍历过的拦截器的afterCompletion(…)方法,返回false,结束dispatcher()
6) 如果全部拦截器的preHandler(…)方法都返回true,会调用handle()方法,去处理请求,获取modelandview对象,在handle()方法中会调用处理器的handleRequest()方法,对于不同的处理器,有不同的处理逻辑:
a)如果是因为在请求映射Map中没有匹配的路径,而使用了默认的ServletHandler,在他的handleRequest()方法内部,会进行请求的转发,解析静态资源(js、html、jsp)(直接去webapp目录下面找),如果没有找到对应的静态资源,也会报404错误,找到了就直接响应界面,返回null。
b)如果使用控制器方法作为处理器,提取request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。 在填充Handler的入参过程中,根据你的参数配置,Spring将帮你做一些额外的工作:
i) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定 的响应信息
ii) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
iii) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
iiii) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
7) Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象(可能为null,因为使用默认的ServletHandler)。
8) 此时将开始执行拦截器的postHandle(...)方法【逆向】。
9) 接着调用processDispatchResult()方法处理分发结果
a)首先会判断是否存在异常,如果存在异常,则执行 HandlerExceptionResolver异常解析器的processHandlerException()方法,进行异常处理,创建新的ModelAndView对象返回
b)如果ModelAndView不为null,渲染视图,选择一个适合的ViewResolver进行视图解析,创建视图对象,模板引擎解析视图对象,设置输出流到响应对象中。
c)渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】
10) 将渲染结果返回给客户端。
如有问题欢迎指正......