转载

springMVC源码分析

本篇将涉及,通过本篇可以对整个springmvc模块有一个清晰的视角,在日常开发中可以快速定位问题和源码

  1. springMVC启动初始化过程分析
  2. 处理请求过程分析
  3. 参数解析、返回值处理

springMVC应用启动初始化过程分析

初始化过程应该分为三步:

  • 第一步,创建两个spring的ApplicationContext,配置DispatcherServlet、filter、ContextLoaderListener到servlet容器中
  • 第二步,servlet容器启动后,调用ContextLoaderListener#contextInitialized初始化必要的bean(如DAO、service相关的bean)
  • 第三步,调用DispatcherServlet#init方法进一步初始化必要的bean(如HandlerMappings、HandlerMappingAdapter等)

第一步

springMVC源码分析 主要完成以下工作:

  1. 通过配置初始化两个容器,分别为spring容器、springMVC容器。(关于为什么需要两个容器,个人认为是为了在两个DispatcherServlet中共享同一个spring容器)
  2. 注册DispatcherServlet到servlet容器中,对外提供服务,处理客户端请求
  3. 注册Filter到servlet容器中

第二步

springMVC源码分析 主要完成以下工作:

  1. 初始化父容器
  2. 绑定到servletContext,在应用中全局共享

第三步

在这一步中,分为两个阶段:

  1. 初始化属于springmvc的ApplicationContext、并添加SourceFilteringListener作为spring容器时间监听器
  2. 在完成初始化属于springmvc的ApplicationContext的工作后,SourceFilteringListener监听到容器时间,调用onApplicationEvent方法,完成剩余的初始化工作(包括初始化HandlerMappings、HandlerMappingAdapter等属于MVC领域的bean)

SourceFilteringListener的职责

先来看一下其类关系图

springMVC源码分析

SourceFilteringListener作为EventListener的实现类,在servlet的ApplicationContext初始化后,将收到事件回调完成剩余的初始化工作

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

在这些初始化方法中,注册到spring容器中的bean由注解@EnableWebMVC导入。

导入工作委托给了DelegatingWebMvcConfiguration类

以上就是springMVC的初始化过程的整体脉络

springMVC处理请求过程分析

整体流程

springMVC源码分析 整个过程相对比较复杂,整个逻辑在DispatcherServlet#doDispatcher方法中实现,该方法内部将具体工作委托给了RequestMappingHandlerMapping、HandlerExcutionChain、RequestMappingHandlerAdapter、ViewResolver、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 {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            // 获取HandlerExcutionChain
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            // 获取HandlerAdapter
            // Determine handler adapter for the current request.
            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()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            // 调用拦截器前置处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            // 真正调用HandlerMethod(即Controller方法),内部涉及了参数解析(处理@RequestParams、@PathVariables注解原理)、参数类型转换、HandlerMethod执行结果处理(@ResponseBody注解原理)这几个步骤。后面内容我们详细剖析这个函数的内部机制
            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            // 调用拦截器后置处理
            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) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        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);
            }
        }
    }
}

根据请求寻找对应的HandlerMethod

springMVC源码分析 具体寻找过程的代码细节如下:

  1. 首先mappingRegistry(类MappingRegistry的实例)实例变量存放了所有的HandlerMethod(在RequestMappingHandlerMapping#afterPropertiesSet()方法中初始化完成,以RequestMappingInfo实例保存在mappingRegistry.urlLookup实例变量中),之后在请求到来时通过请求URI从mappingRegistry获取
  2. 如果在使用路径参数@PathVariables的时候,需要遍历所有Controller方法(因为初始化的时候是以/user/{name}作为key保存在urlLookup变量中的,而实际的请求路径是/user/tom。无法通过this.mappingRegistry.getMappingsByUrl(lookupPath)直接获取到)。这里会涉及到Ant风格解析。O(n)的时间复杂度。效率极低
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // No choice but to go through all mappings...
        // 如果没有找到,则遍历所有Controller方法。这里会涉及到Ant风格解析。O(n)的时间复杂度。效率极低
        // 在使用路径参数@PathVariables的时候,会进入到这段逻辑
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        Collections.sort(matches, comparator);
        if (logger.isTraceEnabled()) {
            logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
                    lookupPath + "] : " + matches);
        }
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                        request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
            }
        }
        // 设置属性到ServletHttpRequest实例中,在解析@PathVariables时使用
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
	super.handleMatch(info, lookupPath, request);

    String bestPattern;
    Map<String, String> uriVariables;
    Map<String, String> decodedUriVariables;

    Set<String> patterns = info.getPatternsCondition().getPatterns();
    if (patterns.isEmpty()) {
        bestPattern = lookupPath;
        uriVariables = Collections.emptyMap();
        decodedUriVariables = Collections.emptyMap();
    }
    else {
        bestPattern = patterns.iterator().next();
        // 使用Ant风格解析路径参数
        uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
        decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
    }

    request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);

    if (isMatrixVariableContentAvailable()) {
        Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
        request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
    }

    if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
        Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
        request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
    }
}

HandlerMethod执行细节

springMVC源码分析

参数解析以及类型转换

主要是通过HandlerMethodArgumentResolverComposite这个特殊的HandlerMethodArgumentResolver聚合了多个HandlerMethodArgumentResolver来解析参数、其内部将调用RequestParamMethodArgumentResolver、PathVariableMapMethodArgumentResolver等来处理参数解析逻辑

返回结果处理

主要是通过HandlerMethodReturnValueHandlerComposite这个特殊的HandlerMethodReturnValueHandler聚合了多个HandlerMethodReturnValueHandler来处理返回结果,其内部将调用RequestResponseBodyMethodProcessor处理@ResponseBody、@RestController标记的方法和类来完成RESTful接口的结果返回

总结

以上就是整个springmvc的工作流程,包括了初始化、请求处理两个步骤。在请求处理的过程中又涉及到了参数解析、参数类型转换、返回结果处理这些流程。由于整个代码比较繁多,这里就不列举所有的流程(视图渲染、异常处理这些细节)通过本篇的主体流程图可以快速定位到具体的代码。后续有需要查看更多细节可参考本篇快速定位

原文  https://blog.luhuancheng.com/2018/12/30/springMVC源码分析/
正文到此结束
Loading...