`

Spring MVC 源码浅析

 
阅读更多
初识SpringMVC

SpringMVC现在较之Struts2可谓是出尽风头大红大致,但是作为一个表现层框架,SpringMVC也不得不面对,Web开发中表现层所要面对几大难题:

  • url与具体的逻辑处理Controller类的映射关系处理
  • HTTP请求参数的绑定
  • HTTP返回响应的渲染

    初始化主线

    Spring MVC 运行基本流程要素

    在原始的纯servlet时代,我们对于请求响应的做法一般是在web.xml中定义一组、的做法,但是当请求响应成倍增加 时,web.xml中的定义变得难以控制,所以现在大多的MVC框架都采取了定义一个核心的分发器来处理所有的请求,大大减轻了web.xml的维护负担。 而SpringMVC正是提炼处理了org.springframework.web.servlet.DispatcherServlet作为其核心分发器,(Struts2则使用了Dispatcher作为其核心 分发器) 故而像Spring、Struts2一样Spring MVC在web.xml也具有自己的程序入口配置,DispatcherServlet被称为核心分发器

 

 <!-- Spring MVC -->
  <servlet>
    <servlet-name>springServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:/spring-mvc*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springServlet</servlet-name>
    <url-pattern>/</url-pattern><!-- 慎重使用"/*"可能导致dispatcher二次分发 -->
  </servlet-mapping>

 提炼出了核心分发器,我们将面临如何使用这一个核心分发器将之前的大量的请求准确的找到他们想要的执行器?也即:

 

  • 核心分发器可按照一定的规则将不同的HTTP请求分发到不同的servlet对象上去处理
  • 核心分发器建立起一套完整的对所有的HTTP请求进行规范化处理的流程

DispatcherServlet的提炼是SpringMVC框架规范化编程的第一步,DispatcherServlet将之前的普通Servlet抽象出来规范化成一个统一的Servlet, 接下来DispatcherServlet要做的就是如何将这些不同的HTTP请求分发到对应的Servlet上,也即:HTTP协议到java对象世界的转换处理。 SpringMVC为我们提供的方案是采用组件的形式:将整个流程处理的每个步骤交给各个组件去处理

  • 处理流程规范化:将处理流程划分成若干个步骤,使用一套明确的逻辑主线将所有主线串连起来
  • 处理流程组件化:将处理流程划分成若干个步骤,将每个步骤定义成接口的形式,并为没个接口赋予不同的实现方式,也大大方便扩展

通过以上可知:处理流程的规范化是通过流程步骤的划分流程组件化定义来实现的。流程步骤的划分一般可分成:

  • 1.对HTTP请求初步处理,找到对应的Controller-------程序化实现---------对应SpringMVC中的 HandlerMapping接口
  • 2.调用相应Controller中的处理方法-----------------程序化实现---------对应SpringMVC中的HandlerAdapter接口
  • 3.对于处理类Controller中可能出现的异常进行处理----程序化实现---------对应SpringMVC中的HandlerExceptionResolver接口
  • 4.根据处理类Controller处理结果,进行HTTP响应处理--程序化实现---------对应SpringMVC中的ViewResolver接口

SpringMVC组件几乎涵盖了每一个处理过程中的重要节点。

了解了SpringMVC的大体思想其实是在HTTP请求到HTTP响应返回过程中将请求到响应的处理过程规范化成一个个组件来完成,那么这些组件是如何协作、 串联起来完成一个完整的HTTP请求的呢? 回过头我们来捋一捋SpringMVC的大体思路:

  • 将HTTP请求响应过程抽象成一个个处理单元
  • 定义一系列的接口(即组件)对应具体的处理单元
  • 由DispatcherServlet将这些组件串联起来,完成整个HTTP请求到响应的过程

在整个过程中DispatcherServlet和组件维持着一个相互支撑的关系:

  • DispatcherServlet:串联着整个流程的组件。
  • 组件:流程规范化分解步骤的具体程序实现,起承上启下的作用,处理单元的实际执行者。

DispatcherServlet

从DispatcherServlet所实现的接口来看其实它是一个标准的Serlvet。 根据Servlet规范的定义,Servlet中包含两大核心方法init和service,他们的运行时间和触发条件也都不相同

  • init 整个应用程序启动时,并且只运行一次。故而init方法中包含我们对整个应用程序的初始化过程,包括对容器(WebApplicationContext)、组件和 外部资源的初始化等等。
  • service 在整个应用程序运行过程中处于侦听模式,侦听并处理所有HTTP请求。因此,service方法中包含的是对于HTTP请求的具体处理逻辑。

Servlet的这一特性(根据Serlvet对象中的方法的不同生命周期进行划分),将SpringMVC分成两条互补干扰的主线:

  •  1.init方法的初始化主线
  •  2.service方法中的HTTP请求处理主线

 

<!-- Spring MVC -->
  <servlet>
    <servlet-name>springServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:/spring-mvc*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springServlet</servlet-name>
    <url-pattern>/</url-pattern><!-- 慎重使用"/*"可能导致dispatcher二次分发 -->
  </servlet-mapping>

 DispatcherServlet默认使用WebApplicationContext作为上下文,SpringMVC默认配置文件为“/WEB-INF/[servlet名字]-servlet.xml”。 DispatcherServlet可配置参数:

 

参数 描述
contextClass 实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定, 默认使用XmlWebApplicationContext。
contextConfigLocation 传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。
namespace WebApplicationContext命名空间。默认值是[server-name]-servlet。

通过上述配置SpringMVC将加载classpath:/spring-mvc.xml来初始化SpringMVC上下文

 

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath*:/context/spring-*.xml,
			classpath*:/service/spring-soap*.xml
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

 以上配置是Spring集成web环境的通用配置,主要在初始化中加载Dao、Service方法,便于与其他web框架集成。 * contextConfigLocation:表示用于加载Bean的配置文件; * contextClass:表示用于加载Bean的ApplicationContext实现类,默认WebApplicationContext。

 

创建完毕后会将该上下文放在ServletContext中:servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

ContextLoaderListener和DispatcherServlet加载的初始化上下文关系如图:



 从上图中我们可以得出: ContextLoaderListener初始化上下文加载的是整个应用程序公用的bean,而DispatcherServlet加载的初始化上下文是只对SpringMVC 有效的bean,eg:handlerMapping、handlerAdapter、Controller等等。

DispatcherServlet继承关系:



 

1.HttpSerlvetBean继承HttpServlet,因此,web容器启动时将调用其init方法,该初始化方法主要是将Servlet初始化参数(init-param)设置到 该组件上(如contextAttribute、contextClass、namespace、contextConfigLocation)通过BeanWrapper简化设置过程,方便后续使用。 提供给子类扩展点initServletBean(),该方法由FrameworkServlet覆盖。

 

@SuppressWarnings("serial")
public abstract class HttpServletBean extends HttpServlet
		implements EnvironmentCapable, EnvironmentAware {
@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}
		//将Servlet初始化参数设置到该组件上
		//如contextAttribute、contextClass、namespace、contextConfigLocation
		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}
		//提供给子类扩展点initServletBean(),该方法由FrameworkServlet覆盖。
		// Let subclasses do whatever initialization they like.
		initServletBean();

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

 2.FrameworkServlet继承HttpServletBean,通过initServletBean()进行web上下文初始化,该覆盖方法主要做了两件事:

 

  • 1.初始化web上下文
  • 2.提供子类初始化扩展点

 

@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			//初始化web上下文
			this.webApplicationContext = initWebApplicationContext();
			//提供子类初始化扩展点
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}
}

 

@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
	protected WebApplicationContext initWebApplicationContext() {
		//ROOT上下文(ContextLoaderListener加载的,即SpringMVC的父上下文)
			WebApplicationContext rootContext =
					WebApplicationContextUtils.getWebApplicationContext(getServletContext());
			WebApplicationContext wac = null;
	
			if (this.webApplicationContext != null) {
				//创建该servlet注入的上下文
				// A context instance was injected at construction time -> use it
				wac = this.webApplicationContext;
				if (wac instanceof ConfigurableWebApplicationContext) {
					ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
					if (!cwac.isActive()) {
						// The context has not yet been refreshed -> provide services such as
						// setting the parent context, setting the application context id, etc
						if (cwac.getParent() == null) {
							// The context instance was injected without an explicit parent -> set
							// the root application context (if any; may be null) as the parent
							cwac.setParent(rootContext);
						}
						configureAndRefreshWebApplicationContext(cwac);
					}
				}
			}
			if (wac == null) {
				// No context instance was injected at construction time -> see if one
				// has been registered in the servlet context. If one exists, it is assumed
				// that the parent context (if any) has already been set and that the
				// user has performed any initialization such as setting the context id
				//查找已绑定的上下文
				wac = findWebApplicationContext();
			}
			if (wac == null) {
				// No context instance is defined for this servlet -> create a local one
				//如果没有找到相应的上下文,并指定父亲为ContextLoaderListener  
				wac = createWebApplicationContext(rootContext);
			}
	
			if (!this.refreshEventReceived) {
				// Either the context is not a ConfigurableApplicationContext with refresh
				// support or the context injected at construction time had already been
				// refreshed -> trigger initial onRefresh manually here.
				//刷新上下文(执行一些初始化)  
				onRefresh(wac);
			}
	
			if (this.publishContext) {
				// Publish the context as a servlet context attribute.
				String attrName = getServletContextAttributeName();
				getServletContext().setAttribute(attrName, wac);
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
							"' as ServletContext attribute with name [" + attrName + "]");
				}
			}
	
			return wac;
		}
}

 从initWebApplicationContext()方法中可以看出,ContextLoaderListener加载的上下文作为了DispatcherServlet加载的上下文的父上下文,他们之间 形成了上下文的层级关系。 最后通过调用onRefresh(wac)方法,来进行容器的初始化操作,这个具体实现交给了子类。

 

3.DispatcherServlet继承了FrameworkServlet方法并且实现了onRefresh()方法提供了一些前端控制器相关的配置

 

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
	//实现子类的onRefresh()方法,该方法委托为initStrategies()方法。  
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	 //初始化默认的SpringMVC使用的策略(eg:HandlerMapping)
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
}

如上代码,DispatcherServlet启动时会进行我们需要的web层bean的配置,如HandlerMapping、HandlerAdapter等,若我们没有配置,SpringMVC也会 为我们提供默认配置。综上可知,DispatcherServlet初始化实际完成了两项工作:

  •  1.初始化SpringMVC的上下文,并可能将ContextLoaderListener加载的上下文设置为父上下文 
  • 2.初始化DisaptcherServlet使用的策略,eg,HandlerMapping、HandlerAdapter等 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.annotation.DefaultAnnotationHandlerMapping

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

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
	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

 如上配置可知,DispatcherServlet启动时会自动注册这些特殊的bean,若我们注册了,那么默认的将不会被注册。

DispatcherServlet中使用的特使的Bean
  • 1.Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理;
  • 2.HandlerMapping:请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器) 对象、多个HandlerInterceptor拦截器)对象;如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器;
  • 3.HandlerAdapter:HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支 持很多类型的处理器;如SimpleControllerHandlerAdapter将对实现了Controller接口的Bean进行适配,并且invoke处理器的handleRequest方法进行功能处理;
  • 4.ViewResolver:ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视 图名映射为jsp视图;
  • 5.LocalResolver:本地化解析,因为Spring支持国际化,因此LocalResover解析客户端的Locale信息从而方便进行国际化;
  • 6.ThemeResolver:主题解析,通过它来实现一个页面多套风格,即常见的类似于软件皮肤效果;
  • 7.MultipartResolver:文件上传解析,用于支持文件上传;
  • 8.HandlerExceptionResolver:处理器异常解析,可以将异常映射到相应的统一错误界面,从而显示用户友好的界面(而不是给用户看到具体的错误信息);
  • 9.RequestToViewNameTranslator:当处理器没有返回逻辑视图名等相关信息时,自动将请求URL映射为逻辑视图名;
  • 10.FlashMapManager:用于管理FlashMap的策略接口,FlashMap用于存储一个请求的输出,当进入另一个请求时作为该请求的输入,通常用于重定向场景。

运行主线

SpringMVC URL映射处理机制

当一个HTTP请求到来时,DispatcherServlet首先接收,然后调用servlet标准的doGet或doPost方法,经过几次转发后,最终将会循环遍历DispatcherServlet中注册的 private List<HandlerMapping> handlerMappings;并调用他们的getHandler方法,并返回他们中第一个返回值不为null的HandlerMapping。

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

 经过上面getHandler处理,我们获得了一个HandlerExecutionChain对象。

public interface HandlerMapping {
		... ...
		HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
	}

 我们可以明显的看到getHandler只有唯一一个参数HttpServletRequest,我们可以从中获取请求头、url路径、cookie、session、参数等等信息,来决策HandlerExecutionChain的生成。

扩展点1:

我们可以创建一个实现HandlerMapping的类,依据自己自定义的决策决定一个web请求到HandlerExecutionChain对象的生成。

HandlerExecutionChain这个类从字面的意思可以看出他一个执行链的封装。类似于Struts2中被Interceptor包裹的Action形成的执行链

public class HandlerExecutionChain {

	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

	private final Object handler;

	private HandlerInterceptor[] interceptors;

	private List<HandlerInterceptor> interceptorList;

	private int interceptorIndex = -1;
	
	... ...	
	}

 HandlerExecutionChain这个类虽然不长,但是我们只需关注 private final Object handler; private HandlerInterceptor[] interceptors; 这两行代码就OK了。 这就类似于Struts2一样,只有一个实际执行类,外加一堆的拦截器,形成了HandlerExecutionChain执行链。

HandlerInterceptor也是SpringMVC中的核心接口,其定义如下:

public interface HandlerInterceptor {
	/**
	 * 
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler chosen handler to execute, for type and/or instance evaluation
	 * @return {@code true} if the execution chain should proceed with the
	 * next interceptor or the handler itself. Else, DispatcherServlet assumes
	 * that this interceptor has already dealt with the response itself.
	 * @throws Exception in case of errors
	 */
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
	    throws Exception;

	/**
	 * 
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler (or {@link HandlerMethod}) that started async
	 * execution, for type and/or instance examination
	 * @param modelAndView the {@code ModelAndView} that the handler returned
	 * (can also be {@code null})
	 * @throws Exception in case of errors
	 */
	void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception;

	/**
	 * 
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler (or {@link HandlerMethod}) that started async
	 * execution, for type and/or instance examination
	 * @param ex exception thrown on handler execution, if any
	 * @throws Exception in case of errors
	 */
	void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception;

}

 从HandlerInterceptor接口中的方法,外加如果我们熟悉Struts2的那一套执行流程,那么SpringMVC的HandlerExecutionChain执行链的过程就显而易见了。 在真正调用handler对象之前,将会遍历由HandlerInterceptor接口实现类组成的数组,然后首先调用preHandle()方法,然后才调用真正的handler对象。

handler对象被调用后,生成了需要的响应内容,然后在将视图渲染之前(即将处理结果写到HttpServletResponse中)我们将依次循环遍历HandlerInterceptor 接口实现类组成的数组调用其postHandle()方法。视图渲染结束后,再依次调用其afterCompletion()方法,这样一个HTTP请求处理过程就结束了。

扩展点2:

我们可以自定义HandlerInterceptor的实现类,我们可以在一个请求在被处理之前、被处理之后而未进行视图渲染时、视图渲染结束后三个时间点去做我们想做的事。 SpringMVC也是吸收了Struts2 这种拦截器包裹action在真正请求处理之前和之后做一些工作。

那么HandlerExecutionChain中定义的Object引用的Handler对象是怎么被调用的呢? 带着这些疑问我们来看看SpringMVC另一个核心接口:HandlerAdapter

public interface HandlerAdapter {
	
	boolean supports(Object handler);
	
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	
	long getLastModified(HttpServletRequest request, Object handler);
}

 在DispatcherServlet中定义了如下内容:

/** List of HandlerMappings used by this servlet */
	private List<HandlerMapping> handlerMappings;

	/** List of HandlerAdapters used by this servlet */
	private List<HandlerAdapter> handlerAdapters;

 以及

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

 很明显,handler会被当做参数传入getHandlerAdapter()方法中,然后方法内循环遍历由handlerAdapter接口实现类组成的数组,并返回第一个support 该handler的HandlerAdapter对象,然后使用返回的HandlerAdapter对象中的handle方法来处理handler对象,并返回ModelAndView这个包含了视图和数据 的对象。

扩展点3:

自定义HandlerAdapter的实现类,用自己的自定义实现类来规定handler的执行规则。

接下来我们再关注一下SpringMVC中最后一个核心接口:View (至于ModelAndView作为SpringMVC中的一个VO,没有什么逻辑就不拿出来研究了)

public interface View {

	String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";

	String PATH_VARIABLES = View.class.getName() + ".pathVariables";

	String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

	String getContentType();

	void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

 从View接口方法中可以看出,所有数据,最终都会作为一个Map对象的形式传入到render()方法中,调用这个reader()方法就完成了视图到响应的渲染。 这个View的实现类,就是来自HandlerAdapter的handle方法的返回结果。当然从ModelAndView到真正的View实现类有一个解析过程,ModelAndView中可以有 真正的视图对象,也可以只有一个视图的名字,SpringMVC会负责将视图名称解析成真正的视图对象。

在一个典型的SpringMVC调用中,HandlerExecutionChain中封装的Handler对象就是用@Controller注解标识的类的一个实例,根据类级别和方法级别的 @RequestMapping注解,由默认注册的DefaultAnnotationHandlerMapping(3.1.3更新为RequestMappingHandlerMapping类,为了向后兼容DefaultAnnotationHanlderMapping 仍然可以使用)生成HandlerExecutionChain对象,再由AnnotationMethodHandlerAdapter(3.1.3更新为RequestMappingHandlerAdapter类,为了向后兼容 AnnotationMethodHandlerAdapter仍可以使用)来执行HandlerExecutionChain对象,生成最终的ModelAndView对象,再由具体的View对象的render方法渲染视图。

  • 大小: 72.5 KB
  • 大小: 62.4 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics