從spring容器啓動解析webmvc源碼

Spring webmvc啓動流程

本文將從spring容器啓動過程去分析springmvc源碼,首先我們思考一個問題:

web容器在啓動的時候是怎麼將bean解析並加載到spring容器中的,像我們自己如果要啓動容器,那是不是要new ApplicationContext(),並且把xml配置文件路徑或者包掃描路徑傳進去。那麼webmvc是怎麼解決這個問題的呢?

以tomcat爲例,我們的web應用程序是部署在tomcat的容器中的,那麼tomcat就爲我們的應用提供了一個全局的上下文環境,也就是ServletContext,spring的applicationContext最終也是注入到ServletContext當中的。我們在springmvc的開發過程中,在web.xml中經常會有如下配置:

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:application.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

web容器在啓動的時候會加載三個組件:listener >> filter >> servlet

而我們在web.xml中配置的一個很重要的listener就是ContextLoaderListener,這個監聽器實現了ServletContextListener,ServletContextListener它能夠監聽ServletContext對象的生命週期,也就是web應用的生命週期。在web容器啓動或者終止時會觸發ServletContextEvent事件,ServletContextListener監聽的就是這個事件,分別調用contextInitialized和contextDestroyed

也就是說容器啓動的時候會調用ContextLoaderListener的contextInitialized方法,ContextLoaderListener同時還繼承了ContextLoader

	// 初始化web容器上下文
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

轉到父類ContextLoader中的initWebApplicationContext,spring容器的初始化入口

// ContextLoader:

	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		// 這些都忽略,往下看重點
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		Log logger = LogFactory.getLog(ContextLoader.class);
		servletContext.log("Initializing Spring root WebApplicationContext");
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				// 這裏就是重點了,創建applicationContext,默認爲xmlWebApplicationContext
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					// 重點,就是在這一步加載的spring 配置,
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
// 在這裏將applicationContext注入到servletContext當中	servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
			}
			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
		catch (Error err) {
			logger.error("Context initialization failed", err);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
			throw err;
		}
	}

我們看一下創建spring容器的過程:createWebApplicationContext(servletContext)

// ContextLoader:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        // 很明顯這裏決定了spring容器的類型
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

往下看determineContextClass的源碼:

// ContextLoader:390

protected Class<?> determineContextClass(ServletContext servletContext) {
		// 默認的contextClass的名稱
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
			// 從properties中獲取類名,接着看這個defaultStrategies是什麼時候賦值的
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

	static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
			// 從DEFAULT_STRATEGIES_PATH獲取到資源
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
			// 就是在這裏對 defaultStrategies進行的初始化
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
		}
	}

我們可以看到是從DEFAULT_STRATEGIES_PATH這個路徑獲取到的類名配置

	private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

這個ContextLoader.properties其實就是在spring-web中org.springframework.web.context.ContextLoader的同級目錄下:

可以看到webmvc默認創建容器的實現類就是XmlWebApplicationContext,最後通過反射獲取實例

至此spring容器已創建,下面bean的初始化configureAndRefreshWebApplicationContext

// ContextLoader:413

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}

		wac.setServletContext(sc);
		// 這一步得到了web.xml中設置的<param-name>:contextConfigLocation,即spring配置文件路徑
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			// contextConfigLocation賦值
			wac.setConfigLocation(configLocationParam);
		}

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		// 初始化spirng環境
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);
		// 重點,spring的bean加載,實例化等相關操作都在這裏了
		wac.refresh();
	}

在這裏把web.xml中配置的contextConfigLocation參數傳給了applicationContext。最後是spring真正初始化的地方:wac.refresh();

// AbstractApplicationContext:509

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

這裏涉及spring初始化過程的代碼比較複雜,就不在此文中敘述了。下面開始探討DispatcherServlet

DispatcherServlet

在開始分析DispatcherServlet之前,我們看一下springmvc的整個流程

接着分析DispatcherServlet

  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

在web.xml中配置的DispatcherServlet是springmvc的核心組件,接收到的請求都交由DispatcherServlet來處理。web容器在加載完listener後filter之後,會根據load-on-startup指定的級別開始初始化servlet,並調用其init()方法

從上面的類圖我們可以看到,DispatcherServlet實現了Servlet接口,Servlet中提供了初始化方法:init(),其具體實現在DispatcherServlet繼承的HttpServletBean中

// servlet >> HttpServletBean

	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// 獲取servlet初始化配置參數
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// 重點是這裏,初始化servlet實例
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

繼續看初始化servlet的過程:initServletBean,其具體實現在FrameworkServlet中

// FrameworkServlet

	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// 重點,初始化webapplicationContext
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		......
	}

這裏又看到了initWebApplicationContext,是不是很眼熟,我們進入到initWebApplicationContext中

// FrameworkServlet:522

	protected WebApplicationContext initWebApplicationContext() {
		// 由於我們配置了ContextLoaderListener,前面創建xmlWebApplicationContext的時候已經給
		// rootContext注入了值,這裏就是從servletContext當中獲取之前的根上下文
		// 如果沒有配置ContextLoaderListener,那麼此處的rootContext爲null,不過不影響springmvc,我們接着往下看
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		// dispatcher在初始化的時候會建立自己的IOC上下文,
		// 用來存儲springmvc相關的bean,在創建之前,
		// 要判斷一下servlet的上下文中是否已經存在webApplicationContext

		// 這個時候dispatcher的webApplicationContext還是null,所以默認情況不會走這個分支
		if (this.webApplicationContext != null) {
			// 如果存在那就直接拿來用
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// 將之前的根上下文作爲自己的parent
						cwac.setParent(rootContext);
					}
					// 初始化applicationContext
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		// 因爲wac還是null,所以會進入到這個分支
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context.
			// 去servlet的上下文中找是否已經存在webApplicationContext
			wac = findWebApplicationContext();
		}
		// 上述都找不到,所以會進到這裏
		if (wac == null) {
			// 重點,創建自己的WebApplicationContext
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}
		// 將dispatcher創建的WebApplicationContext注入到servletContext中
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

我們再轉到createWebApplicationContext,前面分析listener創建springioc容器時候,也有調用類似方法

// FrameworkServlet:614

	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		// 獲取class對象,這裏也是XmlWebApplicationContext
		Class<?> contextClass = getContextClass();
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Servlet with name '" + getServletName() +
					"' will try to create custom WebApplicationContext context of class '" +
					contextClass.getName() + "'" + ", using parent context [" + parent + "]");
		}
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException(
					"Fatal initialization error in servlet with name '" + getServletName() +
					"': custom WebApplicationContext class [" + contextClass.getName() +
					"] is not of type ConfigurableWebApplicationContext");
		}
		// 通過反射實例化XmlWebApplicationContext
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		// 設置spring環境
		wac.setEnvironment(getEnvironment());
		// 將從servletContext中獲取的跟上下文作爲自己的parent
		wac.setParent(parent);
		// 設置我們在web.xml中dispatcherServlet節點下配置的<param-name>contextConfigLocation,本文中配置的是mvc.xml
		wac.setConfigLocation(getContextConfigLocation());
		// 這裏最終還是調用refresh,進行springmvc相關bean加載和初始化等工作,前面已經說過了,這裏不再說
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

這裏最終是調用configureAndRefreshWebApplicationContext,又到了這個熟悉的方法,繼續跟進

// FrameworkServlet:639

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		.......

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		// 在這裏初始化
		wac.refresh();
	}
	// 接口
	void refresh() throws BeansException, IllegalStateException;

接着追蹤wac.refresh()的具體實現
// AbstractApplicationContext:509

	public void refresh() throws BeansException, IllegalStateException {
		// 先來個鎖,不然容器還沒refresh,你又啓動個容器,那不得炸了,要知道是可以啓動多個容器的
		synchronized (this.startupShutdownMonitor) {
			// 記錄下容器啓動時間,標記爲“已啓動”
			prepareRefresh();

			// 這一步針對不同方式注入採取不同實現
			// 如果是xml方式,是在這一步初始化beanfactory,解析xml配置文件得到bean定義,
			// 掃描@component,@service,@repository,完成bean註冊
			// 如果爲annotation方式,在執行refresh之前就已經完成了BeanFactory的初始化
			// 和Bean容器註冊,此時裏面的邏輯是非常簡單的,我們進去看看
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 設置beanfactory的類加載器
			prepareBeanFactory(beanFactory);

			try {
				// Bean如果實現了BeanFactoryPostProcessor,容器在初始化以後會調用postProcessBeanFactory方法,並且子類可以對這步進行擴展
				postProcessBeanFactory(beanFactory);
    
				// 調用BeanFactoryPostProcessor實現類的postProcessBeanFactory回調
				invokeBeanFactoryPostProcessors(beanFactory);

				// 註冊BeanPostProcessor實現類
				registerBeanPostProcessors(beanFactory);

				// 初始化MessageSource,國際化
				initMessageSource();

				// 初始化事件廣播器
				initApplicationEventMulticaster();

				// 模版方法,子類可以在這一步初始化一些bean
				onRefresh();

				// 註冊監聽器(實現ApplicationListener)
				registerListeners();

				// 實例化所有非懶加載的singleton benas
				finishBeanFactoryInitialization(beanFactory);

				// 初始化完成廣播事件
				finishRefresh();
			}

			......
		}
	}

這裏又到了spring的最核心的方法:refresh(),這個方法裏有一個比較重要的步驟: obtainFreshBeanFactory(),我們在mvc相關的配置文件中配置的<context:component-scan>,就是一步通過解析xml拿到掃描路徑的。經過一系列的調用鏈路,會調用doScan,傳入包掃描路徑,將@component,@service,@repository這些註解標識的類註冊到spring ioc容器。

<context:component-scan base-package="com.cf.spring.code"></context:component-scan>

spring完成bean註冊後,會在finishBeanFactoryInitialization這一步統一完成bean的實例化。spring容器的bean初始化完成後最還有一個廣播事件:finishRefresh():
經過一系列的調用鏈路
AbstractApplicationContext.finishRefresh()
>> AbstractApplicationContext.publishEvent(new ContextRefreshedEvent(this)) //向所有監聽者發佈容器刷新事件>> FrameworkServlet.onApplicationEvent(ContextRefreshedEvent event) //接收到容器刷新事件,調用DispatcherServlet的onRefresh
>> DispatcherServlet.onRefresh()

最終會調用DispatcherServlet的onRefresh()方法,而onRefresh()裏就一行代碼,真正幹實事的是initStrategies

// DispatcherServlet

	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}
    // springmvc組件一系列初始化操作
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		// 初始化handlerMapping
		initHandlerMappings(context);
		// 初始化HandlerAdapters
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		// 初始化視圖解析器
		initViewResolvers(context);
		initFlashMapManager(context);
	}

DispatcherServlet的initStrategies完成了springmvc組件的一系列初始化操作,也就是說springmvc組件在DispatcherServlet初始化自己的上下文(webApplicationContext)的時候就已經實例化完成了。

至此我們已經知道springmvc是怎麼加載bean並完成初始化的流程,接下來分析一波dispatcherServlet如何處理請求。

DispatcherServlet繼承了HttpServlet,servlet處理請求最核心的方法就是service()方法,但是在DispatcherServlet的父類FrameworkServlet中重寫了service方法:

// FrameworkServlet

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		// 判斷是不是patch的請求方式
		if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
			processRequest(request, response);
		}
		// 如果不是patch的請求方式,調用父類的service方法,最終還是會調用本類中的doGet和doPost方法
		else {
			super.service(request, response);
		}
	}
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

不管是doPost還是doGet,都 會調用processRequest(request, response)方法

// FrameworkServlet

	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		......

		try {
			// 這裏纔是重點,別的都略過,跟進去
			doService(request, response);
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		......

			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

這裏的doService是abstract的,其實現是在DipatcherServlet中

// DipatcherServlet

	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		......
		try {
            // 繼續,跟進
			doDispatch(request, response);
		}
		......
	}

終於到了SpringMvc最核心的代碼:doDispatch

// DipatcherServlet

	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;//定義ModelAndView
			Exception dispatchException = null;

			try {
				// 判斷是否上傳文件請求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 通過當前request對象獲取handler,handler是什麼?就是指處理器(像controller)
				// 下面重點分析一下handler獲取的過程
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 獲取對應的handlerAdapter.
				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;
				}

				// 真正執行handle
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				// 如果沒有返回視圖,則使用默認的
				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			// 處理返回結果
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", 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);
				}
			}
		}
	}

處理請求時首先會根據當前的request獲取一個handler

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// 遍歷handlerMappings
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			// 找到具體handler
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// 真正獲取handler
		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);
		}
		// 獲取handler執行鏈
		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;
	}

spring會遍歷handlerMappings,詢問每一個HandlerMapping,有沒有我要的控制器,最終返回了HandlerExecutionChain
接着追蹤下getHandlerInternal,以AbstractUrlHandlerMapping爲例,分析一下根據url獲取handler

// AbstractUrlHandlerMapping

	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		// url路徑
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		// 尋找handler,就是這裏了,我們接着往下看
		Object handler = lookupHandler(lookupPath, request);
		......
		return handler;
	}

	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// handlerMap這是一個LinkedHashMap<String, Object>,鍵值爲url,value爲controller實例
		// 這裏通過url從map中 獲取到真正執行業務的controller的實例(handler)
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {
			// Bean name or resolved handler?
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = getApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		}

			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		......
	}

我們繼續看下一個關鍵步驟getHandlerAdapter(mappedHandler.getHandler()

// DipatcherServlet

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
				......
				// 獲取適配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				......
	}

接着看獲取適配器的步驟

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

在這裏根據handler匹配合適的適配器,接着往下看.applyPreHandle(processedRequest, response)

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
				......

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				......
		}
	}

	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

在applyPreHandle這一步調用了一系列攔截器的preHandle方法,接下來就是去執行handle

				// 正在的執行handle
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

找到handle方法的具體實現

// AnnotationMethodHandlerAdapter 

	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 獲取controller的class對象
		Class<?> clazz = ClassUtils.getUserClass(handler);
		Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
		if (annotatedWithSessionAttributes == null) {
			annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
			this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
		}

		if (annotatedWithSessionAttributes) {
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
		}
		else {
			checkAndPrepare(request, response, true);
		}

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandlerMethod(request, response, handler);
				}
			}
		}
		// 這裏纔是執行controller對應方法的地方,繼續看
		return invokeHandlerMethod(request, response, handler);
	}

	protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
		// 獲取到要執行的方法對象
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();
		// 通過反射執行url對應controller中的方法
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		// 獲取modelAndView
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}

到這裏已經執行完url對應controller方法中的業務邏輯,並且返回了modelAndView

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章