本文主要对SpringMVC从接收一个请求,到请求处理结束返回给浏览器的整个过程源码进行学习。为了更好明确学习目标,我们先将上篇文章中SpringMVC的工作流程图还是展示一下,针对图中每一个流程在源码上进行解释其设计和实现思路,中间可能会穿插其它相关联的SpringMVC知识点,这部分关联知识点会分别用独立的文章进行链接描述。最后再对整个工作流程的源码实现进行总结。本文中的代码样例来源于上一篇应用文章https://blog.csdn.net/qq_20395245/article/details/106304402,为了便于理解代码下面只将Controller.java的内容发出来。
上图就是SpringMVC的工作流程图:
1.首先用户发送http请求到前端控制器DispatchServlet上,
2.前端控制器调用处理器映射器HandlerMapping,HandlerMapping根据配置或注解找到具体处理器handler,
3.HandlerMapping将具体处理器handler返回给DispatchServlet,
4.前端控制器请求HandlerAdatper,根据handler规则调用找到具体类型的handler,
5.处理器适配器执行具体的处理器handler(Controller或Action),并执行内部逻辑,
6.handler执行完毕后返回ModelAndView给处理器适配器HandlerAdatper,
7.HandlerAdatper将得到的ModelAndView返回给前端控制器,
8.前端控制器将ModelAndView传给视图解析器ViewResolver,
9.视图解析器对ViewResource进行解析后返回具体的VIew给前端控制器,
10.前端控制器根据View进行渲染视图(将模型数据填充到视图中),
11.把渲染好的视图把过response响应给用户。
下图是MyController.java控制器的内容:
-
package com.myspringmvc.controller;
-
import org.apache.http.HttpResponse;
-
import org.springframework.http.HttpRequest;
-
import org.springframework.stereotype.Controller;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.ResponseBody;
-
import java.util.HashMap;
-
import java.util.Map;
-
-
@Controller
-
@RequestMapping(value="/web")
-
public class MyController {
-
-
@RequestMapping(value="/index", method = RequestMethod.GET)
-
public String indexPage(){
-
return "index";
-
}
-
-
@RequestMapping(value="/hi", method = RequestMethod.GET)
-
@ResponseBody
-
public String sayHi(String name){
-
System.out.println("sayHi调用了...");
-
return "hello "+name;
-
}
-
-
}
我们知道,当我们在浏览器发起http请求http://localhost:9090/web/hi?name=yang时,浏览器最终会响应
那么我们的这个http请求会最先到达SpringMVC的哪里呢?根据上面的工作流程可知,这个请求会先到达一个名为DispatcherServlet的前端控制器上。那么让我们看一下这个DispatcherServlet是什么呢,这个前端控制器接收到这个http请求后接下来会做什么事情呢?点开这个DispatcherServlet我们可以发现它实际上就是一个HttpServlet:
也就是说实际上DispatcherServlet接收请求就相当于是一个特殊的Servlet接收了这个请求。我们知道servlet接收请求后会调用一些servlet方法例如doGet,doPost,doService等。这是一个Get类型的http请求,因此这里会执行doGet的方法,实际上这个方法会在FrameworkServlet中调用,实际处理方法是:
这个processRequest方法内doService(request,response)之前主要对请求的request做一些数据转化和封装,核心逻辑在doService方法中,而doService内部的核心代码在925行的doDispatch上,
继续点开,这个方法内就做了比较多的事情了,需要单独拎出来进行必要的注释。
-
/**
-
* Process the actual dispatching to the handler.
-
*
<p>The handler will be obtained by applying the servlet's HandlerMappings in order.
-
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
-
* to find the first that supports the handler class.
-
*
<p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
-
* themselves to decide which methods are acceptable.
-
* @param request current HTTP request
-
* @param response current HTTP response
-
* @throws Exception in case of any kind of processing failure
-
*/
-
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; //创建要返回的ModelAndView对象
-
Exception dispatchException = null;
-
-
try {
-
-
//checkMultipart这一步是判断这个请求是不是一个带文件的请求,比如文件上传,
-
//这种请求会将http请求头的content-type是不是以multipart/前缀
-
processedRequest = checkMultipart(request);
-
multipartRequestParsed = (processedRequest != request);
-
-
//根据当前请求查找匹配的handlerMapping,并且查找到请求对应的handler
-
mappedHandler = getHandler(processedRequest);
-
if (mappedHandler == null) {
-
noHandlerFound(processedRequest, response);
-
return;
-
}
-
-
// Determine handler adapter for the current request.
-
// 找到找能处理这个请求的的handler后,找到与之匹配的适配器
-
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
-
-
// Process last-modified header, if supported by the handler.
-
String method = request.getMethod();
-
boolean isGet = "GET".equals(method);
-
if (isGet || "HEAD".equals(method)) {
-
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
-
if (logger.isDebugEnabled()) {
-
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
-
}
-
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
-
return;
-
}
-
}
-
-
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
-
return;
-
}
-
-
// Actually invoke the handler.
-
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
-
-
if (asyncManager.isConcurrentHandlingStarted()) {
-
return;
-
}
-
-
applyDefaultViewName(processedRequest, mv);
-
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);
-
}
-
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(processedRequest, response);
-
}
-
}
-
else {
-
// Clean up any resources used by a multipart request.
-
if (multipartRequestParsed) {
-
cleanupMultipart(processedRequest);
-
}
-
}
-
}
-
}
doDispatch方法分析:
首先,这个方法会先判断这个请求是不是一个带文件请求比如文件上传processedRequest = checkMultipart(request);
对于这种请求有独立的处理逻辑(960行内)。
本例中显然不是,那么接下来就会拿这个请求去根据当前请求查找匹配的handlerMapping并且会在handlerMapping中找到能处理这个请求的handler(964行),这行代码说明包含了两个步骤,分别是流程图中的步骤2和步骤3。
那么这个查找handlerMapping的过程以及匹配处理请求handler的实现是怎么样的呢?让我们进入到这个方法中一看究竟:
mappedHandler = getHandler(processedRequest);
这个方法就比较值得研究了。我们可以看到,要想为请求匹配到正确的handlerMapping,实际上是在handlerMappings这个Map集合中进行遍历查找。通过调试我们注意到,这个handlerMappings实际上有很多个对象,这些对象都是些什么对象呢,为什么是会有这些对象呢?
转载:https://blog.csdn.net/qq_20395245/article/details/106321161