java面试

Spring MVC 关键流程源码分析

2021-09-15  本文已影响0人  Scallion

Spring MVC 源码分析

1. 前端控制器DispatcherServlet继承结构

前端控制器继承结构

前端控制器DispatcherServlet继承自FrameworkServlet。FrameworkServlet继承自HttpServletBean,并实现了ApplicationContextAware接口。HttpServletBean又继承自HttpServlet。当用户发起一个请求,首先会被HttpServlet中的doGet/doPost处理,通过分析DispatcherServlet的继承结构,可以发现用户发起的请求实际被FrameworkServlet(重写了HttpServlet中的doGet、doPost方法)中的doGet/doPost处理,FrameworkServlet的doGet/doPost方法收到用户发起的请求后,会调用processRequest方法处理,processRequest函数内部会调用一个抽象方法doService,该方法在DispatcherServlet中被实现。DispatcherServlet中的doService方法最终会调用doDispatch核心方法处理请求。

2. SpringMVC处理请求的重要时机

2.1. Handler方法的执行时机

通过在Controller中加入断点,开启Debug调试,分析SpringMVC处理请求的掉用栈,可以明确看到用户的请求进入DispatcherServlet中的doDispatch函数,由此可见用户都请求都是被doDispatch函数处理。

Controller断点

2.2. 页面渲染时机

通过在 Jsp 中加入断点,启动Debug调试,当Controller处理完用户请求后,向页面响应,分析SpringMVC响应请求的调用栈。分析调用栈可以看出DispatcherServlet中的doDispatch函数处理完用户请求,紧接着调用processDispatchResult函数,将用户请求响应到页面。

jsp页面断点

2.3. doDispatch源码分析

通过分析Handler方法的执行时机和页面渲染时机,可以看到用户发起一个请求,最终调用到了DispatcherServlet中的doDispatch方法,在doDispatch方法中主要会执行以下逻辑完成对用户的请求处理以及处理结果的响应:

  1. 首先会执行checkMultipart函数,检查是否为文件上传请求,如果当前请求是上传的请求,会将processedRequest变量置成true,交给后续业务处理;
  2. 调用getHandler(processedRequest)函数,获取处理当前请求的Controller,也称为Handler。getHandler(processedRequest)方法不是直接返回Controller,而是返回HandlerExecutionChain请求处理链对象,这个对象封装了Handler和Inteceptor。如果Handler为空就会通过noHandlerFound函数向页面返回404;
  3. 接着通过getHandlerAdapter(mappedHandler.getHandler())函数获取HandlerAdapter处理器适配器;
  4. 最终调用HandlerAdapter的handle方法(ha.handle(processedRequest, response, mappedHandler.getHandler()))处理请求,并将处理请求的结果封装到ModleAndView对象中;
  5. 处理ModleAndView对象,通过processDispatchResult函数将处理请求的结果响应到页面。
    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 {
                // 1 检查是否是文件上传的请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                /*
                    2 取得处理当前请求的Controller,这里也称为Handler,即处理器
                      这里并不是直接返回 Controller,而是返回 HandlerExecutionChain 请求处理链对象
                      该对象封装了Handler和Inteceptor
                 */
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 如果 handler 为空,则返回404
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                // 3 获取处理请求的处理器适配器 HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                // 处理 last-modified 请求头
                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;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                // 4 实际处理器处理请求,返回结果视图对象
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                // 结果视图对象的处理
                applyDefaultViewName(processedRequest, mv);
        //拦截器的第二个拦截时机,在业务处理器(即Controller类)处理完请求后,会执行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 other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
      //将处理好的结果响应到页面
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //最终会调用HandlerInterceptor的afterCompletion 方法,在DispatcherServlet处理完请求之后,才会执行afterCompletion()方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            //最终会调用HandlerInterceptor的afterCompletion 方法,在DispatcherServlet处理完请求之后,才会执行afterCompletion()方法
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", 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);
                }
            }
        }
    }

doDispatch方法核心步骤:

2.4. 核心步骤getHandler()方法剖析

通过在doDispatch函数调用getHandler()方法上加入断点,分析getHandler()方法是如何获取HandlerExecutionChain执行器链

doDispatch方法

通过断点向下执行,进入到getHandler()方法可以到获取到了两个handlerMappings,分别是:

  1. BeanNameUrlHandlerMapping是SpringMVC早期的使用方式,直接在类中实现Controller接口,这种方式现在很少用了;
  2. RequestMappingHandlerMapping是SpringMVC目前最流行的使用方式,通过在类上添加@Controller、@RequestMapping注解实现
getHandler方法

getHandler方法执行步骤:

  1. 判断handlerMappings是否为null,不为null继续向下执行;
  2. 通过for循环获取到具体的HandlerMapping对象;
  3. 在for循环中通过HandlerMapping的getHandler()方法,获取到能处理当前请求的HandlerExecutionChain对象;
  4. 判断HandlerExecutionChain对象是否为null,不为null表示获取到处理当前请求的HandlerExecutionChain,并向上返回。

2.5. 核心步骤getHandlerAdapter方法剖析

2.5.1. getHandlerAdapter执行流程分析

通过在doDispatch函数调用getHandlerAdapter方法获取HandlerAdapter处加入断点,分析getHandlerAdapter函数的执行流程

doDispatch

断点进入getHandlerAdapter函数,可以看到SpringMVC内部有三种HandlerAdapter,分别是:

  1. HttpRequestHandlerAdapter处理器适配器能够处理的请求需要实现HttpRequestHandler接口;
  2. SimpleControllerHandlerAdpter处理器适配器能够处理的请求需要实现Controller接口;
  3. RequestMappingHandlerAdpter处理器适配器能够处理在Controller中的方法上加入了@Controller、@RequestMapping注解的请求,也就是我们现在访问的请求。
getHandlerAdapter方法

断点继续向下分析,获取HandlerAdapter对象都会进入到adapter.supports(handler)函数中,基于上面的断点我们可以看出SpringMVC内部有三种HandlerAdapter,所有在最差的情况下会进行三次adapter.supports(handler)函数中。

第一次进入supports函数

第一次进入的是HttpRequestHandleAdapter的suppors函数,传入的handler通过 instanceof 判断出当前的handler不是HttpRequestHandler类型,返回false

第一次进入supports函数

第二次进入supports函数

第二次进入的是SimpleControllerHandlerAdapter的suppors函数,传入的handler通过instanceof判断出当前的handler不是Controller类型,返回false

第二次进入supports函数

第三次进入supports函数

第三次进入的是AbstractHandlerMethodAdapter的suppors函数,传入的handler通过instanceof判断出当前的handler是HandlerMethod类型,返回true。

第三次进入supports函数

断点继续向下,最终将HandlerAdapter返回到doDispatch函数中

doDispatch方法

实际返回的HandlerAdapter为RequestMappingHandlerAdapter,这是因为RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter。

2.5.2.getHandlerAdapter源码分析

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                //判断当前的HandlerAdapter能否处理handler,如果能够处理,将当前HandlerAdapter返回
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

getHandlerAdapter函数,实际上是遍历SpringMVC内部的HandlerAdapter,并调用HandlerAdapter的supports函数传入handler,判断当前handler能否被SpringMVC内部的HandlerAdapter处理,如果可以处理将当前HandlerAdapter返回,如果都不能处理则抛出ServletException异常。

2.6. 核心步骤ha.handle方法剖析

handle方法 handleInternal方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
      // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中配置的InitBinder注解,用于进行参数的绑定,例如时间格式转换
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      // 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

      // 将handlerMethod封装为一个ServletInvocableHandlerMethod对象,封装成一个可被调用的invocableHandlerMethod
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      if (this.argumentResolvers != null) {
         // 设置当前容器中配置的所有ArgumentResolver,设置请求参数解析器
         invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }
      if (this.returnValueHandlers != null) {
         // 设置当前容器中配置的所有ReturnValueHandler,设置返回值解析器
         invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      }
      // 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
      invocableMethod.setDataBinderFactory(binderFactory);

      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
      //实例化ModelAndViewContainer对象
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      //向ModleAndViewContainer容器中添加request属性
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      // 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
      // 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
      //设置异步请求处理
      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);

      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.setTaskExecutor(this.taskExecutor);
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

      if (asyncManager.hasConcurrentResult()) {
         Object result = asyncManager.getConcurrentResult();
         mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
         asyncManager.clearConcurrentResult();
         LogFormatUtils.traceDebug(logger, traceOn -> {
            String formatted = LogFormatUtils.formatValue(result, !traceOn);
            return "Resume with async result [" + formatted + "]";
         });
         invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }

      // 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }

      // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
      // 还会判断是否需要将FlashAttributes封装到新的请求中
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}

2.7. 页面渲染processDispatchResult方法剖析

processDispatchResult方法 render方法 resolveViewName方法 createView方法 AbstractCachingViewResolver中的createView renderMergedOutputModel方法

3. SpringMVC九大组件初始化

3.1 SpringMVC中的九大组件

DispatcherServlet中定义了九个属性,每一种属性都对应了一个组件

    /** MultipartResolver used by this servlet. */
    //多部件解析器,一般用于文件上传
    @Nullable
    private MultipartResolver multipartResolver;

    /** LocaleResolver used by this servlet. */
    //区域化,国际化相关
    @Nullable
    private LocaleResolver localeResolver;

    /** ThemeResolver used by this servlet. */
    //主题解析器
    @Nullable
    private ThemeResolver themeResolver;

    /** List of HandlerMappings used by this servlet. */
    //处理器映射器组件
    @Nullable
    private List<HandlerMapping> handlerMappings;

    /** List of HandlerAdapters used by this servlet. */
    //处理器适配器组件
    @Nullable
    private List<HandlerAdapter> handlerAdapters;

    /** List of HandlerExceptionResolvers used by this servlet. */
    //异常解析器组件
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;

    /** RequestToViewNameTranslator used by this servlet. */
    //默认的视图名转换器组件
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;

    /** FlashMapManager used by this servlet. */
    //flash属性管理组件
    @Nullable
    private FlashMapManager flashMapManager;

    /** List of ViewResolvers used by this servlet. */
    //视图解析器组件
    @Nullable
    private List<ViewResolver> viewResolvers;

九大组件都是定义了接口,接口其实就是定义了该组件的规范,比如ViewResolver、HandlerAdapter等都是接口

3.2. 九大组件初始化时机

    @Override
    protected void onRefresh(ApplicationContext context) {
        // 初始化策略
        initStrategies(context);
    }
protected void initStrategies(ApplicationContext context) {
   // 多文件上传的组件
   initMultipartResolver(context);
   // 初始化本地语言环境
   initLocaleResolver(context);
   // 初始化模板处理器
   initThemeResolver(context);
   // 初始化HandlerMapping
   initHandlerMappings(context);
   // 初始化参数适配器
   initHandlerAdapters(context);
   // 初始化异常拦截器
   initHandlerExceptionResolvers(context);
   // 初始化视图预处理器
   initRequestToViewNameTranslator(context);
   // 初始化视图转换器
   initViewResolvers(context);
   // 初始化 FlashMap 管理器
   initFlashMapManager(context);
}
  1. 由于detectAllHandlerMappings的默认值为true,所以会先进入IOC容器中,按照HandlerMapping类型去找到所有的HandlerMapping;
  2. 如果按照类型没有在IOC容器中找到,继续按照id(handlerMapping)在IOC容器中查找;
  3. 若是以上两种方式都没在IOC容器中找到,就会按照默认的策略生成HandlerMapping。
private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;

   if (this.detectAllHandlerMappings) {
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
      // 按照HandlerMapping类型去IOC容器中找到所有的HandlerMapping
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerMappings = new ArrayList<>(matchingBeans.values());
         // We keep HandlerMappings in sorted order.
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   else {
      try {
         // 否则在ioc中按照固定名称id(handlerMapping)去找
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm);
      }
      catch (NoSuchBeanDefinitionException ex) {
         // Ignore, we'll add a default HandlerMapping later.
      }
   }

   // Ensure we have at least one HandlerMapping, by registering
   // a default HandlerMapping if no other mappings are found.
   if (this.handlerMappings == null) {
      // 最后还为空,则按照默认策略生成HandlerMapping
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isTraceEnabled()) {
         logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
               "': using default strategies from DispatcherServlet.properties");
      }
   }
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
   String key = strategyInterface.getName();
   //实际上获取的是DispatcherServlet.properties中的HandlerMapping
   String value = defaultStrategies.getProperty(key);
   if (value != null) {
      String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
      List<T> strategies = new ArrayList<>(classNames.length);
      for (String className : classNames) {
         try {
            Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
            Object strategy = createDefaultStrategy(context, clazz);
            strategies.add((T) strategy);
         }
         catch (ClassNotFoundException ex) {
            throw new BeanInitializationException(
                  "Could not find DispatcherServlet's default strategy class [" + className +
                  "] for interface [" + key + "]", ex);
         }
         catch (LinkageError err) {
            throw new BeanInitializationException(
                  "Unresolvable class definition for DispatcherServlet's default strategy class [" +
                  className + "] for interface [" + key + "]", err);
         }
      }
      return strategies;
   }
   else {
      return new LinkedList<>();
   }
}

分析defaultStrategies的初始化时机,发现defaultStrategies是在DispatcherServlet的静态代码块中完成的初始化,其中 DEFAULT_STRATEGIES_PATH 对应的就是DispatcherServlet.properties,静态代码块读取DispatcherServlet.properties中的属性,并封装到defaultStrategies Properties集合中。

defaultStrategies DispatcherServlet.properties multipartResolver
上一篇下一篇

猜你喜欢

热点阅读