Springmvc请求处理流程

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
<servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <!--配置contextAttribute,从servletContext中找hehe的webApplicationContext-->
    <param-name>contextAttribute</param-name> 
    <param-value>hehe</param-value>
  </init-param>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-mvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
  <async-supported>true</async-supported>
</servlet>
  1. 自己创建(正常情况)

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。

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

输入图片说明

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);
   }
}

输入图片说明

转载于:https://my.oschina.net/u/1245414/blog/1546348

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值