SpringMVC中自定義參數解析器及內置類型的綁定

SpringMVC中自定義參數解析器及內置類型的綁定

  
   前一篇文章講述了使用標註方式進行參數綁定背後的參數解析原理,今天來整理一下沒有使用標註的參數是怎樣解析出來的。

一,自定義參數解析器
  有的時候我們希望在HandlerMethod中直接使用一些對象,而不需要主動去創建或獲取。例如,我們想在一個請求方法的第一行打印出系統登錄用戶的相關信息,而這個用戶信息已經再Request中作爲Attribute保存好了。這時候通常的做法就是下面這樣:
    @RequestMapping("paramTest")
    @ResponseBody
    public Object paramTest(HttpServletRequest request){
        User user = (User) request.getAttribute("user");
        System.out.println(user);
        return "ok";
    }
  這樣的方式顯然有一些麻煩,最好可以達到下面的效果
    @RequestMapping("paramTest")
    @ResponseBody
    public Object paramTest(User user){
        System.out.println(user);
        return "ok";
    }
  這樣一來,就方便了好多。有人可能會有些懷疑,不就是少了一行代碼嗎,這裏只是舉一個簡單的例子,如果現實中需要成百上千行的代碼來構造這個User呢?那麼構造這個User的邏輯就可以封裝在自定義的參數解析器中。
  首先,寫一個自己的參數解析器,像下面這樣:
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.getParameterType().equals(User.class)) {
            return true;
        }
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return webRequest.getAttribute("user", RequestAttributes.SCOPE_REQUEST);
    }

}
  它的第一個方法用於匹配參數的類型,如果是User類型,則用第二個方法解析出一個User對象返回。然後還需要把這個參數解析器註冊到HandlerAdapter中,像下面這樣:
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		
		<property name="customArgumentResolvers">
			<list>
				<bean class="com.ebaoyang.controller.resolver.UserArgumentResolver" />
			</list>
		</property>
		<property name="order" value="0" />
	</bean>
  通過上面的配置就可以實現自定義類型User的綁定,可以在HandlerMethod中直接使用。

二,內置類型的綁定
  最初接觸SpringMVC的時候就發現它的一些用法很神奇,諸如在方法中直接聲明HttpServletRequest就可以直接使用。一直很好奇隱藏在它背後的原理是什麼,後來系統的學習spring的源碼才瞭解到這樣的參數綁定機制。其實可以值這樣進行綁定的參數類型不只是HttpServletRequest,還有很多其他的類型,看一看ServletRequestMethodArgumentResolver的supportsParameter方法就知道了:
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return WebRequest.class.isAssignableFrom(paramType) ||
				ServletRequest.class.isAssignableFrom(paramType) ||
				MultipartRequest.class.isAssignableFrom(paramType) ||
				HttpSession.class.isAssignableFrom(paramType) ||
				Principal.class.isAssignableFrom(paramType) ||
				Locale.class.equals(paramType) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType);
	}
  這些類型的參數的綁定方法見resolveArgument方法:
	public Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws IOException {
		
		Class<?> paramType = parameter.getParameterType();
		if (WebRequest.class.isAssignableFrom(paramType)) {
			return webRequest;
		}
		
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
			Object nativeRequest = webRequest.getNativeRequest(paramType);
			if (nativeRequest == null) {
				throw new IllegalStateException(
						"Current request is not of type [" + paramType.getName() + "]: " + request);
			}
			return nativeRequest;
		}
		else if (HttpSession.class.isAssignableFrom(paramType)) {
			return request.getSession();
		}
		else if (Principal.class.isAssignableFrom(paramType)) {
			return request.getUserPrincipal();
		}
		else if (Locale.class.equals(paramType)) {
			return RequestContextUtils.getLocale(request);
		}
		else if (InputStream.class.isAssignableFrom(paramType)) {
			return request.getInputStream();
		}
		else if (Reader.class.isAssignableFrom(paramType)) {
			return request.getReader();
		}
		else {
			// should never happen..
			Method method = parameter.getMethod();
			throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method);
		}
	}

三,一般類型參數的綁定
  如果HandlerMethod的參數即沒有PathVariable這樣的標註,也沒有自定義的參數解析器來處理它,也不是HttpServlet這樣的內置類型,它是如何綁定的呢?其實,這樣的參數屬性會到查詢參數裏面去尋找。比如剛纔的例子,如果我們沒有定義自己的UserArgumentResolver ,而客戶端傳來的請求行中是這樣的/paramTest?id=456,那麼user的id就會被設置爲456。對於沒有對應查詢參數的屬性會設置爲默認值,若客戶端傳來的是數組,則會設置爲取其中一個值。在spring中,大多數情況下客戶端若傳來id=123&id=456這樣的數字過來,服務端是可以解析出來int類型的id的。但是,應該儘量避免這樣的事情發生,因爲這樣看起來模糊不清,並且它本身就是一個錯誤。


 


發佈了51 篇原創文章 · 獲贊 11 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章