SpringMVC源码分析(3)Dis

发布时间:2019-09-21 10:51:44编辑:auto阅读(1558)

    <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);
    }
    
    /**
     * Delegate POST requests to {@link #proce***equest}.
     * @see #doService
     */
    @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方式处理拦截统一处理。

关键字