SpringMVC源码解析

配置文件

分析SpringMVC之前,首先要看一下配置文件,因为它是我们分析SpringMVC源码的入口。其实里面的重点就是配置了一个ServletContext监听器,他的作用就是监听ServletContext的启动,并且调用重写的contextInitialized方法,这里将会是源码分析的开始。然后就是配置了一个DispatchServlet类型的Servlet,这个大家都熟悉,就是SpringMVC用来处理请求分发和渲染视图的一个Servlet。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <welcome-file-list>
        <welcome-file>login.do</welcome-file>
    </welcome-file-list>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/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>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

WepApplicationContext的创建

当我们在web.xml里面配置某个我们实现的了ServletContextListener接口的监听器后,就会监听ServletContext的启动,在ServletContext启动的时候,就会回调重写的contextInitialized。SpringMVC正是通过这个方法,开始WebApplicationContext的创建(现在还不知道要创建的WebApplicationContext具体是什么类型)
org.springframework.web.context.ContextLoaderListener#contextInitialized

	@Override
	public void contextInitialized(ServletContextEvent event) {
		/**
		 * web.xml配置文件中配置了ContextLoaderListener
		 * 他是ServletContextListener的实现了
		 * 配置了监听器,Servlet容器启动时回先执行他的contextInitialized
		 * 然后才回去执行load on startup的Servlet的init方法
		 */
		initWebApplicationContext(event.getServletContext());
	}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

org.springframework.web.context.ContextLoader#initWebApplicationContext

/**
	 * 为给定的servlet context初始化Spring的WebApplicationContext,
	 * 使用构成器提供的WebApplicationContext,
	 * 或者根据 "contextClass"
	 * 和 "contextConfigLocation" 两个context-params
	 * 创建一个新的
	 */
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		...省略不重要的代码
		try {
			// 创建WebApplicationContext
			// 存储WebApplicationContext到本地实例变量,
			// 保证他在ServletContext关闭的时候也是可用的
			// 当然,同时也会保存一份到servletContext中
			if (this.context == null) {
				// 在这里就会创建WebApplicationContext并返回
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					/*这里会手动执行WebApplicationContext的refresh方法*/
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			/* 存储WebApplicationContext到servletContext中 */
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码	
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

org.springframework.web.context.ContextLoader#createWebApplicationContext

	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		/*获取WebApplicationContext的具体类型 */
		Class<?> contextClass = determineContextClass(sc);
		...省略不重要的代码
		/*通过返回的Class对象,反射实例化WebApplicationContext并且强转成ConfigurableWebApplicationContext返回*/
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里就是确定具体的WebApplicationContext类型的地方
org.springframework.web.context.ContextLoader#determineContextClass

protected Class<?> determineContextClass(ServletContext servletContext) {
		/*如果web.xml中指定了初始化参数ContextClass,就尝试获取此Class*/
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				/*获取到了,则返回contextClassName对应的WebApplicationContext的Class对象*/
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
			/*没有指定WebApplicationContext类型,则从defaultStrategies中获取默认的,defaultStrategies就是一个Properties的对象*/
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				/*返回默认的WebApplicationContext的Class对象 */
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

可以看到首先会看看有没有在web.xml中指定初始化化参数ContextClass,这个参数需要指定具体的WebApplicationContext的完整类名,然后根据它加载对应的Class对象。但是我们一般不指定这东西,就是使用默认的,默认就会从Properties类型的对象defaultStrategies当中获取,而defaultStrategies其实是在ContextLoader的静态块中创建

	private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Properties defaultStrategies<span class="token punctuation">;</span>

<span class="token keyword">static</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">/*在这里就会读取到配置文件ContextLoader.properties中默认的WebApplicationContext类型,并且保存到defaultStrategies中*/</span>
		ClassPathResource resource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassPathResource</span><span class="token punctuation">(</span>DEFAULT_STRATEGIES_PATH<span class="token punctuation">,</span> ContextLoader<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		defaultStrategies <span class="token operator">=</span> PropertiesLoaderUtils<span class="token punctuation">.</span><span class="token function">loadProperties</span><span class="token punctuation">(</span>resource<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">(</span><span class="token string">"Could not load 'ContextLoader.properties': "</span> <span class="token operator">+</span> ex<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

可以看到这里是读取配置文件ContextLoader.properties中配置的文件

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

  • 1
  • 2
  • 3
  • 4
  • 5

可以看到,默认的类型就是XmlWebApplicationContext。通过返回XmlWebApplicationContext的Class对象,反射实例化XmlWebApplicationContext。
创建WebApplicationContext成功后,就会手动执行刷新,就是上面ContextLoader类的initWebApplicationContext中的configureAndRefreshWebApplicationContext(cwac, servletContext);。其实就是执行AbstractApplicationContext中的refresh方法,他是Spring中在ApplicationContext创建的时候,进行BeanDefinition的加载,BeanFactory创建,Bean的创建和保存等等一系列动作的入口。

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		...省略不重要的代码
		/* 把ServletContext设置到WebApplicationContext中 */
		wac.setServletContext(sc);
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码
	
	<span class="token comment">/*执行WebApplicationContext的refresh方法,该方法继承于父类AbstractApplicationContext*/</span>
	wac<span class="token punctuation">.</span><span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

DispatchServlet的初始化

然后就是DispatchServlet的创建,因为web.xml中配置了load-on-startup为1,所以Servlet容器启动后马上启动。Servlet创建后会调用init初始化方法,而DispatchServlet的init的方法在他间接继承的父类HttpServletBean中
org.springframework.web.servlet.HttpServletBean#init

	/**
	 * 将配置参数映射到这个servlet的bean属性上,并调用子类初始化
	 */
	@Override
	public final void init() throws ServletException {
		// 从init参数设置bean属性.
		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;
			}
		}
	<span class="token comment">// 子类的初始化工作.</span>
	<span class="token function">initServletBean</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

org.springframework.web.servlet.FrameworkServlet#initServletBean

	@Override
	protected final void initServletBean() throws ServletException {
		...省略不重要的代码
		try {
			/**
			 * 初始化WebApplicationContext, 并保存到DispatchServlet的成员变量中
			 * 这里之所以要这样做,是因为DispatchServlet重载类构造方法,支持通过构造方法参入一个WebApplicationContext
			 * 另外之前创建的WebApplicationContext,是保存到ContextLoader的成员变量和ServletContext中的
			 * DispatchServlet中并没有
			 */
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}
	...省略不重要的代码
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

	protected WebApplicationContext initWebApplicationContext() {
		/*ServletContext中获取WebApplicationContext,就是ContextLoader中创建的WebApplicationContext*/
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		// 这里要判断一次啊DispatchServlet中的成员变量webApplicationContext
		// 因为DispatchServlet重载的构造,支持传入WebApplicationContext类型的参数
		if (this.webApplicationContext != null) {
			// 在构造器注入上下文实例,使用它
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// 上下文尚未刷新 -> 提供诸如设置父上下文、设置应用程序上下文id等服务
					if (cwac.getParent() == null) {
						// 如果当前的WebApplicationContext对象的parent属性为空,则赋值为rootContext
						cwac.setParent(rootContext);
					}
					/*手动refresh当前的WebApplicationXml,和前面一样,因为是构造方法传入的,还没用refresh */
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// 在构建时没有注入上下文实例,看看是否已经在servlet上下文中注册了一个。
			// 如果存在,则假定父上下文(如果有的话)已经设置,
			// 并且用户已经执行了任何初始化,比如设置上下文id
			// 其实就是从ServletContext找到ContextLoader创建的WebApplicationContext
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// 没有为这个servlet定义上下文实例->创建一个
			wac = createWebApplicationContext(rootContext);
		}
	<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>refreshEventReceived<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// 要么上下文不是具有刷新支持的可配置applicationcontext,</span>
		<span class="token comment">// 要么在构建时注入的上下文已经被刷新</span>
		<span class="token comment">// 在这里手动触发初始化onRefresh方法</span>
		<span class="token comment">// 会调用到DispatchServlet重写的方法</span>
		<span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>onRefreshMonitor<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">// 接下来就是分析这里</span>
			<span class="token function">onRefresh</span><span class="token punctuation">(</span>wac<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>

	<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>publishContext<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// 将WebApplicationContext作为servlet上下文属性发布。</span>
		String attrName <span class="token operator">=</span> <span class="token function">getServletContextAttributeName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token function">getServletContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span>attrName<span class="token punctuation">,</span> wac<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token keyword">return</span> wac<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

org.springframework.web.servlet.DispatcherServlet#onRefresh

/**
	 * 这个实现调用{@link #initStrategies}。
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}
<span class="token comment">/**
 * 初始化这个servlet使用的策略对象。
 * 可以在子类中重写,以便进一步初始化策略对象。
 */</span>
<span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">initStrategies</span><span class="token punctuation">(</span>ApplicationContext context<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token function">initMultipartResolver</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">initLocaleResolver</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">initThemeResolver</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">/* 初始化HandlerMappings */</span>
	<span class="token function">initHandlerMappings</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">/* 初始化HandlerAdapters */</span>
	<span class="token function">initHandlerAdapters</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">initHandlerExceptionResolvers</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">initRequestToViewNameTranslator</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">/* 初始化ViewResolvers */</span>
	<span class="token function">initViewResolvers</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">initFlashMapManager</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

可以看到,接下来就是初始化DispatchServlet中的各种组件了,比较重要的有HandlerMappings,HandlerAdapters,ViewResolvers。

DispatchServlet重要组件的初始化

org.springframework.web.servlet.DispatcherServlet#initHandlerMappings

/**
	 * 初始化该类使用的处理器映射。
	 * 如果在这个名称空间的BeanFactory中没有定义HandlerMapping bean,
	 * 我们默认使用BeanNameUrlHandlerMapping。
	 */
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		/*detectAllHandlerMappings 默认true*/
		if (this.detectAllHandlerMappings) {
			// 找到所有ApplicationContext中的HandlerMapping,包括祖先ApplicationContext
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				/*保存所有的HandlerMapping*/
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// 对HandlerMapping进行排序.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			/**
			 * 默然情况下,SpringMVC会加载所有实现了HandlerMapping接口的Bean,
			 * 但是也可以让SpringMVC只加载BeanName为handlerMapping的HandlerMapping实现类,
			 * 就是在web.xml配置文件中配置初始化化参数detectAllHandlerMappings为false
			 * 如果没有找到BeanName为handlerMapping的Bean,
			 * 则加载DispatcherServlet.properties配置文件中默认的handlerMappings
			 */
			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.
			}
		}
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码

	<span class="token keyword">for</span> <span class="token punctuation">(</span>HandlerMapping mapping <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handlerMappings<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>mapping<span class="token punctuation">.</span><span class="token function">usesPathPatterns</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token keyword">this</span><span class="token punctuation">.</span>parseRequestPath <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
			<span class="token keyword">break</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters

	/**
	 * 初始化该类使用的handleradapter。
	 * 如果在此名称空间的BeanFactory中没有定义HandlerAdapter bean,
	 * 我们默认为SimpleControllerHandlerAdapter。
	 */
	private void initHandlerAdapters(ApplicationContext context) {
		this.handlerAdapters = null;
		/*detectAllHandlerAdapters 默认true*/
		if (this.detectAllHandlerAdapters) {
			// 找到所有ApplicationContext中的HandlerAdapter,包括祖先ApplicationContext.
			Map<String, HandlerAdapter> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
			if (!matchingBeans.isEmpty()) {
				/*保存所有的HandlerAdapter*/
				this.handlerAdapters = new ArrayList<>(matchingBeans.values());
				// 对HandlerAdapter进行排序..
				AnnotationAwareOrderComparator.sort(this.handlerAdapters);
			}
		}
		else {
			/**
			 * 如果在web.xml中初始化参数detectAllHandlerAdapters为false,
			 * 则只在WebApplicationContext中查找名为handlerAdapter的Bean
			 * 如果没有,则使用DispatcherServlet.properties配置文件中默认的的HandlerAdapter
			 */
			try {
				HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
				this.handlerAdapters = Collections.singletonList(ha);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerAdapter later.
			}
		}
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

org.springframework.web.servlet.DispatcherServlet#initViewResolvers

	/**
	 * 初始化该类使用的ViewResolvers。
	 * 如果在BeanFactory中没有为此定义ViewResolver bean
	 * 名称空间,默认为InternalResourceViewResolver。
	 */
	private void initViewResolvers(ApplicationContext context) {
		this.viewResolvers = null;
		/*detectAllViewResolvers 默认true*/
		if (this.detectAllViewResolvers) {
			// 找到所有ApplicationContext中的ViewResolver,包括祖先ApplicationContext.
			Map<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				/*保存所有的ViewResolver*/
				this.viewResolvers = new ArrayList<>(matchingBeans.values());
				// 对ViewResolver进行排序
				AnnotationAwareOrderComparator.sort(this.viewResolvers);
			}
		}
		else {
			/**
			 * 如果在web.xml中初始化参数detectAllViewResolvers为false,
			 * 则只在WebApplicationContext中查找名为viewResolver的Bean
			 * 如果没有,则使用DispatcherServlet.properties配置文件中默认的的ViewResolver
			 */
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}
		...省略不重要的代码
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

DispatchServlet的组件全部初始化完毕后,一切的准备工作就都做好了,接下来就可以正常的处理请求。在此之前先贴上spring-webmvc指定默认组件类型的properties文件DispatcherServlet.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.method.annotation.RequestMappingHandlerMapping,
org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,
org.springframework.web.servlet.function.support.HandlerFunctionAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

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

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

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

DispatchServlet的处理逻辑

	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	<span class="token function">processRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">/**
 * Delegate POST requests to {@link #processRequest}.
 * @see #doService
 */</span>
<span class="token annotation punctuation">@Override</span>
<span class="token keyword">protected</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">doPost</span><span class="token punctuation">(</span>HttpServletRequest request<span class="token punctuation">,</span> HttpServletResponse response<span class="token punctuation">)</span>
		<span class="token keyword">throws</span> ServletException<span class="token punctuation">,</span> IOException <span class="token punctuation">{<!-- --></span>

	<span class="token function">processRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可以看到,无论是get请求,或者是post请求,都调用的是processRequest方法来处理请求
org.springframework.web.servlet.FrameworkServlet#processRequest

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
			...省略不重要的代码
			doService(request, response);
			...省略不重要的代码
			}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

org.springframework.web.servlet.DispatcherServlet#doService

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
			/*doDispatch中就是完整的请求处理过程*/
			doDispatch(request, response);
}

 
 
  • 1
  • 2
  • 3
  • 4

下面就是DispatchServlet真正处理请求的过程了,在继续往下看前,先贴一张DispatchServlet处理请求的完整流程图
在这里插入图片描述

然后对照代码看一下,是否流程完全一样?
org.springframework.web.servlet.DispatcherServlet#doDispatch

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
	WebAsyncManager asyncManager <span class="token operator">=</span> WebAsyncUtils<span class="token punctuation">.</span><span class="token function">getAsyncManager</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
		ModelAndView mv <span class="token operator">=</span> null<span class="token punctuation">;</span>
		Exception dispatchException <span class="token operator">=</span> null<span class="token punctuation">;</span>

		<span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
			processedRequest <span class="token operator">=</span> <span class="token function">checkMultipart</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span>
			multipartRequestParsed <span class="token operator">=</span> <span class="token punctuation">(</span>processedRequest <span class="token operator">!=</span> request<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token comment">/*根据request寻找对应的mappedHandler,然后获取HandlerExecutionChain*/</span>
			mappedHandler <span class="token operator">=</span> <span class="token function">getHandler</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token comment">/*如果没有找到对应的Handler,则通过response返回错误信息*/</span>
				<span class="token function">noHandlerFound</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token keyword">return</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>

			<span class="token comment">/*根据当前的mappedHandler的getHandler方法返回的handler(一般就是HandlerMethod,老式用法就是Controller),寻找对应的HandlerAdapter*/</span>
			HandlerAdapter ha <span class="token operator">=</span> <span class="token function">getHandlerAdapter</span><span class="token punctuation">(</span>mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token comment">// Process last-modified header, if supported by the handler.</span>
			String method <span class="token operator">=</span> request<span class="token punctuation">.</span><span class="token function">getMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">boolean</span> isGet <span class="token operator">=</span> <span class="token string">"GET"</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>isGet <span class="token operator">||</span> <span class="token string">"HEAD"</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token keyword">long</span> lastModified <span class="token operator">=</span> ha<span class="token punctuation">.</span><span class="token function">getLastModified</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ServletWebRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">checkNotModified</span><span class="token punctuation">(</span>lastModified<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> isGet<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
					<span class="token keyword">return</span><span class="token punctuation">;</span>
				<span class="token punctuation">}</span>
			<span class="token punctuation">}</span>

			<span class="token comment">/**
			 * 调用拦截器链上,各拦截器的preHandle方法
			 * 如果其中一个返回false,
			 * 就执行拦截器链所有的拦截器的afterCompletion方法
			 * 然后返回false,然后这里就会return
			 * 正常情况下不进这个分支
			 */</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mappedHandler<span class="token punctuation">.</span><span class="token function">applyPreHandle</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token keyword">return</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>

			<span class="token comment">// 处理请求并返回ModelAndView对象.</span>
			mv <span class="token operator">=</span> ha<span class="token punctuation">.</span><span class="token function">handle</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">isConcurrentHandlingStarted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token keyword">return</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>

			<span class="token comment">/*如果ModelAndView中没有View,则添加默认的viewName*/</span>
			<span class="token function">applyDefaultViewName</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> mv<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token comment">/*调用各拦截器链上拦截器的postHandler方法*/</span>
			mappedHandler<span class="token punctuation">.</span><span class="token function">applyPostHandle</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mv<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			dispatchException <span class="token operator">=</span> ex<span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> err<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">// As of 4.3, we're processing Errors thrown from handler methods as well,</span>
			<span class="token comment">// making them available for @ExceptionHandler methods and other scenarios.</span>
			dispatchException <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NestedServletException</span><span class="token punctuation">(</span><span class="token string">"Handler dispatch failed"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token comment">/*处理ModelAndView到View,再渲染jsp,通过response输出页面的过程。 当然最后还会调用拦截器的afterCompletion方法 */</span>
		<span class="token function">processDispatchResult</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">,</span> mv<span class="token punctuation">,</span> dispatchException<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token function">triggerAfterCompletion</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> err<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token function">triggerAfterCompletion</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">,</span>
				<span class="token keyword">new</span> <span class="token class-name">NestedServletException</span><span class="token punctuation">(</span><span class="token string">"Handler processing failed"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">isConcurrentHandlingStarted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">// Instead of postHandle and afterCompletion</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				mappedHandler<span class="token punctuation">.</span><span class="token function">applyAfterConcurrentHandlingStarted</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">// Clean up any resources used by a multipart request.</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>multipartRequestParsed<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token function">cleanupMultipart</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

获取mappedHandler

这里会遍历DispatchSerlet中所有的HandlerMapping,尝试通过request查找对应的handler。handler有可能有不同类型,如果我们平常的那种注解式开发的Controller,就会返回HandlerMethod。但是还用一种方式,就是我们编写的Controller类继承AbstractController,那么这里就会返回Controller。然后把handler和拦截器封装成一个HandlerExecutionChain对象
org.springframework.web.servlet.DispatcherServlet#getHandler

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			/*遍历所有的HandlerMapping,直到通过request可以获取到HandlerExecutionChain */
			for (HandlerMapping mapping : this.handlerMappings) {
				/*返回的是HandlerExecutionChain对象,不是想象中的一个HandlerMapping,更不是直接返回handler*/
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler

	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// 根据request对象获取handler,其实就是根据url获取对应的HandlerMethod
		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 = obtainApplicationContext().getBean(handlerName);
		}
		/*把HandlerMethod封装成HandlerExecutionChain,里面包含了各个拦截器*/
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码

	<span class="token keyword">return</span> executionChain<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

下面就是根据request中的url匹配到对应的HandlerMethode的过程

	/**
	 * 查找给定请求的URL路径的handler.
	 * @param request current HTTP request
	 * @return the handler instance, or {@code null} if none found
	 */
	@Override
	@Nullable
	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = initLookupPath(request);
		Object handler;
		if (usesPathPatterns()) {
			RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
			handler = lookupHandler(path, lookupPath, request);
		}
		else {
			handler = lookupHandler(lookupPath, request);
		}
		if (handler == null) {
			// We need to care for the default handler directly, since we need to
			// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
			Object rawHandler = null;
			if ("/".equals(lookupPath)) {
				rawHandler = getRootHandler();
			}
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				// Bean name or resolved handler?
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = obtainApplicationContext().getBean(handlerName);
				}
				validateHandler(rawHandler, request);
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
		return handler;
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

然后把获取到的HandlerMethod封装成HandlerExecutionChain,其实就是添加上各种拦截器

	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
	<span class="token keyword">for</span> <span class="token punctuation">(</span>HandlerInterceptor interceptor <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>adaptedInterceptors<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>interceptor <span class="token keyword">instanceof</span> <span class="token class-name">MappedInterceptor</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			MappedInterceptor mappedInterceptor <span class="token operator">=</span> <span class="token punctuation">(</span>MappedInterceptor<span class="token punctuation">)</span> interceptor<span class="token punctuation">;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>mappedInterceptor<span class="token punctuation">.</span><span class="token function">matches</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				chain<span class="token punctuation">.</span><span class="token function">addInterceptor</span><span class="token punctuation">(</span>mappedInterceptor<span class="token punctuation">.</span><span class="token function">getInterceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
			chain<span class="token punctuation">.</span><span class="token function">addInterceptor</span><span class="token punctuation">(</span>interceptor<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">return</span> chain<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

返回的是一个HandlerExecutionChain 对象,然后下面就是HandlerExecutionChain 的成员变量

public class HandlerExecutionChain {
	/*真正的handler,就是HandlerMethod或者Controller*/
	private final Object handler;
<span class="token comment">/*存放拦截器的数组*/</span>
<span class="token annotation punctuation">@Nullable</span>
<span class="token keyword">private</span> HandlerInterceptor<span class="token punctuation">[</span><span class="token punctuation">]</span> interceptors<span class="token punctuation">;</span>

<span class="token comment">/*存放拦截器的list*/</span>
<span class="token annotation punctuation">@Nullable</span>
<span class="token keyword">private</span> List<span class="token generics function"><span class="token punctuation">&lt;</span>HandlerInterceptor<span class="token punctuation">&gt;</span></span> interceptorList<span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

顺便贴一下HandlerMethod中关键的成员变量

public class HandlerMethod {
	// 这个就是我们打了@Controller标签的那个类
	private final Object bean;
<span class="token annotation punctuation">@Nullable</span>
<span class="token keyword">private</span> <span class="token keyword">final</span> BeanFactory beanFactory<span class="token punctuation">;</span>

<span class="token keyword">private</span> <span class="token keyword">final</span> Class<span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">&gt;</span> beanType<span class="token punctuation">;</span>

<span class="token comment">// 这个是他要执行的方法</span>
<span class="token keyword">private</span> <span class="token keyword">final</span> Method method<span class="token punctuation">;</span>

<span class="token keyword">private</span> <span class="token keyword">final</span> Method bridgedMethod<span class="token punctuation">;</span>

<span class="token comment">// 方法参数</span>
<span class="token keyword">private</span> <span class="token keyword">final</span> MethodParameter<span class="token punctuation">[</span><span class="token punctuation">]</span> parameters<span class="token punctuation">;</span>

<span class="token annotation punctuation">@Nullable</span>
<span class="token keyword">private</span> HttpStatus responseStatus<span class="token punctuation">;</span>

<span class="token annotation punctuation">@Nullable</span>
<span class="token keyword">private</span> String responseStatusReason<span class="token punctuation">;</span>

<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

根据mappedHandler中的handler对象,获取HandlerAdapter

因为有不同的handler,不同的handler会有不同的接口,所有需要HandlerAdapter去做适配,这里用到的就是设计模式中的适配器模式。拿到了返回的HandlerExecutionChain 对象之后,就会通过里面的handler,获取HandlerAdapter。
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			/*遍历所有的HandlerAdapter,直到该HandlerAdapter的supports方法返回true*/
			for (HandlerAdapter adapter : this.handlerAdapters) {
				/*如果adapter的supports方法返回true,代表就是要找的handler*/
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

如果handler的类型是HandlerMethoded的话
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports

	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}

 
 
  • 1
  • 2
  • 3
  • 4

或者handler的类型是老式用法的继承Controller
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#supports

	@Override
	public boolean supports(Object handler) {
		/*当前handler对象时Controller类型,就返回*/
		return (handler instanceof Controller);
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

处理请求并返回ModelAndView对象

先贴一行DispatchServlet中走到的当前的步骤
org.springframework.web.servlet.DispatcherServlet#doDispatch

// 处理请求并返回ModelAndView对象
// ha就是上面寻找到的HandlerAdapter,mappedHandler.getHandler()返回的是HandlerMethod或者Controller.
// 下面就只分析HandlerMethod的了
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 
 
  • 1
  • 2
  • 3
  • 4

org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle

	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	<span class="token keyword">return</span> <span class="token function">handleInternal</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> <span class="token punctuation">(</span>HandlerMethod<span class="token punctuation">)</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

下面就是处理请求返回ModelAndView的过程了
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

	@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ModelAndView mav<span class="token punctuation">;</span> <span class="token comment">// 要返回的ModelAndView</span>
	<span class="token function">checkRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token comment">// Execute invokeHandlerMethod in synchronized block if required.</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>synchronizeOnSession<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		HttpSession session <span class="token operator">=</span> request<span class="token punctuation">.</span><span class="token function">getSession</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>session <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			Object mutex <span class="token operator">=</span> WebUtils<span class="token punctuation">.</span><span class="token function">getSessionMutex</span><span class="token punctuation">(</span>session<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">synchronized</span> <span class="token punctuation">(</span>mutex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token comment">// 执行HandlerMethod中的方法,返回ModelAndView</span>
				mav <span class="token operator">=</span> <span class="token function">invokeHandlerMethod</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handlerMethod<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">// 执行HandlerMethod中的方法,返回ModelAndView</span>
			mav <span class="token operator">=</span> <span class="token function">invokeHandlerMethod</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handlerMethod<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// 执行HandlerMethod中的方法,返回ModelAndView</span>
		mav <span class="token operator">=</span> <span class="token function">invokeHandlerMethod</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handlerMethod<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码

	<span class="token keyword">return</span> mav<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		/*封装请求参数webRequest*/
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
			/*根据当前的HandlerMethod创建HandlerMethod的间接子类ServletInvocableHandlerMethod*/
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
		<span class="token comment">/*ModelAndViewContainer 意思就是ModelAndView的容器*/</span>
		ModelAndViewContainer mavContainer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ModelAndViewContainer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		mavContainer<span class="token punctuation">.</span><span class="token function">addAllAttributes</span><span class="token punctuation">(</span>RequestContextUtils<span class="token punctuation">.</span><span class="token function">getInputFlashMap</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		modelFactory<span class="token punctuation">.</span><span class="token function">initModel</span><span class="token punctuation">(</span>webRequest<span class="token punctuation">,</span> mavContainer<span class="token punctuation">,</span> invocableMethod<span class="token punctuation">)</span><span class="token punctuation">;</span>
		mavContainer<span class="token punctuation">.</span><span class="token function">setIgnoreDefaultModelOnRedirect</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>ignoreDefaultModelOnRedirect<span class="token punctuation">)</span><span class="token punctuation">;</span>

		AsyncWebRequest asyncWebRequest <span class="token operator">=</span> WebAsyncUtils<span class="token punctuation">.</span><span class="token function">createAsyncWebRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
		asyncWebRequest<span class="token punctuation">.</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>asyncRequestTimeout<span class="token punctuation">)</span><span class="token punctuation">;</span>

		WebAsyncManager asyncManager <span class="token operator">=</span> WebAsyncUtils<span class="token punctuation">.</span><span class="token function">getAsyncManager</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span>
		asyncManager<span class="token punctuation">.</span><span class="token function">setTaskExecutor</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>taskExecutor<span class="token punctuation">)</span><span class="token punctuation">;</span>
		asyncManager<span class="token punctuation">.</span><span class="token function">setAsyncWebRequest</span><span class="token punctuation">(</span>asyncWebRequest<span class="token punctuation">)</span><span class="token punctuation">;</span>
		asyncManager<span class="token punctuation">.</span><span class="token function">registerCallableInterceptors</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>callableInterceptors<span class="token punctuation">)</span><span class="token punctuation">;</span>
		asyncManager<span class="token punctuation">.</span><span class="token function">registerDeferredResultInterceptors</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>deferredResultInterceptors<span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">hasConcurrentResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			Object result <span class="token operator">=</span> asyncManager<span class="token punctuation">.</span><span class="token function">getConcurrentResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			mavContainer <span class="token operator">=</span> <span class="token punctuation">(</span>ModelAndViewContainer<span class="token punctuation">)</span> asyncManager<span class="token punctuation">.</span><span class="token function">getConcurrentResultContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
			asyncManager<span class="token punctuation">.</span><span class="token function">clearConcurrentResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			LogFormatUtils<span class="token punctuation">.</span><span class="token function">traceDebug</span><span class="token punctuation">(</span>logger<span class="token punctuation">,</span> traceOn <span class="token operator">-</span><span class="token operator">&gt;</span> <span class="token punctuation">{<!-- --></span>
				String formatted <span class="token operator">=</span> LogFormatUtils<span class="token punctuation">.</span><span class="token function">formatValue</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> <span class="token operator">!</span>traceOn<span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token keyword">return</span> <span class="token string">"Resume with async result ["</span> <span class="token operator">+</span> formatted <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			invocableMethod <span class="token operator">=</span> invocableMethod<span class="token punctuation">.</span><span class="token function">wrapConcurrentResult</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token comment">/*执行请求处理*/</span>
		invocableMethod<span class="token punctuation">.</span><span class="token function">invokeAndHandle</span><span class="token punctuation">(</span>webRequest<span class="token punctuation">,</span> mavContainer<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">isConcurrentHandlingStarted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token keyword">return</span> null<span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token comment">// 获取并返回ModelAndView</span>
		<span class="token keyword">return</span> <span class="token function">getModelAndView</span><span class="token punctuation">(</span>mavContainer<span class="token punctuation">,</span> modelFactory<span class="token punctuation">,</span> webRequest<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span>
		webRequest<span class="token punctuation">.</span><span class="token function">requestCompleted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		/*执行请求处理,继续点进去*/
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		...省略不重要代码
		}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		/*获取方法的参数*/
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		/*里面是真的要执行了*/
		return doInvoke(args);
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			/*利用反射的方式执行方法,就是method.invoke(object, args)*/
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			...省略不重要代码
		}
		catch (InvocationTargetException ex) {
			...省略不重要代码
		}
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

请求已经处理完了,还有一步返回ModelAndView
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView

	@Nullable
	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
	modelFactory<span class="token punctuation">.</span><span class="token function">updateModel</span><span class="token punctuation">(</span>webRequest<span class="token punctuation">,</span> mavContainer<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>mavContainer<span class="token punctuation">.</span><span class="token function">isRequestHandled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">return</span> null<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token comment">/*从ModelAndViewContainer中获取ModelMap*/</span>
	ModelMap model <span class="token operator">=</span> mavContainer<span class="token punctuation">.</span><span class="token function">getModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">/*创建ModelAndView*/</span>
	ModelAndView mav <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ModelAndView</span><span class="token punctuation">(</span>mavContainer<span class="token punctuation">.</span><span class="token function">getViewName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> model<span class="token punctuation">,</span> mavContainer<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
	<span class="token comment">/*返回ModelAndView*/</span>
	<span class="token keyword">return</span> mav<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

viewResolver通过viewName获取view

现在已经处理完请求,返回了ModelAndView,回到DispatcServlet的doDispatch方法中,进行下一步的分析。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	...
	/*处理ModelAndView到View,再渲染jsp,通过response输出页面的过程 */
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	...
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

org.springframework.web.servlet.DispatcherServlet#processDispatchResult

	/**
	 * 处理handler选择和handler调用的结果,该结果要么是ModelAndView,要么是要解析到ModelAndView的异常。
	 */
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
	<span class="token keyword">boolean</span> errorView <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
	<span class="token comment">/*发生异常的处理 */</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>exception <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>exception <span class="token keyword">instanceof</span> <span class="token class-name">ModelAndViewDefiningException</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			logger<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"ModelAndViewDefiningException encountered"</span><span class="token punctuation">,</span> exception<span class="token punctuation">)</span><span class="token punctuation">;</span>
			mv <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>ModelAndViewDefiningException<span class="token punctuation">)</span> exception<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getModelAndView</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
			Object handler <span class="token operator">=</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">!=</span> null <span class="token operator">?</span> mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
			mv <span class="token operator">=</span> <span class="token function">processHandlerException</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handler<span class="token punctuation">,</span> exception<span class="token punctuation">)</span><span class="token punctuation">;</span>
			errorView <span class="token operator">=</span> <span class="token punctuation">(</span>mv <span class="token operator">!=</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>

	<span class="token comment">// 处理器是否返回一个要渲染的view?</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>mv <span class="token operator">!=</span> null <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>mv<span class="token punctuation">.</span><span class="token function">wasCleared</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">/*viewResolver通过ModelAndView获取View,进行渲染*/</span>
		<span class="token function">render</span><span class="token punctuation">(</span>mv<span class="token punctuation">,</span> request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>errorView<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			WebUtils<span class="token punctuation">.</span><span class="token function">clearErrorRequestAttributes</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码

	<span class="token keyword">if</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// 调用该Handler上所有拦截器链中的afterCompletion方法</span>
		mappedHandler<span class="token punctuation">.</span><span class="token function">triggerAfterCompletion</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

在render方法中就会开始进行,里面将会遍历viewResolvers,尝试通过ModelAndView获取View对象。然后通过View对象的render方法,进行渲染工作。
org.springframework.web.servlet.DispatcherServlet#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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);
	View view<span class="token punctuation">;</span>
	<span class="token comment">/*通过ModelAndView获取viewName*/</span>
	String viewName <span class="token operator">=</span> mv<span class="token punctuation">.</span><span class="token function">getViewName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>viewName <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// ModelAndView中包含viewName,则尝试通过viewResolver解析</span>
		view <span class="token operator">=</span> <span class="token function">resolveViewName</span><span class="token punctuation">(</span>viewName<span class="token punctuation">,</span> mv<span class="token punctuation">.</span><span class="token function">getModelInternal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> locale<span class="token punctuation">,</span> request<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>view <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ServletException</span><span class="token punctuation">(</span><span class="token string">"Could not resolve view with name '"</span> <span class="token operator">+</span> mv<span class="token punctuation">.</span><span class="token function">getViewName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span>
					<span class="token string">"' in servlet with name '"</span> <span class="token operator">+</span> <span class="token function">getServletName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// 不需要查找:ModelAndView对象包含实际的视图对象。</span>
		view <span class="token operator">=</span> mv<span class="token punctuation">.</span><span class="token function">getView</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>view <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ServletException</span><span class="token punctuation">(</span><span class="token string">"ModelAndView ["</span> <span class="token operator">+</span> mv <span class="token operator">+</span> <span class="token string">"] neither contains a view name nor a "</span> <span class="token operator">+</span>
					<span class="token string">"View object in servlet with name '"</span> <span class="token operator">+</span> <span class="token function">getServletName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>

	<span class="token comment">// 委托给view对象进行渲染。</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Rendering view ["</span> <span class="token operator">+</span> view <span class="token operator">+</span> <span class="token string">"] "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>mv<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			response<span class="token punctuation">.</span><span class="token function">setStatus</span><span class="token punctuation">(</span>mv<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token comment">/*视图渲染(jsp),然后通过response返回渲染的页面*/</span>
		view<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span>mv<span class="token punctuation">.</span><span class="token function">getModelInternal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isDebugEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			logger<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Error rendering view ["</span> <span class="token operator">+</span> view <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">throw</span> ex<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

这里就会遍历所有的viewResolver,都尝试获取View,如果获取到了就返回。
org.springframework.web.servlet.DispatcherServlet#resolveViewName

	@Nullable
	protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {
	<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>viewResolvers <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">/*遍历所有的viewResolver*/</span>
		<span class="token keyword">for</span> <span class="token punctuation">(</span>ViewResolver viewResolver <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>viewResolvers<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">/*尝试通过viewResolver解析viewName,看是否能返回view*/</span>
			View view <span class="token operator">=</span> viewResolver<span class="token punctuation">.</span><span class="token function">resolveViewName</span><span class="token punctuation">(</span>viewName<span class="token punctuation">,</span> locale<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token comment">/*获取都view不为空,则返回*/</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>view <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token keyword">return</span> view<span class="token punctuation">;</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">return</span> null<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里会判断一下是否有缓存,有的话就尝试从缓存中取,没有的话直接创建
org.springframework.web.servlet.view.AbstractCachingViewResolver#resolveViewName

	@Override
	@Nullable
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		if (!isCache()) {
			/*不存在缓存的情况下直接创建view*/
			return createView(viewName, locale);
		}
		else {
			/*否则先尝试从缓存中取*/
			Object cacheKey = getCacheKey(viewName, locale);
			View view = this.viewAccessCache.get(cacheKey);
			if (view == null) {
				synchronized (this.viewCreationCache) {
					view = this.viewCreationCache.get(cacheKey);
					if (view == null) {
						// Ask the subclass to create the View object.
						view = createView(viewName, locale);
						if (view == null && this.cacheUnresolved) {
							view = UNRESOLVED_VIEW;
						}
						if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
							this.viewAccessCache.put(cacheKey, view);
							this.viewCreationCache.put(cacheKey, view);
						}
					}
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace(formatKey(cacheKey) + "served from cache");
				}
			}
			return (view != UNRESOLVED_VIEW ? view : null);
		}
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

我们直接看createView方法,因为当前viewResolver默认的类型是InternalResourceViewResolver,在上面的DispatcherServlet.properties中可以看到。而InternalResourceViewResolver又继承了UrlBasedViewResolver,createView方法在UrlBasedViewResolver中重写了,所以这里首先会调用类UrlBasedViewResolver的createView方法
org.springframework.web.servlet.view.UrlBasedViewResolver#createView

	@Override
	protected View createView(String viewName, Locale locale) throws Exception {
		// 如果这个解析器不支持处理给定的view,
		// 返回null以传递给链中的下一个解析器
		if (!canHandle(viewName, locale)) {
			return null;
		}
	<span class="token comment">// "redirect:" 前缀,重定向的处理。</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>viewName<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span>REDIRECT_URL_PREFIX<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		String redirectUrl <span class="token operator">=</span> viewName<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>REDIRECT_URL_PREFIX<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		RedirectView view <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RedirectView</span><span class="token punctuation">(</span>redirectUrl<span class="token punctuation">,</span>
				<span class="token function">isRedirectContextRelative</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">isRedirectHttp10Compatible</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		String<span class="token punctuation">[</span><span class="token punctuation">]</span> hosts <span class="token operator">=</span> <span class="token function">getRedirectHosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>hosts <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			view<span class="token punctuation">.</span><span class="token function">setHosts</span><span class="token punctuation">(</span>hosts<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">return</span> <span class="token function">applyLifecycleMethods</span><span class="token punctuation">(</span>REDIRECT_URL_PREFIX<span class="token punctuation">,</span> view<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token comment">// "forward:" 前缀,内部转发的处理。</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>viewName<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span>FORWARD_URL_PREFIX<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		String forwardUrl <span class="token operator">=</span> viewName<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>FORWARD_URL_PREFIX<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		InternalResourceView view <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InternalResourceView</span><span class="token punctuation">(</span>forwardUrl<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">return</span> <span class="token function">applyLifecycleMethods</span><span class="token punctuation">(</span>FORWARD_URL_PREFIX<span class="token punctuation">,</span> view<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token comment">// 否则回到超类实现:调用loadView.</span>
	<span class="token keyword">return</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">createView</span><span class="token punctuation">(</span>viewName<span class="token punctuation">,</span> locale<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

org.springframework.web.servlet.view.AbstractCachingViewResolver#createView
这里除了处理重定向和转发的处理,剩下的就是回调父类AbstractCachingViewResolver的createView方法,然后里面又会调用loadView方法。

	@Nullable
	protected View createView(String viewName, Locale locale) throws Exception {
		return loadView(viewName, locale);
	}

 
 
  • 1
  • 2
  • 3
  • 4

UrlBasedViewResolver重写了loadView方法,所以又会回到UrlBasedViewResolver中,这里就会真正的创建View并返回
org.springframework.web.servlet.view.UrlBasedViewResolver#loadView

	@Override
	protected View loadView(String viewName, Locale locale) throws Exception {
		/* 通过viewName获取View的子类AbstractUrlBasedView */
		AbstractUrlBasedView view = buildView(viewName);
		/* 执行bean的初始化回调方法initializeBean */
		View result = applyLifecycleMethods(viewName, view);
		// 返回view
		return (view.checkResource(locale) ? result : null);
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

buildView方法会调用InternalResourceViewResolver的buildView,然后又会调用父类UrlBasedViewResolver的buildView方法
org.springframework.web.servlet.view.InternalResourceViewResolver#buildView

	@Override
	protected AbstractUrlBasedView buildView(String viewName) throws Exception {
		/*调用UrlBasedViewResolver的buildView方法*/
		InternalResourceView view = (InternalResourceView) super.buildView(viewName);
		if (this.alwaysInclude != null) {
			view.setAlwaysInclude(this.alwaysInclude);
		}
		view.setPreventDispatchLoop(true);
		return view;
	}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

buildView方法中会反射实例化一个AbstractUrlBasedView对象,然后对他进行一推的属性设置
org.springframework.web.servlet.view.UrlBasedViewResolver#buildView

	protected AbstractUrlBasedView buildView(String viewName) throws Exception {
		Class<?> viewClass = getViewClass();
		Assert.state(viewClass != null, "No view class");
	AbstractUrlBasedView view <span class="token operator">=</span> <span class="token punctuation">(</span>AbstractUrlBasedView<span class="token punctuation">)</span> BeanUtils<span class="token punctuation">.</span><span class="token function">instantiateClass</span><span class="token punctuation">(</span>viewClass<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">/*添加前缀以及后缀*/</span>
	view<span class="token punctuation">.</span><span class="token function">setUrl</span><span class="token punctuation">(</span><span class="token function">getPrefix</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> viewName <span class="token operator">+</span> <span class="token function">getSuffix</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	view<span class="token punctuation">.</span><span class="token function">setAttributesMap</span><span class="token punctuation">(</span><span class="token function">getAttributesMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

	String contentType <span class="token operator">=</span> <span class="token function">getContentType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>contentType <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		view<span class="token punctuation">.</span><span class="token function">setContentType</span><span class="token punctuation">(</span>contentType<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	String requestContextAttribute <span class="token operator">=</span> <span class="token function">getRequestContextAttribute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>requestContextAttribute <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		view<span class="token punctuation">.</span><span class="token function">setRequestContextAttribute</span><span class="token punctuation">(</span>requestContextAttribute<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	Boolean exposePathVariables <span class="token operator">=</span> <span class="token function">getExposePathVariables</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>exposePathVariables <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		view<span class="token punctuation">.</span><span class="token function">setExposePathVariables</span><span class="token punctuation">(</span>exposePathVariables<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	Boolean exposeContextBeansAsAttributes <span class="token operator">=</span> <span class="token function">getExposeContextBeansAsAttributes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>exposeContextBeansAsAttributes <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		view<span class="token punctuation">.</span><span class="token function">setExposeContextBeansAsAttributes</span><span class="token punctuation">(</span>exposeContextBeansAsAttributes<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	String<span class="token punctuation">[</span><span class="token punctuation">]</span> exposedContextBeanNames <span class="token operator">=</span> <span class="token function">getExposedContextBeanNames</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>exposedContextBeanNames <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		view<span class="token punctuation">.</span><span class="token function">setExposedContextBeanNames</span><span class="token punctuation">(</span>exposedContextBeanNames<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token keyword">return</span> view<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

最后就会返回这个view,然后调用他的render方法

view的渲染

先回到DispatchServlet的render方法,看看我们接下来要看的代码

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	/*视图渲染(jsp),然后通过response返回渲染的页面*/
	view.render(mv.getModelInternal(), request, response);
}

 
 
  • 1
  • 2
  • 3
  • 4

org.springframework.web.servlet.view.AbstractView#render

	/**
	 * 根据指定的模型准备视图,如有必要,将其与静态属性和RequestContext属性合并。
	 * 委托renderMergedOutputModel进行实际的渲染。
	 * @see #renderMergedOutputModel
	 */
	@Override
	public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
			HttpServletResponse response
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值