模型视图详解

模型视图详解

在doDispatch中,SpringMVC会调用HandlerAdapter的handler方法获取ModelAndView对象,最后进行视图解析,视图渲染,都跟这个对象有着密切的关联。

/**
 * Holder for both Model and View in the web MVC framework.
 * Note that these are entirely distinct. This class merely holds
 * both to make it possible for a controller to return both model
 * and view in a single return value.
 *
 * <p>Represents a model and view returned by a handler, to be resolved
 * by a DispatcherServlet. The view can take the form of a String
 * view name which will need to be resolved by a ViewResolver object;
 * alternatively a View object can be specified directly. The model
 * is a Map, allowing the use of multiple objects keyed by name.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @author Rossen Stoyanchev
 * @see DispatcherServlet
 * @see ViewResolver
 * @see HandlerAdapter#handle
 * @see org.springframework.web.servlet.mvc.Controller#handleRequest
 */
public class ModelAndView {

	/** View instance or view name String */
	private Object view;

	/** Model Map */
	private ModelMap model;

	/** Optional HTTP status for the response */
	private HttpStatus status;

	/** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
	private boolean cleared = false;
}

doDispatch中获取ModelAndView

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

下面,我将详细分析模型视图,其间将会看到常见ModelAttribute,参数解析,返回值转换等SpringMVC的主要内容。

1. 全局初始化:@ControllerAdvice

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean

透过RequestMappingHandlerAdapter的实现,我们知道RequestMappingHandlerAdapter是实现InitializingBean的,明显在初始化完成后会调用afterPropertiesSet方法,在afterPropertiesSet会调用initControllerAdviceCache()方法。
initControllerAdviceCache序列图

  • 查找并构造ControllerAdviceBean
    会从Bean容器中查找带有@ControllerAdvice注解的bean
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
  List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
  for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
    if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
      beans.add(new ControllerAdviceBean(name, applicationContext));
    }
  }
  return beans;
}
  • 查找所有@ModelAttribute注解的方法
    会选择不包含@RequestMapping但包汉@ModelAttribute注解的方法
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
  @Override
  public boolean matches(Method method) {
    return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
        (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
  }
};
  • 查找@InitBinder方法
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
		@Override
		public boolean matches(Method method) {
			return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
		}
	};
  • 缓存所有的requestResponseBodyAdviceBeans
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
  requestResponseBodyAdviceBeans.add(bean);
  if (logger.isInfoEnabled()) {
    logger.info("Detected RequestBodyAdvice bean in " + bean);
  }
}
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
  requestResponseBodyAdviceBeans.add(bean);
  if (logger.isInfoEnabled()) {
    logger.info("Detected ResponseBodyAdvice bean in " + bean);
  }
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
  this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}

2. 模型那些事

  • 相关类图Overview
    模型相关类图
  • 序列图Overview
    ModelAttribute序列图
  • 浅谈SessionAttributeHandler
    由前面的类图可知,SeesionAttibuteHandler代理了SessionAttibuteStore。而SessionAttibuteStore提供了简化从request的作用域中存取对象的操作,默认的DefaultSessionAttributeStore则是从session作用域中存取。
public interface SessionAttributeStore {
	/**
	 * Store the supplied attribute in the backend session.
	 * <p>Can be called for new attributes as well as for existing attributes.
	 * In the latter case, this signals that the attribute value may have been modified.
	 * @param request the current request
	 * @param attributeName the name of the attribute
	 * @param attributeValue the attribute value to store
	 */
	void storeAttribute(WebRequest request, String attributeName, Object attributeValue);

	/**
	 * Retrieve the specified attribute from the backend session.
	 * <p>This will typically be called with the expectation that the
	 * attribute is already present, with an exception to be thrown
	 * if this method returns {@code null}.
	 * @param request the current request
	 * @param attributeName the name of the attribute
	 * @return the current attribute value, or {@code null} if none
	 */
	Object retrieveAttribute(WebRequest request, String attributeName);

	/**
	 * Clean up the specified attribute in the backend session.
	 * <p>Indicates that the attribute name will not be used anymore.
	 * @param request the current request
	 * @param attributeName the name of the attribute
	 */
	void cleanupAttribute(WebRequest request, String attributeName);
}

DefaultSessionAttributeStore中的实现,我们一其中一个实现一窥究竟。

@Override
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
  Assert.notNull(request, "WebRequest must not be null");
  Assert.notNull(attributeName, "Attribute name must not be null");
  Assert.notNull(attributeValue, "Attribute value must not be null");
  String storeAttributeName = getAttributeNameInSession(request, attributeName);
  request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
}

在构造SessionAttributeHandler时,会构造绑定到Session作用域的对象名称,通过@SessionAttributes注解来定义。

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
		Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
		this.sessionAttributeStore = sessionAttributeStore;

		SessionAttributes annotation =
				AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
		if (annotation != null) {
			this.attributeNames.addAll(Arrays.asList(annotation.names()));
			this.attributeTypes.addAll(Arrays.asList(annotation.types()));
		}

		for (String attributeName : this.attributeNames) {
			this.knownAttributeNames.add(attributeName);
		}
	}

在调用完成所有@ModelAttribute注解的方法后,会解析HandlerMethod方法的参数中带有@ModelAttribute注解的参数,如果在ModelAndViewContainer容器中没有包含此ModelAttribute,则会从Session作用域中查找该值,如果不存在,直接抛出异常,交给框架顶层处理。

for (String name : findSessionAttributeArguments(handlerMethod)) {
  if (!container.containsAttribute(name)) {
    Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
    if (value == null) {
      throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
    }
    container.addAttribute(name, value);
  }
}
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
  List<String> result = new ArrayList<String>();
  for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
    if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
      String name = getNameForParameter(parameter);
      Class<?> paramType = parameter.getParameterType();
      if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
        result.add(name);
      }
    }
  }
  return result;
}
  • 浅谈@ModelAttibute
    前面在@ControllerAdvice一节中,有提过,afterPropertiesSet中会全局缓存@ModelAttibute方法至modelAttributeAdviceCache中。注意@ModelAttibute注解,不仅可以出现先全局的@ControllerAdvice定义的bean中,还可以出现在@Controller注解的普通Controller中。所以在查询并构造@ModelAttibute方法时,会首先从全局缓存的@ControllerAdvice的bean中查找,然后从Controller中查找带有@ModelAttibute的方法。
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
  SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
  Class<?> handlerType = handlerMethod.getBeanType();
  Set<Method> methods = this.modelAttributeCache.get(handlerType);
  if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
    this.modelAttributeCache.put(handlerType, methods);
  }
  List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
  // Global methods first
  for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
    if (entry.getKey().isApplicableToBeanType(handlerType)) {
      Object bean = entry.getKey().resolveBean();
      for (Method method : entry.getValue()) {
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
      }
    }
  }
  for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
  }
  return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
  InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
  attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  attrMethod.setDataBinderFactory(factory);
  return attrMethod;
}

注意,@ModelAttribute方法同样支持@ModelAttribute参数,此为强依赖关系。所有的@ModelAttribute方法,最后都是缓存在ModelFactory的modelMethods中的。

public ModelFactory(List<InvocableHandlerMethod> handlerMethods,WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
  if (handlerMethods != null) {
    for (InvocableHandlerMethod handlerMethod : handlerMethods) {
      this.modelMethods.add(new ModelMethod(handlerMethod));
    }
  }
  this.dataBinderFactory = binderFactory;
  this.sessionAttributesHandler = attributeHandler;
}

ModelFactory的initModel中,获取完session作用域的对象后,会钓鱼嗯invokModelAttributeMethods方法,会依次调用所的@ModelAttribute方法。在请求的时候可以拿到request对象,很明显可以解析大量的参数,对@ModelAttribute方法的参数赋值。

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
  while (!this.modelMethods.isEmpty()) {
    InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
    ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
    if (container.containsAttribute(ann.name())) {
      if (!ann.binding()) {
        container.setBindingDisabled(ann.name());
      }
      continue;
    }

    Object returnValue = modelMethod.invokeForRequest(request, container);
    if (!modelMethod.isVoid()){
      String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
      if (!ann.binding()) {
        container.setBindingDisabled(returnValueName);
      }
      if (!container.containsAttribute(returnValueName)) {
        container.addAttribute(returnValueName, returnValue);
      }
    }
  }
}

顺便提一下,SpringMvc调用handlerMethod和ModelAttribute方法获取结果基本是一致的方式,只是handlerMethod方法需要再进行结果集的进一步加工,而ModelAttibute方法会将结果保存在ModelAndViewContainer中。

3. 参数解析

  • 类图
    参数解析相关类图
    注意这边使用了两个组合模式,parameterNameDiscoverer族和handlerMethodArgumentResolver族
  • 模型视图详解

在doDispatch中,SpringMVC会调用HandlerAdapter的handler方法获取ModelAndView对象,最后进行视图解析,视图渲染,都跟这个对象有着密切的关联。

/**
 * Holder for both Model and View in the web MVC framework.
 * Note that these are entirely distinct. This class merely holds
 * both to make it possible for a controller to return both model
 * and view in a single return value.
 *
 * <p>Represents a model and view returned by a handler, to be resolved
 * by a DispatcherServlet. The view can take the form of a String
 * view name which will need to be resolved by a ViewResolver object;
 * alternatively a View object can be specified directly. The model
 * is a Map, allowing the use of multiple objects keyed by name.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @author Rossen Stoyanchev
 * @see DispatcherServlet
 * @see ViewResolver
 * @see HandlerAdapter#handle
 * @see org.springframework.web.servlet.mvc.Controller#handleRequest
 */
public class ModelAndView {

	/** View instance or view name String */
	private Object view;

	/** Model Map */
	private ModelMap model;

	/** Optional HTTP status for the response */
	private HttpStatus status;

	/** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
	private boolean cleared = false;
}

doDispatch中获取ModelAndView

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

下面,我将详细分析模型视图,其间将会看到常见ModelAttribute,参数解析,返回值转换等SpringMVC的主要内容。

1. 全局初始化:@ControllerAdvice

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean

透过RequestMappingHandlerAdapter的实现,我们知道RequestMappingHandlerAdapter是实现InitializingBean的,明显在初始化完成后会调用afterPropertiesSet方法,在afterPropertiesSet会调用initControllerAdviceCache()方法。
initControllerAdviceCache序列图

  • 查找并构造ControllerAdviceBean
    会从Bean容器中查找带有@ControllerAdvice注解的bean
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
  List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
  for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
    if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
      beans.add(new ControllerAdviceBean(name, applicationContext));
    }
  }
  return beans;
}
  • 查找所有@ModelAttribute注解的方法
    会选择不包含@RequestMapping但包汉@ModelAttribute注解的方法
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
  @Override
  public boolean matches(Method method) {
    return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
        (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
  }
};
  • 查找@InitBinder方法
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
		@Override
		public boolean matches(Method method) {
			return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
		}
	};
  • 缓存所有的requestResponseBodyAdviceBeans
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
  requestResponseBodyAdviceBeans.add(bean);
  if (logger.isInfoEnabled()) {
    logger.info("Detected RequestBodyAdvice bean in " + bean);
  }
}
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
  requestResponseBodyAdviceBeans.add(bean);
  if (logger.isInfoEnabled()) {
    logger.info("Detected ResponseBodyAdvice bean in " + bean);
  }
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
  this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}

2. 模型那些事

  • 相关类图Overview
    模型相关类图
  • 序列图Overview
    ModelAttribute序列图
  • 浅谈SessionAttributeHandler
    由前面的类图可知,SeesionAttibuteHandler代理了SessionAttibuteStore。而SessionAttibuteStore提供了简化从request的作用域中存取对象的操作,默认的DefaultSessionAttributeStore则是从session作用域中存取。
public interface SessionAttributeStore {
	/**
	 * Store the supplied attribute in the backend session.
	 * <p>Can be called for new attributes as well as for existing attributes.
	 * In the latter case, this signals that the attribute value may have been modified.
	 * @param request the current request
	 * @param attributeName the name of the attribute
	 * @param attributeValue the attribute value to store
	 */
	void storeAttribute(WebRequest request, String attributeName, Object attributeValue);

	/**
	 * Retrieve the specified attribute from the backend session.
	 * <p>This will typically be called with the expectation that the
	 * attribute is already present, with an exception to be thrown
	 * if this method returns {@code null}.
	 * @param request the current request
	 * @param attributeName the name of the attribute
	 * @return the current attribute value, or {@code null} if none
	 */
	Object retrieveAttribute(WebRequest request, String attributeName);

	/**
	 * Clean up the specified attribute in the backend session.
	 * <p>Indicates that the attribute name will not be used anymore.
	 * @param request the current request
	 * @param attributeName the name of the attribute
	 */
	void cleanupAttribute(WebRequest request, String attributeName);
}

DefaultSessionAttributeStore中的实现,我们一其中一个实现一窥究竟。

@Override
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
  Assert.notNull(request, "WebRequest must not be null");
  Assert.notNull(attributeName, "Attribute name must not be null");
  Assert.notNull(attributeValue, "Attribute value must not be null");
  String storeAttributeName = getAttributeNameInSession(request, attributeName);
  request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
}

在构造SessionAttributeHandler时,会构造绑定到Session作用域的对象名称,通过@SessionAttributes注解来定义。

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
		Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
		this.sessionAttributeStore = sessionAttributeStore;

		SessionAttributes annotation =
				AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
		if (annotation != null) {
			this.attributeNames.addAll(Arrays.asList(annotation.names()));
			this.attributeTypes.addAll(Arrays.asList(annotation.types()));
		}

		for (String attributeName : this.attributeNames) {
			this.knownAttributeNames.add(attributeName);
		}
	}

在调用完成所有@ModelAttribute注解的方法后,会解析HandlerMethod方法的参数中带有@ModelAttribute注解的参数,如果在ModelAndViewContainer容器中没有包含此ModelAttribute,则会从Session作用域中查找该值,如果不存在,直接抛出异常,交给框架顶层处理。

for (String name : findSessionAttributeArguments(handlerMethod)) {
  if (!container.containsAttribute(name)) {
    Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
    if (value == null) {
      throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
    }
    container.addAttribute(name, value);
  }
}
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
  List<String> result = new ArrayList<String>();
  for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
    if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
      String name = getNameForParameter(parameter);
      Class<?> paramType = parameter.getParameterType();
      if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
        result.add(name);
      }
    }
  }
  return result;
}
  • 浅谈@ModelAttibute
    前面在@ControllerAdvice一节中,有提过,afterPropertiesSet中会全局缓存@ModelAttibute方法至modelAttributeAdviceCache中。注意@ModelAttibute注解,不仅可以出现先全局的@ControllerAdvice定义的bean中,还可以出现在@Controller注解的普通Controller中。所以在查询并构造@ModelAttibute方法时,会首先从全局缓存的@ControllerAdvice的bean中查找,然后从Controller中查找带有@ModelAttibute的方法。
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
  SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
  Class<?> handlerType = handlerMethod.getBeanType();
  Set<Method> methods = this.modelAttributeCache.get(handlerType);
  if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
    this.modelAttributeCache.put(handlerType, methods);
  }
  List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
  // Global methods first
  for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
    if (entry.getKey().isApplicableToBeanType(handlerType)) {
      Object bean = entry.getKey().resolveBean();
      for (Method method : entry.getValue()) {
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
      }
    }
  }
  for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
  }
  return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
  InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
  attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  attrMethod.setDataBinderFactory(factory);
  return attrMethod;
}

注意,@ModelAttribute方法同样支持@ModelAttribute参数,此为强依赖关系。所有的@ModelAttribute方法,最后都是缓存在ModelFactory的modelMethods中的。

public ModelFactory(List<InvocableHandlerMethod> handlerMethods,WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
  if (handlerMethods != null) {
    for (InvocableHandlerMethod handlerMethod : handlerMethods) {
      this.modelMethods.add(new ModelMethod(handlerMethod));
    }
  }
  this.dataBinderFactory = binderFactory;
  this.sessionAttributesHandler = attributeHandler;
}

ModelFactory的initModel中,获取完session作用域的对象后,会钓鱼嗯invokModelAttributeMethods方法,会依次调用所的@ModelAttribute方法。在请求的时候可以拿到request对象,很明显可以解析大量的参数,对@ModelAttribute方法的参数赋值。

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
  while (!this.modelMethods.isEmpty()) {
    InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
    ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
    if (container.containsAttribute(ann.name())) {
      if (!ann.binding()) {
        container.setBindingDisabled(ann.name());
      }
      continue;
    }

    Object returnValue = modelMethod.invokeForRequest(request, container);
    if (!modelMethod.isVoid()){
      String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
      if (!ann.binding()) {
        container.setBindingDisabled(returnValueName);
      }
      if (!container.containsAttribute(returnValueName)) {
        container.addAttribute(returnValueName, returnValue);
      }
    }
  }
}

顺便提一下,SpringMvc调用handlerMethod和ModelAttribute方法获取结果基本是一致的方式,只是handlerMethod方法需要再进行结果集的进一步加工,而ModelAttibute方法会将结果保存在ModelAndViewContainer中。

3. 参数解析

  • 类图
    参数解析相关类图
    注意这边使用了两个组合模式,parameterNameDiscover族和handlerMethodArgumentResolver族。
  • parameterNameDiscover族,默认使用的是DefaultParameterNameDiscover。parameterNameDiscover只有两个方法,故名思议,不做过多解释
public interface ParameterNameDiscoverer {
	String[] getParameterNames(Method method);
	String[] getParameterNames(Constructor<?> ctor);
}
public DefaultParameterNameDiscoverer() {
  if (standardReflectionAvailable) {
    addDiscoverer(new StandardReflectionParameterNameDiscoverer());
  }
  addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}

默认的实现最终是使用LocalVariableTableParameterNameDiscoverer。

/**
 *Implementation of {@link ParameterNameDiscoverer} that uses the LocalVariableTable
 *information in the method attributes to discover parameter names. Returns
 *{@code null} if the class file was compiled without debug information.
 *<p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer instance
 *caches the ASM discovered information for each introspected Class, in a thread-safe
 *manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
 *@author Adrian Colyer
 *@author Costin Leau
 *@author Juergen Hoeller
 *@author Chris Beams
 *@since 2.0
 */
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {

LocalVariableTableParameterNameDiscoverer的实现会通过ASM分析.class文件,如果.class中不包含调式信息,是没办法知道参数名称,编译的时候会将本地变量重新编码优化,这地方确实是有点坑。好在SpringMVC中首先会从参数的注解中获取name,如果没有定义,才会使用变量名作为参数来解析。应当尽量在注解中定义参数名称,这样可以省去不少麻烦。

  • HandlerMethodArgumentResolver族:
public interface HandlerMethodArgumentResolver {
	boolean supportsParameter(MethodParameter parameter);
	Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

只有两个方法,第一个方法supportsParameter的功能是判断该解析器是否支持传入的参数的解析,如果不支持会去下一个解析器来判断,知道找到合适的解析器,第二个才是真实的解析参数的方法。在RequestMappingHandlerAdapter初始化完成后的afterPropertiesSet方法会添加框架支持的参数解析器列表。

if (this.argumentResolvers == null) {
  List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
  this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.argumentResolvers.supportsParameter(parameter)) {
  try {
    args[i] = this.argumentResolvers.resolveArgument(
        parameter, mavContainer, request, this.dataBinderFactory);
    continue;
  }
  catch (Exception ex) {
    if (logger.isDebugEnabled()) {
      logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
    }
    throw ex;
  }
}

本文先把目光聚焦到框架的实现上,以后在来详细分析每一个参数解析器的具体实现。HandlerMethodArgumentResolverComposite是一个合成对象,里面包含所有的参数解析器。

3. 返回值处理

  • 类图
    HandlerMethodReturnValueHandler
  • SpringMVC对返回值的处理跟参数解析实现类似都是使用组合模式,关键方法也是类似的两个方法,同样本文不作详细分析,后面笔者会详细分析每一中返回值的解析。
public interface HandlerMethodReturnValueHandler {
	boolean supportsReturnType(MethodParameter returnType);
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
if (this.returnValueHandlers == null) {
  List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
  this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}

4. 返回ModelAndView

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
    ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
  modelFactory.updateModel(webRequest, mavContainer);
  if (mavContainer.isRequestHandled()) {
    return null;
  }
  ModelMap model = mavContainer.getModel();
  ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
  if (!mavContainer.isViewReference()) {
    mav.setView((View) mavContainer.getView());
  }
  if (model instanceof RedirectAttributes) {
    Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
  }
  return mav;
}
posted @ 2016-12-19 23:35  dragonfei  阅读(1346)  评论(0编辑  收藏  举报