SpringBootHandlerMethodReturnValueHandler

Posted 疯狂的妞妞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBootHandlerMethodReturnValueHandler相关的知识,希望对你有一定的参考价值。

你可能会有这样的需求,对你的Controller返回值进行一个二次封装,
如下:code是结果码(1、成功,0、失败,2、未登录...),data为携带数据

{"code":"1","data":{"name":"xiaoming","age":"30"}}


在代码中的使用效果如下,添加一个自定义注解ResponseData,自动地就把数据封装成上面的格式

@Controller
public class Core {

    @ResponseData
    @RequestMapping("/data")
    public Map<String, Object> data() {
        Map<String,Object> res = new HashMap<>();
        res.put("name","xiaoming");
        res.put("age","30");
        return res;
    }
}

HandlerMethodReturnValueHandler

这个接口的名字有点怪,处理函数返回值的处理器?姑且叫他返回值处理器吧,

这是一个处理Controller返回值的接口,比如我们熟悉的ResponseBody注解,使用下面的代码就能简单地实现,
这个切面的缺点是:如果函数没有返回值时,这个接口的代码就不会执行,如果经常使用ModelAndView传递返回值,用这个接口会觉得不习惯。

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配置

<mvc:annotation-driven>  
    <mvc:return-value-handlers>  
        <bean class="xxxx全类名"></bean>  
    </mvc:return-value-handlers>  
</mvc:annotation-driven>

SpringBoot配置

因为在SpringBoot下,默认的ResponseBody处理类就是HandlerMethodReturnValueHandler,此处理器的优先级比我们新添加的实现类高,

WebMvcConfigurerAdapter 中配置HandlerMethodReturnValueHandler,代码其实是并不生效,

如果你希望添加额外的返回值处理器,需要做的是改造默认的处理类(如果直接替换默认的处理类,但是原先的ResponseBody的处理逻辑将出错)。

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");
        //我添加了一个自定义注解ResponseData,不知道怎么写可以直接复制ResponseBody源码
        return methodParameter.hasMethodAnnotation(ResponseData.class)||
                methodParameter.hasMethodAnnotation(ResponseBody.class);
    }

    @Override
    public void handleReturnValue(Object o, MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest) throws Exception {
        System.out.println("TestReturnValueHandler:handleReturnValue");
        if(methodParameter.hasMethodAnnotation(ResponseData.class)){
            //如果Controller中使用了我的自定义注解,那么对返回值进行封装
            Map<String,Object> res = new HashMap<>();
            res.put("code","1");
            res.put("data",o);
            target.handleReturnValue(res,methodParameter,modelAndViewContainer,nativeWebRequest);
        } else {
            target.handleReturnValue(o,methodParameter,modelAndViewContainer,nativeWebRequest);
        }
    }

    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return target.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
}

替换SpringBoot中HandlerMethodReturnValueHandler的默认实现类,需要在Spring中找到合适的切面,

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

 









以上是关于SpringBootHandlerMethodReturnValueHandler的主要内容,如果未能解决你的问题,请参考以下文章