我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringMVC源码分析(3)Dis

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringMVC源码分析(3)Dis

<SpringMVC源码分析(1)标签解析>:介绍了解析过程中,初始化若干组件。

<SpringMVC源码分析(2)DispatcherServlet的初始化>:初始化DispatcherServlet的多个组件。

本文继续分析DispatcherServlet解析请求的过程。

概览

231546366285136.jpg

  ①:DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件.

  ②:HanlerMapping是springmvc中完成url到controller映射的组件.DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.

  ③:Cntroller处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件.

  ④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端.

要点

  1. 维护url和controller的映射

    这部分工作由DefaultAnnotationHandlerMapping.setApplicationContext的父类

    org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping.initApplicationContext实现。具体方法为detectHandlers

protected void detectHandlers() throws BeansException {
   if (logger.isDebugEnabled()) {
      logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
   }
   String[] beanNames = (this.detectHandlersInAncestorContexts ?
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
         getApplicationContext().getBeanNamesForType(Object.class));

   // Take any bean name that we can determine URLs for.
   for (String beanName : beanNames) {
      String[] urls = determineUrlsForHandler(beanName);
      if (!ObjectUtils.isEmpty(urls)) {
         // URL paths found: Let's consider it a handler.
         registerHandler(urls, beanName);
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
         }
      }
   }
}

2.准确定位处理请求的具体方法(在AnnotationMethodHandlerAdapter中实现)

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
   Method handlerMethod = methodResolver.resolveHandlerMethod(request);//具体实现方法的匹配
   ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   ExtendedModelMap implicitModel = new BindingAwareModelMap();

   Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
   ModelAndView mav =
         methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
   methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
   return mav;
}





1.请求入口

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   proce***equest(request, response);
}


@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   proce***equest(request, response);
}

protected final void proce***equest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;

   // Expose current LocaleResolver and request as LocaleContext.
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
   LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
   // Expose current RequestAttributes to current thread.
   RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
   ServletRequestAttributes requestAttributes = null;
   if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
      requestAttributes = new ServletRequestAttributes(request);
      RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
   }

   if (logger.isTraceEnabled()) {
      logger.trace("Bound request context to thread: " + request);
   }

   try {
      doService(request, response);
   }
   catch (ServletException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (IOException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
   }

   finally {
      // Clear request attributes and reset thread-bound context.
      LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
      if (requestAttributes != null) {
         RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
         requestAttributes.requestCompleted();
      }
      if (logger.isTraceEnabled()) {
         logger.trace("Cleared thread-bound request context: " + request);
      }

      if (failureCause != null) {
         this.logger.debug("Could not complete request", failureCause);
      }
      else {
         this.logger.debug("Successfully completed request");
      }
      if (this.publishEvents) {
         // Whether or not we succeeded, publish an event.
         long processingTime = System.currentTimeMillis() - startTime;
         this.webApplicationContext.publishEvent(
               new ServletRequestHandledEvent(this,
                     request.getRequestURI(), request.getRemoteAddr(),
                     request.getMethod(), getServletConfig().getServletName(),
                     WebUtils.getSessionId(request), getUsernameForRequest(request),
                     processingTime, failureCause));
      }
   }
}

proce***equest方法主要做4项工作。

  1. 得到当前线程的LocaleContext和RequestAttributes,创建新的LocaleContext和RequestAttributes并重新绑定到当前线程。

  2. 调用子类实现的doService()

  3. 重置当前线程的LocaleContext和RequestAttributes

  4. 执行成功后,发布ServletRequestHandledEvent事件。

2.DispatcherServlet自定义的doService方法

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   if (logger.isDebugEnabled()) {
      String requestUri = urlPathHelper.getRequestUri(request);
      logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
            " request for [" + requestUri + "]");
   }

   // 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)) {
      logger.debug("Taking snapshot of request attributes before include");
      attributesSnapshot = new HashMap<String, Object>();
      Enumeration attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
            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());

   try {
      doDispatch(request, response);
   }
   finally {
      // Restore the original attribute snapshot, in case of an include.
      if (attributesSnapshot != null) {
         restoreAttributesAfterInclude(request, attributesSnapshot);
      }
   }
}

主要做两部分工作

  1. 如果是include请求,先保存一份request域数据的快照,doDispatch执行过后,将会用快照数据恢复。

  2. 调用doDispatch方法,完成请求转发。

3.doDispatch方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   int interceptorIndex = -1;

   try {
      ModelAndView mv;
      boolean errorView = false;

      try {
      // 1.检查是否是文件上传的请求
         processedRequest = checkMultipart(request);

         // Determine handler for the current request.
          // 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.
          //这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
         mappedHandler = getHandler(processedRequest, false);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
         //3. 获取处理request的处理器适配器handler adapter 
         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()) {
               String requestUri = urlPathHelper.getRequestUri(request);
               logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
            }
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

         // Apply preHandle methods of registered interceptors.
          // 4.拦截器的预处理方法
         HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
         if (interceptors != null) {
            for (int i = 0; i < interceptors.length; i++) {
               HandlerInterceptor interceptor = interceptors[i];
               if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                  triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                  return;
               }
               interceptorIndex = i;
            }
         }

         // Actually invoke the handler.
         // 5.实际的处理器处理请求,返回结果视图对象
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         // Do we need view name translation?
         if (mv != null && !mv.hasView()) {
            mv.setViewName(getDefaultViewName(request));
         }

         // Apply postHandle methods of registered interceptors.
         // 6.拦截器的后处理方法
         if (interceptors != null) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
               HandlerInterceptor interceptor = interceptors[i];
               interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
            }
         }
      }
      catch (ModelAndViewDefiningException ex) {
         logger.debug("ModelAndViewDefiningException encountered", ex);
         mv = ex.getModelAndView();
      }
      catch (Exception ex) {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(processedRequest, response, handler, ex);
         errorView = (mv != null);
      }

      // Did the handler return a view to render?
      if (mv != null && !mv.wasCleared()) {
         render(mv, processedRequest, response);
         if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
         }
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                  "': assuming HandlerAdapter completed request handling");
         }
      }

      // Trigger after-completion for successful outcome.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
   }

   catch (Exception ex) {
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
   }
   catch (Error err) {
      ServletException ex = new NestedServletException("Handler processing failed", err);
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
   }

   finally {
      // Clean up any resources used by a multipart request.
      if (processedRequest != request) {
         cleanupMultipart(processedRequest);
      }
   }
}

很明显这儿是SpringMVC核心。

1.根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法)(DispatcherServlet.getHandler完成)

2.匹配路径对应的拦截器(DispatcherServlet.getHandler完成)

3.获得HandlerExecutionChain对象(DispatcherServlet.getHandler完成)

4.通过HandlerAdapter对象进行处理得到ModelAndView对象(HandlerAdapter.handle)

5.调用HandlerInterceptor.preHandle

6.调用HandlerInterceptor.postHandle

7. 渲染


4.总结

wKioL1g-153C8LZpAACyG_bLg7c573.png

简单粗暴的总结下

step1-6: 获取controller

step5-15 :调用controller方法

step17-20:渲染view

其他:aop方式处理拦截统一处理。

SpringMVC源码分析(3)Dis

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

SpringMVC源码分析(3)Dis

:介绍了解析过程中,初始化若干组件。:初始化DispatcherServlet的多个组件。本文继续分析Dispatch
2023-01-31

SpringMVC源码分析6:SpringMVC的视图解析原理

转自 SpringMVC视图机制详解[附带源码分析]
2023-06-02

SpringMVC异常处理源码分析

今天小编给大家分享一下SpringMVC异常处理源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。背景我们的代码中,总是
2023-07-05

wifidog 源码初分析(3)

上一篇分析了 接入设备 在接入路由器,并发起首次 HTTP/80 请求到路由器上时,wifidog 是如何将此 HTTP 请求重定向至 auth-server 的流程。 之后 接入设备 的浏览器接收到 wifidog 返回的 302 重定向
2023-01-31

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC是目前主流的Web MVC框架之一。
2023-06-02

SocketServer 源码分析

Creating network servers.contentsSocketServer.pycontentsfile headBaseServerBaseServer.serve_foreverBaseServer.shutdownBa
2023-01-31

CesiumJS源码分析

这篇文章主要介绍“CesiumJS源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“CesiumJS源码分析”文章能帮助大家解决问题。1. 有什么光CesiumJS 支持的光的类型比较少,默认场
2023-07-06

RateLimiter 源码分析

俗话说得好,缓存,限流和降级是系统的三把利剑。刚好项目中每天早上导出数据时因调订单接口频率过高,订单系统担心会对用户侧的使用造成影响,让我们对调用限速一下,所以就正好用上了。 常用的限流算法有2种:漏桶算法和令牌桶算法。漏桶算法漏桶算法:请
2023-05-31

Android AsyncTask源码分析

Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler。除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池。本文先简要介绍AsyncTask的用法,然后分析具
2022-06-06

Pythonkeras.metrics源代码分析

最近在用keras写模型的时候,参考别人代码时,经常能看到各种不同的metrics,因此会产生几个问题,下面主要介绍了Pythonkeras.metrics源代码分析
2022-11-13

Kafka源码分析(一)

Apache Kafka® 是 一个分布式流处理平台. 这到底意味着什么呢?我们知道流处理平台有以下三种特性:可以让你发布和订阅流式的记录。这一方面与消息队列或者企业消息系统类似。可以储存流式的记录,并且有较好的容错性。可以在流式记录产生时就进行处理。Kafk
Kafka源码分析(一)
2019-10-17

Spring refresh()源码分析

今天小编给大家分享一下Spring refresh()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。正文public
2023-07-05

Java从源码角度解析SpringMVC执行流程

这篇文章主要介绍了Java从源码角度解析SpringMVC执行流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-16

Android LayoutInflater.inflate源码分析

LayoutInflater.inflate源码详解 LayoutInflater的inflate方法相信大家都不陌生,在Fragment的onCreateView中或者在BaseAdapter的getView方法中我们都会经常用这个方法来
2022-06-06

HBase WAL源码分析

这篇文章主要讲解了“HBase WAL源码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“HBase WAL源码分析”吧!WAL(Write-Ahead Logging)是数据库系统中保障
2023-06-03

ahooks useRequest源码分析

这篇“ahooks useRequest源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“ahooks useReque
2023-07-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录