SpringMVC 的实现过程

SpringMVC 的实现过程主要分为 2 个步骤,初始化阶段和调用阶段

  1. 初始化阶段,这个阶段主要是在 DispatcherServlet 初始化的时候调用,完成组件的初始化

  2. 调用阶段,这个阶段主要体现在请求过来的时候,通过 doDispatch 方法来完成调用

    具体实现时序图如下:

    image-20200903172942096

具体分析

  1. 首先我们知道 SpringMVC 的实现过程主要是通过 DispatcherServlet 来实现的,因为它一个 Servlet,那么必将遵守 Servlet 的生命周期,初始化调用 init 方法,我们在 DispatcherServlet 没有找到,然后往它的父类寻找,结果发现在它的祖父类里面找到对应的 init 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
    logger.debug("Initializing servlet '" + getServletName() + "'");
    }
    // Set bean properties from init parameters.
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
    try {
    //定位资源
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
    //加载配置信息
    ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
    initBeanWrapper(bw);
    bw.setPropertyValues(pvs, true);
    }
    catch (BeansException ex) {
    if (logger.isErrorEnabled()) {
    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    }
    throw ex;
    }
    }

    // Let subclasses do whatever initialization they like.
    initServletBean();

    if (logger.isDebugEnabled()) {
    logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
    }

    在父类中调用子类的 initServletBean 方法 ->initWebApplicationContext 方法

  2. initWebApplicationContext 方法的具体实现如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
    // A context instance was injected at construction time -> use it
    wac = this.webApplicationContext;
    if (wac instanceof ConfigurableWebApplicationContext) {
    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    if (!cwac.isActive()) {
    // The context has not yet been refreshed -> provide services such as
    // setting the parent context, setting the application context id, etc
    if (cwac.getParent() == null) {
    // The context instance was injected without an explicit parent -> set
    // the root application context (if any; may be null) as the parent
    cwac.setParent(rootContext);
    }
    configureAndRefreshWebApplicationContext(cwac);
    }
    }
    }
    if (wac == null) {
    // No context instance was injected at construction time -> see if one
    // has been registered in the servlet context. If one exists, it is assumed
    // that the parent context (if any) has already been set and that the
    // user has performed any initialization such as setting the context id
    wac = findWebApplicationContext();
    }
    if (wac == null) {
    // No context instance is defined for this servlet -> create a local one
    wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
    // Either the context is not a ConfigurableApplicationContext with refresh
    // support or the context injected at construction time had already been
    // refreshed -> trigger initial onRefresh manually here.
    onRefresh(wac);
    }

    if (this.publishContext) {
    // Publish the context as a servlet context attribute.
    String attrName = getServletContextAttributeName();
    getServletContext().setAttribute(attrName, wac);
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
    "' as ServletContext attribute with name [" + attrName + "]");
    }
    }

    return wac;
    }

    这里和 IOC 类似,调用了 onRefresh 方法,->initStrategies 方法

  3. initStrategies 方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    protected void initStrategies(ApplicationContext context) {
    //初始化文件上传组件
    initMultipartResolver(context);
    //初始化本地语言
    initLocaleResolver(context);
    //初始化主题组件
    initThemeResolver(context);
    //初始化所有的映射
    initHandlerMappings(context);
    //初始化处理器适配器
    initHandlerAdapters(context);
    //处理话异常处理器
    initHandlerExceptionResolvers(context);
    //初始化默认的视图解析
    initRequestToViewNameTranslator(context);
    //初始化视图解析器
    initViewResolvers(context);
    //初始化FLashMap,用于保存重定向传值
    initFlashMapManager(context);
    }

    以上完成了 MVC 的初始化阶段

  4. 现在来到 MVC 的调用阶段。根据 servlet 的规范,在执行请求的时候会调用 doService 方法,DispatchServlet 重写了 doservice 方法,调用了 doDispatch 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    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);

    //获取对应的handleMapper
    // Determine handler for the current request.
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
    }

    //获取对应的适配器
    // 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;
    }

    //调用对应的Controller中的method方法
    // 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);
    }
    }
    }
    }
  5. 细看获取 HandleMapper 方法,也就是 getHandler 方法,这个方法最终调用的是 AbstractHandlerMapping 的 getHandler 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //通过url找到对应的Controller里面的method,即对应的处理器handleMethod
    Object handler = getHandlerInternal(request);
    if (handler == null) {
    handler = getDefaultHandler();
    }
    if (handler == null) {
    return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
    String handlerName = (String) handler;
    handler = obtainApplicationContext().getBean(handlerName);
    }

    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    if (CorsUtils.isCorsRequest(request)) {
    CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
    CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
    return executionChain;
    }

    getHandlerInternal 方法得到对应的 handleMethod 处理器,getHandlerExecutionChain 得到所有匹配的拦截器链路 getHandlerExecutionChain 方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    //获取请求地址,是否满足拦截条件
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
    if (interceptor instanceof MappedInterceptor) {
    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
    if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
    chain.addInterceptor(mappedInterceptor.getInterceptor());
    }
    }
    else {
    chain.addInterceptor(interceptor);
    }
    }
    return chain;
    }
  6. 再回到 DispacherServlet 的 doDispacher 方法中,得到拦截器链路之后,获取到对应的 handleAdapter 适配器。然后应用前置拦截器,通过反射调用真正的处理器 Controller 里面的 method 返回对应的 modelAndView 视图对象,然后再配置默认视图,应用后置拦截,最后在 processDispatchResult 方法中调用 render 方法中完成结果渲染。