• SpringMVC流程源码分析及DispatcherServlet核心源码


    一、源码分析前还是需要一张流程图作为指导,如下:


    二、简单介绍以及源码定位

        DispatcherServlet其实就是一个HttpServlet,他是HttpServlet的子类,所以它和普通的HttpServlet在web.xml里同样的配置。

        这个Servlet的doPost和doGet方法的实现是DispatcherServlet的父类FrameworkServlet中实现的,两个方法里都是调用processRequest方法。processRequest的实现是在FrameworkServlet中,此方法中最主要的操作就是调用doService方法。

        doService方法的最终实现是在DispatcherServlet中,这样所有的Http请求(GET、POST、PUT和DELETE等)的最终操作就DispatcherServlet中实现了。

        DispatcherServlet中doService的实现如下,对Request设置了一些全局属性,最终接下来的操作是在doDispatcher函数中实现了。

    [java] view plain copy
    //获取请求,设置一些request的参数,然后分发给doDispatch  
        @Override  
        protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
            if (logger.isDebugEnabled()) {  
                String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";  
                logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +  
                        " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");  
            }  
      
            // Keep a snapshot of the request attributes in case of an include,  
            // to be able to restore the original attributes after the include.  
            Map<String, Object> attributesSnapshot = null;  
            if (WebUtils.isIncludeRequest(request)) {  
                attributesSnapshot = new HashMap<String, Object>();  
                Enumeration<?> attrNames = request.getAttributeNames();  
                while (attrNames.hasMoreElements()) {  
                    String attrName = (String) attrNames.nextElement();  
                    if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {  
                        attributesSnapshot.put(attrName, request.getAttribute(attrName));  
                    }  
                }  
            }  
      
            // Make framework objects available to handlers and view objects.  
            /* 设置web应用上下文**/  
            request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  
            /* 国际化本地**/  
            request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);  
            /* 样式**/  
            request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  
            //设置样式资源  
            request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());  
            //请求刷新时保存属性  
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);  
            if (inputFlashMap != null) {  
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));  
            }  
            //Flash attributes 在对请求的重定向生效之前被临时存储(通常是在session)中,并且在重定向之后被立即移除  
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());  
            //FlashMap 被用来管理 flash attributes 而 FlashMapManager 则被用来存储,获取和管理 FlashMap 实体.  
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);  
      
            try {  
                doDispatch(request, response);  
            }  
            finally {  
                if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {  
                    // Restore the original attribute snapshot, in case of an include.  
                    if (attributesSnapshot != null) {  
                        restoreAttributesAfterInclude(request, attributesSnapshot);  
                    }  
                }  
            }  
        }  
    


    doDispatch()方法源码如下:

    /**
    	 * Process the actual dispatching to the handler.
    	 * <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.
    				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);
    				}
    			}
    		}
    	}

    三、通过阅读源码总结分析流程如下:

    1. web应用服务器接收到一个新请求是,读取web.xml中的配置,如果匹配DispatcherServlet的请求映射路径,web容器将该请求转发给DispatcherServlet进行处理 


    2. DispatcherServlet接收到请求后,执行doDispatch方法,此方法遍历DispatcherServlet中的HandlerMapping(处理器映射器)集合,根据请求的HttpServletRequest信息通过HandlerMapping对象方法找到HandlerExecutionChain(执行链,内含拦截器和处理器)


    3. DispatcherServlet继续执行doDispatch方法,根据得到的HandlerExecutionChain(执行链)中的handler遍历自己的HandlerAdapter(处理器适配器)集合,找到支持这个HandlerHandlerAdapter并返回。

     

    4.继续doDispatch方法,把HandlerExecutionChain(执行链)内部的那些前置拦截器逻辑都执行完,然后再再通过得到的HandlerAdapter执行HandlerExecutionChain内部的处理器,会返回一个ModelAndView包含了视图逻辑名和模型数据信息 

     

    5.调用执行链的方法,执行拦截器的后置拦截器


    6. ModelAndView中包含的是逻辑视图名,而非真正的视图对象,DispatcherServlet借助ViewResolver完成逻辑视图名到真实视图名对象的解析工作 


    7. 当得到真实的视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染 


    8. 最终客户端得到HTML页面什么的


  • 相关阅读:
    FZU-Problem 2150 Fire Game
    LeetCode120——Triangle
    Coder-Strike 2014
    AP INVOICES IMPORT API(NOT request)
    NYOJ-277-车牌号
    软件測试方法
    C++中字符数组和字符串string
    【机器学习算法-python实现】PCA 主成分分析、降维
    主题讲座:移动互联网时代的创业机会
    ubuntu环境eclipse配置
  • 原文地址:https://www.cnblogs.com/chz-blogs/p/9380919.html
Copyright © 2020-2023  润新知