这就解释了为什么实现 ResponseBodyAdvice
接口的子类一定要与@ControllerAdvice
一起使用的原因了。
接下来我们来看下 ResponseBodyAdvice
的执行流程。
这里教给大家一个代码调试的小技巧,当我们不知道一个类在源码中如何被调用的时候,我们可以使用 IDEA 代码调试功能,然后查看代码调用栈。
如上面的所示,我们可以很清楚观察 ResponseBodyAdvice
调用关系。这里的类调用关系相对还是比较复杂,下面给大家简化一下。
前面的逻辑就不说了,就是 Spring MVC 通用流程。重点逻辑位于 RequestResponseBodyAdviceChain
,我们具体看下源码:
嗯呐嗯呐,请忽略上图的 ③
其实逻辑非常简单,遍历所有的 ResponseBodyAdvice
的子类,首先调用其 supports
判断是否支持,如果支持的调用的 beforeBodyWrite
修改返回信息。
Filter
、Interceptor
、ResponseBodyAdvice
区别
Filter
属于 Servlet 组件,所有请求将会先进入 Filter
,判断通过之后才会在进入到真正的具体的请求中。
上图代表是用 Spring MVC 的一个 Web 项目,所有请求将会先进入到 Filter
,通过之后才会进入到 SpringMVC 中最重要的组件 DispatchServlet
。
而 Interceptor
是 SpringMVC 的组件,它的作用实际上与 Filter
类似, 只不过的它的作用是位于自定义的 Controller
前后。
不管是 Filter
还是 Interceptor
,它们的作用方法域内只能拿到 ServletResponse
的参数,这个时候返回值已经被写入 ServletResponse
,我们很难再去修改。
而 ResponseBodyAdvice
作用时机位于写入之前,所以这个时候可以很容易拿到原值进行修改。
总结
SpringMVC 初始化的过程中,将会扫描所有带有 @ControllerAdvice
注解的类,将其生成为 ControllerAdviceBean
。如果这类刚好为 ResponseBodyAdvice
接口的子类,Spring 将会为其单独保存起来,后续将会封装到的 RequestResponseBodyAdviceChain
,使用责任链的模式对请求、响应进行处理。
最后我们解释了一下 Filter
,Interceptor
,ResponseBodyAdvice
区别,从作用范围上来讲:
Filter>Interceptor>ResponseBodyAdvice
但是前两者没办法修改返回值(时机太晚),只有后者才可以真正在返回值返回之前做到修改。
好了,今天文章就到这里了,下次我们分享一下如何写出优雅的 Dubbo 接口,下次见。