RequestBodyAdvice 和 ResponseBodyAdvice 原理

RequestBodyAdvice

RequestBodyAdvice是 Spring 框架中的一个接口,用于在读取请求体之前(或读取得到的对象作为参数传入 @RequestBody 或 HttpEntity Controller 方法之前)进行自定义处理。

RequestBodyAdvice (Spring Framework 6.2.8 API):Allows customizing the request before its body is read and converted into an Object and also allows for processing of the resulting Object before it is passed into a controller method as an @RequestBody or an HttpEntity method argument.

下面是一个简单的示例:

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.IOException;
import java.lang.reflect.Type;

@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 这里可以根据需要判断是否支持特定的请求
        return true; // 支持所有请求
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        // 在请求体被读取之前,可以对输入流进行处理
        // 例如:记录日志、解密等
        return inputMessage; // 返回处理后的输入流
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 请求体被读取之后,可以对其进行处理
        // 例如:修改请求体内容
        return body; // 返回处理后的请求体
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 处理空请求体的情况
        return body; // 返回处理后的请求体
    }
}

先在CustomRequestBodyAdvice的各个方法中添加断点,通过调用下面的接口来进行测试:

import com.source.mybatis.entity.User;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getDetail")
    public User getDetail(@RequestBody User user) {
        return user;
    }
}

首先进入的是supports方法:

image-20250625201015051
image-20250625201118960

InvocableHandlerMethod#invokeForRequest开始看,其调用getMethodArgumentValues获取参数:

image-20250625201823451

getMethodArgumentValues方法遍历各个方法参数,调用HandlerMethodArgumentResolverComposite#resolveArgument逐个处理:

image-20250625201954575

该方法先调用HandlerMethodArgumentResolverComposite#getArgumentResolver获取到参数对应的HandlerMethodArgumentResolver,然后调用HandlerMethodArgumentResolver#resolveArgument获得参数值:

image-20250625202149032

查看HandlerMethodArgumentResolverComposite#getArgumentResolver可以看到其遍历argumentResolvers列表,返回支持处理该参数的第一个HandlerMethodArgumentResolver实例:

image-20250625202546864

因为当前 Controller 方法参数被@RequestBody标注,而RequestResponseBodyMethodProcessorsupportsParameter对改情况返回 true,所以最终处理参数的是RequestResponseBodyMethodProcessor实例:

image-20250625202950861
image-20250625203015384

继续看RequestResponseBodyMethodProcessor#resolveArgument如何解析参数,可以看到其调用readWithMessageConverters读取参数:

image-20250625203224991

readWithMessageConverters方法内部调用另一个readWithMessageConverters

image-20250625203305135

readWithMessageConverters内部调用getAdvice获取切面,然后调用切面的beforeBodyRead方法:

image-20250625203832000

beforeBodyRead方法内遍历各个RequestBodyAdvice,调用其supports方法:

image-20250625204027416

这里也就是CustomRequestBodyAdvice#supports被调用到的点。supports返回 true,所以下面马上调用到CustomRequestBodyAdvice#beforeBodyRead

image-20250625204349340

执行完beforeBodyRead之后,下一步就是执行afterBodyRead,类似地也是获取切面,调用其support方法,返回 true 则执行afterBodyRead

image-20250625204546817

可以看到调用afterBodyRead方法时已经完成了参数解析:

image-20250625204806363

afterBodyRead方法调用后,才进入到 Controller 方法:

image-20250625204912199

ResponseBodyAdvice

ResponseBodyAdvice是另一个对应的常见接口,用于在执行完@ResponseBodyResponseEntity控制器方法后,响应体被处理之前对 Controller 方法值进行自定义处理。

ResponseBodyAdvice (Spring Framework 6.2.8 API):Allows customizing the response after the execution of an @ResponseBody or a ResponseEntity controller method but before the body is written with an HttpMessageConverter.

下面是一个简单的示例:

import com.source.mybatis.entity.User;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        // 这里可以根据需要判断是否支持特定的响应
        return true; // 支持所有响应
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 在响应体写入之前,可以对其进行处理
        // 例如:添加额外的字段、修改数据格式等
        if (body instanceof User) {
            User user = (User) body;
            user.setName(user.getName() + " (处理后)");
        }
        return body; // 返回处理后的响应体
    }
}

同样还是在CustomResponseBodyAdvice中添加断点,调用前面的接口调试。

首先调用到的是supports方法:

image-20250625221320441
image-20250625221418797

先从ServletInvocableHandlerMethod#invokeAndHandle看起,可以看到其先调用前面提到的InvocableHandlerMethod#invokeForRequest得到 Controller 方法返回值,再调用HandlerMethodReturnValueHandlerComposite#handleReturnValue处理返回值:

image-20250625223504513

HandlerMethodReturnValueHandlerComposite#handleReturnValue方法内先调用selectHandler获取处理返回值的HandlerMethodReturnValueHandler实例,再调用其handleReturnValue方法,而且可以看到这里获取到的是RequestResponseBodyMethodProcessor实例,且returnValue即 Controller 方法的返回值:

image-20250625232136993

进入RequestResponseBodyMethodProcessor#handleReturnValue,其调用AbstractMessageConverterMethodProcessor#writeWithMessageConverters方法:

image-20250625232733655

writeWithMessageConverters方法内,也是调用getAdvice获取切面,然后调用切面的beforeBodyWrite方法:

image-20250625234114903

具体会调用到RequestResponseBodyAdviceChain#beforeBodyWrite方法,方法内部调用RequestResponseBodyAdviceChain#processBody方法:

image-20250625234215984

RequestResponseBodyAdviceChain#processBody方法内部,遍历ResponseBodyAdvice切面,调用切面的support方法,这里也就是断点的入口:

image-20250625234327517

如果support方法返回 true,则调用beforeBodyWrite方法,所以断点马上会进入beforeBodyWrite

image-20250625234722195
image-20250625234710484

如此由ResponseBodyAdvice处理完返回值之后,才会写入请求:

image-20250625235355891
posted @ 2025-06-26 00:04  Higurashi-kagome  阅读(386)  评论(0)    收藏  举报