springboot2 - HandlerMethodReturnValueHandler
业务需求
对于 Controller 返回值,一般需要进行二次打包。
比如:code 是结果码(0 表示成功,其它表示异常...),data 为携带数据
{
"code": "1",
"data": {
"name": "xiaoming",
"age": "30"
}
}
简介
HandlerMethodReturnValueHandler
这个接口的名字有点怪,处理函数返回值的处理器?姑且叫他返回值处理器吧,
使用这个接口需要注意,如果 Controller 没有返回值时,这个接口的代码就不会执行。
(因为存在另类的写法,Controller 的返回值不一定是 return 回去的,可以用类似于 ModuleAndView 之类的对象传递)
springboot 环境下,推荐使用 ResponseBodyAdvice,能起到相同效果。
代码样例:
import com.alibaba.druid.support.json.JSONUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.annotation.ResponseBody;
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.HttpServletResponse;
/**
* Created by 12614 on 2018/5/11.
*/
public class Example implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter methodParameter) {
return methodParameter.hasMethodAnnotation(ResponseBody.class);
}
@Override
public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest) throws Exception {
//TODO .setRequestHandled(true)表示此函数可以处理请求,不必交给别的代码处理
//E.G.
modelAndViewContainer.setRequestHandled(true);
nativeWebRequest
.getNativeResponse(HttpServletResponse.class)
.getWriter()
.write(JSONUtils.toJSONString(o));
}
}
Spring配置
传统 spring 环境下配置非常简单,在 xml 配置文件中,加这么几行就能启用生效了。
<mvc:annotation-driven>
<mvc:return-value-handlers>
<bean class="xxxx全类名"></bean>
</mvc:return-value-handlers>
</mvc:annotation-driven>
SpringBoot配置
在 springboot 环境下,这个过程就变得很复杂。
因为在 springboot 下,默认的 ResponseBody 处理类就是 HandlerMethodReturnValueHandler,默认处理器的优先级比我们新添加的高,
所以在 WebMvcConfigurerAdapter 中配置 HandlerMethodReturnValueHandler,代码其实并不生效,
注册方式如下:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
/**
* Created by 12614 on 2018/5/11.
*/
@Configuration
public class ApplicationConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
super.addReturnValueHandlers(returnValueHandlers);
returnValueHandlers.add(new Example());
}
}
因为 RequestResponseBodyMethodProcessor 是 ResponseBody 的默认处理程序,不是十分清楚它的作用,因此要避免完全替换这个类,
我们可以建立一个代理类,在代码中植入一部分我们的代码,代码如下:
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.util.HashMap;
import java.util.Map;
/**
* Created by 12614 on 2018/5/11.
*/
public class TestReturnValueHandler implements HandlerMethodReturnValueHandler {
private RequestResponseBodyMethodProcessor target;
public TestReturnValueHandler(RequestResponseBodyMethodProcessor target) {
this.target = target;
}
@Override
public boolean supportsReturnType(MethodParameter methodParameter) {
System.out.println("TestReturnValueHandler:supportsReturnType");
return methodParameter.hasMethodAnnotation(ResponseBody.class);
}
@Override
public void handleReturnValue(Object o, MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest) throws Exception {
System.out.println("TestReturnValueHandler:handleReturnValue");
Map<String,Object> res = new HashMap<>();
res.put("code","1");
res.put("data",o);
target.handleReturnValue(res,methodParameter,modelAndViewContainer,nativeWebRequest);
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return target.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
注册到 spring 容器
替换默认的 HandlerMethodReturnValueHandler,需要找一个合适的时机,
InitializingBean 在执行完所有属性设置方法(即setXxx)之后被自动调用,满足我们的需求,在 InitializingBean 中编辑我们的代码即可,代码如下:
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;
/**
* 初始化切面
*
* Created by 12614 on 2018/5/11.
*/
@Configuration
public class InitializingAdvice implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter adapter;
@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers);
this.decorateHandlers(handlers);
adapter.setReturnValueHandlers(handlers);
}
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
TestReturnValueHandler decorator = new TestReturnValueHandler(
(RequestResponseBodyMethodProcessor) handler);
int index = handlers.indexOf(handler);
handlers.set(index, decorator);
break;
}
}
}
}