Spring MVC的工作原理,我们来看看其源码实现

  • 时间:
  • 浏览:1

前言

  开心一刻

    晚上陪老丈人吃饭,总是手机响了,我手贱按了免提……哥们:快出来喝酒!哥哪几个前会 呢!我:今天不行,我现在陪老丈人吃饭呢。哥们:那你抓紧喝,我三杯茅台散清香型散装茅台酱香型高度散装白酒,把我岳父放倒了才出来的,你也快点。看着我老丈人的脸,我不在乎 该咋样在么在回了……

猪一样的队友

遗留哪些地方的问题

  在关于利用maven搭建ssm的博客,亲戚亲戚另一其他人另一其他人 一同来探讨下问的最多的哪些地方的问题中,我遗留了另一一5个多 哪些地方的问题:Spring mvc是好久、何地、咋样将Model中的属性绑定到哪个作用域,这里的作用域指的是Servlet的四大作用域;不了解哪些地方的问题背景的可不才能回过头去看看我的上篇博文。

  明确的解答我会倒进最后,在解答哪些地方的问题完后 ,我先和亲戚亲戚另一其他人另一其他人 一同来捋一捋Spring mvc的工作原理。废话太满说,现在开始了了亲戚亲戚另一其他人另一其他人 神秘的探险之旅!

应用示例

  在讲工作原理完后 ,亲戚亲戚另一其他人另一其他人 先看另一一5个多 简单的spring mvc(ssm)示例,以及实现的效果

  工程代码地址:ssm-web 

  工程形状与效果如上所示,亲戚亲戚另一其他人另一其他人 不做太满的探究,亲戚亲戚另一其他人另一其他人 打起精神往下看本篇的重点

工作原理

  准备 - 资源的加载与初始化

    1、DispatcherServlet 静态初始化

      DispatcherServlet暗含如下静态块

static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
        }
    }

      这里会将DispatcherServlet.properties中的内容读取到DispatcherServlet的属性:private static final Properties defaultStrategies中,DispatcherServlet.properties内容如下

      指定了DispatcherServlet策略接口的默认实现,后续DispatcherServlet初始化策略的完后 会用到

    2、interceptor定义的加载

      spring启动过程中会调用InterceptorsBeanDefinitionParser的parse法子来解挥发性亲戚亲戚另一其他人另一其他人 自定义的interceptor定义,封装成MappedInterceptor类型的bean定义,并倒进spring容器中;亲戚亲戚另一其他人另一其他人 可不才能简单的认为spring容器中咋样让占据 了亲戚亲戚另一其他人另一其他人 自定义的interceptor的bean定义

    3、DispatcherServlet初始化策略:initStrategies

      DispatcherServlet的继承图如下

      DispatcherServlet是另一一5个多 Servlet,tomcat启动过程中会调用其init法子,一串的调用后,会调用DispatcherServlet的initStrategies法子

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

      实例化步骤1中的默认实现,并填充到DispatcherServlet各个属性值中

    4、DefaultAnnotationHandlerMapping的拦截器初始化

      DispatcherServlet.properties种指定了另一一5个多 默认的HandlerMapping:BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping,这两者的类继承图如下(亲戚亲戚另一其他人另一其他人 暂时只关注DefaultAnnotationHandlerMapping)

      DefaultAnnotationHandlerMapping间接实现了ApplicationContextAware,如此 在DefaultAnnotationHandlerMapping实例初始化过程中,会调用setApplicationContext(ApplicationContext applicationContext)法子,一串调用后,会来到AbstractUrlHandlerMapping的initApplicationContext()

@Override
protected void initApplicationContext() throws BeansException {
    extendInterceptors(this.interceptors);
    detectMappedInterceptors(this.mappedInterceptors);
    initInterceptors();
}

      初始化了DefaultAnnotationHandlerMapping的拦截器:interceptor

    亲戚亲戚另一其他人另一其他人 来看下具体的初始化过程,看看上方的顺序与非 只是此人 的臆想?

    可不才能看了,初始化顺序只是亲戚亲戚另一其他人另一其他人 上方说的,前会 我此人 的意淫;此时的DefaultAnnotationHandlerMapping暗含亲戚亲戚另一其他人另一其他人 自定义的MyInterceptor。初始化过程亲戚亲戚另一其他人另一其他人 可不才能关注的只是上述哪些地方地方,下面亲戚亲戚另一其他人另一其他人 一同看看具体请求的过程

  请求的避免

    请求从servlet的service现在开始了了,一路到DispatcherServlet的doDispatch,如下图

    doDispatch

/**
 * Process the actual dispatching to the handler. 将请求架构设计

到具体的handler,也只是亲戚亲戚另一其他人另一其他人

的controller
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
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. 决定哪个handler来避免当前的请求
            // mappedHandler是由handler和interceptor集合组成的另一一5个多

执行链,不得劲类似FilterChain
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request. 决定哪个adapter来避免当前的请求
            // handlerMapping是找出适配的handler,而真正回调handler的是adapter
            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()) {
                    String requestUri = urlPathHelper.getRequestUri(request);
                    logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            // handler的前置避免,也只是调用适配当前url的interceptor的preHandler法子
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            try {
                // Actually invoke the handler. 真正调用handler
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }

            applyDefaultViewName(request, mv);
            // handler的后置避免,也只是调用适配当前url的interceptor的postHandler法子
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        // 避免handler返回的结果,会调用适配当前url的interceptor的afterCompletion法子
        // 这里会将响应结果返回给请求者
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Error err) {
        triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            return;
        }
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
        }
    }
}
View Code

      handlerMapping具体咋样找到匹配当前url的handler(一般而言只是亲戚亲戚另一其他人另一其他人 的controller)、handlerAdapter具体咋样回调真正的handler,有兴趣的可不才能自行去跟下,让人不跟了。亲戚亲戚另一其他人另一其他人 具体看下processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 这些与亲戚亲戚另一其他人另一其他人 最初的哪些地方的问题有关

    processDispatchResult

      可不才能看了model中的persons会被设置到request的attributes中,咋样让转发请求到show_person.jsp,转发过程中request作用域的变量仍然有效,统统show_person.jsp中的jstl标签和el表达式才能取到persons变量,最后将show_person.jsp中的内容填充好完后 的静态内容返回给请求者;至此就完成了一次请求的响应

哪些地方的问题解答

  回到亲戚亲戚另一其他人另一其他人 开篇的哪些地方的问题:Spring mvc是好久、何地、咋样将Model中的属性绑定到哪个作用域?想必亲戚亲戚另一其他人另一其他人 咋样让知道答案了

  Controller中的model、ModelMap的注入由spring mvc完成,这些前会 请求传入的参数,用于绑定变量到Servlet作用域;默认情况报告下,在DispatcherServlet调用了真正的handler完后 ,将结果返回给请求者的过程中,将model、modelMap中的变量设置到了request的attributes中,转发的过程中,request中的变量仍然有效,统统show_person.jsp中能取到persons这些变量,自此哪些地方的问题得到解答

总结

  1、Spring MVC工作原理图

    图是用的别人的,具体是谁的让人是记得了(捂脸)

  2、DefaultAnnotationHandlerMapping在spring3.2中被废弃,替加上了RequestMappingHandlerMapping