天天看點

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家族中的其他類這裡不再一個個進行分析,感興趣的童鞋,可以直接檢視源碼。關于異步處理相關知識,後續學習過程中再整理記錄。

繼續閱讀