DispatcherServlet类源码分析

  • DispatcherServlet类源码分析已关闭评论
  • 141 次浏览
  • A+
所属分类:Web前端
摘要

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


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()方法来统一描述,这就是接口和抽象的威力,万变不离其宗。