zoukankan      html  css  js  c++  java
  • spring MVC mybatis dispacherServlet(源码解读)

    以下源码版本(4.2.0.RELEASE)

    dispacherServlet是servlet的实现类,是spring MVC的前端转发器,是spring MVC的核心。

    那么它做了哪些事呢?

    它主要做了两件事:

    NO1:

    看如下源码:

    /**
         * Initialize the strategy objects that this servlet uses.
         * <p>May be overridden in subclasses in order to initialize further strategy objects.
         */
    protected void initStrategies(ApplicationContext context) {  
              //初始化分段上传解析器(成对文件上传的解析和封装工作),没有默认的实现    
              initMultipartResolver(context);  
              //初始化地域解析器,默认实现是AcceptHeaderLocaleResolver  
              initLocaleResolver(context);  
               //初始化主题解析器,默认实现是FixedThemeResolver  
              initThemeResolver(context);  
               //初始化处理器映射,这是个集合, 默认实现是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping   
              initHandlerMappings(context);  
               //初始化处理器适配器,这是个集合,默认实现是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter    
              initHandlerAdapters(context);  
                //初始化处理器异常解析器,这是个集合,默认实现是AnnotationMethodHandlerExceptionResolver,ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver   
              initHandlerExceptionResolvers(context);  
              //初始化请求到视图名解析器,默认实现是DefaultRequestToViewNameTranslator    
              initRequestToViewNameTranslator(context);  
              //初始化视图解析器,这是个集合,默认实现是InternalResourceViewResolver    
              initViewResolvers(context);  
         }  

    NO2:

    每次请求都会调用它的doService方法,在doService方法中调用它的doDispatch方法。

    doService方法可看代码中的两处注释:

     重点在于调用它的doDispatch方法:

    doDispatch(request, response);

     首先我们来看方法doDispatch的注释:

    可以简单理解成:所有的请求都将在这个方法里映射到指定的action的指定的方法(前提:请求路径正确)。

    我们来一步一步解析这个方法:

    以下,为

    doDispatch方法的源码:
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // processedRequest是经过checkMultipart方法处理过的request请求
            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.
                    mappedHandler = getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == 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;
                    }
    
                    // 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;
                }
                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
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                }
                else {
                    // Clean up any resources used by a multipart request.
                    if (multipartRequestParsed) {
                        cleanupMultipart(processedRequest);
                    }
                }
            }
        }

     第一行新建的变量processedRequest 会在

     后面某一行(在doDispatch方法中,如下:)

    processedRequest = checkMultipart(request);

     中被方法处理加工,checkMultipart方法也是dispacherServlet类中的一个方法,源码如下:

     那么这个方法的作用是什么呢?

    关键是下面这一句:

    this.multipartResolver != null && this.multipartResolver.isMultipart(request)

     这一句代码做了如下两个判断:

    1.dispatcherServlet中初始化后的multipartResolver是否为空(配置文件中是否配置,初始化dispatcherServlet时没有默认实例);

    2.multipartResolver的方法isMultipart的作用是判断本次请求是否为文件上传。

    如下为isMultipart的源码:

    通过注释,可简单理解为:该方法通常以检查content-type是否为multipart/form-data的方式判断本次请求是否为文件上传,但实际功能需要看具体解析器对该方法的实现细节。

    checkMultipart方法大致如此,再具体一些的内容在这里我就不赘述了,有兴趣的朋友可以自己看源码。

     (多嘴一句:若想使用multipartResolver不要忘了增加jar包支持:commons.fileupload,common.io).

    ---------------------------------------

    方法doDispatch的第二行新建了变量mappedHandler ,

    类型为HandlerExecutionChain ,通过查看该类的源码,可清楚其作用,如下:

    可理解其为一个处理链,包含了所有的handler和拦截器。

     以下三行代码为doDispatch的核心:

    // Determine handler for the current request.
                    mappedHandler = getHandler(processedRequest);
    // Determine handler adapter for the current request.
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

     通过注释,可理解前一句的作用:找到指定的handler,

    后一句:找到对应的适配器adapter,

    第三句:在这个函数里面会真正执行request请求相对于handler的方法(这只是大概的代码流程,真正的调用方法之前还有很多先前处理。)

    以下是我debug断点的显示数据:

    该方法时DispatcherServlet类中的方法getHandler,可以看到这里会对所有handlerMapper进行遍历,后执行HandlerMappering的getHandler方法,

    如下:为HandlerMappering的getHandler方法:

    这个方法会返回一个handler和所有的拦截器,组装成了一个HandlerExecutionChain类。也就是在方法doDispatch第二行代码里定义的mappedHandler,

    该方法的具体实现是在org.springframework.web.servlet.handler.AbstractHandlerMapping中,如下:

        /**
         * Look up a handler for the given request, falling back to the default
         * handler if no specific one is found.
         * @param request current HTTP request
         * @return the corresponding handler instance, or the default handler
         * @see #getHandlerInternal
         */
        @Override
        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            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 = getApplicationContext().getBean(handlerName);
            }
    
            HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
                CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
                executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
            }
            return executionChain;
        }

    这里的getHandlerInternal是个抽象方法,有具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,若为string类型,则表示要去spring配置容器中去找这个bean,

    该方法剩下的部分正如HandlerMappering的getHandler方法注释所描述的:返回一个handler和所有的拦截器(有兴趣可深入了解)。

     --------------------------

     再来看doDispath方法里的

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    它首先调用了dispatcherServlet里的 getHandlerAdapter方法;看其源码如下:

     

    看其注释:该方法会通过已确定映射的handler类来寻找满足条件的adapter,(注意,若没有找到,会直接报servletException异常)。

    这个方法的核心是 方法supports,这是个抽象方法,不同的适配器会有不同的内部细节,如下分别为

    SimpleControllerHandlerAdapter和SimpleServletHandlerAdapter的实现:

    
    

    通过supports方法,dispatcherervlet类可以确定本次请求request映射的adapter.

    ------------------------

    最后看

    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    这里的mv是一个ModelAndView类,ha是前面获得的适配器adapter,

    方法handler()是个抽象方法,具体实现细节依赖于适配器adapter的实现类,

    但我门可看其注释,理解该方法的作用,
    源码如下:

    其注释的意思是是:使用提供的handler(前面确定的handler)来处理这个request请求。

    以下,已实现类SimpleControllerHandlerAdapter为例,

    这里会执行handlerRequest(该方法的实现细节依赖于不同的实现类,最后返回一个ModelAndAndView。

    以上,为我本次研读dispatcherServlet类的所得。

    可总结为,dispatcherServlet根据默认或配置的handlermapping和adapter,选择不同的方式对请求request请求进行处理,最后返回一个视图ModeAndView。

    (那么返回值为json是如何实现的?····问题越来越多了)

    ----------------------------------补充----------------------------

    可以看出,关于

    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    部分,阐述的比较模糊,或者说,基本没什么描述,虽然这部分是核心,

    我试图过阐述其实现细节,但这部分的实现细节根据不同的handlerMapping和不同的adpater而不同,

    所以,我准备在其他章节对其进行分别阐述。

  • 相关阅读:
    iOS开发 数据缓存-数据库
    我是程序猿,我自豪,我骄傲!嗷嗷嗷!
    【Golang】练习-复制文件
    【Golang】练习-读取目录下的文件并按时间进行排序
    【Golang】练习 tailf 简单模拟
    【Golang】基础-切片 for 循环删除元素
    【Golang】基础-操作 csv 文件
    【Golang】Godoc 或者第三方 plugins 插件介绍
    【Golang】类型转换之 cast 包
    【Golang】解决 go get下载包慢或者失败的问题
  • 原文地址:https://www.cnblogs.com/zqsky/p/6184436.html
Copyright © 2011-2022 走看看