Spring MVC 关键流程源码分析

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方法核心步骤:

  • 调用getHandler()获取到能够处理当前请求的执行链HandlerExecutionChain;
  • 调用getHandlerAdapter()方法,获取能够执行getHandler()获取到的Handler的适配器;
  • 适配器调用Handler执行ha.handle(),总能返回一个ModleAndView对象;
  • 调用processDispatchResult()方法完成页面渲染跳转。

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方法剖析

  • ha.handle方法执行的入口

    通过在ha.handle()方法加入断点,可以看到handle方法执行前会找到具体的handler方法,例如在下图的断点中可以看到当前的mappedHandler.getHandler()获取到当前请求应该被DemoController中handle01 uri对应的方法处理。

handle方法
  • handleInternal处理请求的核心方法

    断点继续向下会先进入AbstractHandlerMethodAdapter类中的handle方法,在handler方法中调用本类中的抽象方法handleInternal,断点继续向下会进入RequestMappingHandlerAdapter类中的handleInternal方法,RequestMappingHandlerAdapter类继承了AbstractHandlerMethodAdapter类,并实现了该类的handleInternal方法。进入handleInternal方法首先会校验当前请求的类型,然后继续向下判断当前请求是否需要支持在同一个session中处理请求,如果需要支持在同一个session中处理请求,就会为当前session生成一个唯一的可以用于锁定的key,对当前请求进行加锁。无论是否需要支持在同一个session中处理请求,最终都会调用invokeHandlerMethod函数,实现请求处理,并将处理结果封装到ModelAndView中。

handleInternal方法
  • RequestMappingHandlerAdapter中的invokeHandlerMethod执行流程

    invokeHandlerMethod首先会执行getDataBinderFactory方法,从容器中获取全局配置的InitBinder和Controller中配置的InitBinder返回WebDataBinderFactory对象。紧接着执行getModelFactory方法,从容器中获取全局配置的ModelAttribute和当前Controller中配置的ModelAttribute返回ModelFactory。继续执行createInvocableHandlerMethod方法将当前handlerMethod封装成一个可被调用的ServletInvocableHandlerMethod对象。通过setHandlerMethodArgumentResolvers方法、setHandlerMethodReturnValueHandlers方法与setDataBinderFactory方法,分别将请求参数解析器、返回值解析器与ModelFactory,设置到ServletInvocableHandlerMethod中。接着通过new ModelAndViewContainer()实例化ModelAndViewContainer容器,并向容器中添加request属性。通过ModelFactory调用initModel方法,从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的。继续调用WebAsyncUtils.createAsyncWebRequest将当前请求设置为异步请求。最终通过ServletInvocableHandlerMethod调用invokeAndHandle方法,对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象。返回ModelAndView对象后,会继续调用getModelAndView方法对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,还会判断是否需要将FlashAttributes封装到新的请求中,并将ModelAndView对象返回。

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();
   }
}
  • ServletInvocableHandlerMethod中的invokeAndHandle方法

    断点继续向下,就会执行到ServletInvocableHandlerMethod中的invokeAndHandle方法,在invokeAndHandle方法中,核心调用了InvocableHandlerMethod中的invokeForRequest方法,处理请求参数中的并调用调用目标handler

    invokeAndHandle方法
  • InvocableHandlerMethod中的invokeForRequest方法

    invokeForRequest方法的核心主要有两个,先调用getMethodArgumentValues方法将request中的参数转换成当前handler的参数形式。最终通过doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用

    invokeForRequest方法

2.7. 页面渲染processDispatchResult方法剖析

  • processDispatchResult方法首先会判断是否需要处理异常信息,紧接着调用核心方法render,实现页面视图渲染。
processDispatchResult方法
  • render方法先定义了一个view对象,核心调用resolveViewName获取view对象。
render方法
  • resolveViewName方法中主要获取了springmvc.xml配置的视图解析器,通过试图解析器调用viewResoler.resolveViewName方法,获取View视图
resolveViewName方法
  • 视图解析器中调用resolveViewName方法,首先会尝试从缓存中获取视图,如果缓存中没有就会调用createView创建视图

    视图解析器resolveViewName
  • createView方法会先判断当前返回的视图类型是否为重定向类型或为转发类型,如果以上都不是继续调用父类的createView方法。
createView方法
  • 断点继续向下,先进入AbstractCachingViewResolver类中的createView方法,在createView方法中调用loadView。进入loadView方法,会调用到UrlBasedViewResolver类中的loadView方法,loadView方法内部会先调用buildView创建一个AbstractUrlBasedView。
AbstractCachingViewResolver中的createView
  • 断点继续向下,进入InternalResourceViewResolver中的buildView方法,buildView方法核心调用父类的buildView方法;

    buildView方法
  • 断点继续向下,在UrlBasedViewResolver类中的bulidView方法中,会将逻辑视图名转换成物理视图名

    bulidView方法
  • 创建完View对象后,回到DispatcherServlet中的render方法,render方法获取到view视图后,通过view视图调用render方法,封装数据;

    render方法
  • 断点继续向下,进入AbstractView类中的render方法,调用createMergedOutputModel方法获取返回页面的数据,并调用renderMergedOutputModel进行数据线渲染;

    AbstractView类中的render方法
  • 断点继续向下,进入到InternalResourceView类中的renderMergedOutputModel方法,在此方法中主要调用了exposeModelAsRequestAttributes
renderMergedOutputModel方法
  • 在exposeModelAsRequestAttributes方法中,会将数据设置到请求的域中。这也是为什么后台model.add之后在jsp中可以从请求 域取出来的根本原因。

    exposeModelAsRequestAttributes方法
  • 最终调用RequestDispatcher中的forward方法,跳转到success页面

    forward方法

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. 九大组件初始化时机

  • DispatcherServlet中的onRefresh()方法,该方法初始化了SpringMVC的九大组件

    onRefresh()方法只调用了initStrategies(context)函数,可以明确九大组件初始化是在initStrategies函数中完成的

    @Override
    protected void onRefresh(ApplicationContext context) {
        // 初始化策略
        initStrategies(context);
    }
  • initStrategies方法

    在initStrategies函数中,依次对springMVC九大组件进行了初始化。挑取几个重要的初始化函数进行后续分析,例如:initHandlerMappings

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);
}
  • 分析其中的一个组件initHandlerMappings(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");
      }
   }
}
  • 分析getDefaultStrategies默认生成HandlerMapping的策略

    如果按照类型和按照固定id从ioc容器中找不到对应的组件,则会按照默认的额策略进行初始化,默认策略在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

    在DispatcherServlet.properties中可以看到SpringMVC默认的两种HandleMapping类型的定义。默认的三种HandlerAdapter也是在DispatcherServlet.properties中定义的,同时HandlerAdapter初始化的逻辑与HandleMapping初始化逻辑基本一致。

DispatcherServlet.properties
  • 多部件MultipartResolver文件上传组件初始化

    SpringMVC的多部件解析器的初始化必须是按照id(multipartResolver)注册对象

    private void initMultipartResolver(ApplicationContext context) {
       try {
          //将MULTIPART_RESOLVER_BEAN_NAME作为id注册对象
          this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
          if (logger.isTraceEnabled()) {
             logger.trace("Detected " + this.multipartResolver);
          }
          else if (logger.isDebugEnabled()) {
             logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
          }
       }
       catch (NoSuchBeanDefinitionException ex) {
          // Default is no multipart resolver.
          this.multipartResolver = null;
          if (logger.isTraceEnabled()) {
             logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
          }
       }
    }
    

    多部件解析器的初始化id默认是multipartResolver

multipartResolver
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容