写在前面的话
前几篇博文,大致了解了SpringMVC
请求流程中的入参和出参处理环节,后续的几篇博文,会将流程中涉及的若干关键环节单独拿出来讲解,并结合实战中的运用,帮助领略SpringMVC
带来的定制和扩展能力。
本篇文章先介绍一下返回值处理器相关内容。
学前准备与回顾
本篇为 SpringMVC 源码分析系列文章,正片开始前,先总结回顾一下全流程。
【一次请求的主链路节点】
DispatcherServlet#doDispatch(入口方法)
DispatcherServlet#getHandler(根据path
找到对应的HandlerExecutionChain
)
DispatcherServlet#getHandlerAdapter(根据handle
找到对应的HandlerAdapter
)
HandlerExecutionChain#applyPreHandle(触发拦截器的前置逻辑)
AbstractHandlerMethodAdapter#handle(核心逻辑)
HandlerExecutionChain#applyPostHandle(触发拦截器的后置逻辑)
【核心handle方法的主链路节点】
RequestMappingHandlerAdapter#handleInternal(入口方法)
RequestMappingHandlerAdapter#invokeHandlerMethod(入口方法2)
ServletInvocableHandlerMethod#invokeAndHandle(入口方法3)
InvocableHandlerMethod#invokeForRequest(参数和实际执行的所在,3.1)
InvocableHandlerMethod#getMethodArgumentValues(参数处理,3.1.1)
InvocableHandlerMethod#doInvoke(实际执行,3.1.2)
HandlerMethodReturnValueHandlerComposite#handleReturnValue(返回处理,3.2)
【针对 @RequestBody 和 @ResponseBody 场景】
相关博文
《学会 SpringMVC 系列 · 基础篇》
《学会 SpringMVC 系列 · 剖析篇(上)》
《学会 SpringMVC 系列 · 剖析入参处理》
《学会 SpringMVC 系列 · 剖析出参处理》
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》
SpringMVC 返回值处理器
技术说明
HandlerMethodReturnValueHandler 是 Spring MVC 中的一个接口,用于处理控制器方法的返回值。它是处理返回值与 HTTP 响应之间关系的核心机制。通过自定义 HandlerMethodReturnValueHandler,可以实现特定返回值类型的处理逻辑,例如格式化输出、包装响应数据等。
接口信息
HandlerMethodReturnValueHandler 接口定义了两个主要方法:
boolean supportsReturnType(MethodParameter returnType):支持处理给定返回值类型的处理器方法。
void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest):处理实际的返回值。
MethodParameter:封装方法参数或方法返回值的元数据,包括类型、注解等信息。
ModelAndViewContainer:用于存储处理器方法的返回值以及视图名称或 View 对象。
NativeWebRequest:封装当前 HTTP 请求和响应。
常见列表
RequestResponseBodyMethodProcessor – 处理 @ResponseBody
ViewNameMethodReturnValueHandLer – 处理返回 String(视图)
自定义 HandlerMethodReturnValueHandler – 包装返回值输出
应用场景
统一响应格式:可以用于统一返回格式,将所有返回值封装为特定的响应对象。
数据包装:在返回数据之前,进行某些特定的包装操作,如添加元数据、状态码等。
权限过滤:在返回之前,检查并过滤掉用户无权限访问的数据。
日志记录:对返回的数据进行记录,用于审计或分析。
自定义示例
代码:study-up#MyHandlerMethodReturnValueHandler
逻辑:对添加了自定义注解的接口,包装上一层实体返回。
实现步骤:
Step1、添加自定义返回值处理器
public class MyHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResultModelAnnotation.class) || returnType.hasMethodAnnotation(ResultModelAnnotation.class));
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
ResultModel<Object> resultModel;
String message = "";
Operation methodAnnotation = returnType.getMethodAnnotation(Operation.class);
if (methodAnnotation != null) {
message = methodAnnotation.summary() + "成功";
}
if (returnValue instanceof ResultModel) {
resultModel = (ResultModel<Object>) returnValue;
if (!resultModel.isSuccess()) {
resultModel.setMessage(message + "失败");
}
} else {
resultModel = ResultModel.success(returnValue, message);
}
mavContainer.setRequestHandled(true);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (response != null) {
response.setStatus(HttpStatus.OK.value());
response.setHeader("result-model", "true");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
try (PrintWriter writer = response.getWriter()) {
writer.write(JSON.toJSONString(resultModel, SerializerFeature.WriteMapNullValue));
writer.flush();
// 设置请求处理已完成,防止Spring继续处理返回值
mavContainer.setRequestHandled(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
Step2、配置返回值处理器
通过自定义 RequestMappingHandlerAdapter 的方式实现效果。
public class CustomResultMappingHandlerAdapter extends RequestMappingHandlerAdapter {
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
List<HandlerMethodReturnValueHandler> returnValueHandlers = super.getReturnValueHandlers();
MyHandlerMethodReturnValueHandler handler = new MyHandlerMethodReturnValueHandler();
List<HandlerMethodReturnValueHandler> list = new ArrayList<>();
list.add(handler);
list.addAll(returnValueHandlers);
super.setReturnValueHandlers(list);
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public WebMvcRegistrations feignWebRegistrations() {
return new WebMvcRegistrations() {
@Override
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return new CustomResultMappingHandlerAdapter();
}
};
}
}
Step3、测试效果
正常书写Controller,该处理器会包裹ResultModel返回。
@RestController
@ResultModelAnnotation
@Tag(name = "教师信息控制层", description = "教师信息")
@RequestMapping(value = "/zyTeacherInfo")
public class ZyTeacherInfoController extends BaseController {
@Autowired
private ZyTeacherInfoService zyTeacherInfoService;
@GetMapping("/{id}")
public ZyTeacherInfo get(@PathVariable String id) {
return zyTeacherInfoService.getById(id);
}
}
返回结果如下:
{
"code": "00000",
"data": {
"createdTime": "2024-05-16 20:07:21",
"modifiedTime": "2024-07-29 14",
"sortNo": null,
"stuItem": null,
"teaCode": "1",
"teaConfig": null,
"teaImg": null,
"teaName": "张老师",
"teaPhone": null,
"teaType": null,
"validFlag": "1"
},
"error": "",
"message": "获取教师信息表详细信息成功",
"success": true
}
观察源码可以看到,这里走的是自定义的返回值处理器,细节就不展开了。
RequestResponseBodyMethodProcessor
前面源码分析章节,可以看到,参数解析和返回值解析中,都用到了 RequestResponseBodyMethodProcessor,那它为何可以如此呢?其实看一下类图就明白了。
既实现了HandlerMethodReturnValueHandler接口,也实现了HandlerMethodArgumentResolver,看它的匹配方法也可以看出来,就是针对 @RequestBody 和 @ResponseBody 的处理器。
总结陈词
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。