/**
-
Allows customizing the response after the execution of an {@code @ResponseBody}
-
or a {@code ResponseEntity} controller method but before the body is written
-
with an {@code HttpMessageConverter}.
-
<p>Implementations may be registered directly with
-
{@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
-
or more likely annotated with {@code @ControllerAdvice} in which case they
-
will be auto-detected by both.
-
@author Rossen Stoyanchev
-
@since 4.1
-
@param <T> the body type */ public interface ResponseBodyAdvice<T> {
/**
- Whether this component supports the given controller method return type
- and the selected {@code HttpMessageConverter} type.
- @param returnType the return type
- @param converterType the selected converter type
- @return {@code true} if {@link #beforeBodyWrite} should be invoked;
- {@code false} otherwise */ boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
/**
- Invoked after an {@code HttpMessageConverter} is selected and just before
- its write method is invoked.
- @param body the body to be written
- @param returnType the return type of the controller method
- @param selectedContentType the content type selected through content negotiation
- @param selectedConverterType the converter type selected to write to the response
- @param request the current request
- @param response the current response
- @return the body that was passed in or a modified (possibly new) instance */ @Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
} 解释一下,什么意思?
这是一个 Spring MVC 框架中的核心接口 ResponseBodyAdvice<T>,它的主要目的是让你能够在控制器方法返回响应体(@ResponseBody 或 ResponseEntity)之后、但在 HTTP 消息转换器(HttpMessageConverter)将响应体写入 HTTP 响应流之前,对响应体进行自定义处理。
核心功能:拦截并修改响应体
想象一下控制器方法执行完毕,它返回了一个对象(响应体)。Spring 会选择一个合适的 HttpMessageConverter(比如 MappingJackson2HttpMessageConverter 用于 JSON)来把这个对象转换成 HTTP 响应的字节流。ResponseBodyAdvice 就在这个转换发生之前介入,给你一个机会检查和修改这个响应体对象。
关键点解释:
-
触发时机:
- 在控制器方法成功执行并返回一个
@ResponseBody标记的返回值时。 - 在控制器方法返回一个
ResponseEntity对象时。 - 在 选定的
HttpMessageConverter调用其write()方法将响应体对象转换成字节流并写入 HTTP 响应之前。
- 在控制器方法成功执行并返回一个
-
两个核心方法:
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType)- 作用: 判断这个
ResponseBodyAdvice实现是否应该对当前请求的响应进行处理。 - 参数:
returnType: 控制器方法的返回类型信息(包含方法本身、返回的泛型类型等)。converterType: Spring 为当前响应选择的HttpMessageConverter的类型。
- 返回值:
true表示这个 Advice 适用于当前响应,接下来会调用beforeBodyWrite;false表示不适用,跳过处理。 - 应用场景: 你可以在这里根据控制器方法的返回类型(比如只处理某个特定类或注解标记的方法)、或者根据选定的消息转换器类型(比如只处理 JSON 响应)来决定是否启用处理逻辑。
- 作用: 判断这个
@Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response)- 作用: 这是实际执行响应体修改逻辑的地方。它在
supports返回true后被调用。 - 参数:
body: 控制器方法返回的原始响应体对象(可能为null)。returnType: 同上。selectedContentType: 通过内容协商最终确定的响应的Content-Type(如application/json)。selectedConverterType: 最终被选用来写入响应的HttpMessageConverter的类型。request: 当前的 HTTP 请求对象。response: 当前的 HTTP 响应对象(你可以在这里设置响应头等,但主要操作对象是body)。
- 返回值: 这个方法应该返回最终要写入 HTTP 响应的响应体对象。它可以是:
- 原始的
body对象(不做修改)。 - 修改后的
body对象(例如,添加字段、包装结构)。 - 一个全新的对象(完全替换原始响应体)。
null(如果确实需要返回空响应体)。
- 原始的
- 应用场景: 这是你实现自定义逻辑的核心位置。
- 作用: 这是实际执行响应体修改逻辑的地方。它在
-
注册方式:
- 直接注册: 可以将你的
ResponseBodyAdvice实现直接配置到RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver中(较少用)。 - 自动检测(推荐): 更常见的是将你的实现类用
@ControllerAdvice(或@RestControllerAdvice)注解标记。Spring 会自动扫描并注册这些类中的ResponseBodyAdvicebean,使其对所有符合条件的控制器方法生效。这是实现全局响应处理的推荐方式。
- 直接注册: 可以将你的
典型应用场景:
- 统一响应包装: 这是最常见的用途。例如,你可能希望所有成功的 JSON 响应都被包装成一个标准结构:
在JSON
{ "code": 200, "message": "Success", "data": { ... } // 原始控制器返回的对象放在这里 }beforeBodyWrite中,你可以获取原始的body(即data部分),创建一个新的ResponseWrapper对象(包含 code, message, data 属性),将原始body放入data中,然后返回这个ResponseWrapper对象。 - 全局响应日志: 记录即将返回给客户端的响应体内容(注意敏感信息过滤)。
- 响应体加密: 在将特定类型的数据发送给客户端前进行加密。
- 响应体压缩预处理(较少见): 在消息转换器处理前对数据进行特定处理(但通常压缩由其他机制处理)。
- 响应头设置: 虽然主要操作
body,但也可以通过传入的response参数设置一些基于响应体内容的响应头(但更常见的响应头设置可能在拦截器或控制器内完成)。 - 统一处理
null响应: 将控制器返回的null转换成有意义的空对象表示。
总结:
ResponseBodyAdvice<T> 是 Spring MVC 提供的一个强大的扩展点,允许你在 HTTP 响应体被序列化并发送给客户端之前的最后时刻,对响应体进行拦截和修改。它通常与 @ControllerAdvice 结合使用,以实现全局性、非侵入式的响应处理逻辑(如统一响应包装),极大地提高了代码的复用性和可维护性。它的工作流程由 supports 进行条件判断,由 beforeBodyWrite 执行具体的修改操作。
Advice 是什么意思?
在编程领域,尤其是 Spring 框架 和 AOP(面向切面编程) 的上下文中,“Advice” 这个词有其特定的含义。它不是指日常生活中的“建议”,而是指在程序执行的特定时机插入的代码逻辑。
你可以把它理解为:“增强” 或 “通知”。
核心概念:
想象程序执行就像一条直线(主流程)。AOP 允许你在某些特定的点(这些点称为 Join Point - 连接点,比如方法调用前、方法调用后、方法抛出异常时等)插入额外的代码。这些插入的代码片段就叫做 Advice(增强/通知)。
在 ResponseBodyAdvice 中的具体含义:
ResponseBodyAdvice这个接口的名字本身就体现了它的作用:它提供了一种机制,让你能够在响应体(Response Body)即将被写入 HTTP 响应流之前这个特定时机,插入自定义的处理逻辑。- 这里的 “Advice” 指的就是
beforeBodyWrite方法里你编写的代码。这个方法就是 Spring 框架提供的一个“钩子”(hook),让你有机会在关键时刻“增强”或“通知”响应处理过程。 - 它本质上是一种拦截器或后处理器,作用于控制器方法返回响应体之后、消息转换器写入响应体之前这个环节。
类比理解:
- 日常生活: 想象你要寄出一封信(主流程)。在把信投进邮筒(最终发送)之前,你可能会请一位顾问(Advisor)检查一下信的内容(Advice)。这位顾问可能会建议你修改措辞(修改响应体
body),或者加个附件(包装响应体),或者告诉你这封信根本不适合寄出(返回null或抛出异常)。ResponseBodyAdvice就是 Spring 框架提供的这样一个“顾问”机制。 - AOP 术语: 在 AOP 中,常见的 Advice 类型有:
Before advice: 在连接点(如方法调用)之前执行。After returning advice: 在连接点成功完成之后执行(方法正常返回)。After throwing advice: 在连接点抛出异常后执行。After (finally) advice: 无论连接点如何结束(正常返回或异常)之后执行。Around advice: 环绕连接点执行,可以在方法调用前后都加入逻辑,甚至可以控制是否执行原方法。ResponseBodyAdvice的beforeBodyWrite方法类似于一种特殊的Around advice,它环绕着“将响应体对象写入 HTTP 响应流”这个动作。
总结:
在 ResponseBodyAdvice 以及 Spring AOP 中,“Advice” 指的是:
- 一段代码: 在程序执行的特定时机(连接点)被自动执行的代码块。
- 一种增强: 用于修改、扩展或补充程序核心流程(如响应处理)的行为。
- 一种通知: 它“通知”框架在某个点执行你的自定义逻辑。
因此,ResponseBodyAdvice 接口的作用就是:提供一个在响应体写入响应流之前执行自定义逻辑(Advice)的扩展点。 它的名字非常贴切地反映了它的功能。其他类似的接口如 RequestBodyAdvice 也是同样的命名逻辑。