接口标记为@ResponseBody却不进入ResponseBodyAdvice
一、背景:
我们的接口为了统一,在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
 - 返回空结果
 
接口标记为@ResponseBody却不进入ResponseBodyAdvice的更多相关文章
- 接口没添加@responseBody注解
		
今天在重写springaop小demo时,发现调用接口时,可以在控制台上正常返回结果,但是页面报错,debug半天,可以看到是调用了modelview的时候出错,找不到视图了.. debug的时候控制 ...
 - 为什么这些java接口没有抽象方法?浅谈Java标记接口
		
在jdk的源码中,存在这样的一些接口,他们不包含任何的(抽象)方法,但是却广泛的存在. 这种接口我们称之为Mark Interface,也就是标记接口. 这些接口呢,我们不用来实现任何的方法,他们的作 ...
 - 内功心法 -- Java标记接口
		
写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------这篇博客主要来谈谈" ...
 - Java 标记接口
		
没有声明或定义方法的接口称为标记接口(Mark Interface).某个类实现该接口时不需要重写方法,表明具有接口标记的功能.Java中常用的3个标记接口如下: 1 Serializable jav ...
 - Java的四个标记接口:Serializable、Cloneable、RandomAccess和Remote接口
		
一.概述 标记接口是一些没有属性和方法的接口,也是一种设计思想.Java中的一个标记接口表示的的是一种类的特性,实现了该标记接口的类则具有该特性.如实现了Serializable接口的类,表示这个类的 ...
 - Java标记接口
		
写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------这篇博客主要来谈谈" ...
 - java接口调用——webservice就是一个RPC而已
		
很多新手一听到接口就蒙逼,不知道接口是什么!其实接口就是RPC,通过远程访问别的程序提供的方法,然后获得该方法执行的接口,而不需要在本地执行该方法.就是本地方法调用的升级版而已,我明天会上一篇如何通过 ...
 - IGS_学习笔记05_IREP开发Concurrent Program为客户化集合接口(案例)
		
20150819 Created By BaoXinjian
 - 利用Swagger2自动生成对外接口的文档
		
一直以来做对外的接口文档都比较原始,基本上都是手写的文档传来传去,最近发现了一个新玩具,可以在接口上省去不少麻烦. swagger是一款方便展示的API文档框架.它可以将接口的类型最全面的展示给对方开 ...
 
随机推荐
- 框架 get 请求乱码
			
解决方案: 在 tomcat 配置文件中添加 URIEncoding="utf-8"
 - vuejs cli3 env配置文件实践指南
			
env文件 配置四个环境名字分别 VUE_APP_ENV=dev 只支持:VUE_APP_ 开头,比如设置其它变量 VUE_APP_NAME=stark package.json 配置 方式 &quo ...
 - 可变参数的函数(c++)【转载】
			
摘自<c语言精彩编程百例>,要定义可变参数的函数,在c++当中当包含<cstdarg>,在c语言当中当包含<stdarg.h>,使用任何可变长度的变元被访问之前,必 ...
 - windows 共享网络
			
windows 共享网络 今天单位的网络突然断了,光猫LOS亮红灯,宽带报修.等了半天还没来,下面科室要上报资料,急着用网, 通过windows的共享网络+无线网卡暂时用我的手机流量. 找了一台电脑插 ...
 - deepnude | 福利
			
程序好下载github有,但是没有lib,就是没有训练好的model. 以下是搜到的win平台程序的下载链接: magnet:?xt=urn:btih:7BE4EB8D640742D2FFEBD649 ...
 - Redis和MongoDB的区别以及应用场景
			
Redis和MongoDB的区别以及应用场景 项目中用的是MongoDB,但是为什么用其实当时选型的时候也没有太多考虑,只是认为数据量比较大,所以采用MongoDB. 最近又想起为什么用MongoDB ...
 - uploadifive 1.1.2 动态传参
			
之前用过Flash版本的uploadify,写过一篇关于uploadify动态传参的文章(点击打开链接).后来有了HTML5版本的上传控件,叫uploadifive,测试着用了一下,效果还不错.这里主 ...
 - Linux下打开超大文件的方法
			
Linux下打开超大文件方法 在Linux下用VIM打开大小几个G.甚至几十个G的文件时,是非常慢的. 这时,我们可以利用下面的方法分割文件,然后再打开. 1 查看文件的前多少行 head -1000 ...
 - openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息
			
openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 为了实现业务系统针对不同地区IP访问,展示包含不同地区信息的业务交互界面.很多情况下系统需要根据用户访问的IP信息 ...
 - Navicat 破解版链接
			
本文为转载内容 百度网盘地址: https://pan.baidu.com/s/1nvIIOad 压缩包中有注册码和使用方法