参考文献 Springboot 之 HandlerMethodReturnValueHandler 运用简介 现在项目中大部分采用 - 掘金
在开发 RESTful API 时,为了确保接口返回数据的一致性,我们通常会采用统一的响应格式。例如:
{
"status": "success",
"message": "",
"data": { /* 业务数据 */ }
}
然而,传统的做法往往需要在每个控制器方法中手动封装 Result.success(data)
,这导致了大量的重复代码。今天,我们将探讨如何通过自定义 HandlerMethodReturnValueHandler
来实现返回值的自动封装,从而简化我们的工作流程,提高开发效率。
🎯问题背景:重复的返回值封装
在没有进行统一处理的情况下,我们的 Controller 方法可能会像这样编写:
@RequestMapping(value = "/getBaseInfo")
public Result<StaffInfo> getBaseInfo(SessMerchant sessMerchant, String id) {
Assert.notNull(id, "ID不能为空");
StaffInfo item = staffInfoService.getById(id);
if (null == item) {
Assert.isTrue(false, "ID错误");
}
return Result.success(item); // 每个方法都要手动封装
}
这种方法不仅繁琐,而且容易遗漏,增加了出错的风险。
二、解决方案:自定义 HandlerMethodReturnValueHandler
Spring MVC 提供了 HandlerMethodReturnValueHandler
接口,允许我们在控制器方法执行后、响应写出前,对返回值进行拦截和处理。我们可以利用它来自动包装返回结果。
1. 核心组件:HandlerMethodReturnValueHandlerProxy
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class HandlerMethodReturnValueHandlerProxy implements HandlerMethodReturnValueHandler {
private final HandlerMethodReturnValueHandler proxyObject;
public HandlerMethodReturnValueHandlerProxy(HandlerMethodReturnValueHandler proxyObject) {
this.proxyObject = proxyObject;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 仅代理支持 @ResponseBody 的处理器
return proxyObject.supportsReturnType(returnType);
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String uri = request.getRequestURI(); // 可用于日志或特殊处理
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("status", "success");
resultMap.put("message", "");
resultMap.put("data", returnValue); // 将原始返回值包装进data字段
// 继续交给原始处理器(如 RequestResponseBodyMethodProcessor)处理
proxyObject.handleReturnValue(resultMap, returnType, mavContainer, webRequest);
}
private static final String STATUS_CODE_SUCCEEDED = "success";
}
🔍 解析:
- 构造函数注入原始处理器:保存原始的
HandlerMethodReturnValueHandler
(如RequestResponseBodyMethodProcessor
),用于最终序列化。 supportsReturnType
:判断是否支持当前返回类型,直接委托给原始处理器。handleReturnValue
:这是核心逻辑,在这里将原始返回值returnValue
包装成我们期望的统一结构{status, message, data}
,然后再交给原始处理器进行JSON序列化。
2. 配置类:替换默认处理器链
import com.atguigu.common.utils.aspect.HandlerMethodReturnValueHandlerProxy;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestReturnValueHandlerConfigurer implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodReturnValueHandler> originalHandlers = handlerAdapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();
for (HandlerMethodReturnValueHandler handler : originalHandlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
// 对支持 @ResponseBody 的处理器进行代理包装
newHandlers.add(new HandlerMethodReturnValueHandlerProxy(handler));
} else {
newHandlers.add(handler);
}
}
// 替换原有的处理器链
handlerAdapter.setReturnValueHandlers(newHandlers);
}
}
🔍 关键点解析
@Autowired RequestMappingHandlerAdapter
:这是Spring MVC处理请求的核心组件之一,管理着所有HandlerMethodReturnValueHandler
。afterPropertiesSet
:在Bean初始化完成后执行,确保handlerAdapter
已注入。- 只代理
RequestResponseBodyMethodProcessor
:该处理器负责处理带有@ResponseBody
或@RestController
的返回值并进行JSON转换,是我们关注的重点。 - 保留其他处理器:如视图解析、
ModelAndView
等不受影响。
三、效果对比:从繁琐到简洁
✅ 改造前(需手动封装)
@RequestMapping("/getBaseInfo")
public Result<StaffInfo> getBaseInfo(String id) {
StaffInfo item = staffInfoService.getById(id);
return Result.success(item); // 每次都要写
}
✅ 改造后(自动封装)
@RequestMapping("/getBaseInfo")
public StaffInfo getBaseInfo(String id) {
Assert.notNull(id, "ID不能为空");
StaffInfo item = staffInfoService.getById(id);
if (item == null) throw new IllegalArgumentException("ID错误");
return item; // 直接返回业务对象,框架自动包装
}
响应结果自动变为:
{
"status": "success",
"message": "",
"data": {
"id": "123",
"name": "张三"
}
}
可进一步优化,仅对特定注解(如
@RestResult
)的方法进行包装,避免影响所有接口。
如果你的项目正在使用Spring Boot构建REST API,强烈建议引入此类统一返回值处理机制,让代码更干净!