SpringBoot(四)HandlerMethodReturnValueHandler


你可能會有這樣的需求,對你的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注解,使用下面的代碼就能簡單地實現,
這個切面的缺點是:如果函數沒有返回值時,這個接口的代碼就不會執行,如果通過其他方式傳遞返回值,用這個接口會覺得不習慣。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM