📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
文章目录
写在前面的话
前几篇博文,大致了解了
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、添加自定义返回值处理器
publicclassMyHandlerMethodReturnValueHandlerimplementsHandlerMethodReturnValueHandler{@OverridepublicbooleansupportsReturnType(MethodParameter returnType){return(AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ResultModelAnnotation.class)|| returnType.hasMethodAnnotation(ResultModelAnnotation.class));}@OverridepublicvoidhandleReturnValue(Object returnValue,MethodParameter returnType,ModelAndViewContainer mavContainer,NativeWebRequest webRequest)throwsIOException,HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException{ResultModel<Object> resultModel;String message ="";Operation methodAnnotation = returnType.getMethodAnnotation(Operation.class);if(methodAnnotation !=null){
message = methodAnnotation.summary()+"成功";}if(returnValue instanceofResultModel){
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、配置返回值处理器
Tips:使用addReturnValueHandlers方式要注意顺序的影响。
通过自定义 RequestMappingHandlerAdapter 的方式实现效果。
publicclassCustomResultMappingHandlerAdapterextendsRequestMappingHandlerAdapter{@OverridepublicvoidafterPropertiesSet(){super.afterPropertiesSet();List<HandlerMethodReturnValueHandler> returnValueHandlers =super.getReturnValueHandlers();MyHandlerMethodReturnValueHandler handler =newMyHandlerMethodReturnValueHandler();List<HandlerMethodReturnValueHandler> list =newArrayList<>();
list.add(handler);
list.addAll(returnValueHandlers);super.setReturnValueHandlers(list);}}
@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@BeanpublicWebMvcRegistrationsfeignWebRegistrations(){returnnewWebMvcRegistrations(){@OverridepublicRequestMappingHandlerAdaptergetRequestMappingHandlerAdapter(){returnnewCustomResultMappingHandlerAdapter();}};}}
Step3、测试效果
正常书写Controller,该处理器会包裹ResultModel返回。
@RestController@ResultModelAnnotation@Tag(name ="教师信息控制层", description ="教师信息")@RequestMapping(value ="/zyTeacherInfo")publicclassZyTeacherInfoControllerextendsBaseController{@AutowiredprivateZyTeacherInfoService zyTeacherInfoService;@GetMapping("/{id}")publicZyTeacherInfoget(@PathVariableString 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 的处理器。
总结陈词
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。
版权归原作者 战神刘玉栋 所有, 如有侵权,请联系我们删除。