14.1、SpringMVC 常用组件

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求

  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供

作用:根据请求的 url、method 等信息查找 Handler,即控制器方法

  • Handler:处理器,需要工程师开发

作用:在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理

  • HandlerAdapter:**处理器适配器**,不需要工程师开发,由框架提供

作用:通过 HandlerAdapter 对处理器(控制器方法)进行执行

  • ViewResolver:视图解析器,不需要工程师开发,由框架提供

作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、

RedirectView

  • View:视图

作用:将模型数据通过页面展示给用户

14.2、DispatcherServlet 初始化过程

DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。

① 初始化 WebApplicationContext

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {
   WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;
   if (this.webApplicationContext != null) {
       // A context instance was injected at construction time -> use it
       wac = this.webApplicationContext;
       if (wac instanceof ConfigurableWebApplicationContext) {
           ConfigurableWebApplicationContext cwac =(ConfigurableWebApplicationContext) wac;
           if (!cwac.isActive()) {
               // The context has not yet been refreshed -> provide services such as
                   // setting the parent context, setting the application context id, etc
                   if (cwac.getParent() == null) {
                       // The context instance was injected without an explicit parent -> set
                           // the root application context (if any; may be null) as the parent
                           cwac.setParent(rootContext);
                  }
               configureAndRefreshWebApplicationContext(cwac);
          }
      }
  }
   if (wac == null) {
       // No context instance was injected at construction time -> see if one
       // has been registered in the servlet context. If one exists, it is assumed
           // that the parent context (if any) has already been set and that the
           // user has performed any initialization such as setting the context id
           wac = findWebApplicationContext();
  }
   if (wac == null) {
       // No context instance is defined for this servlet -> create a local one
       // 创建WebApplicationContext
       wac = createWebApplicationContext(rootContext);
  }
   if (!this.refreshEventReceived) {
       // Either the context is not a ConfigurableApplicationContext with refresh
           // support or the context injected at construction time had already been
           // refreshed -> trigger initial onRefresh manually here.
           synchronized (this.onRefreshMonitor) {
           // 刷新WebApplicationContext
           onRefresh(wac);
      }
  }
   if (this.publishContext) {
       // Publish the context as a servlet context attribute.
       // 将IOC容器在应用域共享
       String attrName = getServletContextAttributeName();
       getServletContext().setAttribute(attrName, wac);
  }
   return wac;
}

② 创建 WebApplicationContext

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
   Class<?> contextClass = getContextClass();
   if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))
  {
       throw new ApplicationContextException("Fatal initialization error in servlet with name '" +getServletName() +
                                             "': custom WebApplicationContext class [" + contextClass.getName() +
                                             "] is not of type ConfigurableWebApplicationContext");
  }
   // 通过反射创建 IOC 容器对象
   ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
   wac.setEnvironment(getEnvironment());
   // 设置父容器
   wac.setParent(parent);
   String configLocation = getContextConfigLocation();
   if (configLocation != null) {
       wac.setConfigLocation(configLocation);
  }
   configureAndRefreshWebApplicationContext(wac);
   return wac;
}

③DispatcherServlet 初始化策略

FrameworkServlet 创建 WebApplicationContext 后,刷新容器,调用 onRefresh(wac),此方法在

DispatcherServlet 中进行了重写,调用了 initStrategies(context)方法,初始化策略,即初始化

DispatcherServlet 的各个组件

所在类:org.springframework.web.servlet.DispatcherServlet

protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   initHandlerMappings(context);
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}

14.3、DispatcherServlet 调用组件处理请求

①processRequest()

FrameworkServlet 重写 HttpServlet 中的 service()和 doXxx(),这些方法中调用了

processRequest(request, response)

所在类:org.springframework.web.servlet.FrameworkServlet

protected final void processRequest(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException
{
   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
   LocaleContext localeContext = buildLocaleContext(request);
   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request,response, previousAttributes);
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),new RequestBindingInterceptor());
   initContextHolders(request, localeContext, requestAttributes);
   try {
       // 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
       doService(request, response);
  }
   catch (ServletException | IOException ex) {
       failureCause = ex;
       throw ex;
  }
   catch (Throwable ex) {
       failureCause = ex;
       throw new NestedServletException("Request processing failed", ex);
  }
   finally {
       resetContextHolders(request, previousLocaleContext, previousAttributes);
       if (requestAttributes != null) {
           requestAttributes.requestCompleted();
      }
       logResult(request, response, failureCause, asyncManager);
       publishRequestHandledEvent(request, response, startTime, failureCause);
  }
}

②doService()

所在类:org.springframework.web.servlet.DispatcherServlet

@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);
  }
   RequestPath requestPath = null;
   if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
       requestPath = ServletRequestPathUtils.parseAndCache(request);
  }
   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);
          }
      }
       if (requestPath != null) {
           ServletRequestPathUtils.clearParsedRequestPath(request);
      }
  }
}

③doDispatch()

所在类:org.springframework.web.servlet.DispatcherServlet

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.
           /*
               mappedHandler:调用链
               包含handler、interceptorList、interceptorIndex
               handler:浏览器发送的请求所匹配的控制器方法
               interceptorList:处理控制器方法的所有拦截器集合
               interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
            */
           mappedHandler = getHandler(processedRequest);
           if (mappedHandler == null) {
               noHandlerFound(processedRequest, response);
               return;
          }
           // Determine handler adapter for the current request.
           // 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
           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 (new ServletWebRequest(request,response).checkNotModified(lastModified) && isGet) {
                   return;
              }
          }
           // 调用拦截器的preHandle()
           if (!mappedHandler.applyPreHandle(processedRequest, response)) {
               return;
          }
           // Actually invoke the handler.
           // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
           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 otherscenarios.
           dispatchException = new NestedServletException("Handler dispatchfailed", 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 processingfailed",
                                                                                                   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);
          }
      }
  }
}                                                          

④processDispatchResult()

private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,@Nullable HandlerExecutionChain
                                  mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {
   boolean errorView = false;
   if (exception != null) {
       if (exception instanceof ModelAndViewDefiningException) {
           logger.debug("ModelAndViewDefiningException encountered",exception);
           mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
       else {
           Object handler = (mappedHandler != null ? mappedHandler.getHandler(): null);
           mv = processHandlerException(request, response, handler, exception);
           errorView = (mv != null);
      }
  }
   // Did the handler return a view to render?
   if (mv != null && !mv.wasCleared()) {
       // 处理模型数据和渲染视图
       render(mv, request, response);
       if (errorView) {
           WebUtils.clearErrorRequestAttributes(request);
      }
  }
   else {
       if (logger.isTraceEnabled()) {
           logger.trace("No view rendering, null ModelAndView returned.");
      }
  }
   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
       // Concurrent handling started during a forward
       return;
  }
   if (mappedHandler != null) {
       // Exception (if any) is already handled..
       // 调用拦截器的afterCompletion()
       mappedHandler.triggerAfterCompletion(request, response, null);
  }
}                                  

14.4、SpringMVC 的执行流程

\1) 用户向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获。

\2) DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI),判断请求 URI 对应的映射:

a) 不存在

i. 再判断是否配置了 mvc:default-servlet-handler

ii. 如果没配置,则控制台报映射查找不到,客户端展示 404 错误

​​​​

iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示 404

错误

b) 存在则执行下面的流程

\3) 根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及

Handler 对象对应的拦截器),最后以 HandlerExecutionChain 执行链对象的形式返回。

\4) DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。

\5) 如果成功获得 HandlerAdapter,此时将开始执行拦截器的 preHandler(…)方法【正向】

\6) 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)方法,处理请求。

在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

a) HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定

的响应信息

b) 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等

c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中

\7) Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。

\8) 此时将开始执行拦截器的 postHandle(...)方法【逆向】。

\9) 根据返回的 ModelAndView(此时会判断是否存在异常:如果存在异常,则执行

HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver 进行视图解析,根据 Model

和 View,来渲染视图。

\10) 渲染视图完毕执行拦截器的 afterCompletion(…)方法【逆向】。

\11) 将渲染结果返回给客户端。

14. SpringMVC执行流程的更多相关文章

  1. springmvc执行流程详细介绍

    1.什么是MVC MVC是Model View Controller的缩写,它是一个设计模式 2.springmvc执行流程详细介绍 第一步:发起请求到前端控制器(DispatcherServlet) ...

  2. springMVC执行流程及原理

    spring的MVC执行原理 1.spring mvc将所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求 进行真正的处理工作. 2.DispatcherSer ...

  3. 2.SpringMVC执行流程

    SpringMVC 执行流程: 执行流程简单分析: 1.浏览器提交请求到中央调度器 2.中央调度器直接将请求转给处理器映射器 3.处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行 ...

  4. SpringMvc执行流程及底层代码流程

    SpringMVC执行流程   01.客户端发送请求被我们在web.xml中配置DispatcherServlet(核心控制器)拦截: 默认执行DispatcherServlet中的 protecte ...

  5. 面试高频SpringMVC执行流程最优解(源码分析)

    文章已托管到GitHub,大家可以去GitHub查看阅读,欢迎老板们前来Star! 搜索关注微信公众号 码出Offer 领取各种学习资料! SpringMVC执行流程 SpringMVC概述 Spri ...

  6. SpringMVC执行流程总结

    SpringMVC 执行流程: 用户发送请求至前端控制器 DispatcherServlet DispatcherServlet 收到请求调用处理映射器 HandlerMapping 处理映射器根据请 ...

  7. 【阅读SpringMVC源码】手把手带你debug验证SpringMVC执行流程

    ✿ 阅读源码思路: 先跳过非重点,深入每个方法,进入的时候可以把整个可以理一下方法的执行步骤理一下,也可以,理到某一步,继续深入,回来后,接着理清除下面的步骤. ✿ 阅读本文的准备工作,预习一下Spr ...

  8. Servlet、Struts2、SpringMVC执行流程

    Servlet 有以下四个阶段: 1.加载和实例化 Servlet容器负责加载和实例化Servlet. 当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Ser ...

  9. SSM-SpringMVC-01:SpringMVC是什么?SpringMVC执行流程

     ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- SpringMVC简介: SpringMVC也叫Spring Web  mvc,属于表现层的框架.Sprin ...

  10. springMVC执行流程及架构

    目录 springMVC简单执行流程 springMVC框架 注解实现 springMVC简单执行流程 springMVC框架 执行流程: 1.用户发送请求至前端控制器DispatcherServle ...

随机推荐

  1. [网络]内网IP的判别与分类

    1 内网IP划分 内网IP地址分为A类.B类和C类,其地址范围如下: A类地址: 10.0.0.0 - 10.255.255.255 B类地址: 172.16.0.0 - 172.31.255.255 ...

  2. 三天吃透Redis八股文

    Redis连环40问,绝对够全! Redis是什么? Redis(Remote Dictionary Server)是一个使用 C 语言编写的,高性能非关系型的键值对数据库.与传统数据库不同的是,Re ...

  3. DG:Oracle查看是否搭建DataGuard

    Oracle查看是否是DataGuard 1.查看归档路径 show parameter log_archive_dest LOG_ARCHIVE_DEST_n, 归档文件的生成路径, LOCATIO ...

  4. spring事务里面开启线程插入,报错了是否会回滚?

    1.前言 一道非常有意思的面试题目.大概是这样子的,如果在一个事务中,开启线程进行插入更新等操作,如果报错了,事务是否会进行回滚 2.代码 示例1 @RequestMapping("/tes ...

  5. 链式描述线性表(C++实现)

    在链式描述中,线性表的元素在内存中的存储位置是随机的,每个元素都有一个明确的指针或链指向下一个元素的位置 chain类 在此使用单向链表实现了线性表,其中最后一个节点的指针域为NULL,即它用单向链接 ...

  6. 基于RL(Q-Learning)的迷宫寻路算法

    强化学习是一种机器学习方法,旨在通过智能体在与环境交互的过程中不断优化其行动策略来实现特定目标.与其他机器学习方法不同,强化学习涉及到智能体对环境的观测.选择行动并接收奖励或惩罚.因此,强化学习适用于 ...

  7. 安装MongoDB、及基本使用

    1.MongoDB简介 MongoDB是一个介于关系数据库和非关系数据库之间的产品,基于分布式文件存储的数据库.是非关系数据库当中功能最丰富,最像关系数据库的.它支持的数据结构非常松散,是类似json ...

  8. ES6之数组的Array.from()方法

    Array.from()方法就是构造函数本身的方法 将一个类数组对象或者可遍历对象转换成一个真正的数组. 那么什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有length属性的对象. 1.将类 ...

  9. 【机器学习与深度学习理论要点】07.A/B测试的概念及用法

    1)什么是A/B测试? A/B测试就是两种模型同时运行,并在实际环境中验证其效果的方式.在互联网公司中,A/B测试是验证新模块.新功能.新产品是否有效,新算法.新模型的效果是否有提升,新设计是否收到用 ...

  10. java镜子之反射篇

    文章目录 注解 内置注解 元注解 反射 类的初始化 类加载器 双亲委派机制 反射方法的使用 调用类的方法.成员变量.构造器等 总结 注解和反射是Java中非常重要的知识,一些优秀开源的框架都是大量运用 ...