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;
            }
        }
    }
}

posted on 2018-05-27 11:30  疯狂的妞妞  阅读(7029)  评论(0编辑  收藏  举报

导航