一、背景:

我们的接口为了统一,在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);
} // 略 ...
}

三、问题小结

通过上面的分析,问题已经明朗了。

  1. 接口一直都是返回null的。开发此次修改的时候,在接口入参里面加了HttpServletResponse
  2. 因为有HttpServletResponse入参,就会进入到ServletResponseMethodArgumentResolver
  3. 因为ModelAndViewContainer不为null,mavContainer.setRequestHandled(true);
  4. 满足前面ServletInvocableHandlerMethod#invokeAndHandle的条件,直接return
  5. 返回空结果

接口标记为@ResponseBody却不进入ResponseBodyAdvice的更多相关文章

  1. 接口没添加@responseBody注解

    今天在重写springaop小demo时,发现调用接口时,可以在控制台上正常返回结果,但是页面报错,debug半天,可以看到是调用了modelview的时候出错,找不到视图了.. debug的时候控制 ...

  2. 为什么这些java接口没有抽象方法?浅谈Java标记接口

    在jdk的源码中,存在这样的一些接口,他们不包含任何的(抽象)方法,但是却广泛的存在. 这种接口我们称之为Mark Interface,也就是标记接口. 这些接口呢,我们不用来实现任何的方法,他们的作 ...

  3. 内功心法 -- Java标记接口

    写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------这篇博客主要来谈谈" ...

  4. Java 标记接口

    没有声明或定义方法的接口称为标记接口(Mark Interface).某个类实现该接口时不需要重写方法,表明具有接口标记的功能.Java中常用的3个标记接口如下: 1 Serializable jav ...

  5. Java的四个标记接口:Serializable、Cloneable、RandomAccess和Remote接口

    一.概述 标记接口是一些没有属性和方法的接口,也是一种设计思想.Java中的一个标记接口表示的的是一种类的特性,实现了该标记接口的类则具有该特性.如实现了Serializable接口的类,表示这个类的 ...

  6. Java标记接口

    写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------这篇博客主要来谈谈" ...

  7. java接口调用——webservice就是一个RPC而已

    很多新手一听到接口就蒙逼,不知道接口是什么!其实接口就是RPC,通过远程访问别的程序提供的方法,然后获得该方法执行的接口,而不需要在本地执行该方法.就是本地方法调用的升级版而已,我明天会上一篇如何通过 ...

  8. IGS_学习笔记05_IREP开发Concurrent Program为客户化集合接口(案例)

    20150819 Created By BaoXinjian

  9. 利用Swagger2自动生成对外接口的文档

    一直以来做对外的接口文档都比较原始,基本上都是手写的文档传来传去,最近发现了一个新玩具,可以在接口上省去不少麻烦. swagger是一款方便展示的API文档框架.它可以将接口的类型最全面的展示给对方开 ...

随机推荐

  1. 洛谷P1799 数列[NOI导刊]

    题目 dp状态定义的好题,初看这个题其实并不好想到他的状态,但是可以根据状态的定义,需要满足最优子结构.还有比较重要的一点就是方便转移方程. 首先我们定义dp[i]表示前i个数所能得到的最多个数,发现 ...

  2. Go-Json操作

    /** * @Author: jadeshu * @Description: * @File: main * @Version: 1.0.0 * @Date: 2019/11/7 2:33 */ pa ...

  3. mapreduce中reduce没有执行

    hadoop执行mapreduce过程reduce不执行原因 1.如果你的map过程中没有context.write()是不执行reduce过程的:2.如果你的map过程中context.write( ...

  4. (三)Cisco dhcp snooping实例1-单交换机(DHCP服务器和DHCP客户端位于同一VLAN)

    环境:cisco dhcp server和客户端都属于vlan27,dhcp server 接在交换机G0/1,客户端接在交换机的G0/2 cisco dhcp server相关配置 ip dhcp ...

  5. 堆(heap)和栈(stack)有什么区别??

    简单的可以理解为: heap:是由malloc之类函数分配的空间所在地.地址是由低向高增长的. stack:是自动分配变量,以及函数调用的时候所使用的一些空间.地址是由高向低减少的. 预备知识—程序的 ...

  6. 剑指offer:数组中只出现一次的数字

    题目描述: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 思路分析: 1. 直接想法,每个数字遍历,统计出现次数,复杂度O(n^2),超时. 2. 借助 ...

  7. v-if和v-for一起使用的几个方法

    方法一(推荐): 不带if <ul> <li v-for="(item, index) in list" :key="index" > ...

  8. odoo开发笔记--日期or时间字段给定默认值

    开发中经常有这样的场景,需要给某个日期或者时间的字段默认值: 例如: 日期,默认今天 时间,默认当前时间 可以在odoo模型定义中进行设置, 如下样例提供参考: test_data = fields. ...

  9. c++ stl bind函数介绍

    /* stl::bind 使用 */ #include <iostream> #include <string> #include <functional> /* ...

  10. bat curl 定时请求

    @echo off :loop call:sleep 30 ::调用方法call:sleep [毫秒] (1秒=1000毫秒) curl https://stage.coolfen.com/suppl ...