SpringBoot中添加@ResponseBody註解會發生什麼?

【1】SpringBoot接收到請求

① springboot接收到一個請求返回json格式的列表

將要調用的目標方法如下:

    @ApiOperation(value="分頁查詢")
    @RequestMapping(value = "/listPage",method = RequestMethod.POST)
    @ResponseBody
    public ResponseBean listPage(@RequestBody JSONObject params){
        Integer pageNum = params.getInteger("pageNum");
        Integer pageSize = params.getInteger("pageSize");
        String vagueParam = params.getString("vagueParam");
        IPage<TbSysGoodsCategory> indexPage = new Page<>(pageNum, pageSize);
        QueryWrapper<TbSysGoodsCategory> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(vagueParam)){
            queryWrapper.like("name",vagueParam).or().like("code",vagueParam);
        }
        //排序
        queryWrapper.orderByDesc("id");
        indexPage = tbSysGoodsCategoryService.page(indexPage,queryWrapper);
        return new ResponseBean<>(true, indexPage, CommonEnum.SUCCESS_OPTION);
    }

如下所示,首先進入DispatcherServlet使用RequestMappingHandlerAdapter進行處理。
在這裏插入圖片描述
而RequestMappingHandlerAdapter (extends AbstractHandlerMethodAdapter)會調用父類AbstractHandlerMethodAdapter的handle方法進行處理:
在這裏插入圖片描述

② RequestMappingHandlerAdapter.handleInternal

這裏首先在this.checkRequest(request)對請求進行了檢測,HttpRequestMethodNotSupportedException異常就是這裏拋出的。

	/**
	 * Check the given request for supported methods and a required session, if any.
	 * @param request current HTTP request
	 * @throws ServletException if the request cannot be handled because a check failed
	 * @since 4.2
	 */
	protected final void checkRequest(HttpServletRequest request) throws ServletException {
		// Check whether we should support the request method.
		String method = request.getMethod();
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
			throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
		}

		// Check whether a session is required.
		if (this.requireSession && request.getSession(false) == null) {
			throw new HttpSessionRequiredException("Pre-existing session required but none found");
		}
	}

其他沒有什麼需要特殊說明的,直接調用了invokeHandlerMethod方法進行實際業務處理。
在這裏插入圖片描述


【2】RequestMappingHandlerAdapter.invokeHandlerMethod核心處理

RequestMappingHandlerAdapter.invokeHandlerMethod

這個方法十分重要,是請求處理流程中的核心方法。這個方法會根據handlerMethod獲取一個ServletInvocableHandlerMethod 並對其進行各種屬性設置然後調用其invokeAndHandle方法進行處理。

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 對應 2 
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        Object result;
        try {
        // 對應 3
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
   // 對應 4
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
   // 對應 5
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }

            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }

            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
 // 對應 6
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//對應 7
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
            if (asyncManager.hasConcurrentResult()) {
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
//這裏會跳到【3】ServletInvocableHandlerMethod.invokeAndHandle
            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if (!asyncManager.isConcurrentHandlingStarted()) {
//這裏會跳到【4】RequestMappingHandlerAdapter.getModelAndView
                ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
                return var15;
            }

            result = null;
        } finally {
            webRequest.requestCompleted();
        }

        return (ModelAndView)result;
    }

① 此時的handlerMethod是什麼?

如下圖所示:
在這裏插入圖片描述
② 此時的ServletWebRequest webRequest是什麼?

如下圖所示:
在這裏插入圖片描述


③ 此時的WebDataBinderFactory binderFactory

WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);

在這裏插入圖片描述

這個很關鍵。SpringBoot請求處理流程中最重要的一步就是數據綁定,即將參數寫到目標對象上面。那麼這裏就涉及到參數校驗、數據格式轉換、綁定結果對象、錯誤對象等。
在這裏插入圖片描述
其屬性ConfigurableWebBindingInitializer對象提供了基礎功能,該對象中WebConversionService中轉換器實例如下:
在這裏插入圖片描述

④ 根據handlerMethod和binderFactory獲取到ModelFactory modelFactory

 ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);

可以看到modelFactory主要屬性dataBindFactory和sessionAttributeHandler都是爲參數綁定數據服務的。
在這裏插入圖片描述


⑤ 創建核心處理對象ServletInvocableHandlerMethod invocableMethod併爲其屬性賦值

 ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
     invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
 }

 if (this.returnValueHandlers != null) {
     invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
 }

 invocableMethod.setDataBinderFactory(binderFactory);
 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

argumentResolvers 看名字就應該知道是爲參數解析進行服務的,其值列表如下:
在這裏插入圖片描述
我想你現在應該知道爲什麼方法參數使用@RequestBody就可以進行參數綁定了吧!

繼續看returnValueHandlers,也就是返回結果處理器。其中returnValueHandlers是HandlerMethodReturnValueHandlerComposite實例,就像HandlermethodArgumentResolverComposite一樣,它包含了所有HandlerMethodReturnValueHandler的列表,並在Spring啓動時完成註冊。其值列表如下:
在這裏插入圖片描述
ok,我們的主題來了。就是這個RequestResponseBodyMethodProcessor後置處理器對@ResponseBody註解進行的處理!

繼續往下走,給invocableMethod設置了DataBinderFactory。這個同上都是爲數據參數綁定服務,繼續往下看invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);parameterNameDiscoverer這裏值列表如下:

在這裏插入圖片描述


⑥ 創建mavContainer進行數據的初步處理

//創建ModelAndViewContainer 實例對象
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//從請求中獲取InputFlashMap並把其數據放入defaultModel中,flashmap的作用是在redirect中傳遞參數
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//調用modelFactory對model進行初始化
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//重定向時忽略默認Model
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

首先看下ModelAndViewContainer,其核心有三個屬性view-視圖對象,defaultModel-默認的數據存放地方以及redirectModel-重定向時數據存放地方。
在這裏插入圖片描述

modelFactory.initModel(webRequest, mavContainer, invocableMethod);,這裏對model做了處理。也可以說是對目標方法實際調用前對數據做的最後一次處理:

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
			throws Exception {
	//嘗試獲取會話中保存的屬性值
		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		//對model中屬性值進行合併處理:稱之爲補缺更合適
		container.mergeAttributes(sessionAttributes);
		
		//調用標註了@ModelAttribute註解的方法
		invokeModelAttributeMethods(request, container);

		//如果handlerMethod的方法參數標註了@ModelAttribute註解並且在sessionAttributetes存在,則對其進行遍歷
		//嘗試獲取值,如果獲取不到值就會拋出異常;如果獲取到值就會放到model-defaultModel中
		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);
			}
		}
	}

關於mergeAttributes合併屬性方法源碼如下:

#也就是說遍歷sessionAttributes ,如果model中不存在,就放入。如果存在,就跳過!注意,不會進行值覆蓋
 public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
        if (attributes != null) {
            attributes.forEach((key, value) -> {
                if (!this.containsKey(key)) {
                    this.put(key, value);//this這裏值的是modelMap,也就是defaultModel
                }

            });
        }
        return this;
    }

關於findSessionAttributeArguments方法源碼如下:

	 //從方法參數中找到在(@SessionAttributes註解的屬性/參數)中存在的
	 // 且方法參數上標註了@ModelAttribute註解的
	private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
		List<String> result = new ArrayList<>();
		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;
	}

⑦ 異步請求

這一塊先不用管,後續分析

AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

if (asyncManager.hasConcurrentResult()) {
	Object result = asyncManager.getConcurrentResult();
	mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
	asyncManager.clearConcurrentResult();
	LogFormatUtils.traceDebug(logger, traceOn -> {
		String formatted = LogFormatUtils.formatValue(result, !traceOn);
		return "Resume with async result [" + formatted + "]";
	});
	invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

接下來調用invocableMethod.invokeAndHandle(webRequest, mavContainer);就到了ServletInvocableHandlerMethod.invokeAndHandle。


【3】調用目標方法並對返回值進行處理ServletInvocableHandlerMethod.invokeAndHandle

方法源碼如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//調用目標方法並獲取返回值,這裏對應 【3.1】 InvocableHandlerMethod.invokeForRequest調用目標方法
		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;
		}
	}

【3.1】 InvocableHandlerMethod.invokeForRequest調用目標方法

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
//解析參數--這裏對應 1
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	//根據上面得到的參數值調用目標方法  這裏對應2 
	return doInvoke(args);
}

① 解析參數getMethodArgumentValues

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//獲取到方法的參數對象   MethodParameter[]數組
		MethodParameter[] parameters = getMethodParameters();

//如果爲空,返回空參數組
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
		Object[] args = new Object[parameters.length];
		
		//遍歷MethodParameter[] parameters,對每一個方法參數對象獲取到具體參數並解析得到參數值
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			//綁定參數名稱發現器
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			//從providedArgs中嘗試獲取到參數名
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			//如果方法參數解析器不支持parameter,則拋出異常
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
			//
				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;
	}

MethodParameter[] parameters = getMethodParameters();這裏獲取的 MethodParameter[] parameters如下圖所示:
在這裏插入圖片描述
參數解析器組合對象( this.resolvers)列表如下所示:
在這裏插入圖片描述

爲什麼稱之爲參數解析器組合對象?其實這裏的this.resolvers並不是具體的參數解析器而是argumentResolvers、argumentResolverCache組合而成的HandlerMethodArgumentResolverComposite!

可以看到起還有argumentResolverCache屬性,其值列表如下:
在這裏插入圖片描述
默認argumentResolverCache是一個容量爲256的ConcurrentHashMap,是HandlerMethodArgumentResolverComposite的成員變量:

private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
			new ConcurrentHashMap<>(256);

這個argumentResolverCache是在動態改變,其在判斷是否支持paramter的方法中會改變,HandlerMethodArgumentResolverComposite.getArgumentResolver源碼如下:

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//如果緩存中有,則直接返回
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		//如果緩存中沒有就嘗試從解析器列表中獲取一個支持parameter的,並將解析器 parameter放入緩存
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

爲什麼要有argumentResolverCache ?你可以沒有,但是你就需要每次從argumentResolvers遍歷尋找支持當前MethodParameter的參數解析器!之所以保存一份鍵值對數據到argumentResolverCache ,就是爲了下次不用尋找,就是爲了更快!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章