1. 程式人生 > >Spring原始碼解析(四):SpringMVC原始碼解析

Spring原始碼解析(四):SpringMVC原始碼解析

SpringMVC是Spring一個非常重要的模組,從大體上看,在使用SpringMVC的時候,需要在web.xml中配置DispatcherServlet,這個DispatcherServlet可以看成是一個前端控制器的實現,web請求會通過它分發給各個對應的Controller,然後會看到ModelAndView資料的生成,並把ModelAndView資料交給對應的View檢視來進行呈現。下面我們來對SpringMVC的設計進行詳細的分析。

一、根上下文在容器中的啟動

SpringMVC作為Spring的一個模組,也是建立在IOC容器的基礎上的,首先我們來了解下Spring的IOC容器是如何在Web環境中載入並起作用的。如果要在web環境中使用IOC容器,需要為IOC容器設計一個啟動過程,把IOC容器匯入,這個啟動的過程是與web容器的啟動過程整合在一起的,我們以tomcat作為web容器來進行分析。先來看一個SpringMVC相關的web.xml中的配置

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

在這個web.xml中,定義了一個Servlet物件——DispatcherSerlvet,這個類是SpringMVC的核心類,它的實現原理我們後面再來分析,context-param引數的配置用來指定SpringIOC容器讀取Bean定義的xml檔案的路徑。在這個檔案中,一般用來定義Spring應用的Bean配置。最後,我們可以看到一個監聽器的配置——ContextLoaderListener,這個類作為SpringMVC的啟動類,負責完成IOC容器在web環境中的啟動工作,它是與web容器的生命週期相關聯的,下面我們來具體看看IOC容器在web容器中的啟動過程是如何實現的。

容器的啟動過程是與ServletContext相伴而生的,由ContextLoaderListener啟動的上下文為根上下文,此外,還有一個與Web MVC相關的上下文用來儲存控制器(DispatcherServlet)需要的MVC物件,作為根上下文的子上下文,構成一個層次化的上下文體系。

ContextLoaderListener實現了ServletContextListener介面,這個接口裡的函式會結合Web容器的生命週期被呼叫,因為ServletContextListener是ServletContext的監聽者,如果ServletContext發生變化會觸發相應的事件,這些事件包括在伺服器啟動,ServletContext被建立的時候回觸發contextInitialized方法;服務關閉時,ServletContext被銷燬的時候觸發contextDestroyed方法。IOC容器的初始化就是在contextInitialized方法中完成,這個方法的程式碼如下:


    public void contextInitialized(ServletContextEvent event) {
	initWebApplicationContext(event.getServletContext());
    }

​

initWebApplicationContext方法的具體實現在的ContextLoaderListener父類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 {
	                //初始化上下文
			if (this.context == null) {
				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);
					}

                                //對上下文的屬性進行一些設定並呼叫refresh方法初始化
				configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
	
                                //將上下文物件設定到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;
		}
	}

首先來看看createWebApplicationContext方法中如何初始化根上下文

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		//判斷作為IOC容器的類的型別
                Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		//例項化IOC容器
                return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
protected Class<?> determineContextClass(ServletContext servletContext) {
                //讀取對contextClass的配置
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
                //如果配置了contextClass那麼就使用這個配置的class,當然前提是這個class可用
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
	        //否則使用預設的類,這個預設的上下文的類是org.springframework.web.context.support.XmlWebApplicationContext,
                //是在ContextLoader.properties檔案中配置的
                else {
			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);
			}
		}
	}

然後看看configureAndRefreshWebApplicationContext方法中對於容器的引數的一些配置,方法程式碼如下

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()));
			}
		}

		//將ServletContext設定到容器中
                wac.setServletContext(sc);
		//設定配置檔案的位置引數
                String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			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
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);
		//呼叫refresh方法啟動容器的初始化
                wac.refresh();
	}

這就是IOC容器在web容器中的啟動過程,在初始化這個上下文之後,該上下文會被儲存到ServletContext中,這樣就建立了一個全域性的關於整個應用的根上下文。在後面我們還會看到,在DispatcherServlet進行自己的上下文初始化時,會將這個根上下文設定為DispatcherServlet自帶的上下文的雙親上下文。

二、Spring MVC的設計與實現

在前面的web.xml中我們看到,除了配置ContextLoaderListener之外,還要對DispatcherServlet進行配置。DispatcherServlet十分重要,它可以說是Spring MVC實現中最核心的部分,它作為一個前端控制器,所有的web請求都需要通過它來處理,進行轉發、匹配、資料處理後,轉由頁面進行展現,它的設計與分析也是下面分析Spring MVC的一條主線。

DispatcherServlet的工作大致可以分為兩個部分:一是初始化部分,DispatcherServlet通過這部分功能對MVC模組的其他部分進行了初始化,比如handlerMapping、ViewResolver等;另一個是對http請求進行響應,DispatcherServlet對請求的轉發及處理在這部分功能中完成。

2.1 DispatcherServlet的啟動及初始化

我們先來分析DispatcherServlet的初始化過程,作為一個Servlet,DispatcherServlet啟動時首先會呼叫它的init方法,這個方法的實現在它的父類HttpServletBean中,程式碼如下

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

		// 獲取Servlet的初始化引數,對Bean的屬性進行設定
		try {
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			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) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}

		// 呼叫子類的initServletBean方法進行具體的初始化,主要的初始化過程在這個方法中
		initServletBean();

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

initServletBean的實現在DispatcherServlet的父類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 {
			//在這裡初始化上下文
                        this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}
protected WebApplicationContext initWebApplicationContext() {
		//獲取之前在ContextLoaderListener中儲存到ServletContext中的根上下文
                WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		        //如果當前類中的webApplicationContext不為空 
                        if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					//將當前根上下文作為雙親上下文
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
                                //設定上下文的相關屬性並呼叫refresh方法觸發初始化					
                                configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			wac = findWebApplicationContext();
		}
                if (wac == null) {
			//具體的初始化的過程
			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.
			onRefresh(wac);
		}

		//將當前建立的上下文存到Servletcontext中,屬性名為
                //org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher
                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;
	}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		//獲取需要初始化的上下文的對應的類,預設是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");
		}
		//通過反射初始化得到上下文物件
                ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
                //設定一些相關的屬性並將根上下文設定為雙親上下文,這樣一來根上下文中
                //的Bean也可以被DispatcherServlet中的上下文使用
		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		wac.setConfigLocation(getContextConfigLocation());
                //設定相關屬性並呼叫refresh方法進行容器的初始化
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

configureAndRefreshWebApplicationContext與前面的ContextLoader中的configureAndRefreshWebApplicationContext有些類似

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		//對上下文的屬性進行設定
                if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		//呼叫refresh方法觸發容器的初始化
                wac.refresh();
	}

初始化的過程中還會呼叫DispatcherServlet中的onRefresh方法,onRefresh方法中會呼叫DispatcherServlet的initStrategies,這個方法會觸發整個Spring MVC框架的初始化

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);
	}

對於上面的方法名稱,很容易理解。以initHandlerMappings方法為例來看看這個初始化過程,方法程式碼如下

private void initHandlerMappings(ApplicationContext context) {
		//這個handlerMappings是個List,用來存放所有的HandlerMapping
                this.handlerMappings = null;
                //匯入所有的HandlerMapping,detectAllHandlerMappings預設為真,即預設的從所有的IOC容器中取
		if (this.detectAllHandlerMappings) {
			// 從容器中取得所有的HandlerMapping,包括父級容器,如果配置了<mvc:annotation-driven/>,
                        //那麼這裡預設是BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,
                        //matchingBeans的鍵分別是它們完整的類名
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {//也可以根據名稱從當前的IOC容器中取得handlerMapping
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// 如果都沒有取到的話,那麼從DispatcherServlet.properties中取預設設定的HandlerMapping
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

這樣,DispatcherServlet就把所有的HandlerMapping初始化到了handlerMapping變數中。其他的初始化過程與HandlerMapping比較類似,都是從IOC容器中讀取配置。經過這些過程,DispatcherServlet的初始化就全部完成了。

2.2 SpringMVC的分發請求

在分析SpringMVC對請求的分發之前,我們先來看看HandlerMapping的設計原理,HandlerMapping在SpringMVC分發請求的過程中起著十分重要的作用,一般每個handlerMapping都持有一系列從url請求到Controller的對映,以RequestMappingHandlerMapping為例,RequstMappingHandlerMapping是我們經常會用到的一個HandlerMapping,當我們配置<mvc:annotation-driven/>時,DispatcherServlet中會用它作為HandlerMapping。

RequstMappingHandlerMapping的父類實現了InitializingBean,所以通過getBean例項化它時會呼叫它的afterPropertiesSet方法,該方法程式碼如下

        public void afterPropertiesSet() {
		//例項化一個BuilderConfiguration物件併為它設定一些屬性
                this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());
                //呼叫父類的afterPropertiesSet方法
		super.afterPropertiesSet();
	}

該方法中會呼叫它的父類AbstractHandlerMethodMapping中的afterPropertiesSet方法

        public void afterPropertiesSet() {
		//該方法用來將請求路徑與控制器中的方法進行匹配
                initHandlerMethods();
	}
protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		//獲取上下文物件中所有的beanName
                String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		//遍歷所有的bean
                for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					//獲取它們的型別
                                        beanType = getApplicationContext().getType(beanName);
				}
				catch (Throwable ex) {
					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
					}
				}
				//判斷是否為Controller或者RequestMapping註解,如果是則取得類中處理請求的方法
                                if (beanType != null && isHandler(beanType)) {
					detectHandlerMethods(beanName);
				}
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
protected void detectHandlerMethods(final Object handler) {
		//獲取類的型別
                Class<?> handlerType = (handler instanceof String ?
				getApplicationContext().getType((String) handler) : handler.getClass());
		
                final Class<?> userType = ClassUtils.getUserClass(handlerType);

		//這個map的key是Controller中的Method,value是RequestMappingInfo,
                //MetadataLookup中的方法會在selectMethods中被回撥
                Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				new MethodIntrospector.MetadataLookup<T>() {
					@Override
					public T inspect(Method method) {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					}
				});

		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}
		//遍歷methods並註冊處理請求的方法
                for (Map.Entry<Method, T> entry : methods.entrySet()) {
			Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
			T mapping = entry.getValue();
			registerHandlerMethod(handler, invocableMethod, mapping);
		}
	}

下面來看看selectMethods中是如何獲取到RequestMappingInfo的

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
		final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();
		Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
		Class<?> specificHandlerType = null;
        
		//新增處理請求的bean(Controller)
                if (!Proxy.isProxyClass(targetType)) {
			handlerTypes.add(targetType);
			specificHandlerType = targetType;
		}
		handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));
                //遍歷這些bean
		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
                        //doWithMethods中會呼叫MethodCallback的doWith方法對bean中的所有方法進行處理,包括父類的方法
			ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
				@Override
				public void doWith(Method method) {
					Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
					//呼叫metadataLookup的inspect方法獲取RequestioMappingInfo
                                        T result = metadataLookup.inspect(specificMethod);
					//將方法新增到map中
                                        if (result != null) {
						Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
						if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
							methodMap.put(specificMethod, result);
						}
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}

		return methodMap;
	}

具體取得RequestMappingInfo的過程在RequestMappingHandlerMapping的getMappingForMethod方法中

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
		}
		return info;
	}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
                RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		RequestCondition<?> condition = (element instanceof Class<?> ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		//這裡是真正生成RequestMappingHandlerMapping的地方
                return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}
protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, RequestCondition<?> customCondition) {
                //這裡使用建造者模式構造RequestMappingHandlerMapping,build方法的實現在
		return RequestMappingInfo
				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
				.methods(requestMapping.method())
				.params(requestMapping.params())
				.headers(requestMapping.headers())
				.consumes(requestMapping.consumes())
				.produces(requestMapping.produces())
				.mappingName(requestMapping.name())
				.customCondition(customCondition)
				.options(this.config)
				.build();
	}
public RequestMappingInfo build() {
			ContentNegotiationManager manager = this.options.getContentNegotiationManager();

			PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
					this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
					this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
					this.options.getFileExtensions());

			return new RequestMappingInfo(this.mappingName, patternsCondition,
					new RequestMethodsRequestCondition(methods),
					new ParamsRequestCondition(this.params),
					new HeadersRequestCondition(this.headers),
					new ConsumesRequestCondition(this.consumes, this.headers),
					new ProducesRequestCondition(this.produces, this.headers, manager),
					this.customCondition);
		}

到這裡,我們不難看出RequestMappingInfo實際上是封裝了請求路徑到對應的處理的方法的對映。然後回到方法detectHandlerMethods中,接下來就是將處理請求的方法進行註冊

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		//這個mappingRegistry是一個MappingRegistry的例項
                this.mappingRegistry.register(mapping, handler, method);
	}
public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);

				if (logger.isInfoEnabled()) {
					logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
				}
				//將對映與對應方法存起來
                                this.mappingLookup.put(mapping, handlerMethod);
                                //將請求路徑與對映物件存起來
				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				//獲取RequestMappingInfo的name,將name與method存起來
                                if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}
                                //將對映物件和對映註冊物件存起來
				this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

這樣,所有請求的路徑到Controller中處理請求的方法的對映都被註冊到了RequestMappingHandlerMapping中。下面我們就來具體看看SpringMVC中對請求的分發處理的過程。讓我們重新回到DispatcherServlet中,前面說過,web請求的分發主要通過它來完成,作為一個Servlet,DispatcherServlet通過doService方法來相應http請求,下面我們來看看doService方法的實現

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));
				}
			}
		}

		// 將一些引數設定到request請求中
		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);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}
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);

				// 根據請求獲取HandlerExecutionChain,這個mappedHandler是一個HandlerExecutionChain物件,
                                //HandlerExecutionChain中設定了一個攔截器鏈,用來對handler進行增強,handler實際就是請求對應的Controller中的處理方法
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 獲取對應的HandlerAdapter,如果是配置了<mvc:annotation-driven/>,那麼這裡是RequestMappingHandlerAdapter
				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;
					}
				}

				//呼叫HandlerExecutionChain中的interceptor進行前置處理
                                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// 呼叫HandlerAdapter的handler方法對handler進行處理並將處理的結果封裝到ModelAndView中,為檢視展現提供資料
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
                                //如果檢視為空那麼設定一個預設的檢視
				applyDefaultViewName(processedRequest, mv);
				//呼叫HandlerExecutionChain中的interceptor進行後置處理
                                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);
			}
			//對ModelAndView檢視的展現
                        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);
				}
			}
		}
	}

我們來看看getHandler中是如何獲取HandlerExecutionChain的

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//遍歷handlerMappings,呼叫HandlerMapping的getHandler方法獲取HandlerExecutionChain,
                //在其中一個取到了立即返回
                for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

該方法的實現在AbstractHandlerMapping中,是一個final方法

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//獲取對應的處理請求的方法
                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);
		}

		//獲取HandlerExecutionChain
                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;
	}

getHandlerInternal方法的實現在AbstractHandlerMethodMapping類中實現

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		//獲取請求的路徑
                String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
			//根據請求路徑獲取對應的處理請求的HandlerMethod
                        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			//將找到的HandlerMethod返回
                        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<Match>();
		//這個directPathMatches實際上是一個RequestMappingInfo的集合,getMappingsByUrl方法會從之前

                //RequestMappingHandlerMapping初始化時快取到mappingRegistry中的urlLookup裡取得對應的RequestMappingInfo
                List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		//從RequestMappingHandlerMapping初始化時快取到mappingRegistry中的mappingLookup裡獲取對應的HandlerMethod,
                //並和RequestMappingHandlerMapping、RequestMappingInfo一起封裝到matches中
                if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}
                //返回一個最匹配的HandlerMethod
		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
						lookupPath + "] : " + matches);
			}
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
							request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

getHandlerExecutionChain的程式碼如下

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		//建立一個HandlerExecutionChain 
                HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
                //獲取請求的路徑
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		//遍歷adaptedInterceptors,這個adaptedInterceptors是一個HandlerInterceptor的集合,集合中的interceptor是在初始化時新增進去的
                for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				//如果攔截器與路徑匹配的話就將攔截器新增到HandlerExecutionChain的interceptorList中
                                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

這樣就取到了所需要的HandlerExecutionChain,然後回到DispatcherServlet的doDispatch方法中,接下來我們看看這個方法中如何通過呼叫HandlerAdapter的handle方法獲取到ModelAndView,以RequestMappingHandlerAdapter為例,如果配置了<mvc:annotation-driven/>,就會以這個類作為HandlerAdapter的實現,具體的獲取ModelAndView的過程在handleInternal方法中

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// 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) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			//呼叫處理請求的handlerMethod,將方法中的引數及返回的檢視封裝到ModelAndView中
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

再回到doDispatcher中,然後會呼叫processDispatchResult方法對檢視進行展現

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// 這裡是展現檢視的地方
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, 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");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

具體的展現檢視的方法在render方法中

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 從request中獲取locale資訊並設定到response中
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);

		View view;
		if (mv.isReference()) {
			// 從ModelAndView物件中獲取檢視名稱並用ViewResolver解析得到檢視物件
			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 {
			// 如果ModelAndView中已經包含檢視物件那麼直接取
			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() + "'");
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
			//呼叫檢視物件的render方法展現檢視
                        view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
						getServletName() + "'", ex);
			}
			throw ex;
		}
	}

SpringMVC中封裝了很多檢視,比如JSP檢視,FreeMarker檢視,Velocity檢視,Excel和PDF檢視等等,我們來看看我們平常用的最多的JSP檢視是如何展現的。具體的render方法的實現在AbstractView中

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
				" and static attributes " + this.staticAttributes);
		}
                //將model中的資料都收集到一個map中
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		prepareResponse(request, response);
		//展現模型資料到檢視中
                renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}

renderMergedOutputModel的實現在InternalResourceView中

protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// 將模型的資料設定到HTTPServletRequest中
		exposeModelAsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		exposeHelpers(request);

		// 獲取檢視頁面的內部資源路徑
		String dispatcherPath = prepareForRendering(request, response);

		// 獲取RequestDispatcher
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.include(request, response);
		}

		else {
			// 將請求到資源上,比如JSP頁面,JSP頁面的展現由web容器負責,view只是轉發請求
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.forward(request, response);
		}
	}

至此,SpringMVC就完成了將HTTP請求通過DispatcherServlet轉發給頁面呈現的過程。總結一下,SpringMVC的實現由以下三個步驟完成:

1、初始化handlerMapping時,http請求到Controller之間的對映會被載入到HandlerMapping中

2、當接收到http請求之後,DispatcherServlet會根據具體的URL資訊,在HandlerMapping中查詢,然後得到對應的HandlerExecutionChain。HandlerExecutionChain中封裝了對應的處理請求的HandlerMethod,然後HandlerAdapter會執行處理請求的方法,生成需要的ModelAndView物件。

3、得到ModelAndView物件之後,DispatcherServlet把獲取的模型資料交給特定的檢視物件,從而完成這些資料的呈現工作,呈現的過程由對應的檢視類的render方法完成。