Spring MVC 源码刨析

目录

一、刨析前准备

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

DispatcherServlet 继承结构

2、重要时机点分析

1)Handler 方法的执行时机

打断点

断点图

观察调用栈

调用栈

doDispathch 方法中的 1064 行代码完成 handler 方法的调用。

2)⻚面渲染时机(打断点并观察调用栈)

观察调用栈

3、Spring MVC 处理请求的流程

Spring MVC 处理请求的流程即为 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法的执行过程,其中步骤 2、3、4、5 是核心步骤(下面 4 中源码步骤)。

1)调用 getHandler() 获取到能够处理当前请求的执行链 HandlerExecutionChain(Handler + 拦截器)。
但是如何去 getHandler 的 ? 后面进行分析。
2)调用 getHandlerAdapter() 获取能够执行 (1) 中 Handler 的适配器。
但是如何去 getHandlerAdapter 的 ? 后面进行分析。
3)适配器调用 Handler 执行 ha.handle(总会返回一个 ModelAndView 对象)。
4)调用 processDispatchResult() 方法完成视图渲染跳转。

4、DispatcherServlet 类中的方法 doDispatch 源码

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);
            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);
        }
        // 5 跳转⻚面,渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, 
                mv, dispatchException);
    } catch (Exception ex) {
        // 最终会调用 HandlerInterceptor 的 afterCompletion 方法
        triggerAfterCompletion(processedRequest, response, mappedHandler, 
                ex);
    } catch (Throwable err) {
        // 最终会调用 HandlerInterceptor 的 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);
            }
        }
    }
}

二、源码跟踪刨析

1、核心步骤 getHandler 方法剖析

遍历两个 HandlerMapping,试图获取能够处理当前请求的执行链。

跟踪图

2、核心步骤 getHandlerAdapter 方法剖析

遍历各个 HandlerAdapter,看哪个 Adapter 支持处理当前 Handler。

跟踪图

3、核心步骤 ha.handle 方法剖析

1)入口

跟踪图

2)根据断点,跟踪源码

RequestMappingHandlerAdapter#handleInternal

@Override
protected ModelAndView handleInternal(HttpServletRequest request, 
        HttpServletResponse response, HandlerMethod handlerMethod) 
        throws Exception {

    ModelAndView mav;
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    // 判断当前是否需要支持在同一个 session 中只能线性地处理请求
    if (this.synchronizeOnSession) { // synchronizeOnSession : false
        // 获取当前请求的 session 对象
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 为当前 session 生成一个唯一的可以用于锁定的 key
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                // 对 HandlerMethod 进行参数等的适配处理,并调用目标 handler
                mav = invokeHandlerMethod(request, response, 
                        handlerMethod);
            }
        } else {
            // No HttpSession available -> no mutex necessary
            // 如果当前不存在 session,则直接对 HandlerMethod 进行适配
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        // No synchronization on session demanded at all...
        // 如果当前不需要对 session 进行同步处理,则直接对 HandlerMethod 进行适配
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod)
                .hasSessionAttributes()) {
            applyCacheSeconds(response, 
                    this.cacheSecondsForSessionAttributeHandlers);
        } else {
            prepareResponse(response);
        }
    }

    return mav;
}

RequestMappingHandlerAdapter#invokeHandlerMethod

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

ServletInvocableHandlerMethod#invokeAndHandle

// 对目标 handler 的参数进行处理,并且调用目标 handler
Object returnValue = invokeForRequest(webRequest, mavContainer, 
        providedArgs);
// 其他代码 略

InvocableHandlerMethod#invokeForRequest

@Nullable
public Object invokeForRequest(NativeWebRequest request, 
        @Nullable ModelAndViewContainer mavContainer, 
        Object... providedArgs) throws Exception {

    // 将 request 中的参数转换为当前 handler 的参数形式
    Object[] args = getMethodArgumentValues(request, mavContainer, 
            providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 这里 doInvoke() 方法主要是结合处理后的参数,使用反射对目标方法进行调用
    return doInvoke(args);
}

4、核心步骤 processDispatchResult 方法剖析

1)render 方法完成渲染。

DispatcherServlet#processDispatchResult

private void processDispatchResult(HttpServletRequest request, 
        HttpServletResponse response, 
        @Nullable HandlerExecutionChain mappedHandler, 
        @Nullable ModelAndView mv, @Nullable Exception exception) 
        throws Exception {

    boolean errorView = false;

    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", 
                    exception);
            mv = ((ModelAndViewDefiningException) exception)
                    .getModelAndView();
        } else {
            Object handler = (mappedHandler != null ? mappedHandler
                    .getHandler() : null);
            mv = processHandlerException(request, response, handler, 
                    exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 这里
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    } else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request)
            .isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

2)视图解析器解析出 View 视图对象。

DispatcherServlet#resolveViewName

跟踪图

3)在解析出 View 视图对象的过程中会判断是否重定向、是否转发等,不同的情况封装的是不同的 View 实现。

UrlBasedViewResolver#createView

@Override
protected View createView(String viewName, Locale locale) throws Exception {
    // If this resolver is not supposed to handle the given view,
    // return null to pass on to the next resolver in the chain.
    if (!canHandle(viewName, locale)) {
        return null;
    }

    // Check for special "redirect:" prefix.
    if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
        String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
        RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
        String[] hosts = getRedirectHosts();
        if (hosts != null) {
            view.setHosts(hosts);
        }
        return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
    }

    // Check for special "forward:" prefix.
    if (viewName.startsWith(FORWARD_URL_PREFIX)) {
        String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
        InternalResourceView view = new InternalResourceView(forwardUrl);
        return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
    }

    // Else fall back to superclass implementation: calling loadView.
    // 跟踪到这里
    return super.createView(viewName, locale);
}

4)解析出 View 视图对象的过程中,要将逻辑视图名解析为物理视图名。

UrlBasedViewResolver#buildView

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
    Class<?> viewClass = getViewClass();
    Assert.state(viewClass != null, "No view class");

    AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils
            .instantiateClass(viewClass);
    // 逻辑视图名转换为物理视图名,解析出物理视图名(拼接前缀和后缀)。
    view.setUrl(getPrefix() + viewName + getSuffix());
    view.setAttributesMap(getAttributesMap());

    String contentType = getContentType();
    if (contentType != null) {
        view.setContentType(contentType);
    }

    String requestContextAttribute = getRequestContextAttribute();
    if (requestContextAttribute != null) {
        view.setRequestContextAttribute(requestContextAttribute);
    }

    Boolean exposePathVariables = getExposePathVariables();
    if (exposePathVariables != null) {
        view.setExposePathVariables(exposePathVariables);
    }
    Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
    if (exposeContextBeansAsAttributes != null) {
        view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
    }
    String[] exposedContextBeanNames = getExposedContextBeanNames();
    if (exposedContextBeanNames != null) {
        view.setExposedContextBeanNames(exposedContextBeanNames);
    }

    return view;
}

5)封装 View 视图对象之后,调用了 view 对象的 render 方法。

DispatcherServlet#render 方法中。

try {
    if (mv.getStatus() != null) {
        response.setStatus(mv.getStatus().value());
    }
    // 在这儿。
    view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
    if (logger.isDebugEnabled()) {
        logger.debug("Error rendering view [" + view + "]", ex);
    }
    throw ex;
}

6)渲染数据。

@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, 
        HttpServletResponse response) throws Exception {

    if (logger.isDebugEnabled()) {
       logger.debug("View " + formatViewName() + ", model " + 
               (model != null ? model : Collections.emptyMap()) + 
               (this.staticAttributes.isEmpty() ? "" : ", static attributes " + 
               this.staticAttributes));
    }

    Map<String, Object> mergedModel = createMergedOutputModel(model, request, 
            response);
    prepareResponse(request, response);
    // 跟踪到这。
    renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

7)把 modelMap 中的数据暴露到 request 域中,这也是为什么后台 model.add 之后在 jsp 中可以从请求域取出来的根本原因。

InternalResourceView#renderMergedOutputModel

@Override
protected void renderMergedOutputModel(Map<String, Object> model, 
        HttpServletRequest request, HttpServletResponse response) 
        throws Exception {
    // Expose the model object as request attributes.
    exposeModelAsRequestAttributes(model, request);

    // Expose helpers as request attributes, if any.
    // 跟踪到这里。
    exposeHelpers(request);

    // Determine the path for the request dispatcher.
    String dispatcherPath = prepareForRendering(request, response);

    // Obtain a RequestDispatcher for the target resource (typically a JSP).
    RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
    if (rd == null) {
        throw new ServletException("Could not get RequestDispatcher for [" 
                + getUrl() + "]: Check that the corresponding file exists" 
                + " within your web application archive!");
    }

    // If already included or response already committed, perform include, 
    // else forward.
    if (useInclude(request, response)) {
        response.setContentType(getContentType());
        if (logger.isDebugEnabled()) {
            logger.debug("Including [" + getUrl() + "]");
        }
        rd.include(request, response);
    } else {
        // Note: The forwarded resource is supposed to determine 
        // the content type itself.
        if (logger.isDebugEnabled()) {
            logger.debug("Forwarding to [" + getUrl() + "]");
        }
        rd.forward(request, response);
    }
}

8)将数据设置到请求域中。

protected void exposeModelAsRequestAttributes(Map<String, Object> model, 
        HttpServletRequest request) throws Exception {

    model.forEach((name, value) -> {
        if (value != null) {
            request.setAttribute(name, value);
        } else {
            request.removeAttribute(name);
        }
    });
}

三、Spring MVC 九大组件初始化

1、在 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 等都是接口。

2、九大组件的初始化时机

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

@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);
    // 初始化 FlashMap 管理器
    initFlashMapManager(context);
    // 初始化视图转换器
    initViewResolvers(context);
}

2)观察其中的一个组件的初始化 initHandlerMappings(context)。

DispatcherServlet#initHandlerMappings

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, 
        // including ancestor contexts.
        // 按照 HandlerMapping.class 类型去 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");
        }
    }
}

3)如果按照类型和按照固定 id 从 ioc 容器中找不到对应组件,则会按照默认策略进行注册初始化,默认策略在 DispatcherServlet.properties 文件中配置。

DispatcherServlet#getDefaultStrategies

@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, 
        Class<T> strategyInterface) {
    String key = strategyInterface.getName();
    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<>();
    }
}

DispatcherServlet.properties

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
   org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
   org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
   org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
   org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

4)注意:多部件解析器的初始化必须按照 id 注册对象(multipartResolver)。

DispatcherServlet#initMultipartResolver

private void initMultipartResolver(ApplicationContext context) {
    try {
        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");
        }
    }
}

文章内容输出来源:拉勾教育Java高薪训练营;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jason559

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值