一、背景:
我們的接口為了統一,在ResponseBodyAdvice中對返回值做統一處理,默認添加了errorNo和errorInfo字段返回。
最近同事改接口代碼的時候,發現接口返回值是空的。乍一看,沒什么重大修改。
接口代碼大致就是下面這個樣子:
@ResponseBody
@RequestMapping("test")
public void test(HttpServletRequest request, HttpServletResponse response){
// 業務邏輯處理
}
二、問題分析
順着這個接口,單步調試跟到Spring的源碼ServletInvocableHandlerMethod#invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
// 直接進入到這里就return出去了
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
// 要進到這里才會進入到ResponseBodyAdvice中
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
再對着源碼,看下return出去的4個條件,核對一下我們的接口
-
void類型,返回值確實是null
-
isRequestNotModified(webRequest) ,false
-
getResponseStatus() != null ,false
如果在test方法上標記@ResponseStatus(HttpStatus.OK),這里取到的就不是null
-
mavContainer.isRequestHandled(),true
那么問題來了,mavContainer.isRequestHandled()=true是什么時候設置的呢?
重新debug,發現在ServletResponseMethodArgumentResolver#resolveArgument中設置的,源碼截取如下:
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
if (mavContainer != null) {
// 就是這里設置的了
mavContainer.setRequestHandled(true);
}
// 略 ...
}
三、問題小結
通過上面的分析,問題已經明朗了。
- 接口一直都是返回null的。開發此次修改的時候,在接口入參里面加了HttpServletResponse
- 因為有HttpServletResponse入參,就會進入到ServletResponseMethodArgumentResolver
- 因為ModelAndViewContainer不為null,mavContainer.setRequestHandled(true);
- 滿足前面ServletInvocableHandlerMethod#invokeAndHandle的條件,直接return
- 返回空結果