Springmvc请求处理流程

Easter79
• 阅读 435

HttpServletBean

httpServletBean中Environment使用的是StandardServletEnvironment

StandardServletEnvironment

其中propertySources属性封装了如下信息:

  • servletContext 和 servletConfig

  • jndiProperty

  • 系统环境变量

  • 系统属性

    //frameworkServlet中初始化 @Override public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources( this.getPropertySources(), servletContext, servletConfig);//servletContext 和 servletConfig }

    //AbstractEnvironment 构造方法中调用,构造StandardServletEnvironment对象时就有了。 @Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));//jndiProperty } super.customizePropertySources(propertySources); }

    @Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));//系统属性 propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));//系统环境变量 }

init()

@Override
public final void init() throws ServletException {
   if (logger.isDebugEnabled()) {
      logger.debug("Initializing servlet '" + getServletName() + "'");
   }

   // Set bean properties from init parameters.
   try {
      PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
      BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//this就是之后的DispatcherServlet
      ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
      bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));//注册Editor,复合resource类的,就用ResourceEditor来编辑
      initBeanWrapper(bw);
      bw.setPropertyValues(pvs, true);//将ServletConfig设置到dispatcherServlet
   }
   catch (BeansException ex) {
      logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
      throw ex;
   }

   // Let subclasses do whatever initialization they like.
   initServletBean();//模版方法

   if (logger.isDebugEnabled()) {
      logger.debug("Servlet '" + getServletName() + "' configured successfully");
   }
}

HttpServletBean主要作用

  • 定义Environment
  • 设置ServletConfig

FrameworkServlet

implements ApplicationContextAware

从spring中获取WebApplicationContext

initServletBean()

@Override
protected final void initServletBean() throws ServletException {
...
   try {
      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet();
   }
   catch (ServletException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
   }
...
}


protected WebApplicationContext initWebApplicationContext() {
  //获取spring的容器,从ServletContext#getAttribute(WebApplicationContext.class.getName() + ".ROOT")
   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
              //将spring容器设置为springmvc容器的父容器
               cwac.setParent(rootContext);
            }
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
  //this.webApplicationContext = null & wac = null 是什么情况?
   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
     //通过配置servlet中的contextAttribute参数,从ServletContext中获取指定名字的webApplicationContext
      wac = findWebApplicationContext();
   }
   if (wac == null) {
      // No context instance is defined for this servlet -> create a local one
      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.
     // 留给dispatcherServlet
      onRefresh(wac);
   }

   if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      // FrameworkServlet.class.getName() + ".CONTEXT." + getServletName();
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
      if (this.logger.isDebugEnabled()) {
         this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
               "' as ServletContext attribute with name [" + attrName + "]");
      }
   }

   return wac;
}

作用:

  • 初始化WebApplicationContext 从ServletContext中获取
  • initFrameworkServlet模版方法

servlet的webApplicationContext

  1. 已经构造好了webApplicationContext,在servlet构造方法中传入现有的对象。(主要用于servlet3.0以后,ServletContext.addServlet)

  2. webApplication已经在ServletContext中了。可以通过配置来从servletContext中根据contextAttribute获取现有webApplicationContext

    spring org.springframework.web.servlet.DispatcherServlet contextAttribute hehe contextConfigLocation classpath:spring-mvc.xml 1 true
  3. 自己创建(正常情况)

webApplicationContext 中的内容

  • environment
  • parent
  • contextConfigLocation :web.xml中配置,默认WEB-INFO/[servletname]-Servlet.xml
  • servletContext
  • servletConfig
  • namespace
  • applicationListener : ContextRefreshedEvent监听器,收到event后调用onRefresh() 方法
  • postProcessWebApplicationContext() 后置处理

重写doGet doPost.....

将doXXX方法全部交由processRequest() 方法。doOptions 和 doTrace 根据参数判断自己处理还是交给父类

processRequest

  • 对LocaleContext和RequestAttributes设置和恢复
  • doService()
  • 处理完后发布publishRequestHandledEvent

ServletRequestAttributes封装了request,可以从request和session中取值。

requestAttributes.requestCompleted();执行后,不能再对requestAttributes进行操作。

LocaleContextHolder/RequestContextHolder

//根据request设置LocaleContextHolder/RequestContextHolder,可提供locale和request,在service层。
initContextHolders(request, localeContext, requestAttributes);

localeContextHolder、inheritableLocaleContextHolder可以被子线程继承???

没搞懂localeContextHolder inheritableLocaleContextHolder两个ThreadLocal变量的作用,希望大神帮忙解答。

resetContextHolders(request, previousLocaleContext, previousAttributes); //为什么要恢复?怕影响Servlet外面的filter工作,可能有什么修改需要恢复?

publishRequestHandledEvent

配置:web.xml中配置springmvc servlet时配置,默认true。

用法:可以用来记录日志。

Springmvc请求处理流程

DispatcherServlet

初始化各种组件init()

  • MutipartResolver
  • LocaleResolver
  • ThemeResolver
  • HandlerMappings
  • HandlerAdapters
  • HandlerExceptionResolvers
  • RequestToViewNameTranslator
  • ViewResolvers
  • FlashMapManager

doService()

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   if (logger.isDebugEnabled()) {
      String requestUri = urlPathHelper.getRequestUri(request);
      String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
      logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
            " 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.
  //判断是否是include请求,如果是则对request做快照备份,结束后还原
   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());

  //后面3个和flashMap有关。用于redirect传递参数。
   FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
   if (inputFlashMap != null) {
     //先取出flashmap放入INPUT_FLASH_MAP_ATTRIBUTE,再放入model中。在Controller中可以使用。
     //RedirectAttributes.addFlashAttribute() 将参数添加到session中
     //RedirectAttributes.addAttribute() 将参数拼接到url后。
      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);

   try {
      doDispatch(request, response);
   }
   finally {
      if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         return;
      }
      // Restore the original attribute snapshot, in case of an include.
      if (attributesSnapshot != null) {
         restoreAttributesAfterInclude(request, attributesSnapshot);
      }
   }
}
  • 设置request一些属性
  • 处理include请求快照
  • doDispatch()

doDispatch()

  • 根据request找handler

  • 根据handler找HandlerAdapter

  • 用Adapter处理handler

  • processDispathResult() 处理结果,包括找到view并渲染。

    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 { //判断是否是Multipart 请求,用到MultipartResolver processedRequest = checkMultipart(request); multipartRequestParsed = processedRequest != request; // Determine handler for the current request. //根据request获取handler,用到handlerMapping mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. //获取adatper 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; } }
    //interceptor#perHandler if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { //异步处理,则直接返回 if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); //interceptor#postHandler mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }

processDispatchResult()

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
        //用到HanlderExceptionResolver
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // Did the handler return a view to render?
   if (mv != null && !mv.wasCleared()) {
     //渲染页面,用到themeResolver
      render(mv, request, 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");
      }
   }

  //异步
   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }

   if (mappedHandler != null) {
     //触发Interceptor#afterCompletion()
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

Springmvc请求处理流程

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k