小言_互联网的博客

SpringMVC源码解析

448人阅读  评论(0)

 

本文主要对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控制器的内容:


  
  1. package com.myspringmvc.controller;
  2. import org.apache.http.HttpResponse;
  3. import org.springframework.http.HttpRequest;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RequestMethod;
  7. import org.springframework.web.bind.annotation.ResponseBody;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. @Controller
  11. @RequestMapping(value="/web")
  12. public class MyController {
  13. @RequestMapping(value="/index", method = RequestMethod.GET)
  14. public String indexPage(){
  15. return "index";
  16. }
  17. @RequestMapping(value="/hi", method = RequestMethod.GET)
  18. @ResponseBody
  19. public String sayHi(String name){
  20. System.out.println("sayHi调用了...");
  21. return "hello "+name;
  22. }
  23. }

我们知道,当我们在浏览器发起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上,

继续点开,这个方法内就做了比较多的事情了,需要单独拎出来进行必要的注释。


  
  1. /**
  2. * Process the actual dispatching to the handler.
  3. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
  4. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
  5. * to find the first that supports the handler class.
  6. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
  7. * themselves to decide which methods are acceptable.
  8. * @param request current HTTP request
  9. * @param response current HTTP response
  10. * @throws Exception in case of any kind of processing failure
  11. */
  12. protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
  13. throws Exception {
  14. HttpServletRequest processedRequest = request;
  15. HandlerExecutionChain mappedHandler = null;
  16. boolean multipartRequestParsed = false;
  17. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  18. try {
  19. ModelAndView mv = null; //创建要返回的ModelAndView对象
  20. Exception dispatchException = null;
  21. try {
  22. //checkMultipart这一步是判断这个请求是不是一个带文件的请求,比如文件上传,
  23. //这种请求会将http请求头的content-type是不是以multipart/前缀
  24. processedRequest = checkMultipart(request);
  25. multipartRequestParsed = (processedRequest != request);
  26. //根据当前请求查找匹配的handlerMapping,并且查找到请求对应的handler
  27. mappedHandler = getHandler(processedRequest);
  28. if (mappedHandler == null) {
  29. noHandlerFound(processedRequest, response);
  30. return;
  31. }
  32. // Determine handler adapter for the current request.
  33. // 找到找能处理这个请求的的handler后,找到与之匹配的适配器
  34. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  35. // Process last-modified header, if supported by the handler.
  36. String method = request.getMethod();
  37. boolean isGet = "GET".equals(method);
  38. if (isGet || "HEAD".equals(method)) {
  39. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  40. if (logger.isDebugEnabled()) {
  41. logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
  42. }
  43. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  44. return;
  45. }
  46. }
  47. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  48. return;
  49. }
  50. // Actually invoke the handler.
  51. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  52. if (asyncManager.isConcurrentHandlingStarted()) {
  53. return;
  54. }
  55. applyDefaultViewName(processedRequest, mv);
  56. mappedHandler.applyPostHandle(processedRequest, response, mv);
  57. }
  58. catch (Exception ex) {
  59. dispatchException = ex;
  60. }
  61. catch (Throwable err) {
  62. // As of 4.3, we're processing Errors thrown from handler methods as well,
  63. // making them available for @ExceptionHandler methods and other scenarios.
  64. dispatchException = new NestedServletException("Handler dispatch failed", err);
  65. }
  66. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  67. }
  68. catch (Exception ex) {
  69. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  70. }
  71. catch (Throwable err) {
  72. triggerAfterCompletion(processedRequest, response, mappedHandler,
  73. new NestedServletException("Handler processing failed", err));
  74. }
  75. finally {
  76. if (asyncManager.isConcurrentHandlingStarted()) {
  77. // Instead of postHandle and afterCompletion
  78. if (mappedHandler != null) {
  79. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  80. }
  81. }
  82. else {
  83. // Clean up any resources used by a multipart request.
  84. if (multipartRequestParsed) {
  85. cleanupMultipart(processedRequest);
  86. }
  87. }
  88. }
  89. }
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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场