Spring MVC請求參數(shù)的深入解析
請求參數(shù)解析
客戶端請求在handlerMapping中找到對應(yīng)handler后,將會繼續(xù)執(zhí)行DispatchServlet的doPatch()方法。
首先是找到handler對應(yīng)的適配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
進(jìn)入到getHandlerAdapter(mappedHandler.getHandler())方法中
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
這里存在多個適配器,如圖:

其中使用@RequestMaping注解修飾的控制器都將適配第一個適配器;而函數(shù)式方法將會使用第二個適配器。
跟蹤請求,這里將會獲得第一個適配器,判斷也簡單,如下:
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
如果是HandlerMethod類型的處理器就采用這個適配器,而客戶端請求正好對應(yīng)的是HandlerMethod處理器。
找到適配器后,將會真正執(zhí)行處理器邏輯。如下:
// Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
進(jìn)入RequestMappingHandlerAdapter,執(zhí)行適配器核心方法:
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
其核心代碼為實際執(zhí)行處理器方法:
mav = invokeHandlerMethod(request, response, handlerMethod);
同樣,我們打開RequestMappingHandlerAdapter中的invokeHandlerMethod方法:
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//通過處理器獲得真正的執(zhí)行方法及其參數(shù)列表
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//給執(zhí)行方法對象添加參數(shù)解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
//給執(zhí)行方法對象添加返回值處理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//處理器對象裝配完成,執(zhí)行控制器方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
在這個關(guān)鍵方法中,首先執(zhí)行請求對應(yīng)的控制器邏輯,之后進(jìn)行系列處理,根據(jù)返回值處理器處理返回值。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//執(zhí)行控制器方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//處理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
以下給出部分參數(shù)解析器及返回值處理器截圖:
參數(shù)解析器。對應(yīng)每一個參數(shù)(路徑變量、矩陣變量、獲得請求頭、請求域等)的獲取方式

返回值處理器。ModelAndView、ResponseBody等。每個處理器處理不同類別的返回值類型。

接下來,真正進(jìn)入到最終執(zhí)行method方法invocableMethod.invokeAndHandle(webRequest, mavContainer);,這里是真是執(zhí)行控制器中映射的方法。
以下為獲得參數(shù)列表對應(yīng)值的邏輯,參數(shù)獲取完成后將會執(zhí)行真正的控制器邏輯。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//通過參數(shù)解析器獲取參數(shù)列表每一個參數(shù)的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
進(jìn)入解析邏輯,解析是對比每一個參數(shù)綁定的注解,如果注解一致將會使用對應(yīng)的解析器將請求傳遞的參數(shù)值獲取到。
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//獲得控制器參數(shù)列表,每一個參數(shù)包含其參數(shù)類型,參數(shù)次序、注解修飾(用于使用對應(yīng)解析器)
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
//輪循獲得參數(shù)
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//判斷解析器是否支持當(dāng)前參數(shù)的解析
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//獲得參數(shù)值的核心方法
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
查看解析器是否指出當(dāng)前參數(shù)部分代碼,可以了解到SpringMVC的緩存策略。
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//從緩存中獲取當(dāng)前參數(shù)的解析器
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
//緩存中不存在則將這個參數(shù)對應(yīng)的解析器加到緩存中,提升后續(xù)相同請求響應(yīng)速度。
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
參數(shù)列表
以下為獲得的參數(shù)列表第一個參數(shù)部分屬性

其對應(yīng)的是控制器中id參數(shù):
@GetMapping(value = "/student01/{id}/car/{name}")
public Map<String, Object> testAnnotation(@PathVariable(name = "id") String id){
Map<String, Object> map = new HashMap<>();
map.put("id", id);
return map;
}
以上即為獲得請求Handler對應(yīng)的適配器,處理參數(shù)映射、執(zhí)行控制器邏輯、返回值處理的核心源碼處理。
總結(jié)
到此這篇關(guān)于Spring MVC請求參數(shù)解析的文章就介紹到這了,更多相關(guān)SpringMVC請求參數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Mail與Apache Mail發(fā)送郵件示例
這篇文章主要介紹了Java Mail與Apache Mail發(fā)送郵件示例的相關(guān)資料,需要的朋友可以參考下2014-10-10
Java數(shù)組實現(xiàn)動態(tài)初始化的實例詳解
在本篇文章里小編給大家整理的是一篇關(guān)于Java數(shù)組實現(xiàn)動態(tài)初始化的實例詳解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-10-10
解決try-catch捕獲異常信息后Spring事務(wù)失效的問題
這篇文章主要介紹了解決try-catch捕獲異常信息后Spring事務(wù)失效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
Springboot使用redisson?+?自定義注解實現(xiàn)消息的發(fā)布訂閱(解決方案)
Redisson是一個基于Redis的Java駐留內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory?Data?Grid)和分布式鎖框架,它提供了一系列的分布式Java對象和服務(wù),可以幫助開發(fā)者更方便地使用Redis作為數(shù)據(jù)存儲和分布式鎖的解決方案,感興趣的朋友跟隨小編一起看看吧2024-05-05

