ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

DispatcherServlet类源码分析

2022-09-04 02:02:18  阅读:251  来源: 互联网

标签:分析 null mappedHandler request 源码 ex context DispatcherServlet response


DispatcherServlet类结构图

DispatcherServlet类结构图

 

DispatcherServlet源码分析

1. 加载配置文件

/** 
 * This implementation calls {@link #initStrategies}. 
 */  
@Override  
protected void onRefresh(ApplicationContext context) {  
	initStrategies(context);  
}  

/** 
 * 初始化定位解析器、主题解析器、处理器映射器、处理器适配器、异常解析器、视图解析器等等
 */  
protected void initStrategies(ApplicationContext context) {  
	initMultipartResolver(context);  
	initLocaleResolver(context);  
	initThemeResolver(context);  
	initHandlerMappings(context);  
	initHandlerAdapters(context);  
	initHandlerExceptionResolvers(context);  
	initRequestToViewNameTranslator(context);  
	initViewResolvers(context);  
	initFlashMapManager(context);  
} 

initStrategies()方法我们可以看出DispatcherServlet实例化时会初始化web层相关的bean,如HandlerMapping,HandlerAdapter等,并且如果我们没有进行配置,DispatcherServlet会提供默认的配置。以上的Servlet的体系结构以及DispatcherServlet的实例化过程我们可以看出主要完成以下几个事情:

 

(1)通过配置Servlet实现SpringMVC核心控制器DispatcherServlet的初始化;

 

(2)通过ServletContext共享Spring根上下文,使得每一个Servlet实例获取根上下文中的bean,用于实例化SpringMVC web层的相关bean。

(3)初始化DispatcherServlet作为核心控制器,接收处理请求需要的相关资源,如HandlerMapping,HandlerAdapter等。

(4)通过Servlet体系结构中的继承关系以及抽象方法,可以根据具体的需求对各个层级的Servlet抽象方法进行重写以满足不同的功能需要,父类中只定义流程和方法引用,具体实现由子Servlet完成,实现定义与实现的分离,便于扩展。

2. processRequest()方法

@Override  
protected final void doGet(HttpServletRequest request, HttpServletResponse response)  
        throws ServletException, IOException {  
    processRequest(request, response);  
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)  
        throws ServletException, IOException {  
  
    long startTime = System.currentTimeMillis();  
    Throwable failureCause = null;  
  
    // Expose current LocaleResolver and request as LocaleContext.  
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();  
    LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);  
  
    // Expose current RequestAttributes to current thread.  
    RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();  
    ServletRequestAttributes requestAttributes = null;  
    if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {  
        requestAttributes = new ServletRequestAttributes(request);  
        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);  
    }  
  
    if (logger.isTraceEnabled()) {  
        logger.trace("Bound request context to thread: " + request);  
    }  
  
    try {  
        doService(request, response);  
    }  
    catch (ServletException ex) {  
        failureCause = ex;  
        throw ex;  
    }  
    catch (IOException ex) {  
        failureCause = ex;  
        throw ex;  
    }  
    catch (Throwable ex) {  
        failureCause = ex;  
        throw new NestedServletException("Request processing failed", ex);  
    }  
  
    finally {  
        // Clear request attributes and reset thread-bound context.  
        LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);  
        if (requestAttributes != null) {  
            RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);  
            requestAttributes.requestCompleted();  
        }  
        if (logger.isTraceEnabled()) {  
            logger.trace("Cleared thread-bound request context: " + request);  
        }  
  
        if (logger.isDebugEnabled()) {  
            if (failureCause != null) {  
                this.logger.debug("Could not complete request", failureCause);  
            }  
            else {  
                this.logger.debug("Successfully completed request");  
            }  
        }  
        if (this.publishEvents) {  
            // Whether or not we succeeded, publish an event.  
            long processingTime = System.currentTimeMillis() - startTime;  
            this.webApplicationContext.publishEvent(  
                    new ServletRequestHandledEvent(this,  
                            request.getRequestURI(), request.getRemoteAddr(),  
                            request.getMethod(), getServletConfig().getServletName(),  
                            WebUtils.getSessionId(request), getUsernameForRequest(request),  
                            processingTime, failureCause));  
        }  
    }  
}

DispatcherServlet也是通过自己的service()方法来接收和转发Http请求到具体的doGet()或doPost()这些方法的。以一次典型的GET请求为例,经过HttpServlet基类中service()方法的委派,请求会被转发到doGet()方法中。doGet()方法,在DispatcherServlet的父类FrameworkServlet类中被覆写。

 

processRequest()方法理解的要点是以doService()方法为区隔,前一部分是将当前请求的Locale对象和属性,分别设置到LocaleContextHolder和RequestContextHolder这两个抽象类中的ThreadLocal对象中,也就是分别将这两个东西和请求线程做了绑定。在doService()处理结束后,再恢复回请求前的LocaleContextHolder和RequestContextHolder,也即解除线程绑定。每次请求处理结束后,容器上下文都发布了一个ServletRequestHandledEvent事件,你可以注册监听器来监听该事件。
可以看到,processRequest()方法只是做了一些线程安全的隔离,真正的请求处理,发生在doService()方法中。

3. doService()方法

@Override  
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
	if (logger.isDebugEnabled()) {  
		String requestUri = urlPathHelper.getRequestUri(request);  
		logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +  
				" request for [" + requestUri + "]");  
	}  

	// 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)) {  
		logger.debug("Taking snapshot of request attributes before include");  
		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.  
	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));  
	}  
	request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());  
	request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);  

	try {  
		doDispatch(request, response); //这边最终也是调用了doDispatch方法,该方法主要用来处理SPring框架的具体业务分发逻辑。  
	}  
	finally {  
		// Restore the original attribute snapshot, in case of an include.  
		if (attributesSnapshot != null) {  
			restoreAttributesAfterInclude(request, attributesSnapshot);  
		}  
	}  
}

doService()方法中requet.setAttribute()方法的调用,将前面在初始化流程中实例化的对象设置到http请求的属性中,供下一步处理使用,其中有容器的上下文对象、本地化解析器等SpringMVC特有的编程元素。不同于Struts2中的ValueStack,SpringMVC的数据并没有从HttpServletRequest对象中抽离出来再存进另外一个编程元素,这也跟SpringMVC的设计思想有关。因为从一开始,SpringMVC的设计者就认为,不应该将请求处理过程和Web容器完全隔离。所以,真正发生请求转发的方法doDispatch()中,它的参数是HttpServletRequest和HttpServletResponse对象。

4. doDispatch()方法

//Spring框架最终的分发都是通过该方法的  
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
	HttpServletRequest processedRequest = request;  
	HandlerExecutionChain mappedHandler = null;  
	int interceptorIndex = -1;  

	try {  
		ModelAndView mv;  
		boolean errorView = false;  

		try {  
			processedRequest = checkMultipart(request);  

			// Determine handler for the current request.  
			mappedHandler = getHandler(processedRequest, false);  
			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()) {  
					String requestUri = urlPathHelper.getRequestUri(request);  
					logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);  
				}  
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
					return;  
				}  
			}  

			// 这里是处理前置拦截器  
			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();  
			if (interceptors != null) {  
				for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } //处理最终的Action逻辑 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // Do we need view name translation? if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } //处理后置拦截器 if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) {  
					HandlerInterceptor interceptor = interceptors[i];  
					interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);  
				}  
			}  
		}  
		catch (ModelAndViewDefiningException ex) {  
			logger.debug("ModelAndViewDefiningException encountered", ex);  
			mv = ex.getModelAndView();  
		}  
		catch (Exception ex) {  
			Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
			mv = processHandlerException(processedRequest, response, handler, ex);  
			errorView = (mv != null);  
		}  

		// Did the handler return a view to render?  
		if (mv != null && !mv.wasCleared()) {  
			render(mv, processedRequest, response);  
			if (errorView) {  
				WebUtils.clearErrorRequestAttributes(request);  
			}  
		}  
		else {  
			if (logger.isDebugEnabled()) {  
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +  
						"': assuming HandlerAdapter completed request handling");  
			}  
		}  

		// Trigger after-completion for successful outcome.  
		triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  
	}  

	catch (Exception ex) {  
		// Trigger after-completion for thrown exception.  
		triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
		throw ex;  
	}  
	catch (Error err) {  
		ServletException ex = new NestedServletException("Handler processing failed", err);  
		// Trigger after-completion for thrown exception.  
		triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
		throw ex;  
	}  

	finally {  
		// Clean up any resources used by a multipart request.  
		if (processedRequest != request) {  
			cleanupMultipart(processedRequest);  
		}  
	}  
}

doDispatch()是整个请求转发流程中最核心的方法,DispatcherServlet所接收的Http请求,经过层层转发,最终都是汇总到这个方法中来进行最后的请求分发和处理。它通过高度抽象的接口,描述出了一个MVC(Model-View-Controller)设计模式的实现方案。Model、View、Controller三种层次的编程元素,在SpringMVC中都有大量的实现类,各种处理细节也是千差万别。但是,它们最后都是由,也都能由doDispatch()方法来统一描述,这就是接口和抽象的威力,万变不离其宗。

标签:分析,null,mappedHandler,request,源码,ex,context,DispatcherServlet,response
来源: https://www.cnblogs.com/bkycnd/p/16654132.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有