版本说明
jdk
:1.8.0_131
springboot
:2.1.6.RELEAS
maven
:3.6.1
database
:mysql-5.7.14
lombok插件
源码分析
省略构建项目,junit 测试等步骤,只分析代码与实现。 根据spring静态资源加载源码浅析中的分析,我们可以知道 DispatcherServlet
会拦截所有请求,寻找合适的mappedHandler
去处理请求,并根据mappedHandler
去找对应的适配器HandlerAdapter
来实际请求controller
的方法,针对接口来说一般使用的是RequestMappingHandlerAdapter
具体调用controller
方法的细节我们不需要关注,这里我们仅仅关注RequestMappingHandlerAdapter
是如何处理方法的返回值的。 节选部分DispatcherServlet
的doDispatch
方法
1 | // 根据mappedHandler查找合适的适配器 |
RequestMappingHandlerAdapter
的handle
方法调用了内部的handleInternal
方法
1 |
|
我们再查看invokeHandlerMethod
实现细节
1 | protected ModelAndView invokeHandlerMethod(HttpServletRequest request, |
我们查看下invocableMethod.invokeAndHandle
的细节
1 | public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, |
this.returnValueHandlers.handleReturnValue
的实现细节类HandlerMethodReturnValueHandlerComposite
1 |
|
默认的返回值处理器有以下
我们查看下典型的@ResponseBody
的处理器RequestResponseBodyMethodProcessor
的方法supportsReturnType
就明白selectHandler
是如何起作用的
1 |
|
因为我们自定义的处理器是模仿@ResponseBody
,那么我们只需要在returnValueHandlers
中RequestResponseBodyMethodProcessor
位置处插入我们自定义的处理器即可
那么首先我们需要了解下HandlerMethodReturnValueHandlerComposite
的属性returnValueHandlers
是如何被加载赋值的,通过查看调用关系,我们发现 returnValueHandlers 赋值的方法为addHandlers
,此方法被两处调用
第一处,这里是加载 bean 时的初始化方法,即默认returnValueHandlers
为getDefaultReturnValueHandlers
的返回值
1 |
|
第二处
1 | public void setReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) { |
明显我们无法改变afterPropertiesSet
的实现细节,那么继承WebMvcConfigurationSupport
,重写RequestMappingHandlerAdapter
方法,手动调用setReturnValueHandlers
方法即可注入我们自定义的处理器。 但是我们需要取出默认的返回值处理器,避免其他返回值处理器不起作用,getDefaultReturnValueHandlers
是私有方法,所以我们需要使用反射取值。然后将自定义处理器插入到RequestResponseBodyMethodProcessor
之前即可, 这种方式会使@ControllerAdvice
失效,慎用,更好的方式通过@ControllerAdvice
实现同样的效果
1 | package com.li.springboot.config; |
实现后我们可以看到返回值处理器的集合变化