天天看点

Spring MVC学习笔记之Spring MVC组件HandlerMethodReturnValueHandler

1、前言

  在上一篇《Spring MVC组件HandlerMethodArgumentResolver》中,分析了关于Spring MVC中参数解析器HandlerMethodArgumentResolver系列的类,这一节我们分析学习处理器方法返回值的解析器HandlerMethodReturnValueHandler系列的类。和参数解析器相比,因为返回值的相对确定性(返回值个数),所以返回值解析器的逻辑相对比较简单。

2、HandlerMethodReturnValueHandler类图
Spring MVC学习笔记之Spring MVC组件HandlerMethodReturnValueHandler

  HandlerMethodReturnValueHandler用在ServletInvocableHandlerMethod(request处理器)中,主要是用来处理处理器方法执行后的返回值。主要实现了一下功能:

  1. 设置参数到Model;
  2. 设置视图View;
  3. 设置处理状态。如果请求已经处理完则设置ModelAndViewContainer的requestHandled为true。

  在返回值处理器的家族中,其中有一个返回值解析器比较特殊,就是HandlerMethodReturnValueHandlerComposite解析器,和参数解析器中的HandlerMethodArgumentResolverComposite类功能一样,它不具体解析方法的返回值,而是可以将多个别的解析器包含在其中,解析时调用其所包含的解析器具体解析方法返回值。

  在分析参数解析器的时候,我们提到了有两类的解析器:一类只能解析参数,一类既可以做参数解析器,又可以做方法返回值解析器。因此,在方法返回值处理器中,除里前面提到的可以同时处理参数和返回值的处理器外,也提供了只作为方法返回值的处理器XXXReturnValueHandler,其中XXX表示解析器处理的返回值类型。其中还定义了一个AsyncHandlerMethodReturnValueHandler接口,用来定义异步方法的返回值的解析器。

HandlerMethodReturnValueHandler接口

  HandlerMethodReturnValueHandler接口有两个方法, 和参数解析器接口一样。一个用于判断是否支持该类型的判断,一个用来处理方法的返回值。

public interface HandlerMethodReturnValueHandler {
	//判断是否支持该类型
	boolean supportsReturnType(MethodParameter returnType);
	//处理返回值
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
           
3、HandlerMethodReturnValueHandlerComposite类

  在前面我们分析了参数解析器的HandlerMethodArgumentResolverComposite类,这里的HandlerMethodReturnValueHandlerComposite返回值解析器实现基本上一样。我们简单分析一下其中的实现逻辑:

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {

	protected final Log logger = LogFactory.getLog(getClass());
	//定义变量,用来存储注册的真正用来处理方法返回值的解析器
	private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
	//获取当前类,可以使用的真正的解析器集合
	public List<HandlerMethodReturnValueHandler> getHandlers() {
		return Collections.unmodifiableList(this.returnValueHandlers);
	}
	//实现接口方法,用来判断是否支持该类型的返回值,还是通过循环处理器集合,分别判断集合中的每个解析器是否支持该返回值类型,有一个支持即返回true
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return getReturnValueHandler(returnType) != null;
	}
	//循环处理器集合,判断集合中的每个解析器是否支持该返回值类型,有一个支持即返回true
	@Nullable
	private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
	//接口方法实现,处理返回值
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		//查询解析器集合中可以使用的解析器
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		//调用真正的解析器进行解析
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
	//获取符合要求的解析器,需要判断是否是异步处理的解析器
	@Nullable
	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		//首先,判断是否是异步处理的方法调用
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			//如果是异步处理方法,必须是实现了AsyncHandlerMethodReturnValueHandler异步解析器的实现类才可以使用
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			//判断是否支持该返回值类型,并返回处理器实例
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
	//是否异步处理的方法判断
	private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (handler instanceof AsyncHandlerMethodReturnValueHandler &&
					((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
				return true;
			}
		}
		return false;
	}
	//添加支持的解析器
	public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) {
		this.returnValueHandlers.add(handler);
		return this;
	}
	//添加支持的解析器
	public HandlerMethodReturnValueHandlerComposite addHandlers(
			@Nullable List<? extends HandlerMethodReturnValueHandler> handlers) {

		if (handlers != null) {
			this.returnValueHandlers.addAll(handlers);
		}
		return this;
	}

}
           
4、ViewNameMethodReturnValueHandler类

  在HandlerMethodReturnValueHandler家族中,有很多种类型的返回值解析器,这里我们通过分析ViewNameMethodReturnValueHandler类,了解返回值解析器的实现逻辑。ViewNameMethodReturnValueHandler类主要是用来处理视图view是字符串或者void类型的解析器,实现比较简单,我们直接看具体实现:

public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
	//判断是否是Redirect类型的表达式
	@Nullable
	private String[] redirectPatterns;

	//省略 redirectPatterns属性的get/set方法

	//接口方法实现,判断返回值是否是void类型或字符串类型,CharSequence接口是所有字符串类型的抽象接口
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		Class<?> paramType = returnType.getParameterType();
		return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
	}
	//接口方法实现,处理符合要求的返回值
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		//判断如果是字符串类型的,就转换成String 类型,并设置到ModelAndViewContainer的view属性中
		if (returnValue instanceof CharSequence) {
			String viewName = returnValue.toString();
			//设置到ModelAndViewContainer的view属性中
			mavContainer.setViewName(viewName);
			//判断是否是Redirect类型
			if (isRedirectViewName(viewName)) {
				//如果是Redirect类型,设置ModelAndViewContainer中的对应标志
				mavContainer.setRedirectModelScenario(true);
			}
		}
		else if (returnValue != null) {
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}
	//判断是否是Redirect类型
	protected boolean isRedirectViewName(String viewName) {
		return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
	}
}
           
5、总结

  因为有了前面学习参数解析器的经验,分析返回值解析器相对比较简单,因为一些思路是类似的。HandlerMethodReturnValueHandler家族中的其他类这里不再一个个进行分析,感兴趣的童鞋,可以直接查看源码。关于异步处理相关知识,后续学习过程中再整理记录。

继续阅读