SpringMVC原始碼解析(上)

potato123發表於2009-05-22

1.從DispatcherServlet開始
     與很多使用廣泛的MVC框架一樣,SpringMVC使用的是FrontController模式,所有的設計都圍繞DispatcherServlet為中心來展開的。見下圖,所有請求從DispatcherServlet進入,DispatcherServlet根據配置好的對映策略確定處理的Controller,Controller處理完成返回ModelAndView,DispatcherServlet根據配置好的檢視策略確定處理的View,由View生成具體的檢視返回給請求客戶端。


2.初始化
    SpringMVC幾個核心的配置策略包括:
    *HandlerMapping:請求-處理器對映策略處理,根據請求生成具體的處理鏈物件
    *HandlerAdapter:處理器介面卡,由於最終的Handler物件不一定是一個標準介面的實現物件,引數也可能非常的靈活複雜,因此所有的物件需要一個合適的介面卡適配成標準的處理介面來最終執行請求
    *ViewResolver:檢視對映策略,根據檢視名稱和請求情況,最終對映到具體的處理View,由View物件來生成具體的檢視。
    其他的配置策略包括MultipartResolver、LocaleResolver、ThemeResolver等等,但並不影響我們對整個SpringMVC的工作原理的理解,此處並不具體說明。
1)初始化Context
     見下圖DispatcherServlet的繼承結構,其中,HttpServletBean主要功能是在初始化(init)時將servlet的配置引數(init-param)轉換成Servlet的屬性,FrameworkServlet主要功能是與ApplicationContext的整合,因此Context的初始化工作主要在FrameworkServlet中進行。

   
     Context初始化的過程可以通過如下過程來描述:HttServletBean.init --> FrameworkServlet.initServletBean --> FrameworkServlet.initWebApplicationContext。具體的初始化過程可見如下程式碼: 

     FrameworkServlet.initWebApplicationContext

	protected WebApplicationContext initWebApplicationContext() throws BeansException {
		WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = createWebApplicationContext(parent);

		if (!this.refreshEventReceived) {
			// Apparently not a ConfigurableApplicationContext with refresh support:
			// triggering initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (logger.isDebugEnabled()) {
				logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

     FrameworkServlet.createWebApplicationContext

	protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
			throws BeansException {

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet with name '" + getServletName() +
					"' will try to create custom WebApplicationContext context of class '" +
					getContextClass().getName() + "'" + ", using parent context [" + parent + "]");
		}
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) {
			throw new ApplicationContextException(
					"Fatal initialization error in servlet with name '" + getServletName() +
					"': custom WebApplicationContext class [" + getContextClass().getName() +
					"] is not of type ConfigurableWebApplicationContext");
		}

		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());
		wac.setParent(parent);
		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.setConfigLocation(getContextConfigLocation());
		wac.addApplicationListener(new SourceFilteringListener(wac, this));

		postProcessWebApplicationContext(wac);
		wac.refresh();

		return wac;
	}
 

2)初始化策略
   具體與SpringMVC相關的策略在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());
		}
	}

 

   我們可以看到,SpringMVC的策略在與DispatcherServlet同目錄的Dispatcher.properties檔案中配置,如下是Spring2.5的預設配置策略

 

Dispatcher.properties 寫道
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

    當然,我們可以變更其處理策略,通過上面部分,我們知道,FrameworkServlet實現了ApplicationListener,並在構建WebApplicationContext後,將自身(this)向WebApplicationContext註冊,因此WebApplicationContext初始化完畢之後,將傳送ContextRefreshedEvent事件,該事件實際上被DispatcherServlet處理,處理過程如下:

    FrameworkServlet.onApplicationEvent --> DispatcherServlet.onRefresh --> DispatcherServlet.initStrategies

    DispatcherServlet.initStrategies程式碼如下,具體處理過程可參見Spring原始碼

 

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

3.請求處理流程

    SpringMVC的請求處理從doService-->doDispatch為入口,實際上,我們只要緊緊抓住HandlerMapping、HandlerAdapter、ViewResolver這三個核心物件,SpringMVC的一整個執行機制看起來將非常簡單,其主要處理流程包括:

1)將請求對映到具體的執行處理鏈,見如下程式碼

 

                               // Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

   具體看一下getHandler是如何處理的

 

		protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
		HandlerExecutionChain handler =
				(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
		if (handler != null) {
			if (!cache) {
				request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
			}
			return handler;
		}

		Iterator it = this.handlerMappings.iterator();
		while (it.hasNext()) {
			HandlerMapping hm = (HandlerMapping) it.next();
			if (logger.isDebugEnabled()) {
				logger.debug("Testing handler map [" + hm  + "] in DispatcherServlet with name '" +
						getServletName() + "'");
			}
			handler = hm.getHandler(request);
			if (handler != null) {
				if (cache) {
					request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
				}
				return handler;
			}
		}
		return null;
	}

   可以看到,僅僅是遍歷每一個HandlerMapping,如果能夠其能夠處理,則返回執行處理鏈(HandleExecuteChain)

2)執行處理鏈的攔截器列表的preHandle方法,如果執行時返回false,表示該攔截器已處理完請求要求停止執行後續的工作,則倒序執行所有已執行過的攔截器的afterCompletion方法,並返回

 

				// Apply preHandle methods of registered interceptors.
				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;
					}
				}

 3)根據處理物件獲得處理器介面卡(HandlerAdapter),並由處理介面卡負責最終的請求處理,並返回ModelAndView(mv),關於處理器介面卡的作用,見第2部分的說明

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

   具體看getHandlerAdapter如何工作

 

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		Iterator it = this.handlerAdapters.iterator();
		while (it.hasNext()) {
			HandlerAdapter ha = (HandlerAdapter) it.next();
			if (logger.isDebugEnabled()) {
				logger.debug("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: Does your handler implement a supported interface like Controller?");
	}

    非常簡單,僅僅是依次詢問HandlerAdapter列表是否支援處理當前的處理器物件

4)倒序執行處理鏈攔截器列表的postHandle方法

 

				// Apply postHandle methods of registered interceptors.
				if (interceptors != null) {
					for (int i = interceptors.length - 1; i >= 0; i--) {
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}

5)根據ViewResolver獲取相應的View例項,並生成檢視響應給客戶端

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

    再看看render方法

 

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		// Determine locale for request and apply it to the response.
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);

		View view = null;

		if (mv.isReference()) {
			// We need to resolve the view name.
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		view.render(mv.getModelInternal(), request, response);
	}

6)倒序執行處理鏈攔截器列表的afterCompletion方法

	private void triggerAfterCompletion(
			HandlerExecutionChain mappedHandler, int interceptorIndex,
			HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {

		// Apply afterCompletion methods of registered interceptors.
		if (mappedHandler != null) {
			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
			if (interceptors != null) {
				for (int i = interceptorIndex; i >= 0; i--) {
					HandlerInterceptor interceptor = interceptors[i];
					try {
						interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
					}
					catch (Throwable ex2) {
						logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
					}
				}
			}
		}
	}
 

 

相關文章