Spring MCV源码分析(二) DispatcherServlet的生命周期

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

一.DispatcherServlet简述

DispatcherServletSpring MVC框架中的前端控制器,通过调度其他组件来处理用户的请求,因此可以说它是Spring MVC中最核心的部分。

二.分析思路

在分析前,我们应该首先明确分析的思路和过程。从名字中我们可以很容易知道,DispatcherServlet首先是一个Servlet,因此它被配置在web.xml文件中。而我们分析一个Sevlet,通常的思路应当是顺着其生命周期分析。
– init方法
– service方法
– destory方法

三.init方法

1. 找到init方法
直接在DispatcherServlet中并没有找到init()方法,但是发现该类继承自其他类,这是它的继承关系图。

通过向父类中寻找可以发现是HttpServletBean这个类重写了init方法,跳转到HttpServletBean这个类中

2. init方法剖析
– 源码展示

    public final void init() throws ServletException {
      if (logger.isDebugEnabled()) {
       logger.debug("Initializing servlet '" + getServletName() + "'");
      }
      try {
       PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
       BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
       ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
       bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
       initBeanWrapper(bw);
       bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
       logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
       throw ex;
      }
      initServletBean();
      if (logger.isDebugEnabled()) {
       logger.debug("Servlet '" + getServletName() + "' configured successfully");
      }
     }

这个方法和核心涉及到三个类,PropertyValues,BeanWrapper,和ResourceLoader,因此我们应该分别分析这三个类的实例对象在该方法起到的作用

  • PropertyValues
    PropertyValues是HttpServletBean的一个内部类,继承了MutablePropertyValues,用来获取web.xml中的属性如contextConfigLocation
  • BeanWrapper
    BeanWrapper封装了bean的行为,提供了设置和获取属性值。
  • ResourceLoader
    ResourceLoader用来获取spring mvc配置文件的信息

先通过PropertyValues获取web.xml文件init-param的参数值,然后通过ResourceLoader读取.xml配置信息,BeanWrapper对配置的标签进行解析和将系统默认的bean的各种属性设置到对应的bean属性

注意到第17行的initServletBean()方法,这是一个抽象方法,在源代码中作者的注释为Let subclasses do whatever initialization they like.即要求子类实现这个方法去完成子类还需要的其他初始化操作,通过代码的跟踪,我们可以在DispatcherSevlet方法中找到一个方法为initStrategies(),这个方法就是子类为初始化工作编写的代码,该方法代码如下。

    protected void initStrategies(ApplicationContext context) {
      initMultipartResolver(context);
      initLocaleResolver(context);
      initThemeResolver(context);
      initHandlerMappings(context);
      initHandlerAdapters(context);
      initHandlerExceptionResolvers(context);
      initRequestToViewNameTranslator(context);
      initViewResolvers(context);
      initFlashMapManager(context);
     }
  • 一开始我们说到DispatcherServlet是通过调度其他组件来处理请求的,因此该类必定要对那些组件进行初始化,而这段代码就是进行这样的工作的。我们从此也可以知道DispatcherServlet依赖了什么组件。

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

四.Service方法

在Servlet中,每个请求都会被对应的Servlet类中的service方法处理,在DispatcherServlet中,service的实现在其父类FrameworkServlet

     protected void service(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {

      String method = request.getMethod();
      if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
       processRequest(request, response);
      }
      else {
       super.service(request, response);
      }
     }

其核心的方法是processRequest(request, response);

     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(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 {
       resetContextHolders(request, previousLocaleContext, previousAttributes);
       if (requestAttributes != null) {
        requestAttributes.requestCompleted();
       }

       if (logger.isDebugEnabled()) {
        if (failureCause != null) {
         this.logger.debug("Could not complete request", failureCause);
        }
        else {
         if (asyncManager.isConcurrentHandlingStarted()) {
          logger.debug("Leaving response open for concurrent processing");
         }
         else {
          this.logger.debug("Successfully completed request");
         }
        }
       }

       publishRequestHandledEvent(request, startTime, failureCause);
      }
     }

这个方法执行的核心方法为doService,doService是一个抽象方法,在DispatcherServlet中实现

     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.
      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());

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

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

但是控制器和拦截器的代码是在doDispach()中执行的

     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;
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null || mappedHandler.getHandler() == null) {
         noHandlerFound(processedRequest, response);
         return;
        }
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        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;
         }
        }
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
         return;
        }
        try {
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        }
        finally {
         if (asyncManager.isConcurrentHandlingStarted()) {
          return;
         }
        }

        applyDefaultViewName(request, mv);
        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()) {
        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
        return;
       }
       if (multipartRequestParsed) {
        cleanupMultipart(processedRequest);
       }
      }
     }
  • 说明

    • 第33行的mappedHandler.applyPreHandle(processedRequest, response)执行的是拦截器中的pre…方法
    • 第37行的mv = ha.handle(processedRequest, response, mappedHandler.getHandler());执行的是控制器中的方法,返回值是一个ModelAndView
    • 第46行的mappedHandler.applyPostHandle(processedRequest, response, mv);执行拦截器中的post…方法
    • 第51行的processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);中的mappedHandler.triggerAfterCompletion(request, response, null);执行affter…方法.
    • 第51行的processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);中的render(mv, request, response);渲染对应视图的

destory方法在Dispatcher中并不重要,因此在此不做分析。

总结

本章节以分析一个Servlet的方式找到了DispatcherServlet的init方法和service方法,简单的描绘了一个servlet的生命周期,在初始化过程中先对各个组件进行初始化,当用户请求过来时,通过service方法处理,分别执行拦截器中的方法和对应控制器中的方法。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring MCV源码分析(二) DispatcherServlet的生命周期

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏